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