• 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/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 
Peelingv8::internal::compiler::Peeling110   Peeling(Graph* graph, Zone* tmp_zone, size_t max, NodeVector* p)
111       : node_map(graph, static_cast<uint32_t>(max)), pairs(p) {}
112 
mapv8::internal::compiler::Peeling113   Node* map(Node* node) {
114     if (node_map.Get(node) == 0) return node;
115     return pairs->at(node_map.Get(node));
116   }
117 
Insertv8::internal::compiler::Peeling118   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 
CopyNodesv8::internal::compiler::Peeling124   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 
Markedv8::internal::compiler::Peeling148   bool Marked(Node* node) { return node_map.Get(node) > 0; }
149 };
150 
151 
152 class PeeledIterationImpl : public PeeledIteration {
153  public:
154   NodeVector node_pairs_;
PeeledIterationImpl(Zone * zone)155   explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {}
156 };
157 
158 
map(Node * node)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 
CanPeel(LoopTree * loop_tree,LoopTree::Loop * loop)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 
Peel(Graph * graph,CommonOperatorBuilder * common,LoopTree * loop_tree,LoopTree::Loop * loop,Zone * tmp_zone)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 
PeelInnerLoops(Graph * graph,CommonOperatorBuilder * common,LoopTree * loop_tree,LoopTree::Loop * loop,Zone * temp_zone)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 
EliminateLoopExit(Node * node)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
PeelInnerLoopsOfTree(Graph * graph,CommonOperatorBuilder * common,LoopTree * loop_tree,Zone * temp_zone)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
EliminateLoopExits(Graph * graph,Zone * temp_zone)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