• 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 
7 #include "src/compiler/common-operator.h"
8 #include "src/compiler/compiler-source-position-table.h"
9 #include "src/compiler/graph.h"
10 #include "src/compiler/loop-analysis.h"
11 #include "src/compiler/node-marker.h"
12 #include "src/compiler/node-origin-table.h"
13 #include "src/compiler/node-properties.h"
14 #include "src/compiler/node.h"
15 #include "src/zone/zone.h"
16 
17 // Loop peeling is an optimization that copies the body of a loop, creating
18 // a new copy of the body called the "peeled iteration" that represents the
19 // first iteration. Beginning with a loop as follows:
20 
21 //             E
22 //             |                 A
23 //             |                 |                     (backedges)
24 //             | +---------------|---------------------------------+
25 //             | | +-------------|-------------------------------+ |
26 //             | | |             | +--------+                    | |
27 //             | | |             | | +----+ |                    | |
28 //             | | |             | | |    | |                    | |
29 //           ( Loop )<-------- ( phiA )   | |                    | |
30 //              |                 |       | |                    | |
31 //      ((======P=================U=======|=|=====))             | |
32 //      ((                                | |     ))             | |
33 //      ((        X <---------------------+ |     ))             | |
34 //      ((                                  |     ))             | |
35 //      ((     body                         |     ))             | |
36 //      ((                                  |     ))             | |
37 //      ((        Y <-----------------------+     ))             | |
38 //      ((                                        ))             | |
39 //      ((===K====L====M==========================))             | |
40 //           |    |    |                                         | |
41 //           |    |    +-----------------------------------------+ |
42 //           |    +------------------------------------------------+
43 //           |
44 //          exit
45 
46 // The body of the loop is duplicated so that all nodes considered "inside"
47 // the loop (e.g. {P, U, X, Y, K, L, M}) have a corresponding copies in the
48 // peeled iteration (e.g. {P', U', X', Y', K', L', M'}). What were considered
49 // backedges of the loop correspond to edges from the peeled iteration to
50 // the main loop body, with multiple backedges requiring a merge.
51 
52 // Similarly, any exits from the loop body need to be merged with "exits"
53 // from the peeled iteration, resulting in the graph as follows:
54 
55 //             E
56 //             |                 A
57 //             |                 |
58 //      ((=====P'================U'===============))
59 //      ((                                        ))
60 //      ((        X'<-------------+               ))
61 //      ((                        |               ))
62 //      ((   peeled iteration     |               ))
63 //      ((                        |               ))
64 //      ((        Y'<-----------+ |               ))
65 //      ((                      | |               ))
66 //      ((===K'===L'====M'======|=|===============))
67 //           |    |     |       | |
68 //  +--------+    +-+ +-+       | |
69 //  |               | |         | |
70 //  |              Merge <------phi
71 //  |                |           |
72 //  |          +-----+           |
73 //  |          |                 |                     (backedges)
74 //  |          | +---------------|---------------------------------+
75 //  |          | | +-------------|-------------------------------+ |
76 //  |          | | |             | +--------+                    | |
77 //  |          | | |             | | +----+ |                    | |
78 //  |          | | |             | | |    | |                    | |
79 //  |        ( Loop )<-------- ( phiA )   | |                    | |
80 //  |           |                 |       | |                    | |
81 //  |   ((======P=================U=======|=|=====))             | |
82 //  |   ((                                | |     ))             | |
83 //  |   ((        X <---------------------+ |     ))             | |
84 //  |   ((                                  |     ))             | |
85 //  |   ((     body                         |     ))             | |
86 //  |   ((                                  |     ))             | |
87 //  |   ((        Y <-----------------------+     ))             | |
88 //  |   ((                                        ))             | |
89 //  |   ((===K====L====M==========================))             | |
90 //  |        |    |    |                                         | |
91 //  |        |    |    +-----------------------------------------+ |
92 //  |        |    +------------------------------------------------+
93 //  |        |
94 //  |        |
95 //  +----+ +-+
96 //       | |
97 //      Merge
98 //        |
99 //      exit
100 
101 // Note that the boxes ((===)) above are not explicitly represented in the
102 // graph, but are instead computed by the {LoopFinder}.
103 
104 namespace v8 {
105 namespace internal {
106 namespace compiler {
107 
108 class PeeledIterationImpl : public PeeledIteration {
109  public:
110   NodeVector node_pairs_;
PeeledIterationImpl(Zone * zone)111   explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {}
112 };
113 
114 
map(Node * node)115 Node* PeeledIteration::map(Node* node) {
116   // TODO(turbofan): we use a simple linear search, since the peeled iteration
117   // is really only used in testing.
118   PeeledIterationImpl* impl = static_cast<PeeledIterationImpl*>(this);
119   for (size_t i = 0; i < impl->node_pairs_.size(); i += 2) {
120     if (impl->node_pairs_[i] == node) return impl->node_pairs_[i + 1];
121   }
122   return node;
123 }
124 
Peel(LoopTree::Loop * loop)125 PeeledIteration* LoopPeeler::Peel(LoopTree::Loop* loop) {
126   if (!CanPeel(loop)) return nullptr;
127 
128   //============================================================================
129   // Construct the peeled iteration.
130   //============================================================================
131   PeeledIterationImpl* iter = tmp_zone_->New<PeeledIterationImpl>(tmp_zone_);
132   uint32_t estimated_peeled_size = 5 + loop->TotalSize() * 2;
133   NodeCopier copier(graph_, estimated_peeled_size, &iter->node_pairs_, 1);
134 
135   Node* dead = graph_->NewNode(common_->Dead());
136 
137   // Map the loop header nodes to their entry values.
138   for (Node* node : loop_tree_->HeaderNodes(loop)) {
139     copier.Insert(node, node->InputAt(kAssumedLoopEntryIndex));
140   }
141 
142   // Copy all the nodes of loop body for the peeled iteration.
143   copier.CopyNodes(graph_, tmp_zone_, dead, loop_tree_->BodyNodes(loop),
144                    source_positions_, node_origins_);
145 
146   //============================================================================
147   // Replace the entry to the loop with the output of the peeled iteration.
148   //============================================================================
149   Node* loop_node = loop_tree_->GetLoopControl(loop);
150   Node* new_entry;
151   int backedges = loop_node->InputCount() - 1;
152   if (backedges > 1) {
153     // Multiple backedges from original loop, therefore multiple output edges
154     // from the peeled iteration.
155     NodeVector inputs(tmp_zone_);
156     for (int i = 1; i < loop_node->InputCount(); i++) {
157       inputs.push_back(copier.map(loop_node->InputAt(i)));
158     }
159     Node* merge =
160         graph_->NewNode(common_->Merge(backedges), backedges, &inputs[0]);
161 
162     // Merge values from the multiple output edges of the peeled iteration.
163     for (Node* node : loop_tree_->HeaderNodes(loop)) {
164       if (node->opcode() == IrOpcode::kLoop) continue;  // already done.
165       inputs.clear();
166       for (int i = 0; i < backedges; i++) {
167         inputs.push_back(copier.map(node->InputAt(1 + i)));
168       }
169       for (Node* input : inputs) {
170         if (input != inputs[0]) {  // Non-redundant phi.
171           inputs.push_back(merge);
172           const Operator* op = common_->ResizeMergeOrPhi(node->op(), backedges);
173           Node* phi = graph_->NewNode(op, backedges + 1, &inputs[0]);
174           node->ReplaceInput(0, phi);
175           break;
176         }
177       }
178     }
179     new_entry = merge;
180   } else {
181     // Only one backedge, simply replace the input to loop with output of
182     // peeling.
183     for (Node* node : loop_tree_->HeaderNodes(loop)) {
184       node->ReplaceInput(0, copier.map(node->InputAt(1)));
185     }
186     new_entry = copier.map(loop_node->InputAt(1));
187   }
188   loop_node->ReplaceInput(0, new_entry);
189 
190   //============================================================================
191   // Change the exit and exit markers to merge/phi/effect-phi.
192   //============================================================================
193   for (Node* exit : loop_tree_->ExitNodes(loop)) {
194     switch (exit->opcode()) {
195       case IrOpcode::kLoopExit:
196         // Change the loop exit node to a merge node.
197         exit->ReplaceInput(1, copier.map(exit->InputAt(0)));
198         NodeProperties::ChangeOp(exit, common_->Merge(2));
199         break;
200       case IrOpcode::kLoopExitValue:
201         // Change exit marker to phi.
202         exit->InsertInput(graph_->zone(), 1, copier.map(exit->InputAt(0)));
203         NodeProperties::ChangeOp(
204             exit, common_->Phi(LoopExitValueRepresentationOf(exit->op()), 2));
205         break;
206       case IrOpcode::kLoopExitEffect:
207         // Change effect exit marker to effect phi.
208         exit->InsertInput(graph_->zone(), 1, copier.map(exit->InputAt(0)));
209         NodeProperties::ChangeOp(exit, common_->EffectPhi(2));
210         break;
211       default:
212         break;
213     }
214   }
215   return iter;
216 }
217 
PeelInnerLoops(LoopTree::Loop * loop)218 void LoopPeeler::PeelInnerLoops(LoopTree::Loop* loop) {
219   // If the loop has nested loops, peel inside those.
220   if (!loop->children().empty()) {
221     for (LoopTree::Loop* inner_loop : loop->children()) {
222       PeelInnerLoops(inner_loop);
223     }
224     return;
225   }
226   // Only peel small-enough loops.
227   if (loop->TotalSize() > LoopPeeler::kMaxPeeledNodes) return;
228   if (FLAG_trace_turbo_loop) {
229     PrintF("Peeling loop with header: ");
230     for (Node* node : loop_tree_->HeaderNodes(loop)) {
231       PrintF("%i ", node->id());
232     }
233     PrintF("\n");
234   }
235 
236   Peel(loop);
237 }
238 
EliminateLoopExit(Node * node)239 void LoopPeeler::EliminateLoopExit(Node* node) {
240   DCHECK_EQ(IrOpcode::kLoopExit, node->opcode());
241   // The exit markers take the loop exit as input. We iterate over uses
242   // and remove all the markers from the graph.
243   for (Edge edge : node->use_edges()) {
244     if (NodeProperties::IsControlEdge(edge)) {
245       Node* marker = edge.from();
246       if (marker->opcode() == IrOpcode::kLoopExitValue) {
247         NodeProperties::ReplaceUses(marker, marker->InputAt(0));
248         marker->Kill();
249       } else if (marker->opcode() == IrOpcode::kLoopExitEffect) {
250         NodeProperties::ReplaceUses(marker, nullptr,
251                                     NodeProperties::GetEffectInput(marker));
252         marker->Kill();
253       }
254     }
255   }
256   NodeProperties::ReplaceUses(node, nullptr, nullptr,
257                               NodeProperties::GetControlInput(node, 0));
258   node->Kill();
259 }
260 
PeelInnerLoopsOfTree()261 void LoopPeeler::PeelInnerLoopsOfTree() {
262   for (LoopTree::Loop* loop : loop_tree_->outer_loops()) {
263     PeelInnerLoops(loop);
264   }
265 
266   EliminateLoopExits(graph_, tmp_zone_);
267 }
268 
269 // static
EliminateLoopExits(Graph * graph,Zone * tmp_zone)270 void LoopPeeler::EliminateLoopExits(Graph* graph, Zone* tmp_zone) {
271   ZoneQueue<Node*> queue(tmp_zone);
272   ZoneVector<bool> visited(graph->NodeCount(), false, tmp_zone);
273   queue.push(graph->end());
274   while (!queue.empty()) {
275     Node* node = queue.front();
276     queue.pop();
277 
278     if (node->opcode() == IrOpcode::kLoopExit) {
279       Node* control = NodeProperties::GetControlInput(node);
280       EliminateLoopExit(node);
281       if (!visited[control->id()]) {
282         visited[control->id()] = true;
283         queue.push(control);
284       }
285     } else {
286       for (int i = 0; i < node->op()->ControlInputCount(); i++) {
287         Node* control = NodeProperties::GetControlInput(node, i);
288         if (!visited[control->id()]) {
289           visited[control->id()] = true;
290           queue.push(control);
291         }
292       }
293     }
294   }
295 }
296 
297 }  // namespace compiler
298 }  // namespace internal
299 }  // namespace v8
300