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