OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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/common-operator.h" |
| 6 #include "src/compiler/graph.h" |
| 7 #include "src/compiler/loop-peeling.h" |
| 8 #include "src/compiler/node.h" |
| 9 #include "src/compiler/node-marker.h" |
| 10 #include "src/compiler/node-properties-inl.h" |
| 11 #include "src/zone.h" |
| 12 |
| 13 // Loop peeling is an optimization that copies the body of a loop, creating |
| 14 // a new copy of the body called the "peeled iteration" that represents the |
| 15 // first iteration. Beginning with a loop as follows: |
| 16 |
| 17 // E |
| 18 // | A |
| 19 // | | (backedges) |
| 20 // | +---------------|---------------------------------+ |
| 21 // | | +-------------|-------------------------------+ | |
| 22 // | | | | +--------+ | | |
| 23 // | | | | | +----+ | | | |
| 24 // | | | | | | | | | | |
| 25 // ( Loop )<-------- ( phiA ) | | | | |
| 26 // | | | | | | |
| 27 // ((======P=================U=======|=|=====)) | | |
| 28 // (( | | )) | | |
| 29 // (( X <---------------------+ | )) | | |
| 30 // (( | )) | | |
| 31 // (( body | )) | | |
| 32 // (( | )) | | |
| 33 // (( Y <-----------------------+ )) | | |
| 34 // (( )) | | |
| 35 // ((===K====L====M==========================)) | | |
| 36 // | | | | | |
| 37 // | | +-----------------------------------------+ | |
| 38 // | +------------------------------------------------+ |
| 39 // | |
| 40 // exit |
| 41 |
| 42 // The body of the loop is duplicated so that all nodes considered "inside" |
| 43 // the loop (e.g. {P, U, X, Y, K, L, M}) have a corresponding copies in the |
| 44 // peeled iteration (e.g. {P', U', X', Y', K', L', M'}). What were considered |
| 45 // backedges of the loop correspond to edges from the peeled iteration to |
| 46 // the main loop body, with multiple backedges requiring a merge. |
| 47 |
| 48 // Similarly, any exits from the loop body need to be merged with "exits" |
| 49 // from the peeled iteration, resulting in the graph as follows: |
| 50 |
| 51 // E |
| 52 // | A |
| 53 // | | |
| 54 // ((=====P'================U'===============)) |
| 55 // (( )) |
| 56 // (( X'<-------------+ )) |
| 57 // (( | )) |
| 58 // (( peeled iteration | )) |
| 59 // (( | )) |
| 60 // (( Y'<-----------+ | )) |
| 61 // (( | | )) |
| 62 // ((===K'===L'====M'======|=|===============)) |
| 63 // | | | | | |
| 64 // +--------+ +-+ +-+ | | |
| 65 // | | | | | |
| 66 // | Merge <------phi |
| 67 // | | | |
| 68 // | +-----+ | |
| 69 // | | | (backedges) |
| 70 // | | +---------------|---------------------------------+ |
| 71 // | | | +-------------|-------------------------------+ | |
| 72 // | | | | | +--------+ | | |
| 73 // | | | | | | +----+ | | | |
| 74 // | | | | | | | | | | | |
| 75 // | ( Loop )<-------- ( phiA ) | | | | |
| 76 // | | | | | | | |
| 77 // | ((======P=================U=======|=|=====)) | | |
| 78 // | (( | | )) | | |
| 79 // | (( X <---------------------+ | )) | | |
| 80 // | (( | )) | | |
| 81 // | (( body | )) | | |
| 82 // | (( | )) | | |
| 83 // | (( Y <-----------------------+ )) | | |
| 84 // | (( )) | | |
| 85 // | ((===K====L====M==========================)) | | |
| 86 // | | | | | | |
| 87 // | | | +-----------------------------------------+ | |
| 88 // | | +------------------------------------------------+ |
| 89 // | | |
| 90 // | | |
| 91 // +----+ +-+ |
| 92 // | | |
| 93 // Merge |
| 94 // | |
| 95 // exit |
| 96 |
| 97 // Note that the boxes ((===)) above are not explicitly represented in the |
| 98 // graph, but are instead computed by the {LoopFinder}. |
| 99 |
| 100 namespace v8 { |
| 101 namespace internal { |
| 102 namespace compiler { |
| 103 |
| 104 struct Peeling { |
| 105 // Maps a node to its index in the {pairs} vector. |
| 106 NodeMarker<size_t> node_map; |
| 107 // The vector which contains the mapped nodes. |
| 108 NodeVector* pairs; |
| 109 |
| 110 Peeling(Graph* graph, Zone* tmp_zone, size_t max, NodeVector* p) |
| 111 : node_map(graph, static_cast<uint32_t>(max)), pairs(p) {} |
| 112 |
| 113 Node* map(Node* node) { |
| 114 if (node_map.Get(node) == 0) return node; |
| 115 return pairs->at(node_map.Get(node)); |
| 116 } |
| 117 |
| 118 void Insert(Node* original, Node* copy) { |
| 119 node_map.Set(original, 1 + pairs->size()); |
| 120 pairs->push_back(original); |
| 121 pairs->push_back(copy); |
| 122 } |
| 123 |
| 124 void CopyNodes(Graph* graph, Zone* tmp_zone, Node* dead, NodeRange nodes) { |
| 125 NodeVector inputs(tmp_zone); |
| 126 // Copy all the nodes first. |
| 127 for (Node* node : nodes) { |
| 128 inputs.clear(); |
| 129 for (Node* input : node->inputs()) inputs.push_back(map(input)); |
| 130 Insert(node, graph->NewNode(node->op(), node->InputCount(), &inputs[0])); |
| 131 } |
| 132 |
| 133 // Fix remaining inputs of the copies. |
| 134 for (Node* original : nodes) { |
| 135 Node* copy = pairs->at(node_map.Get(original)); |
| 136 for (int i = 0; i < copy->InputCount(); i++) { |
| 137 copy->ReplaceInput(i, map(original->InputAt(i))); |
| 138 } |
| 139 } |
| 140 } |
| 141 |
| 142 bool Marked(Node* node) { return node_map.Get(node) > 0; } |
| 143 }; |
| 144 |
| 145 |
| 146 class PeeledIterationImpl : public PeeledIteration { |
| 147 public: |
| 148 NodeVector node_pairs_; |
| 149 explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {} |
| 150 }; |
| 151 |
| 152 |
| 153 Node* PeeledIteration::map(Node* node) { |
| 154 // TODO(turbofan): we use a simple linear search, since the peeled iteration |
| 155 // is really only used in testing. |
| 156 PeeledIterationImpl* impl = static_cast<PeeledIterationImpl*>(this); |
| 157 for (size_t i = 0; i < impl->node_pairs_.size(); i += 2) { |
| 158 if (impl->node_pairs_[i] == node) return impl->node_pairs_[i + 1]; |
| 159 } |
| 160 return node; |
| 161 } |
| 162 |
| 163 |
| 164 static const Operator* ResizeMergeOrPhi(CommonOperatorBuilder* common, |
| 165 const Operator* op, int size) { |
| 166 if (op->opcode() == IrOpcode::kPhi) { |
| 167 return common->Phi(OpParameter<MachineType>(op), size); |
| 168 } else if (op->opcode() == IrOpcode::kEffectPhi) { |
| 169 return common->EffectPhi(size); |
| 170 } else if (op->opcode() == IrOpcode::kMerge) { |
| 171 return common->Merge(size); |
| 172 } else if (op->opcode() == IrOpcode::kLoop) { |
| 173 return common->Loop(size); |
| 174 } else { |
| 175 UNREACHABLE(); |
| 176 return nullptr; |
| 177 } |
| 178 } |
| 179 |
| 180 |
| 181 PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common, |
| 182 LoopTree* loop_tree, LoopTree::Loop* loop, |
| 183 Zone* tmp_zone) { |
| 184 PeeledIterationImpl* iter = new (tmp_zone) PeeledIterationImpl(tmp_zone); |
| 185 Peeling peeling(graph, tmp_zone, loop->TotalSize() * 2 + 2, |
| 186 &iter->node_pairs_); |
| 187 |
| 188 //============================================================================ |
| 189 // Construct the peeled iteration. |
| 190 //============================================================================ |
| 191 Node* dead = graph->NewNode(common->Dead()); |
| 192 |
| 193 // Map the loop header nodes to their entry values. |
| 194 for (Node* node : loop_tree->HeaderNodes(loop)) { |
| 195 // TODO(titzer): assuming loop entry at index 0. |
| 196 peeling.Insert(node, node->InputAt(0)); |
| 197 } |
| 198 |
| 199 // Copy all the nodes of loop body for the peeled iteration. |
| 200 peeling.CopyNodes(graph, tmp_zone, dead, loop_tree->BodyNodes(loop)); |
| 201 |
| 202 //============================================================================ |
| 203 // Replace the entry to the loop with the output of the peeled iteration. |
| 204 //============================================================================ |
| 205 Node* loop_node = loop_tree->GetLoopControl(loop); |
| 206 Node* new_entry; |
| 207 int backedges = loop_node->InputCount() - 1; |
| 208 if (backedges > 1) { |
| 209 // Multiple backedges from original loop, therefore multiple output edges |
| 210 // from the peeled iteration. |
| 211 NodeVector inputs(tmp_zone); |
| 212 for (int i = 1; i < loop_node->InputCount(); i++) { |
| 213 inputs.push_back(peeling.map(loop_node->InputAt(i))); |
| 214 } |
| 215 Node* merge = |
| 216 graph->NewNode(common->Merge(backedges), backedges, &inputs[0]); |
| 217 |
| 218 // Merge values from the multiple output edges of the peeled iteration. |
| 219 for (Node* node : loop_tree->HeaderNodes(loop)) { |
| 220 if (node->opcode() == IrOpcode::kLoop) continue; // already done. |
| 221 inputs.clear(); |
| 222 for (int i = 0; i < backedges; i++) { |
| 223 inputs.push_back(peeling.map(node->InputAt(1 + i))); |
| 224 } |
| 225 for (Node* input : inputs) { |
| 226 if (input != inputs[0]) { // Non-redundant phi. |
| 227 inputs.push_back(merge); |
| 228 const Operator* op = ResizeMergeOrPhi(common, node->op(), backedges); |
| 229 Node* phi = graph->NewNode(op, backedges + 1, &inputs[0]); |
| 230 node->ReplaceInput(0, phi); |
| 231 break; |
| 232 } |
| 233 } |
| 234 } |
| 235 new_entry = merge; |
| 236 } else { |
| 237 // Only one backedge, simply replace the input to loop with output of |
| 238 // peeling. |
| 239 for (Node* node : loop_tree->HeaderNodes(loop)) { |
| 240 node->ReplaceInput(0, peeling.map(node->InputAt(0))); |
| 241 } |
| 242 new_entry = peeling.map(loop_node->InputAt(1)); |
| 243 } |
| 244 loop_node->ReplaceInput(0, new_entry); |
| 245 |
| 246 //============================================================================ |
| 247 // Find the loop exit region. |
| 248 //============================================================================ |
| 249 NodeVector exits(tmp_zone); |
| 250 Node* end = NULL; |
| 251 for (Node* node : loop_tree->LoopNodes(loop)) { |
| 252 for (Node* use : node->uses()) { |
| 253 if (!loop_tree->Contains(loop, use)) { |
| 254 if (node->opcode() == IrOpcode::kBranch && |
| 255 (use->opcode() == IrOpcode::kIfTrue || |
| 256 use->opcode() == IrOpcode::kIfFalse)) { |
| 257 // This is a branch from inside the loop to outside the loop. |
| 258 exits.push_back(use); |
| 259 } |
| 260 } |
| 261 } |
| 262 } |
| 263 |
| 264 if (exits.size() == 0) return iter; // no exits => NTL |
| 265 |
| 266 if (exits.size() == 1) { |
| 267 // Only one exit, so {end} is that exit. |
| 268 end = exits[0]; |
| 269 } else { |
| 270 // {end} should be the common merge from the exits. |
| 271 NodeVector rets(tmp_zone); |
| 272 for (Node* exit : exits) { |
| 273 Node* found = NULL; |
| 274 for (Node* use : exit->uses()) { |
| 275 if (use->opcode() == IrOpcode::kMerge) { |
| 276 found = use; |
| 277 if (end == NULL) { |
| 278 end = found; |
| 279 } else { |
| 280 CHECK_EQ(end, found); // it should be unique! |
| 281 } |
| 282 } else if (use->opcode() == IrOpcode::kReturn) { |
| 283 found = use; |
| 284 rets.push_back(found); |
| 285 } |
| 286 } |
| 287 // There should be a merge or a return for each exit. |
| 288 CHECK_NE(NULL, found); |
| 289 } |
| 290 // Return nodes, the end merge, and the phis associated with the end merge |
| 291 // must be duplicated as well. |
| 292 for (Node* node : rets) exits.push_back(node); |
| 293 if (end != NULL) { |
| 294 exits.push_back(end); |
| 295 for (Node* use : end->uses()) { |
| 296 if (IrOpcode::IsPhiOpcode(use->opcode())) exits.push_back(use); |
| 297 } |
| 298 } |
| 299 } |
| 300 |
| 301 //============================================================================ |
| 302 // Duplicate the loop exit region and add a merge. |
| 303 //============================================================================ |
| 304 NodeRange exit_range(&exits[0], &exits[0] + exits.size()); |
| 305 peeling.CopyNodes(graph, tmp_zone, dead, exit_range); |
| 306 |
| 307 Node* merge = graph->NewNode(common->Merge(2), end, peeling.map(end)); |
| 308 end->ReplaceUses(merge); |
| 309 merge->ReplaceInput(0, end); // HULK SMASH!! |
| 310 |
| 311 // Find and update all the edges into either the loop or exit region. |
| 312 for (int i = 0; i < 2; i++) { |
| 313 NodeRange range = i == 0 ? loop_tree->LoopNodes(loop) : exit_range; |
| 314 ZoneVector<Edge> value_edges(tmp_zone); |
| 315 ZoneVector<Edge> effect_edges(tmp_zone); |
| 316 |
| 317 for (Node* node : range) { |
| 318 // Gather value and effect edges from outside the region. |
| 319 for (Edge edge : node->use_edges()) { |
| 320 if (!peeling.Marked(edge.from())) { |
| 321 // Edge from outside the loop into the region. |
| 322 if (NodeProperties::IsValueEdge(edge) || |
| 323 NodeProperties::IsContextEdge(edge)) { |
| 324 value_edges.push_back(edge); |
| 325 } else if (NodeProperties::IsEffectEdge(edge)) { |
| 326 effect_edges.push_back(edge); |
| 327 } else { |
| 328 // don't do anything for control edges. |
| 329 // TODO(titzer): should update control edges to peeled? |
| 330 } |
| 331 } |
| 332 } |
| 333 |
| 334 // Update all the value and effect edges at once. |
| 335 if (!value_edges.empty()) { |
| 336 // TODO(titzer): machine type is wrong here. |
| 337 Node* phi = graph->NewNode(common->Phi(kMachAnyTagged, 2), node, |
| 338 peeling.map(node), merge); |
| 339 for (Edge edge : value_edges) edge.UpdateTo(phi); |
| 340 value_edges.clear(); |
| 341 } |
| 342 if (!effect_edges.empty()) { |
| 343 Node* effect_phi = graph->NewNode(common->EffectPhi(2), node, |
| 344 peeling.map(node), merge); |
| 345 for (Edge edge : effect_edges) edge.UpdateTo(effect_phi); |
| 346 effect_edges.clear(); |
| 347 } |
| 348 } |
| 349 } |
| 350 |
| 351 return iter; |
| 352 } |
| 353 |
| 354 } // namespace compiler |
| 355 } // namespace internal |
| 356 } // namespace v8 |
OLD | NEW |