Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: src/compiler/loop-peeling.cc

Issue 816053002: [turbofan] First version of loop peeling. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/compiler/loop-peeling.h ('k') | src/compiler/node.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/common-operator.h"
6 #include "src/compiler/graph.h"
7 #include "src/compiler/loop-peeling.h"
8 #include "src/compiler/node.h"
9 #include "src/compiler/node-marker.h"
10 #include "src/compiler/node-properties-inl.h"
11 #include "src/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
110 Peeling(Graph* graph, Zone* tmp_zone, size_t max, NodeVector* p)
111 : node_map(graph, static_cast<uint32_t>(max)), pairs(p) {}
112
113 Node* map(Node* node) {
114 if (node_map.Get(node) == 0) return node;
115 return pairs->at(node_map.Get(node));
116 }
117
118 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
124 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()) inputs.push_back(map(input));
130 Insert(node, graph->NewNode(node->op(), node->InputCount(), &inputs[0]));
131 }
132
133 // Fix remaining inputs of the copies.
134 for (Node* original : nodes) {
135 Node* copy = pairs->at(node_map.Get(original));
136 for (int i = 0; i < copy->InputCount(); i++) {
137 copy->ReplaceInput(i, map(original->InputAt(i)));
138 }
139 }
140 }
141
142 bool Marked(Node* node) { return node_map.Get(node) > 0; }
143 };
144
145
146 class PeeledIterationImpl : public PeeledIteration {
147 public:
148 NodeVector node_pairs_;
149 explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {}
150 };
151
152
153 Node* PeeledIteration::map(Node* node) {
154 // TODO(turbofan): we use a simple linear search, since the peeled iteration
155 // is really only used in testing.
156 PeeledIterationImpl* impl = static_cast<PeeledIterationImpl*>(this);
157 for (size_t i = 0; i < impl->node_pairs_.size(); i += 2) {
158 if (impl->node_pairs_[i] == node) return impl->node_pairs_[i + 1];
159 }
160 return node;
161 }
162
163
164 static const Operator* ResizeMergeOrPhi(CommonOperatorBuilder* common,
165 const Operator* op, int size) {
166 if (op->opcode() == IrOpcode::kPhi) {
167 return common->Phi(OpParameter<MachineType>(op), size);
168 } else if (op->opcode() == IrOpcode::kEffectPhi) {
169 return common->EffectPhi(size);
170 } else if (op->opcode() == IrOpcode::kMerge) {
171 return common->Merge(size);
172 } else if (op->opcode() == IrOpcode::kLoop) {
173 return common->Loop(size);
174 } else {
175 UNREACHABLE();
176 return nullptr;
177 }
178 }
179
180
181 PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common,
182 LoopTree* loop_tree, LoopTree::Loop* loop,
183 Zone* tmp_zone) {
184 PeeledIterationImpl* iter = new (tmp_zone) PeeledIterationImpl(tmp_zone);
185 Peeling peeling(graph, tmp_zone, loop->TotalSize() * 2 + 2,
186 &iter->node_pairs_);
187
188 //============================================================================
189 // Construct the peeled iteration.
190 //============================================================================
191 Node* dead = graph->NewNode(common->Dead());
192
193 // Map the loop header nodes to their entry values.
194 for (Node* node : loop_tree->HeaderNodes(loop)) {
195 // TODO(titzer): assuming loop entry at index 0.
196 peeling.Insert(node, node->InputAt(0));
197 }
198
199 // Copy all the nodes of loop body for the peeled iteration.
200 peeling.CopyNodes(graph, tmp_zone, dead, loop_tree->BodyNodes(loop));
201
202 //============================================================================
203 // Replace the entry to the loop with the output of the peeled iteration.
204 //============================================================================
205 Node* loop_node = loop_tree->GetLoopControl(loop);
206 Node* new_entry;
207 int backedges = loop_node->InputCount() - 1;
208 if (backedges > 1) {
209 // Multiple backedges from original loop, therefore multiple output edges
210 // from the peeled iteration.
211 NodeVector inputs(tmp_zone);
212 for (int i = 1; i < loop_node->InputCount(); i++) {
213 inputs.push_back(peeling.map(loop_node->InputAt(i)));
214 }
215 Node* merge =
216 graph->NewNode(common->Merge(backedges), backedges, &inputs[0]);
217
218 // Merge values from the multiple output edges of the peeled iteration.
219 for (Node* node : loop_tree->HeaderNodes(loop)) {
220 if (node->opcode() == IrOpcode::kLoop) continue; // already done.
221 inputs.clear();
222 for (int i = 0; i < backedges; i++) {
223 inputs.push_back(peeling.map(node->InputAt(1 + i)));
224 }
225 for (Node* input : inputs) {
226 if (input != inputs[0]) { // Non-redundant phi.
227 inputs.push_back(merge);
228 const Operator* op = ResizeMergeOrPhi(common, node->op(), backedges);
229 Node* phi = graph->NewNode(op, backedges + 1, &inputs[0]);
230 node->ReplaceInput(0, phi);
231 break;
232 }
233 }
234 }
235 new_entry = merge;
236 } else {
237 // Only one backedge, simply replace the input to loop with output of
238 // peeling.
239 for (Node* node : loop_tree->HeaderNodes(loop)) {
240 node->ReplaceInput(0, peeling.map(node->InputAt(0)));
241 }
242 new_entry = peeling.map(loop_node->InputAt(1));
243 }
244 loop_node->ReplaceInput(0, new_entry);
245
246 //============================================================================
247 // Find the loop exit region.
248 //============================================================================
249 NodeVector exits(tmp_zone);
250 Node* end = NULL;
251 for (Node* node : loop_tree->LoopNodes(loop)) {
252 for (Node* use : node->uses()) {
253 if (!loop_tree->Contains(loop, use)) {
254 if (node->opcode() == IrOpcode::kBranch &&
255 (use->opcode() == IrOpcode::kIfTrue ||
256 use->opcode() == IrOpcode::kIfFalse)) {
257 // This is a branch from inside the loop to outside the loop.
258 exits.push_back(use);
259 }
260 }
261 }
262 }
263
264 if (exits.size() == 0) return iter; // no exits => NTL
265
266 if (exits.size() == 1) {
267 // Only one exit, so {end} is that exit.
268 end = exits[0];
269 } else {
270 // {end} should be the common merge from the exits.
271 NodeVector rets(tmp_zone);
272 for (Node* exit : exits) {
273 Node* found = NULL;
274 for (Node* use : exit->uses()) {
275 if (use->opcode() == IrOpcode::kMerge) {
276 found = use;
277 if (end == NULL) {
278 end = found;
279 } else {
280 CHECK_EQ(end, found); // it should be unique!
281 }
282 } else if (use->opcode() == IrOpcode::kReturn) {
283 found = use;
284 rets.push_back(found);
285 }
286 }
287 // There should be a merge or a return for each exit.
288 CHECK_NE(NULL, found);
289 }
290 // Return nodes, the end merge, and the phis associated with the end merge
291 // must be duplicated as well.
292 for (Node* node : rets) exits.push_back(node);
293 if (end != NULL) {
294 exits.push_back(end);
295 for (Node* use : end->uses()) {
296 if (IrOpcode::IsPhiOpcode(use->opcode())) exits.push_back(use);
297 }
298 }
299 }
300
301 //============================================================================
302 // Duplicate the loop exit region and add a merge.
303 //============================================================================
304 NodeRange exit_range(&exits[0], &exits[0] + exits.size());
305 peeling.CopyNodes(graph, tmp_zone, dead, exit_range);
306
307 Node* merge = graph->NewNode(common->Merge(2), end, peeling.map(end));
308 end->ReplaceUses(merge);
309 merge->ReplaceInput(0, end); // HULK SMASH!!
310
311 // Find and update all the edges into either the loop or exit region.
312 for (int i = 0; i < 2; i++) {
313 NodeRange range = i == 0 ? loop_tree->LoopNodes(loop) : exit_range;
314 ZoneVector<Edge> value_edges(tmp_zone);
315 ZoneVector<Edge> effect_edges(tmp_zone);
316
317 for (Node* node : range) {
318 // Gather value and effect edges from outside the region.
319 for (Edge edge : node->use_edges()) {
320 if (!peeling.Marked(edge.from())) {
321 // Edge from outside the loop into the region.
322 if (NodeProperties::IsValueEdge(edge) ||
323 NodeProperties::IsContextEdge(edge)) {
324 value_edges.push_back(edge);
325 } else if (NodeProperties::IsEffectEdge(edge)) {
326 effect_edges.push_back(edge);
327 } else {
328 // don't do anything for control edges.
329 // TODO(titzer): should update control edges to peeled?
330 }
331 }
332 }
333
334 // Update all the value and effect edges at once.
335 if (!value_edges.empty()) {
336 // TODO(titzer): machine type is wrong here.
337 Node* phi = graph->NewNode(common->Phi(kMachAnyTagged, 2), node,
338 peeling.map(node), merge);
339 for (Edge edge : value_edges) edge.UpdateTo(phi);
340 value_edges.clear();
341 }
342 if (!effect_edges.empty()) {
343 Node* effect_phi = graph->NewNode(common->EffectPhi(2), node,
344 peeling.map(node), merge);
345 for (Edge edge : effect_edges) edge.UpdateTo(effect_phi);
346 effect_edges.clear();
347 }
348 }
349 }
350
351 return iter;
352 }
353
354 } // namespace compiler
355 } // namespace internal
356 } // namespace v8
OLDNEW
« no previous file with comments | « src/compiler/loop-peeling.h ('k') | src/compiler/node.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698