V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
loop-analysis.cc
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.
53  public:
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 
68  void Run() {
69  PropagateBackward();
70  PropagateForward();
71  FinishLoopTree();
72  }
73 
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_;
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 
120  int num_nodes() {
121  return static_cast<int>(loop_tree_->node_to_loop_num_.size());
122  }
123 
124  // Tb = Tb | (Fb - 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
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
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)
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 
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.
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.
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 
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 
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 
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 
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.
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 
316  bool IsLoopHeaderNode(Node* node) {
317  return node->opcode() == IrOpcode::kLoop || NodeProperties::IsPhi(node);
318  }
319 
320  bool IsLoopExitNode(Node* node) {
321  return node->opcode() == IrOpcode::kLoopExit ||
322  node->opcode() == IrOpcode::kLoopExitValue ||
323  node->opcode() == IrOpcode::kLoopExitEffect;
324  }
325 
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 
338  int LoopNum(Node* node) { return loop_tree_->node_to_loop_num_[node->id()]; }
339 
340  NodeInfo& info(Node* node) {
341  NodeInfo& i = info_[node->id()];
342  if (i.node == nullptr) i.node = node;
343  return i;
344  }
345 
346  void Queue(Node* node) {
347  if (!queued_.Get(node)) {
348  queue_.push_back(node);
349  queued_.Set(node, true);
350  }
351  }
352 
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 
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).
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.
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.
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 
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 
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 
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
Definition: libplatform.h:13