1 // Copyright 2014 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-analysis.h"
6
7 #include "src/compiler/graph.h"
8 #include "src/compiler/node-marker.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/compiler/node.h"
11 #include "src/zone/zone.h"
12
13 namespace v8 {
14 namespace internal {
15 namespace compiler {
16
17 #define OFFSET(x) ((x)&0x1F)
18 #define BIT(x) (1u << OFFSET(x))
19 #define INDEX(x) ((x) >> 5)
20
21 // Temporary information for each node during marking.
22 struct NodeInfo {
23 Node* node;
24 NodeInfo* next; // link in chaining loop members
25 };
26
27
28 // Temporary loop info needed during traversal and building the loop tree.
29 struct TempLoopInfo {
30 Node* header;
31 NodeInfo* header_list;
32 NodeInfo* exit_list;
33 NodeInfo* body_list;
34 LoopTree::Loop* loop;
35 };
36
37
38 // Encapsulation of the loop finding algorithm.
39 // -----------------------------------------------------------------------------
40 // Conceptually, the contents of a loop are those nodes that are "between" the
41 // loop header and the backedges of the loop. Graphs in the soup of nodes can
42 // form improper cycles, so standard loop finding algorithms that work on CFGs
43 // aren't sufficient. However, in valid TurboFan graphs, all cycles involve
44 // either a {Loop} node or a phi. The {Loop} node itself and its accompanying
45 // phis are treated together as a set referred to here as the loop header.
46 // This loop finding algorithm works by traversing the graph in two directions,
47 // first from nodes to their inputs, starting at {end}, then in the reverse
48 // direction, from nodes to their uses, starting at loop headers.
49 // 1 bit per loop per node per direction are required during the marking phase.
50 // To handle nested loops correctly, the algorithm must filter some reachability
51 // marks on edges into/out-of the loop header nodes.
52 class LoopFinderImpl {
53 public:
LoopFinderImpl(Graph * graph,LoopTree * loop_tree,Zone * zone)54 LoopFinderImpl(Graph* graph, LoopTree* loop_tree, Zone* zone)
55 : zone_(zone),
56 end_(graph->end()),
57 queue_(zone),
58 queued_(graph, 2),
59 info_(graph->NodeCount(), {nullptr, nullptr}, zone),
60 loops_(zone),
61 loop_num_(graph->NodeCount(), -1, zone),
62 loop_tree_(loop_tree),
63 loops_found_(0),
64 width_(0),
65 backward_(nullptr),
66 forward_(nullptr) {}
67
Run()68 void Run() {
69 PropagateBackward();
70 PropagateForward();
71 FinishLoopTree();
72 }
73
Print()74 void Print() {
75 // Print out the results.
76 for (NodeInfo& ni : info_) {
77 if (ni.node == nullptr) continue;
78 for (int i = 1; i <= loops_found_; i++) {
79 int index = ni.node->id() * width_ + INDEX(i);
80 bool marked_forward = forward_[index] & BIT(i);
81 bool marked_backward = backward_[index] & BIT(i);
82 if (marked_forward && marked_backward) {
83 PrintF("X");
84 } else if (marked_forward) {
85 PrintF(">");
86 } else if (marked_backward) {
87 PrintF("<");
88 } else {
89 PrintF(" ");
90 }
91 }
92 PrintF(" #%d:%s\n", ni.node->id(), ni.node->op()->mnemonic());
93 }
94
95 int i = 0;
96 for (TempLoopInfo& li : loops_) {
97 PrintF("Loop %d headed at #%d\n", i, li.header->id());
98 i++;
99 }
100
101 for (LoopTree::Loop* loop : loop_tree_->outer_loops_) {
102 PrintLoop(loop);
103 }
104 }
105
106 private:
107 Zone* zone_;
108 Node* end_;
109 NodeDeque queue_;
110 NodeMarker<bool> queued_;
111 ZoneVector<NodeInfo> info_;
112 ZoneVector<TempLoopInfo> loops_;
113 ZoneVector<int> loop_num_;
114 LoopTree* loop_tree_;
115 int loops_found_;
116 int width_;
117 uint32_t* backward_;
118 uint32_t* forward_;
119
num_nodes()120 int num_nodes() {
121 return static_cast<int>(loop_tree_->node_to_loop_num_.size());
122 }
123
124 // Tb = Tb | (Fb - loop_filter)
PropagateBackwardMarks(Node * from,Node * to,int loop_filter)125 bool PropagateBackwardMarks(Node* from, Node* to, int loop_filter) {
126 if (from == to) return false;
127 uint32_t* fp = &backward_[from->id() * width_];
128 uint32_t* tp = &backward_[to->id() * width_];
129 bool change = false;
130 for (int i = 0; i < width_; i++) {
131 uint32_t mask = i == INDEX(loop_filter) ? ~BIT(loop_filter) : 0xFFFFFFFF;
132 uint32_t prev = tp[i];
133 uint32_t next = prev | (fp[i] & mask);
134 tp[i] = next;
135 if (!change && (prev != next)) change = true;
136 }
137 return change;
138 }
139
140 // Tb = Tb | B
SetBackwardMark(Node * to,int loop_num)141 bool SetBackwardMark(Node* to, int loop_num) {
142 uint32_t* tp = &backward_[to->id() * width_ + INDEX(loop_num)];
143 uint32_t prev = tp[0];
144 uint32_t next = prev | BIT(loop_num);
145 tp[0] = next;
146 return next != prev;
147 }
148
149 // Tf = Tf | B
SetForwardMark(Node * to,int loop_num)150 bool SetForwardMark(Node* to, int loop_num) {
151 uint32_t* tp = &forward_[to->id() * width_ + INDEX(loop_num)];
152 uint32_t prev = tp[0];
153 uint32_t next = prev | BIT(loop_num);
154 tp[0] = next;
155 return next != prev;
156 }
157
158 // Tf = Tf | (Ff & Tb)
PropagateForwardMarks(Node * from,Node * to)159 bool PropagateForwardMarks(Node* from, Node* to) {
160 if (from == to) return false;
161 bool change = false;
162 int findex = from->id() * width_;
163 int tindex = to->id() * width_;
164 for (int i = 0; i < width_; i++) {
165 uint32_t marks = backward_[tindex + i] & forward_[findex + i];
166 uint32_t prev = forward_[tindex + i];
167 uint32_t next = prev | marks;
168 forward_[tindex + i] = next;
169 if (!change && (prev != next)) change = true;
170 }
171 return change;
172 }
173
IsInLoop(Node * node,int loop_num)174 bool IsInLoop(Node* node, int loop_num) {
175 int offset = node->id() * width_ + INDEX(loop_num);
176 return backward_[offset] & forward_[offset] & BIT(loop_num);
177 }
178
179 // Propagate marks backward from loop headers.
PropagateBackward()180 void PropagateBackward() {
181 ResizeBackwardMarks();
182 SetBackwardMark(end_, 0);
183 Queue(end_);
184
185 while (!queue_.empty()) {
186 Node* node = queue_.front();
187 info(node);
188 queue_.pop_front();
189 queued_.Set(node, false);
190
191 int loop_num = -1;
192 // Setup loop headers first.
193 if (node->opcode() == IrOpcode::kLoop) {
194 // found the loop node first.
195 loop_num = CreateLoopInfo(node);
196 } else if (NodeProperties::IsPhi(node)) {
197 // found a phi first.
198 Node* merge = node->InputAt(node->InputCount() - 1);
199 if (merge->opcode() == IrOpcode::kLoop) {
200 loop_num = CreateLoopInfo(merge);
201 }
202 } else if (node->opcode() == IrOpcode::kLoopExit) {
203 // Intentionally ignore return value. Loop exit node marks
204 // are propagated normally.
205 CreateLoopInfo(node->InputAt(1));
206 } else if (node->opcode() == IrOpcode::kLoopExitValue ||
207 node->opcode() == IrOpcode::kLoopExitEffect) {
208 Node* loop_exit = NodeProperties::GetControlInput(node);
209 // Intentionally ignore return value. Loop exit node marks
210 // are propagated normally.
211 CreateLoopInfo(loop_exit->InputAt(1));
212 }
213
214 // Propagate marks backwards from this node.
215 for (int i = 0; i < node->InputCount(); i++) {
216 Node* input = node->InputAt(i);
217 if (IsBackedge(node, i)) {
218 // Only propagate the loop mark on backedges.
219 if (SetBackwardMark(input, loop_num)) Queue(input);
220 } else {
221 // Entry or normal edge. Propagate all marks except loop_num.
222 if (PropagateBackwardMarks(node, input, loop_num)) Queue(input);
223 }
224 }
225 }
226 }
227
228 // Make a new loop if necessary for the given node.
CreateLoopInfo(Node * node)229 int CreateLoopInfo(Node* node) {
230 DCHECK_EQ(IrOpcode::kLoop, node->opcode());
231 int loop_num = LoopNum(node);
232 if (loop_num > 0) return loop_num;
233
234 loop_num = ++loops_found_;
235 if (INDEX(loop_num) >= width_) ResizeBackwardMarks();
236
237 // Create a new loop.
238 loops_.push_back({node, nullptr, nullptr, nullptr, nullptr});
239 loop_tree_->NewLoop();
240 SetLoopMarkForLoopHeader(node, loop_num);
241 return loop_num;
242 }
243
SetLoopMark(Node * node,int loop_num)244 void SetLoopMark(Node* node, int loop_num) {
245 info(node); // create the NodeInfo
246 SetBackwardMark(node, loop_num);
247 loop_tree_->node_to_loop_num_[node->id()] = loop_num;
248 }
249
SetLoopMarkForLoopHeader(Node * node,int loop_num)250 void SetLoopMarkForLoopHeader(Node* node, int loop_num) {
251 DCHECK_EQ(IrOpcode::kLoop, node->opcode());
252 SetLoopMark(node, loop_num);
253 for (Node* use : node->uses()) {
254 if (NodeProperties::IsPhi(use)) {
255 SetLoopMark(use, loop_num);
256 }
257
258 // Do not keep the loop alive if it does not have any backedges.
259 if (node->InputCount() <= 1) continue;
260
261 if (use->opcode() == IrOpcode::kLoopExit) {
262 SetLoopMark(use, loop_num);
263 for (Node* exit_use : use->uses()) {
264 if (exit_use->opcode() == IrOpcode::kLoopExitValue ||
265 exit_use->opcode() == IrOpcode::kLoopExitEffect) {
266 SetLoopMark(exit_use, loop_num);
267 }
268 }
269 }
270 }
271 }
272
ResizeBackwardMarks()273 void ResizeBackwardMarks() {
274 int new_width = width_ + 1;
275 int max = num_nodes();
276 uint32_t* new_backward = zone_->NewArray<uint32_t>(new_width * max);
277 memset(new_backward, 0, new_width * max * sizeof(uint32_t));
278 if (width_ > 0) { // copy old matrix data.
279 for (int i = 0; i < max; i++) {
280 uint32_t* np = &new_backward[i * new_width];
281 uint32_t* op = &backward_[i * width_];
282 for (int j = 0; j < width_; j++) np[j] = op[j];
283 }
284 }
285 width_ = new_width;
286 backward_ = new_backward;
287 }
288
ResizeForwardMarks()289 void ResizeForwardMarks() {
290 int max = num_nodes();
291 forward_ = zone_->NewArray<uint32_t>(width_ * max);
292 memset(forward_, 0, width_ * max * sizeof(uint32_t));
293 }
294
295 // Propagate marks forward from loops.
PropagateForward()296 void PropagateForward() {
297 ResizeForwardMarks();
298 for (TempLoopInfo& li : loops_) {
299 SetForwardMark(li.header, LoopNum(li.header));
300 Queue(li.header);
301 }
302 // Propagate forward on paths that were backward reachable from backedges.
303 while (!queue_.empty()) {
304 Node* node = queue_.front();
305 queue_.pop_front();
306 queued_.Set(node, false);
307 for (Edge edge : node->use_edges()) {
308 Node* use = edge.from();
309 if (!IsBackedge(use, edge.index())) {
310 if (PropagateForwardMarks(node, use)) Queue(use);
311 }
312 }
313 }
314 }
315
IsLoopHeaderNode(Node * node)316 bool IsLoopHeaderNode(Node* node) {
317 return node->opcode() == IrOpcode::kLoop || NodeProperties::IsPhi(node);
318 }
319
IsLoopExitNode(Node * node)320 bool IsLoopExitNode(Node* node) {
321 return node->opcode() == IrOpcode::kLoopExit ||
322 node->opcode() == IrOpcode::kLoopExitValue ||
323 node->opcode() == IrOpcode::kLoopExitEffect;
324 }
325
IsBackedge(Node * use,int index)326 bool IsBackedge(Node* use, int index) {
327 if (LoopNum(use) <= 0) return false;
328 if (NodeProperties::IsPhi(use)) {
329 return index != NodeProperties::FirstControlIndex(use) &&
330 index != kAssumedLoopEntryIndex;
331 } else if (use->opcode() == IrOpcode::kLoop) {
332 return index != kAssumedLoopEntryIndex;
333 }
334 DCHECK(IsLoopExitNode(use));
335 return false;
336 }
337
LoopNum(Node * node)338 int LoopNum(Node* node) { return loop_tree_->node_to_loop_num_[node->id()]; }
339
info(Node * node)340 NodeInfo& info(Node* node) {
341 NodeInfo& i = info_[node->id()];
342 if (i.node == nullptr) i.node = node;
343 return i;
344 }
345
Queue(Node * node)346 void Queue(Node* node) {
347 if (!queued_.Get(node)) {
348 queue_.push_back(node);
349 queued_.Set(node, true);
350 }
351 }
352
AddNodeToLoop(NodeInfo * node_info,TempLoopInfo * loop,int loop_num)353 void AddNodeToLoop(NodeInfo* node_info, TempLoopInfo* loop, int loop_num) {
354 if (LoopNum(node_info->node) == loop_num) {
355 if (IsLoopHeaderNode(node_info->node)) {
356 node_info->next = loop->header_list;
357 loop->header_list = node_info;
358 } else {
359 DCHECK(IsLoopExitNode(node_info->node));
360 node_info->next = loop->exit_list;
361 loop->exit_list = node_info;
362 }
363 } else {
364 node_info->next = loop->body_list;
365 loop->body_list = node_info;
366 }
367 }
368
FinishLoopTree()369 void FinishLoopTree() {
370 DCHECK(loops_found_ == static_cast<int>(loops_.size()));
371 DCHECK(loops_found_ == static_cast<int>(loop_tree_->all_loops_.size()));
372
373 // Degenerate cases.
374 if (loops_found_ == 0) return;
375 if (loops_found_ == 1) return FinishSingleLoop();
376
377 for (int i = 1; i <= loops_found_; i++) ConnectLoopTree(i);
378
379 size_t count = 0;
380 // Place the node into the innermost nested loop of which it is a member.
381 for (NodeInfo& ni : info_) {
382 if (ni.node == nullptr) continue;
383
384 TempLoopInfo* innermost = nullptr;
385 int innermost_index = 0;
386 int pos = ni.node->id() * width_;
387 // Search the marks word by word.
388 for (int i = 0; i < width_; i++) {
389 uint32_t marks = backward_[pos + i] & forward_[pos + i];
390
391 for (int j = 0; j < 32; j++) {
392 if (marks & (1u << j)) {
393 int loop_num = i * 32 + j;
394 if (loop_num == 0) continue;
395 TempLoopInfo* loop = &loops_[loop_num - 1];
396 if (innermost == nullptr ||
397 loop->loop->depth_ > innermost->loop->depth_) {
398 innermost = loop;
399 innermost_index = loop_num;
400 }
401 }
402 }
403 }
404 if (innermost == nullptr) continue;
405
406 // Return statements should never be found by forward or backward walk.
407 CHECK(ni.node->opcode() != IrOpcode::kReturn);
408
409 AddNodeToLoop(&ni, innermost, innermost_index);
410 count++;
411 }
412
413 // Serialize the node lists for loops into the loop tree.
414 loop_tree_->loop_nodes_.reserve(count);
415 for (LoopTree::Loop* loop : loop_tree_->outer_loops_) {
416 SerializeLoop(loop);
417 }
418 }
419
420 // Handle the simpler case of a single loop (no checks for nesting necessary).
FinishSingleLoop()421 void FinishSingleLoop() {
422 // Place nodes into the loop header and body.
423 TempLoopInfo* li = &loops_[0];
424 li->loop = &loop_tree_->all_loops_[0];
425 loop_tree_->SetParent(nullptr, li->loop);
426 size_t count = 0;
427 for (NodeInfo& ni : info_) {
428 if (ni.node == nullptr || !IsInLoop(ni.node, 1)) continue;
429
430 // Return statements should never be found by forward or backward walk.
431 CHECK(ni.node->opcode() != IrOpcode::kReturn);
432
433 AddNodeToLoop(&ni, li, 1);
434 count++;
435 }
436
437 // Serialize the node lists for the loop into the loop tree.
438 loop_tree_->loop_nodes_.reserve(count);
439 SerializeLoop(li->loop);
440 }
441
442 // Recursively serialize the list of header nodes and body nodes
443 // so that nested loops occupy nested intervals.
SerializeLoop(LoopTree::Loop * loop)444 void SerializeLoop(LoopTree::Loop* loop) {
445 int loop_num = loop_tree_->LoopNum(loop);
446 TempLoopInfo& li = loops_[loop_num - 1];
447
448 // Serialize the header.
449 loop->header_start_ = static_cast<int>(loop_tree_->loop_nodes_.size());
450 for (NodeInfo* ni = li.header_list; ni != nullptr; ni = ni->next) {
451 loop_tree_->loop_nodes_.push_back(ni->node);
452 loop_tree_->node_to_loop_num_[ni->node->id()] = loop_num;
453 }
454
455 // Serialize the body.
456 loop->body_start_ = static_cast<int>(loop_tree_->loop_nodes_.size());
457 for (NodeInfo* ni = li.body_list; ni != nullptr; ni = ni->next) {
458 loop_tree_->loop_nodes_.push_back(ni->node);
459 loop_tree_->node_to_loop_num_[ni->node->id()] = loop_num;
460 }
461
462 // Serialize nested loops.
463 for (LoopTree::Loop* child : loop->children_) SerializeLoop(child);
464
465 // Serialize the exits.
466 loop->exits_start_ = static_cast<int>(loop_tree_->loop_nodes_.size());
467 for (NodeInfo* ni = li.exit_list; ni != nullptr; ni = ni->next) {
468 loop_tree_->loop_nodes_.push_back(ni->node);
469 loop_tree_->node_to_loop_num_[ni->node->id()] = loop_num;
470 }
471
472 loop->exits_end_ = static_cast<int>(loop_tree_->loop_nodes_.size());
473 }
474
475 // Connect the LoopTree loops to their parents recursively.
ConnectLoopTree(int loop_num)476 LoopTree::Loop* ConnectLoopTree(int loop_num) {
477 TempLoopInfo& li = loops_[loop_num - 1];
478 if (li.loop != nullptr) return li.loop;
479
480 NodeInfo& ni = info(li.header);
481 LoopTree::Loop* parent = nullptr;
482 for (int i = 1; i <= loops_found_; i++) {
483 if (i == loop_num) continue;
484 if (IsInLoop(ni.node, i)) {
485 // recursively create potential parent loops first.
486 LoopTree::Loop* upper = ConnectLoopTree(i);
487 if (parent == nullptr || upper->depth_ > parent->depth_) {
488 parent = upper;
489 }
490 }
491 }
492 li.loop = &loop_tree_->all_loops_[loop_num - 1];
493 loop_tree_->SetParent(parent, li.loop);
494 return li.loop;
495 }
496
PrintLoop(LoopTree::Loop * loop)497 void PrintLoop(LoopTree::Loop* loop) {
498 for (int i = 0; i < loop->depth_; i++) PrintF(" ");
499 PrintF("Loop depth = %d ", loop->depth_);
500 int i = loop->header_start_;
501 while (i < loop->body_start_) {
502 PrintF(" H#%d", loop_tree_->loop_nodes_[i++]->id());
503 }
504 while (i < loop->exits_start_) {
505 PrintF(" B#%d", loop_tree_->loop_nodes_[i++]->id());
506 }
507 while (i < loop->exits_end_) {
508 PrintF(" E#%d", loop_tree_->loop_nodes_[i++]->id());
509 }
510 PrintF("\n");
511 for (LoopTree::Loop* child : loop->children_) PrintLoop(child);
512 }
513 };
514
515
BuildLoopTree(Graph * graph,Zone * zone)516 LoopTree* LoopFinder::BuildLoopTree(Graph* graph, Zone* zone) {
517 LoopTree* loop_tree =
518 new (graph->zone()) LoopTree(graph->NodeCount(), graph->zone());
519 LoopFinderImpl finder(graph, loop_tree, zone);
520 finder.Run();
521 if (FLAG_trace_turbo_loop) {
522 finder.Print();
523 }
524 return loop_tree;
525 }
526
527
HeaderNode(Loop * loop)528 Node* LoopTree::HeaderNode(Loop* loop) {
529 Node* first = *HeaderNodes(loop).begin();
530 if (first->opcode() == IrOpcode::kLoop) return first;
531 DCHECK(IrOpcode::IsPhiOpcode(first->opcode()));
532 Node* header = NodeProperties::GetControlInput(first);
533 DCHECK_EQ(IrOpcode::kLoop, header->opcode());
534 return header;
535 }
536
537 } // namespace compiler
538 } // namespace internal
539 } // namespace v8
540