Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame^] | 1 | // Copyright 2013 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/graph.h" |
| 6 | #include "src/compiler/loop-analysis.h" |
| 7 | #include "src/compiler/node.h" |
| 8 | #include "src/compiler/node-properties-inl.h" |
| 9 | #include "src/zone.h" |
| 10 | |
| 11 | namespace v8 { |
| 12 | namespace internal { |
| 13 | namespace compiler { |
| 14 | |
| 15 | typedef uint32_t LoopMarks; |
| 16 | |
| 17 | |
| 18 | // TODO(titzer): don't assume entry edges have a particular index. |
| 19 | // TODO(titzer): use a BitMatrix to generalize this algorithm. |
| 20 | static const size_t kMaxLoops = 31; |
| 21 | static const int kAssumedLoopEntryIndex = 0; // assume loops are entered here. |
| 22 | static const LoopMarks kVisited = 1; // loop #0 is reserved. |
| 23 | |
| 24 | |
| 25 | // Temporary information for each node during marking. |
| 26 | struct NodeInfo { |
| 27 | Node* node; |
| 28 | NodeInfo* next; // link in chaining loop members |
| 29 | LoopMarks forward; // accumulated marks in the forward direction |
| 30 | LoopMarks backward; // accumulated marks in the backward direction |
| 31 | LoopMarks loop_mark; // loop mark for header nodes; encodes loop_num |
| 32 | |
| 33 | bool MarkBackward(LoopMarks bw) { |
| 34 | LoopMarks prev = backward; |
| 35 | LoopMarks next = backward | bw; |
| 36 | backward = next; |
| 37 | return prev != next; |
| 38 | } |
| 39 | |
| 40 | bool MarkForward(LoopMarks fw) { |
| 41 | LoopMarks prev = forward; |
| 42 | LoopMarks next = forward | fw; |
| 43 | forward = next; |
| 44 | return prev != next; |
| 45 | } |
| 46 | |
| 47 | bool IsInLoop(size_t loop_num) { |
| 48 | DCHECK(loop_num > 0 && loop_num <= 31); |
| 49 | return forward & backward & (1 << loop_num); |
| 50 | } |
| 51 | |
| 52 | bool IsLoopHeader() { return loop_mark != 0; } |
| 53 | bool IsInAnyLoop() { return (forward & backward) > kVisited; } |
| 54 | |
| 55 | bool IsInHeaderForLoop(size_t loop_num) { |
| 56 | DCHECK(loop_num > 0); |
| 57 | return loop_mark == (kVisited | (1 << loop_num)); |
| 58 | } |
| 59 | }; |
| 60 | |
| 61 | |
| 62 | // Temporary loop info needed during traversal and building the loop tree. |
| 63 | struct LoopInfo { |
| 64 | Node* header; |
| 65 | NodeInfo* header_list; |
| 66 | NodeInfo* body_list; |
| 67 | LoopTree::Loop* loop; |
| 68 | }; |
| 69 | |
| 70 | |
| 71 | static const NodeInfo kEmptyNodeInfo = {nullptr, nullptr, 0, 0, 0}; |
| 72 | |
| 73 | |
| 74 | // Encapsulation of the loop finding algorithm. |
| 75 | // ----------------------------------------------------------------------------- |
| 76 | // Conceptually, the contents of a loop are those nodes that are "between" the |
| 77 | // loop header and the backedges of the loop. Graphs in the soup of nodes can |
| 78 | // form improper cycles, so standard loop finding algorithms that work on CFGs |
| 79 | // aren't sufficient. However, in valid TurboFan graphs, all cycles involve |
| 80 | // either a {Loop} node or a phi. The {Loop} node itself and its accompanying |
| 81 | // phis are treated together as a set referred to here as the loop header. |
| 82 | // This loop finding algorithm works by traversing the graph in two directions, |
| 83 | // first from nodes to their inputs, starting at {end}, then in the reverse |
| 84 | // direction, from nodes to their uses, starting at loop headers. |
| 85 | // 1 bit per loop per node per direction are required during the marking phase. |
| 86 | // To handle nested loops correctly, the algorithm must filter some reachability |
| 87 | // marks on edges into/out-of the loop header nodes. |
| 88 | class LoopFinderImpl { |
| 89 | public: |
| 90 | LoopFinderImpl(Graph* graph, LoopTree* loop_tree, Zone* zone) |
| 91 | : end_(graph->end()), |
| 92 | queue_(zone), |
| 93 | queued_(graph, 2), |
| 94 | info_(graph->NodeCount(), kEmptyNodeInfo, zone), |
| 95 | loops_(zone), |
| 96 | loop_tree_(loop_tree), |
| 97 | loops_found_(0) {} |
| 98 | |
| 99 | void Run() { |
| 100 | PropagateBackward(); |
| 101 | PropagateForward(); |
| 102 | FinishLoopTree(); |
| 103 | } |
| 104 | |
| 105 | void Print() { |
| 106 | // Print out the results. |
| 107 | for (NodeInfo& ni : info_) { |
| 108 | if (ni.node == nullptr) continue; |
| 109 | for (size_t i = 1; i <= loops_.size(); i++) { |
| 110 | if (ni.IsInLoop(i)) { |
| 111 | PrintF("X"); |
| 112 | } else if (ni.forward & (1 << i)) { |
| 113 | PrintF("/"); |
| 114 | } else if (ni.backward & (1 << i)) { |
| 115 | PrintF("\\"); |
| 116 | } else { |
| 117 | PrintF(" "); |
| 118 | } |
| 119 | } |
| 120 | PrintF(" #%d:%s\n", ni.node->id(), ni.node->op()->mnemonic()); |
| 121 | } |
| 122 | |
| 123 | int i = 0; |
| 124 | for (LoopInfo& li : loops_) { |
| 125 | PrintF("Loop %d headed at #%d\n", i, li.header->id()); |
| 126 | i++; |
| 127 | } |
| 128 | |
| 129 | for (LoopTree::Loop* loop : loop_tree_->outer_loops_) { |
| 130 | PrintLoop(loop); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | private: |
| 135 | Node* end_; |
| 136 | NodeDeque queue_; |
| 137 | NodeMarker<bool> queued_; |
| 138 | ZoneVector<NodeInfo> info_; |
| 139 | ZoneVector<LoopInfo> loops_; |
| 140 | LoopTree* loop_tree_; |
| 141 | size_t loops_found_; |
| 142 | |
| 143 | // Propagate marks backward from loop headers. |
| 144 | void PropagateBackward() { |
| 145 | PropagateBackward(end_, kVisited); |
| 146 | |
| 147 | while (!queue_.empty()) { |
| 148 | Node* node = queue_.front(); |
| 149 | queue_.pop_front(); |
| 150 | queued_.Set(node, false); |
| 151 | |
| 152 | // Setup loop headers first. |
| 153 | if (node->opcode() == IrOpcode::kLoop) { |
| 154 | // found the loop node first. |
| 155 | CreateLoopInfo(node); |
| 156 | } else if (node->opcode() == IrOpcode::kPhi || |
| 157 | node->opcode() == IrOpcode::kEffectPhi) { |
| 158 | // found a phi first. |
| 159 | Node* merge = node->InputAt(node->InputCount() - 1); |
| 160 | if (merge->opcode() == IrOpcode::kLoop) CreateLoopInfo(merge); |
| 161 | } |
| 162 | |
| 163 | // Propagate reachability marks backwards from this node. |
| 164 | NodeInfo& ni = info(node); |
| 165 | if (ni.IsLoopHeader()) { |
| 166 | // Handle edges from loop header nodes specially. |
| 167 | for (int i = 0; i < node->InputCount(); i++) { |
| 168 | if (i == kAssumedLoopEntryIndex) { |
| 169 | // Don't propagate the loop mark backwards on the entry edge. |
| 170 | PropagateBackward(node->InputAt(0), |
| 171 | kVisited | (ni.backward & ~ni.loop_mark)); |
| 172 | } else { |
| 173 | // Only propagate the loop mark on backedges. |
| 174 | PropagateBackward(node->InputAt(i), ni.loop_mark); |
| 175 | } |
| 176 | } |
| 177 | } else { |
| 178 | // Propagate all loop marks backwards for a normal node. |
| 179 | for (Node* const input : node->inputs()) { |
| 180 | PropagateBackward(input, ni.backward); |
| 181 | } |
| 182 | } |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | // Make a new loop header for the given node. |
| 187 | void CreateLoopInfo(Node* node) { |
| 188 | NodeInfo& ni = info(node); |
| 189 | if (ni.IsLoopHeader()) return; // loop already set up. |
| 190 | |
| 191 | loops_found_++; |
| 192 | size_t loop_num = loops_.size() + 1; |
| 193 | CHECK(loops_found_ <= kMaxLoops); // TODO(titzer): don't crash. |
| 194 | // Create a new loop. |
| 195 | loops_.push_back({node, nullptr, nullptr, nullptr}); |
| 196 | loop_tree_->NewLoop(); |
| 197 | LoopMarks loop_mark = kVisited | (1 << loop_num); |
| 198 | ni.node = node; |
| 199 | ni.loop_mark = loop_mark; |
| 200 | |
| 201 | // Setup loop mark for phis attached to loop header. |
| 202 | for (Node* use : node->uses()) { |
| 203 | if (use->opcode() == IrOpcode::kPhi || |
| 204 | use->opcode() == IrOpcode::kEffectPhi) { |
| 205 | info(use).loop_mark = loop_mark; |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | // Propagate marks forward from loops. |
| 211 | void PropagateForward() { |
| 212 | for (LoopInfo& li : loops_) { |
| 213 | queued_.Set(li.header, true); |
| 214 | queue_.push_back(li.header); |
| 215 | NodeInfo& ni = info(li.header); |
| 216 | ni.forward = ni.loop_mark; |
| 217 | } |
| 218 | // Propagate forward on paths that were backward reachable from backedges. |
| 219 | while (!queue_.empty()) { |
| 220 | Node* node = queue_.front(); |
| 221 | queue_.pop_front(); |
| 222 | queued_.Set(node, false); |
| 223 | NodeInfo& ni = info(node); |
| 224 | for (Edge edge : node->use_edges()) { |
| 225 | Node* use = edge.from(); |
| 226 | NodeInfo& ui = info(use); |
| 227 | if (IsBackedge(use, ui, edge)) continue; // skip backedges. |
| 228 | LoopMarks both = ni.forward & ui.backward; |
| 229 | if (ui.MarkForward(both) && !queued_.Get(use)) { |
| 230 | queued_.Set(use, true); |
| 231 | queue_.push_back(use); |
| 232 | } |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | bool IsBackedge(Node* use, NodeInfo& ui, Edge& edge) { |
| 238 | // TODO(titzer): checking for backedges here is ugly. |
| 239 | if (!ui.IsLoopHeader()) return false; |
| 240 | if (edge.index() == kAssumedLoopEntryIndex) return false; |
| 241 | if (use->opcode() == IrOpcode::kPhi || |
| 242 | use->opcode() == IrOpcode::kEffectPhi) { |
| 243 | return !NodeProperties::IsControlEdge(edge); |
| 244 | } |
| 245 | return true; |
| 246 | } |
| 247 | |
| 248 | NodeInfo& info(Node* node) { |
| 249 | NodeInfo& i = info_[node->id()]; |
| 250 | if (i.node == nullptr) i.node = node; |
| 251 | return i; |
| 252 | } |
| 253 | |
| 254 | void PropagateBackward(Node* node, LoopMarks marks) { |
| 255 | if (info(node).MarkBackward(marks) && !queued_.Get(node)) { |
| 256 | queue_.push_back(node); |
| 257 | queued_.Set(node, true); |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | void FinishLoopTree() { |
| 262 | // Degenerate cases. |
| 263 | if (loops_.size() == 0) return; |
| 264 | if (loops_.size() == 1) return FinishSingleLoop(); |
| 265 | |
| 266 | for (size_t i = 1; i <= loops_.size(); i++) ConnectLoopTree(i); |
| 267 | |
| 268 | size_t count = 0; |
| 269 | // Place the node into the innermost nested loop of which it is a member. |
| 270 | for (NodeInfo& ni : info_) { |
| 271 | if (ni.node == nullptr || !ni.IsInAnyLoop()) continue; |
| 272 | |
| 273 | LoopInfo* innermost = nullptr; |
| 274 | size_t index = 0; |
| 275 | for (size_t i = 1; i <= loops_.size(); i++) { |
| 276 | if (ni.IsInLoop(i)) { |
| 277 | LoopInfo* loop = &loops_[i - 1]; |
| 278 | if (innermost == nullptr || |
| 279 | loop->loop->depth_ > innermost->loop->depth_) { |
| 280 | innermost = loop; |
| 281 | index = i; |
| 282 | } |
| 283 | } |
| 284 | } |
| 285 | if (ni.IsInHeaderForLoop(index)) { |
| 286 | ni.next = innermost->header_list; |
| 287 | innermost->header_list = ∋ |
| 288 | } else { |
| 289 | ni.next = innermost->body_list; |
| 290 | innermost->body_list = ∋ |
| 291 | } |
| 292 | count++; |
| 293 | } |
| 294 | |
| 295 | // Serialize the node lists for loops into the loop tree. |
| 296 | loop_tree_->loop_nodes_.reserve(count); |
| 297 | for (LoopTree::Loop* loop : loop_tree_->outer_loops_) { |
| 298 | SerializeLoop(loop); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | // Handle the simpler case of a single loop (no checks for nesting necessary). |
| 303 | void FinishSingleLoop() { |
| 304 | DCHECK(loops_.size() == 1); |
| 305 | DCHECK(loop_tree_->all_loops_.size() == 1); |
| 306 | |
| 307 | // Place nodes into the loop header and body. |
| 308 | LoopInfo* li = &loops_[0]; |
| 309 | li->loop = &loop_tree_->all_loops_[0]; |
| 310 | loop_tree_->SetParent(nullptr, li->loop); |
| 311 | size_t count = 0; |
| 312 | for (NodeInfo& ni : info_) { |
| 313 | if (ni.node == nullptr || !ni.IsInAnyLoop()) continue; |
| 314 | DCHECK(ni.IsInLoop(1)); |
| 315 | if (ni.IsInHeaderForLoop(1)) { |
| 316 | ni.next = li->header_list; |
| 317 | li->header_list = ∋ |
| 318 | } else { |
| 319 | ni.next = li->body_list; |
| 320 | li->body_list = ∋ |
| 321 | } |
| 322 | count++; |
| 323 | } |
| 324 | |
| 325 | // Serialize the node lists for the loop into the loop tree. |
| 326 | loop_tree_->loop_nodes_.reserve(count); |
| 327 | SerializeLoop(li->loop); |
| 328 | } |
| 329 | |
| 330 | // Recursively serialize the list of header nodes and body nodes |
| 331 | // so that nested loops occupy nested intervals. |
| 332 | void SerializeLoop(LoopTree::Loop* loop) { |
| 333 | size_t loop_num = loop_tree_->LoopNum(loop); |
| 334 | LoopInfo& li = loops_[loop_num - 1]; |
| 335 | |
| 336 | // Serialize the header. |
| 337 | loop->header_start_ = static_cast<int>(loop_tree_->loop_nodes_.size()); |
| 338 | for (NodeInfo* ni = li.header_list; ni != nullptr; ni = ni->next) { |
| 339 | loop_tree_->loop_nodes_.push_back(ni->node); |
| 340 | // TODO(titzer): lift loop count restriction. |
| 341 | loop_tree_->node_to_loop_num_[ni->node->id()] = |
| 342 | static_cast<uint8_t>(loop_num); |
| 343 | } |
| 344 | |
| 345 | // Serialize the body. |
| 346 | loop->body_start_ = static_cast<int>(loop_tree_->loop_nodes_.size()); |
| 347 | for (NodeInfo* ni = li.body_list; ni != nullptr; ni = ni->next) { |
| 348 | loop_tree_->loop_nodes_.push_back(ni->node); |
| 349 | // TODO(titzer): lift loop count restriction. |
| 350 | loop_tree_->node_to_loop_num_[ni->node->id()] = |
| 351 | static_cast<uint8_t>(loop_num); |
| 352 | } |
| 353 | |
| 354 | // Serialize nested loops. |
| 355 | for (LoopTree::Loop* child : loop->children_) SerializeLoop(child); |
| 356 | |
| 357 | loop->body_end_ = static_cast<int>(loop_tree_->loop_nodes_.size()); |
| 358 | } |
| 359 | |
| 360 | // Connect the LoopTree loops to their parents recursively. |
| 361 | LoopTree::Loop* ConnectLoopTree(size_t loop_num) { |
| 362 | LoopInfo& li = loops_[loop_num - 1]; |
| 363 | if (li.loop != nullptr) return li.loop; |
| 364 | |
| 365 | NodeInfo& ni = info(li.header); |
| 366 | LoopTree::Loop* parent = nullptr; |
| 367 | for (size_t i = 1; i <= loops_.size(); i++) { |
| 368 | if (i == loop_num) continue; |
| 369 | if (ni.IsInLoop(i)) { |
| 370 | // recursively create potential parent loops first. |
| 371 | LoopTree::Loop* upper = ConnectLoopTree(i); |
| 372 | if (parent == nullptr || upper->depth_ > parent->depth_) { |
| 373 | parent = upper; |
| 374 | } |
| 375 | } |
| 376 | } |
| 377 | li.loop = &loop_tree_->all_loops_[loop_num - 1]; |
| 378 | loop_tree_->SetParent(parent, li.loop); |
| 379 | return li.loop; |
| 380 | } |
| 381 | |
| 382 | void PrintLoop(LoopTree::Loop* loop) { |
| 383 | for (int i = 0; i < loop->depth_; i++) PrintF(" "); |
| 384 | PrintF("Loop depth = %d ", loop->depth_); |
| 385 | int i = loop->header_start_; |
| 386 | while (i < loop->body_start_) { |
| 387 | PrintF(" H#%d", loop_tree_->loop_nodes_[i++]->id()); |
| 388 | } |
| 389 | while (i < loop->body_end_) { |
| 390 | PrintF(" B#%d", loop_tree_->loop_nodes_[i++]->id()); |
| 391 | } |
| 392 | PrintF("\n"); |
| 393 | for (LoopTree::Loop* child : loop->children_) PrintLoop(child); |
| 394 | } |
| 395 | }; |
| 396 | |
| 397 | |
| 398 | LoopTree* LoopFinder::BuildLoopTree(Graph* graph, Zone* zone) { |
| 399 | LoopTree* loop_tree = |
| 400 | new (graph->zone()) LoopTree(graph->NodeCount(), graph->zone()); |
| 401 | LoopFinderImpl finder(graph, loop_tree, zone); |
| 402 | finder.Run(); |
| 403 | if (FLAG_trace_turbo_graph) { |
| 404 | finder.Print(); |
| 405 | } |
| 406 | return loop_tree; |
| 407 | } |
| 408 | |
| 409 | } // namespace compiler |
| 410 | } // namespace internal |
| 411 | } // namespace v8 |