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