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