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

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

Issue 803993002: [turbofan] First version of loop analysis: loop finder on the soup of nodes. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 6 years 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-analysis.h ('k') | test/cctest/cctest.gyp » ('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 2013 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/graph.h"
6 #include "src/compiler/loop-analysis.h"
7 #include "src/compiler/node.h"
8 #include "src/compiler/node-properties-inl.h"
9 #include "src/zone.h"
10
11 namespace v8 {
12 namespace internal {
13 namespace compiler {
14
15 typedef uint32_t LoopMarks;
16
17
18 // TODO(titzer): don't assume entry edges have a particular index.
19 // TODO(titzer): use a BitMatrix to generalize this algorithm.
20 static const size_t kMaxLoops = 31;
21 static const int kAssumedLoopEntryIndex = 0; // assume loops are entered here.
22 static const LoopMarks kVisited = 1; // loop #0 is reserved.
23
24
25 // Temporary information for each node during marking.
26 struct NodeInfo {
27 Node* node;
28 NodeInfo* next; // link in chaining loop members
29 LoopMarks forward; // accumulated marks in the forward direction
30 LoopMarks backward; // accumulated marks in the backward direction
31 LoopMarks loop_mark; // loop mark for header nodes; encodes loop_num
32
33 bool MarkBackward(LoopMarks bw) {
34 LoopMarks prev = backward;
35 LoopMarks next = backward | bw;
36 backward = next;
37 return prev != next;
38 }
39
40 bool MarkForward(LoopMarks fw) {
41 LoopMarks prev = forward;
42 LoopMarks next = forward | fw;
43 forward = next;
44 return prev != next;
45 }
46
47 bool IsInLoop(size_t loop_num) {
48 DCHECK(loop_num > 0 && loop_num <= 31);
49 return forward & backward & (1 << loop_num);
50 }
51
52 bool IsLoopHeader() { return loop_mark != 0; }
53 bool IsInAnyLoop() { return (forward & backward) > kVisited; }
54
55 bool IsInHeaderForLoop(size_t loop_num) {
56 DCHECK(loop_num > 0);
57 return loop_mark == (kVisited | (1 << loop_num));
58 }
59 };
60
61
62 // Temporary loop info needed during traversal and building the loop tree.
63 struct LoopInfo {
64 Node* header;
65 NodeInfo* header_list;
66 NodeInfo* body_list;
67 LoopTree::Loop* loop;
68 };
69
70
71 static const NodeInfo kEmptyNodeInfo = {nullptr, nullptr, 0, 0, 0};
72
73
74 // Encapsulation of the loop finding algorithm.
75 // -----------------------------------------------------------------------------
76 // Conceptually, the contents of a loop are those nodes that are "between" the
77 // loop header and the backedges of the loop. Graphs in the soup of nodes can
78 // form improper cycles, so standard loop finding algorithms that work on CFGs
79 // aren't sufficient. However, in valid TurboFan graphs, all cycles involve
80 // either a {Loop} node or a phi. The {Loop} node itself and its accompanying
81 // phis are treated together as a set referred to here as the loop header.
82 // This loop finding algorithm works by traversing the graph in two directions,
83 // first from nodes to their inputs, starting at {end}, then in the reverse
84 // direction, from nodes to their uses, starting at loop headers.
85 // 1 bit per loop per node per direction are required during the marking phase.
86 // To handle nested loops correctly, the algorithm must filter some reachability
87 // marks on edges into/out-of the loop header nodes.
88 class LoopFinderImpl {
89 public:
90 LoopFinderImpl(Graph* graph, LoopTree* loop_tree, Zone* zone)
91 : end_(graph->end()),
92 queue_(zone),
93 queued_(graph, 2),
94 info_(graph->NodeCount(), kEmptyNodeInfo, zone),
95 loops_(zone),
96 loop_tree_(loop_tree),
97 loops_found_(0) {}
98
99 void Run() {
100 PropagateBackward();
101 PropagateForward();
102 FinishLoopTree();
103 }
104
105 void Print() {
106 // Print out the results.
107 for (NodeInfo& ni : info_) {
108 if (ni.node == nullptr) continue;
109 for (size_t i = 1; i <= loops_.size(); i++) {
110 if (ni.IsInLoop(i)) {
111 PrintF("X");
112 } else if (ni.forward & (1 << i)) {
113 PrintF("/");
114 } else if (ni.backward & (1 << i)) {
115 PrintF("\\");
116 } else {
117 PrintF(" ");
118 }
119 }
120 PrintF(" #%d:%s\n", ni.node->id(), ni.node->op()->mnemonic());
121 }
122
123 int i = 0;
124 for (LoopInfo& li : loops_) {
125 PrintF("Loop %d headed at #%d\n", i, li.header->id());
126 i++;
127 }
128
129 for (LoopTree::Loop* loop : loop_tree_->outer_loops_) {
130 PrintLoop(loop);
131 }
132 }
133
134 private:
135 Node* end_;
136 NodeDeque queue_;
137 NodeMarker<bool> queued_;
138 ZoneVector<NodeInfo> info_;
139 ZoneVector<LoopInfo> loops_;
140 LoopTree* loop_tree_;
141 size_t loops_found_;
142
143 // Propagate marks backward from loop headers.
144 void PropagateBackward() {
145 PropagateBackward(end_, kVisited);
146
147 while (!queue_.empty()) {
148 Node* node = queue_.front();
149 queue_.pop_front();
150 queued_.Set(node, false);
151
152 // Setup loop headers first.
153 if (node->opcode() == IrOpcode::kLoop) {
154 // found the loop node first.
155 CreateLoopInfo(node);
156 } else if (node->opcode() == IrOpcode::kPhi ||
157 node->opcode() == IrOpcode::kEffectPhi) {
158 // found a phi first.
159 Node* merge = node->InputAt(node->InputCount() - 1);
160 if (merge->opcode() == IrOpcode::kLoop) CreateLoopInfo(merge);
161 }
162
163 // Propagate reachability marks backwards from this node.
164 NodeInfo& ni = info(node);
165 if (ni.IsLoopHeader()) {
166 // Handle edges from loop header nodes specially.
167 for (int i = 0; i < node->InputCount(); i++) {
168 if (i == kAssumedLoopEntryIndex) {
169 // Don't propagate the loop mark backwards on the entry edge.
170 PropagateBackward(node->InputAt(0),
171 kVisited | (ni.backward & ~ni.loop_mark));
172 } else {
173 // Only propagate the loop mark on backedges.
174 PropagateBackward(node->InputAt(i), ni.loop_mark);
175 }
176 }
177 } else {
178 // Propagate all loop marks backwards for a normal node.
179 for (Node* const input : node->inputs()) {
180 PropagateBackward(input, ni.backward);
181 }
182 }
183 }
184 }
185
186 // Make a new loop header for the given node.
187 void CreateLoopInfo(Node* node) {
188 NodeInfo& ni = info(node);
189 if (ni.IsLoopHeader()) return; // loop already set up.
190
191 loops_found_++;
192 size_t loop_num = loops_.size() + 1;
193 CHECK(loops_found_ <= kMaxLoops); // TODO(titzer): don't crash.
194 // Create a new loop.
195 loops_.push_back({node, nullptr, nullptr, nullptr});
196 loop_tree_->NewLoop();
197 LoopMarks loop_mark = kVisited | (1 << loop_num);
198 ni.node = node;
199 ni.loop_mark = loop_mark;
200
201 // Setup loop mark for phis attached to loop header.
202 for (Node* use : node->uses()) {
203 if (use->opcode() == IrOpcode::kPhi ||
204 use->opcode() == IrOpcode::kEffectPhi) {
205 info(use).loop_mark = loop_mark;
206 }
207 }
208 }
209
210 // Propagate marks forward from loops.
211 void PropagateForward() {
212 for (LoopInfo& li : loops_) {
213 queued_.Set(li.header, true);
214 queue_.push_back(li.header);
215 NodeInfo& ni = info(li.header);
216 ni.forward = ni.loop_mark;
217 }
218 // Propagate forward on paths that were backward reachable from backedges.
219 while (!queue_.empty()) {
220 Node* node = queue_.front();
221 queue_.pop_front();
222 queued_.Set(node, false);
223 NodeInfo& ni = info(node);
224 for (Edge edge : node->use_edges()) {
225 Node* use = edge.from();
226 NodeInfo& ui = info(use);
227 if (IsBackedge(use, ui, edge)) continue; // skip backedges.
228 LoopMarks both = ni.forward & ui.backward;
229 if (ui.MarkForward(both) && !queued_.Get(use)) {
230 queued_.Set(use, true);
231 queue_.push_back(use);
232 }
233 }
234 }
235 }
236
237 bool IsBackedge(Node* use, NodeInfo& ui, Edge& edge) {
238 // TODO(titzer): checking for backedges here is ugly.
239 if (!ui.IsLoopHeader()) return false;
240 if (edge.index() == kAssumedLoopEntryIndex) return false;
241 if (use->opcode() == IrOpcode::kPhi ||
242 use->opcode() == IrOpcode::kEffectPhi) {
243 return !NodeProperties::IsControlEdge(edge);
244 }
245 return true;
246 }
247
248 NodeInfo& info(Node* node) {
249 NodeInfo& i = info_[node->id()];
250 if (i.node == nullptr) i.node = node;
251 return i;
252 }
253
254 void PropagateBackward(Node* node, LoopMarks marks) {
255 if (info(node).MarkBackward(marks) && !queued_.Get(node)) {
256 queue_.push_back(node);
257 queued_.Set(node, true);
258 }
259 }
260
261 void FinishLoopTree() {
262 // Degenerate cases.
263 if (loops_.size() == 0) return;
264 if (loops_.size() == 1) return FinishSingleLoop();
265
266 for (size_t i = 1; i <= loops_.size(); i++) ConnectLoopTree(i);
267
268 size_t count = 0;
269 // Place the node into the innermost nested loop of which it is a member.
270 for (NodeInfo& ni : info_) {
271 if (ni.node == nullptr || !ni.IsInAnyLoop()) continue;
272
273 LoopInfo* innermost = nullptr;
274 size_t index = 0;
275 for (size_t i = 1; i <= loops_.size(); i++) {
276 if (ni.IsInLoop(i)) {
277 LoopInfo* loop = &loops_[i - 1];
278 if (innermost == nullptr ||
279 loop->loop->depth_ > innermost->loop->depth_) {
280 innermost = loop;
281 index = i;
282 }
283 }
284 }
285 if (ni.IsInHeaderForLoop(index)) {
286 ni.next = innermost->header_list;
287 innermost->header_list = &ni;
288 } else {
289 ni.next = innermost->body_list;
290 innermost->body_list = &ni;
291 }
292 count++;
293 }
294
295 // Serialize the node lists for loops into the loop tree.
296 loop_tree_->loop_nodes_.reserve(count);
297 for (LoopTree::Loop* loop : loop_tree_->outer_loops_) {
298 SerializeLoop(loop);
299 }
300 }
301
302 // Handle the simpler case of a single loop (no checks for nesting necessary).
303 void FinishSingleLoop() {
304 DCHECK(loops_.size() == 1);
305 DCHECK(loop_tree_->all_loops_.size() == 1);
306
307 // Place nodes into the loop header and body.
308 LoopInfo* li = &loops_[0];
309 li->loop = &loop_tree_->all_loops_[0];
310 loop_tree_->SetParent(nullptr, li->loop);
311 size_t count = 0;
312 for (NodeInfo& ni : info_) {
313 if (ni.node == nullptr || !ni.IsInAnyLoop()) continue;
314 DCHECK(ni.IsInLoop(1));
315 if (ni.IsInHeaderForLoop(1)) {
316 ni.next = li->header_list;
317 li->header_list = &ni;
318 } else {
319 ni.next = li->body_list;
320 li->body_list = &ni;
321 }
322 count++;
323 }
324
325 // Serialize the node lists for the loop into the loop tree.
326 loop_tree_->loop_nodes_.reserve(count);
327 SerializeLoop(li->loop);
328 }
329
330 // Recursively serialize the list of header nodes and body nodes
331 // so that nested loops occupy nested intervals.
332 void SerializeLoop(LoopTree::Loop* loop) {
333 size_t loop_num = loop_tree_->LoopNum(loop);
334 LoopInfo& li = loops_[loop_num - 1];
335
336 // Serialize the header.
337 loop->header_start_ = static_cast<int>(loop_tree_->loop_nodes_.size());
338 for (NodeInfo* ni = li.header_list; ni != nullptr; ni = ni->next) {
339 loop_tree_->loop_nodes_.push_back(ni->node);
340 // TODO(titzer): lift loop count restriction.
341 loop_tree_->node_to_loop_num_[ni->node->id()] =
342 static_cast<uint8_t>(loop_num);
343 }
344
345 // Serialize the body.
346 loop->body_start_ = static_cast<int>(loop_tree_->loop_nodes_.size());
347 for (NodeInfo* ni = li.body_list; ni != nullptr; ni = ni->next) {
348 loop_tree_->loop_nodes_.push_back(ni->node);
349 // TODO(titzer): lift loop count restriction.
350 loop_tree_->node_to_loop_num_[ni->node->id()] =
351 static_cast<uint8_t>(loop_num);
352 }
353
354 // Serialize nested loops.
355 for (LoopTree::Loop* child : loop->children_) SerializeLoop(child);
356
357 loop->body_end_ = static_cast<int>(loop_tree_->loop_nodes_.size());
358 }
359
360 // Connect the LoopTree loops to their parents recursively.
361 LoopTree::Loop* ConnectLoopTree(size_t loop_num) {
362 LoopInfo& li = loops_[loop_num - 1];
363 if (li.loop != nullptr) return li.loop;
364
365 NodeInfo& ni = info(li.header);
366 LoopTree::Loop* parent = nullptr;
367 for (size_t i = 1; i <= loops_.size(); i++) {
368 if (i == loop_num) continue;
369 if (ni.IsInLoop(i)) {
370 // recursively create potential parent loops first.
371 LoopTree::Loop* upper = ConnectLoopTree(i);
372 if (parent == nullptr || upper->depth_ > parent->depth_) {
373 parent = upper;
374 }
375 }
376 }
377 li.loop = &loop_tree_->all_loops_[loop_num - 1];
378 loop_tree_->SetParent(parent, li.loop);
379 return li.loop;
380 }
381
382 void PrintLoop(LoopTree::Loop* loop) {
383 for (int i = 0; i < loop->depth_; i++) PrintF(" ");
384 PrintF("Loop depth = %d ", loop->depth_);
385 int i = loop->header_start_;
386 while (i < loop->body_start_) {
387 PrintF(" H#%d", loop_tree_->loop_nodes_[i++]->id());
388 }
389 while (i < loop->body_end_) {
390 PrintF(" B#%d", loop_tree_->loop_nodes_[i++]->id());
391 }
392 PrintF("\n");
393 for (LoopTree::Loop* child : loop->children_) PrintLoop(child);
394 }
395 };
396
397
398 LoopTree* LoopFinder::BuildLoopTree(Graph* graph, Zone* zone) {
399 LoopTree* loop_tree =
400 new (graph->zone()) LoopTree(graph->NodeCount(), graph->zone());
401 LoopFinderImpl finder(graph, loop_tree, zone);
402 finder.Run();
403 if (FLAG_trace_turbo_graph) {
404 finder.Print();
405 }
406 return loop_tree;
407 }
408
409 } // namespace compiler
410 } // namespace internal
411 } // namespace v8
OLDNEW
« no previous file with comments | « src/compiler/loop-analysis.h ('k') | test/cctest/cctest.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698