Line data Source code
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 :
112 : Peeling(Graph* graph, size_t max, NodeVector* p)
113 53428 : : node_map(graph, static_cast<uint32_t>(max)), pairs(p) {}
114 :
115 10440485 : Node* map(Node* node) {
116 10440485 : if (node_map.Get(node) == 0) return node;
117 8613552 : return pairs->at(node_map.Get(node));
118 : }
119 :
120 1473917 : void Insert(Node* original, Node* copy) {
121 2947834 : node_map.Set(original, 1 + pairs->size());
122 1473917 : pairs->push_back(original);
123 1473871 : pairs->push_back(copy);
124 1473829 : }
125 :
126 26756 : 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 2765122 : for (Node* node : nodes) {
132 : SourcePositionTable::Scope position(
133 1369181 : source_positions, source_positions->GetSourcePosition(node));
134 : NodeOriginTable::Scope origin_scope(node_origins, "copy nodes", node);
135 : inputs.clear();
136 6478980 : for (Node* input : node->inputs()) {
137 10219599 : inputs.push_back(map(input));
138 : }
139 1369211 : Node* copy = graph->NewNode(node->op(), node->InputCount(), &inputs[0]);
140 1369267 : if (NodeProperties::IsTyped(node)) {
141 : NodeProperties::SetType(copy, NodeProperties::GetType(node));
142 : }
143 1369267 : Insert(node, copy);
144 : }
145 :
146 : // Fix remaining inputs of the copies.
147 2765394 : for (Node* original : nodes) {
148 2738724 : Node* copy = pairs->at(node_map.Get(original));
149 11589542 : for (int i = 0; i < copy->InputCount(); i++) {
150 5110134 : copy->ReplaceInput(i, map(original->InputAt(i)));
151 : }
152 : }
153 26714 : }
154 :
155 : bool Marked(Node* node) { return node_map.Get(node) > 0; }
156 : };
157 :
158 :
159 : class PeeledIterationImpl : public PeeledIteration {
160 : public:
161 : NodeVector node_pairs_;
162 : explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {}
163 : };
164 :
165 :
166 63 : 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 615 : for (size_t i = 0; i < impl->node_pairs_.size(); i += 2) {
171 380 : if (impl->node_pairs_[i] == node) return impl->node_pairs_[i + 1];
172 : }
173 : return node;
174 : }
175 :
176 36781 : 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 36781 : Node* loop_node = loop_tree_->GetLoopControl(loop);
180 3767846 : for (Node* node : loop_tree_->LoopNodes(loop)) {
181 6376685 : for (Node* use : node->uses()) {
182 4511153 : if (!loop_tree_->Contains(loop, use)) {
183 : bool unmarked_exit;
184 197561 : switch (node->opcode()) {
185 : case IrOpcode::kLoopExit:
186 56539 : unmarked_exit = (node->InputAt(1) != loop_node);
187 56539 : break;
188 : case IrOpcode::kLoopExitValue:
189 : case IrOpcode::kLoopExitEffect:
190 62390 : unmarked_exit = (node->InputAt(1)->InputAt(1) != loop_node);
191 62390 : break;
192 : default:
193 78632 : unmarked_exit = (use->opcode() != IrOpcode::kTerminate);
194 : }
195 197561 : if (unmarked_exit) {
196 10052 : 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 0 : use->op()->mnemonic());
204 : }
205 : return false;
206 : }
207 : }
208 : }
209 : }
210 : return true;
211 : }
212 :
213 36764 : PeeledIteration* LoopPeeler::Peel(LoopTree::Loop* loop) {
214 36764 : if (!CanPeel(loop)) return nullptr;
215 :
216 : //============================================================================
217 : // Construct the peeled iteration.
218 : //============================================================================
219 53428 : PeeledIterationImpl* iter = new (tmp_zone_) PeeledIterationImpl(tmp_zone_);
220 26714 : size_t estimated_peeled_size = 5 + (loop->TotalSize()) * 2;
221 26714 : Peeling peeling(graph_, estimated_peeled_size, &iter->node_pairs_);
222 :
223 26714 : Node* dead = graph_->NewNode(common_->Dead());
224 :
225 : // Map the loop header nodes to their entry values.
226 262752 : for (Node* node : loop_tree_->HeaderNodes(loop)) {
227 104662 : peeling.Insert(node, node->InputAt(kAssumedLoopEntryIndex));
228 : }
229 :
230 : // Copy all the nodes of loop body for the peeled iteration.
231 53428 : peeling.CopyNodes(graph_, tmp_zone_, dead, loop_tree_->BodyNodes(loop),
232 53428 : source_positions_, node_origins_);
233 :
234 : //============================================================================
235 : // Replace the entry to the loop with the output of the peeled iteration.
236 : //============================================================================
237 26714 : Node* loop_node = loop_tree_->GetLoopControl(loop);
238 : Node* new_entry;
239 26714 : int backedges = loop_node->InputCount() - 1;
240 26714 : if (backedges > 1) {
241 : // Multiple backedges from original loop, therefore multiple output edges
242 : // from the peeled iteration.
243 3 : NodeVector inputs(tmp_zone_);
244 15 : for (int i = 1; i < loop_node->InputCount(); i++) {
245 12 : inputs.push_back(peeling.map(loop_node->InputAt(i)));
246 : }
247 : Node* merge =
248 3 : graph_->NewNode(common_->Merge(backedges), backedges, &inputs[0]);
249 :
250 : // Merge values from the multiple output edges of the peeled iteration.
251 16 : for (Node* node : loop_tree_->HeaderNodes(loop)) {
252 5 : if (node->opcode() == IrOpcode::kLoop) continue; // already done.
253 : inputs.clear();
254 10 : for (int i = 0; i < backedges; i++) {
255 12 : inputs.push_back(peeling.map(node->InputAt(1 + i)));
256 : }
257 4 : for (Node* input : inputs) {
258 4 : if (input != inputs[0]) { // Non-redundant phi.
259 2 : inputs.push_back(merge);
260 2 : const Operator* op = common_->ResizeMergeOrPhi(node->op(), backedges);
261 2 : Node* phi = graph_->NewNode(op, backedges + 1, &inputs[0]);
262 2 : node->ReplaceInput(0, phi);
263 2 : break;
264 : }
265 : }
266 : }
267 3 : new_entry = merge;
268 : } else {
269 : // Only one backedge, simply replace the input to loop with output of
270 : // peeling.
271 236025 : for (Node* node : loop_tree_->HeaderNodes(loop)) {
272 104657 : node->ReplaceInput(0, peeling.map(node->InputAt(1)));
273 : }
274 26711 : new_entry = peeling.map(loop_node->InputAt(1));
275 : }
276 26714 : loop_node->ReplaceInput(0, new_entry);
277 :
278 : //============================================================================
279 : // Change the exit and exit markers to merge/phi/effect-phi.
280 : //============================================================================
281 234540 : for (Node* exit : loop_tree_->ExitNodes(loop)) {
282 90556 : switch (exit->opcode()) {
283 : case IrOpcode::kLoopExit:
284 : // Change the loop exit node to a merge node.
285 35875 : exit->ReplaceInput(1, peeling.map(exit->InputAt(0)));
286 35875 : NodeProperties::ChangeOp(exit, common_->Merge(2));
287 35875 : break;
288 : case IrOpcode::kLoopExitValue:
289 : // Change exit marker to phi.
290 18816 : exit->InsertInput(graph_->zone(), 1, peeling.map(exit->InputAt(0)));
291 18816 : NodeProperties::ChangeOp(
292 37632 : exit, common_->Phi(MachineRepresentation::kTagged, 2));
293 18816 : break;
294 : case IrOpcode::kLoopExitEffect:
295 : // Change effect exit marker to effect phi.
296 35865 : exit->InsertInput(graph_->zone(), 1, peeling.map(exit->InputAt(0)));
297 35865 : NodeProperties::ChangeOp(exit, common_->EffectPhi(2));
298 35865 : break;
299 : default:
300 : break;
301 : }
302 : }
303 : return iter;
304 : }
305 :
306 41329 : void LoopPeeler::PeelInnerLoops(LoopTree::Loop* loop) {
307 : // If the loop has nested loops, peel inside those.
308 41329 : if (!loop->children().empty()) {
309 10089 : for (LoopTree::Loop* inner_loop : loop->children()) {
310 5579 : PeelInnerLoops(inner_loop);
311 : }
312 : return;
313 : }
314 : // Only peel small-enough loops.
315 36819 : if (loop->TotalSize() > LoopPeeler::kMaxPeeledNodes) return;
316 36755 : if (FLAG_trace_turbo_loop) {
317 0 : PrintF("Peeling loop with header: ");
318 0 : for (Node* node : loop_tree_->HeaderNodes(loop)) {
319 0 : PrintF("%i ", node->id());
320 : }
321 0 : PrintF("\n");
322 : }
323 :
324 36755 : Peel(loop);
325 : }
326 :
327 : namespace {
328 :
329 130508 : 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 1014422 : for (Edge edge : node->use_edges()) {
334 441957 : if (NodeProperties::IsControlEdge(edge)) {
335 : Node* marker = edge.from();
336 441957 : if (marker->opcode() == IrOpcode::kLoopExitValue) {
337 177623 : NodeProperties::ReplaceUses(marker, marker->InputAt(0));
338 177623 : marker->Kill();
339 264334 : } else if (marker->opcode() == IrOpcode::kLoopExitEffect) {
340 130508 : NodeProperties::ReplaceUses(marker, nullptr,
341 130508 : NodeProperties::GetEffectInput(marker));
342 130508 : marker->Kill();
343 : }
344 : }
345 : }
346 130508 : NodeProperties::ReplaceUses(node, nullptr, nullptr,
347 130508 : NodeProperties::GetControlInput(node, 0));
348 130508 : node->Kill();
349 130508 : }
350 :
351 : } // namespace
352 :
353 460759 : void LoopPeeler::PeelInnerLoopsOfTree() {
354 460759 : for (LoopTree::Loop* loop : loop_tree_->outer_loops()) {
355 35750 : PeelInnerLoops(loop);
356 : }
357 :
358 460760 : EliminateLoopExits(graph_, tmp_zone_);
359 460764 : }
360 :
361 : // static
362 464164 : void LoopPeeler::EliminateLoopExits(Graph* graph, Zone* tmp_zone) {
363 464164 : ZoneQueue<Node*> queue(tmp_zone);
364 : ZoneVector<bool> visited(graph->NodeCount(), false, tmp_zone);
365 928320 : queue.push(graph->end());
366 7899847 : while (!queue.empty()) {
367 7435679 : Node* node = queue.front();
368 : queue.pop();
369 :
370 7435689 : if (node->opcode() == IrOpcode::kLoopExit) {
371 130508 : Node* control = NodeProperties::GetControlInput(node);
372 130508 : EliminateLoopExit(node);
373 391524 : if (!visited[control->id()]) {
374 : visited[control->id()] = true;
375 : queue.push(control);
376 : }
377 : } else {
378 22703409 : for (int i = 0; i < node->op()->ControlInputCount(); i++) {
379 7699111 : Node* control = NodeProperties::GetControlInput(node, i);
380 15398214 : if (!visited[control->id()]) {
381 : visited[control->id()] = true;
382 : queue.push(control);
383 : }
384 : }
385 : }
386 : }
387 464168 : }
388 :
389 : } // namespace compiler
390 : } // namespace internal
391 122004 : } // namespace v8
|