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