Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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/v8.h" | |
| 6 | |
| 7 #include "src/compiler/access-builder.h" | |
| 8 #include "src/compiler/common-operator.h" | |
| 9 #include "src/compiler/graph.h" | |
| 10 #include "src/compiler/graph-visualizer.h" | |
| 11 #include "src/compiler/js-graph.h" | |
| 12 #include "src/compiler/js-operator.h" | |
| 13 #include "src/compiler/loop-analysis.h" | |
| 14 #include "src/compiler/node.h" | |
| 15 #include "src/compiler/opcodes.h" | |
| 16 #include "src/compiler/operator.h" | |
| 17 #include "src/compiler/schedule.h" | |
| 18 #include "src/compiler/scheduler.h" | |
| 19 #include "src/compiler/simplified-operator.h" | |
| 20 #include "src/compiler/verifier.h" | |
| 21 #include "test/cctest/cctest.h" | |
| 22 | |
| 23 using namespace v8::internal; | |
| 24 using namespace v8::internal::compiler; | |
| 25 | |
| 26 static Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0, | |
| 27 0, 1, 0, 0); | |
| 28 static Operator kIntLt(IrOpcode::kInt32LessThan, Operator::kPure, | |
| 29 "Int32LessThan", 2, 0, 0, 1, 0, 0); | |
| 30 static Operator kStore(IrOpcode::kStore, Operator::kNoProperties, "Store", 0, 2, | |
| 31 1, 0, 1, 0); | |
| 32 | |
| 33 static const int kNumLeafs = 4; | |
| 34 | |
| 35 // A helper for all tests dealing with LoopFinder. | |
| 36 class LoopFinderTester : HandleAndZoneScope { | |
| 37 public: | |
| 38 LoopFinderTester() | |
| 39 : isolate(main_isolate()), | |
| 40 common(main_zone()), | |
| 41 graph(main_zone()), | |
| 42 jsgraph(&graph, &common, NULL, NULL), | |
| 43 start(graph.NewNode(common.Start(1))), | |
| 44 end(graph.NewNode(common.End(), start)), | |
| 45 p0(graph.NewNode(common.Parameter(0), start)), | |
| 46 zero(jsgraph.Int32Constant(0)), | |
| 47 one(jsgraph.OneConstant()), | |
| 48 half(jsgraph.Constant(0.5)), | |
| 49 self(graph.NewNode(common.Int32Constant(0xaabbccdd))), | |
| 50 dead(graph.NewNode(common.Dead())), | |
| 51 loop_tree(NULL) { | |
| 52 graph.SetEnd(end); | |
| 53 graph.SetStart(start); | |
| 54 leaf[0] = zero; | |
| 55 leaf[1] = one; | |
| 56 leaf[2] = half; | |
| 57 leaf[3] = p0; | |
| 58 } | |
| 59 | |
| 60 Isolate* isolate; | |
| 61 CommonOperatorBuilder common; | |
| 62 Graph graph; | |
| 63 JSGraph jsgraph; | |
| 64 Node* start; | |
| 65 Node* end; | |
| 66 Node* p0; | |
| 67 Node* zero; | |
| 68 Node* one; | |
| 69 Node* half; | |
| 70 Node* self; | |
| 71 Node* dead; | |
| 72 Node* leaf[kNumLeafs]; | |
| 73 LoopTree* loop_tree; | |
| 74 | |
| 75 Node* Phi(Node* a) { | |
| 76 return SetSelfReferences(graph.NewNode(op(1, false), a, start)); | |
| 77 } | |
| 78 | |
| 79 Node* Phi(Node* a, Node* b) { | |
| 80 return SetSelfReferences(graph.NewNode(op(2, false), a, b, start)); | |
| 81 } | |
| 82 | |
| 83 Node* Phi(Node* a, Node* b, Node* c) { | |
| 84 return SetSelfReferences(graph.NewNode(op(3, false), a, b, c, start)); | |
| 85 } | |
| 86 | |
| 87 Node* Phi(Node* a, Node* b, Node* c, Node* d) { | |
| 88 return SetSelfReferences(graph.NewNode(op(4, false), a, b, c, d, start)); | |
| 89 } | |
| 90 | |
| 91 Node* EffectPhi(Node* a) { | |
| 92 return SetSelfReferences(graph.NewNode(op(1, true), a, start)); | |
| 93 } | |
| 94 | |
| 95 Node* EffectPhi(Node* a, Node* b) { | |
| 96 return SetSelfReferences(graph.NewNode(op(2, true), a, b, start)); | |
| 97 } | |
| 98 | |
| 99 Node* EffectPhi(Node* a, Node* b, Node* c) { | |
| 100 return SetSelfReferences(graph.NewNode(op(3, true), a, b, c, start)); | |
| 101 } | |
| 102 | |
| 103 Node* EffectPhi(Node* a, Node* b, Node* c, Node* d) { | |
| 104 return SetSelfReferences(graph.NewNode(op(4, true), a, b, c, d, start)); | |
| 105 } | |
| 106 | |
| 107 Node* SetSelfReferences(Node* node) { | |
| 108 for (Edge edge : node->input_edges()) { | |
| 109 if (edge.to() == self) node->ReplaceInput(edge.index(), node); | |
| 110 } | |
| 111 return node; | |
| 112 } | |
| 113 | |
| 114 const Operator* op(int count, bool effect) { | |
| 115 return effect ? common.EffectPhi(count) : common.Phi(kMachAnyTagged, count); | |
| 116 } | |
| 117 | |
| 118 Node* Return(Node* val, Node* effect, Node* control) { | |
| 119 Node* ret = graph.NewNode(common.Return(), val, effect, control); | |
| 120 end->ReplaceInput(0, ret); | |
| 121 return ret; | |
| 122 } | |
| 123 | |
| 124 LoopTree* GetLoopTree() { | |
| 125 if (loop_tree == NULL) { | |
| 126 if (FLAG_trace_turbo_graph) { | |
| 127 OFStream os(stdout); | |
| 128 os << AsRPO(graph); | |
| 129 } | |
| 130 Zone zone(isolate); | |
| 131 loop_tree = LoopFinder::BuildLoopTree(&graph, &zone); | |
| 132 } | |
| 133 return loop_tree; | |
| 134 } | |
| 135 | |
| 136 void CheckLoop(Node** header, int header_count, Node** body, int body_count) { | |
| 137 LoopTree* tree = GetLoopTree(); | |
| 138 LoopTree::Loop* loop = tree->ContainingLoop(header[0]); | |
| 139 CHECK_NE(NULL, loop); | |
| 140 | |
| 141 CHECK(header_count == static_cast<int>(loop->HeaderSize())); | |
| 142 for (int i = 0; i < header_count; i++) { | |
| 143 // Each header node should be in the loop. | |
| 144 CHECK_EQ(loop, tree->ContainingLoop(header[i])); | |
| 145 CheckRangeContains(tree->HeaderNodes(loop), header[i]); | |
| 146 } | |
| 147 | |
| 148 CHECK_EQ(body_count, static_cast<int>(loop->BodySize())); | |
| 149 for (int i = 0; i < body_count; i++) { | |
| 150 // Each body node should be contained in the loop. | |
| 151 CHECK(tree->Contains(loop, body[i])); | |
| 152 CheckRangeContains(tree->BodyNodes(loop), body[i]); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 void CheckRangeContains(PtrRange<Node*> range, Node* node) { | |
|
Benedikt Meurer
2014/12/16 06:10:25
As Sven mentioned, this is just std::find().
titzer
2014/12/16 07:55:10
Done.
| |
| 157 // O(n) ftw. | |
| 158 bool found = false; | |
| 159 for (Node* n : range) | |
| 160 if (n == node) return; | |
| 161 CHECK(found); // not found. | |
| 162 } | |
| 163 | |
| 164 void CheckNestedLoops(Node** chain, int chain_count) { | |
| 165 LoopTree* tree = GetLoopTree(); | |
| 166 for (int i = 0; i < chain_count; i++) { | |
| 167 Node* header = chain[i]; | |
| 168 // Each header should be in a loop. | |
| 169 LoopTree::Loop* loop = tree->ContainingLoop(header); | |
| 170 CHECK_NE(NULL, loop); | |
| 171 // Check parentage. | |
| 172 LoopTree::Loop* parent = | |
| 173 i == 0 ? NULL : tree->ContainingLoop(chain[i - 1]); | |
| 174 CHECK_EQ(parent, loop->parent()); | |
| 175 for (int j = i - 1; j >= 0; j--) { | |
| 176 // This loop should be nested inside all the outer loops. | |
| 177 Node* outer_header = chain[j]; | |
| 178 LoopTree::Loop* outer = tree->ContainingLoop(outer_header); | |
| 179 CHECK(tree->Contains(outer, header)); | |
| 180 CHECK(!tree->Contains(loop, outer_header)); | |
| 181 } | |
| 182 } | |
| 183 } | |
| 184 }; | |
| 185 | |
| 186 | |
| 187 struct While { | |
| 188 LoopFinderTester& t; | |
| 189 Node* branch; | |
| 190 Node* if_true; | |
| 191 Node* exit; | |
| 192 Node* loop; | |
| 193 | |
| 194 While(LoopFinderTester& R, Node* cond) : t(R) { | |
| 195 loop = t.graph.NewNode(t.common.Loop(2), t.start, t.start); | |
| 196 branch = t.graph.NewNode(t.common.Branch(), cond, loop); | |
| 197 if_true = t.graph.NewNode(t.common.IfTrue(), branch); | |
| 198 exit = t.graph.NewNode(t.common.IfFalse(), branch); | |
| 199 loop->ReplaceInput(1, if_true); | |
| 200 } | |
| 201 | |
| 202 void chain(Node* control) { loop->ReplaceInput(0, control); } | |
| 203 void nest(While& that) { | |
| 204 that.loop->ReplaceInput(1, exit); | |
| 205 this->loop->ReplaceInput(0, that.if_true); | |
| 206 } | |
| 207 }; | |
| 208 | |
| 209 | |
| 210 struct Counter { | |
| 211 Node* base; | |
| 212 Node* inc; | |
| 213 Node* phi; | |
| 214 Node* add; | |
| 215 | |
| 216 Counter(While& w, int32_t b, int32_t k) | |
| 217 : base(w.t.jsgraph.Int32Constant(b)), inc(w.t.jsgraph.Int32Constant(k)) { | |
| 218 Build(w); | |
| 219 } | |
| 220 | |
| 221 Counter(While& w, Node* b, Node* k) : base(b), inc(k) { Build(w); } | |
| 222 | |
| 223 void Build(While& w) { | |
| 224 phi = w.t.graph.NewNode(w.t.op(2, false), base, base, w.loop); | |
| 225 add = w.t.graph.NewNode(&kIntAdd, phi, inc); | |
| 226 phi->ReplaceInput(1, add); | |
| 227 } | |
| 228 }; | |
| 229 | |
| 230 | |
| 231 struct StoreLoop { | |
| 232 Node* base; | |
| 233 Node* val; | |
| 234 Node* phi; | |
| 235 Node* store; | |
| 236 | |
| 237 explicit StoreLoop(While& w) | |
| 238 : base(w.t.jsgraph.Int32Constant(12)), | |
| 239 val(w.t.jsgraph.Int32Constant(13)) { | |
| 240 Build(w); | |
| 241 } | |
| 242 | |
| 243 StoreLoop(While& w, Node* b, Node* v) : base(b), val(v) { Build(w); } | |
| 244 | |
| 245 void Build(While& w) { | |
| 246 phi = w.t.graph.NewNode(w.t.op(2, true), base, base, w.loop); | |
| 247 store = w.t.graph.NewNode(&kStore, phi, val, w.loop); | |
| 248 phi->ReplaceInput(1, store); | |
| 249 } | |
| 250 }; | |
| 251 | |
| 252 | |
| 253 TEST(LaLoop1) { | |
| 254 // One loop. | |
| 255 LoopFinderTester t; | |
| 256 While w(t, t.p0); | |
| 257 t.Return(t.p0, t.start, w.exit); | |
| 258 | |
| 259 Node* chain[] = {w.loop}; | |
| 260 t.CheckNestedLoops(chain, 1); | |
| 261 | |
| 262 Node* header[] = {w.loop}; | |
| 263 Node* body[] = {w.branch, w.if_true}; | |
| 264 t.CheckLoop(header, 1, body, 2); | |
| 265 } | |
| 266 | |
| 267 | |
| 268 TEST(LaLoop1c) { | |
| 269 // One loop with a counter. | |
| 270 LoopFinderTester t; | |
| 271 While w(t, t.p0); | |
| 272 Counter c(w, 0, 1); | |
| 273 t.Return(c.phi, t.start, w.exit); | |
| 274 | |
| 275 Node* chain[] = {w.loop}; | |
| 276 t.CheckNestedLoops(chain, 1); | |
| 277 | |
| 278 Node* header[] = {w.loop, c.phi}; | |
| 279 Node* body[] = {w.branch, w.if_true, c.add}; | |
| 280 t.CheckLoop(header, 2, body, 3); | |
| 281 } | |
| 282 | |
| 283 | |
| 284 TEST(LaLoop1e) { | |
| 285 // One loop with an effect phi. | |
| 286 LoopFinderTester t; | |
| 287 While w(t, t.p0); | |
| 288 StoreLoop c(w); | |
| 289 t.Return(t.p0, c.phi, w.exit); | |
| 290 | |
| 291 Node* chain[] = {w.loop}; | |
| 292 t.CheckNestedLoops(chain, 1); | |
| 293 | |
| 294 Node* header[] = {w.loop, c.phi}; | |
| 295 Node* body[] = {w.branch, w.if_true, c.store}; | |
| 296 t.CheckLoop(header, 2, body, 3); | |
| 297 } | |
| 298 | |
| 299 | |
| 300 TEST(LaLoop1d) { | |
| 301 // One loop with two counters. | |
| 302 LoopFinderTester t; | |
| 303 While w(t, t.p0); | |
| 304 Counter c1(w, 0, 1); | |
| 305 Counter c2(w, 1, 1); | |
| 306 t.Return(t.graph.NewNode(&kIntAdd, c1.phi, c2.phi), t.start, w.exit); | |
| 307 | |
| 308 Node* chain[] = {w.loop}; | |
| 309 t.CheckNestedLoops(chain, 1); | |
| 310 | |
| 311 Node* header[] = {w.loop, c1.phi, c2.phi}; | |
| 312 Node* body[] = {w.branch, w.if_true, c1.add, c2.add}; | |
| 313 t.CheckLoop(header, 3, body, 4); | |
| 314 } | |
| 315 | |
| 316 | |
| 317 TEST(LaLoop2) { | |
| 318 // One loop following another. | |
| 319 LoopFinderTester t; | |
| 320 While w1(t, t.p0); | |
| 321 While w2(t, t.p0); | |
| 322 w2.chain(w1.exit); | |
| 323 t.Return(t.p0, t.start, w2.exit); | |
| 324 | |
| 325 { | |
| 326 Node* chain[] = {w1.loop}; | |
| 327 t.CheckNestedLoops(chain, 1); | |
| 328 | |
| 329 Node* header[] = {w1.loop}; | |
| 330 Node* body[] = {w1.branch, w1.if_true}; | |
| 331 t.CheckLoop(header, 1, body, 2); | |
| 332 } | |
| 333 | |
| 334 { | |
| 335 Node* chain[] = {w2.loop}; | |
| 336 t.CheckNestedLoops(chain, 1); | |
| 337 | |
| 338 Node* header[] = {w2.loop}; | |
| 339 Node* body[] = {w2.branch, w2.if_true}; | |
| 340 t.CheckLoop(header, 1, body, 2); | |
| 341 } | |
| 342 } | |
| 343 | |
| 344 | |
| 345 TEST(LaLoop2c) { | |
| 346 // One loop following another, each with counters. | |
| 347 LoopFinderTester t; | |
| 348 While w1(t, t.p0); | |
| 349 While w2(t, t.p0); | |
| 350 Counter c1(w1, 0, 1); | |
| 351 Counter c2(w2, 0, 1); | |
| 352 w2.chain(w1.exit); | |
| 353 t.Return(t.graph.NewNode(&kIntAdd, c1.phi, c2.phi), t.start, w2.exit); | |
| 354 | |
| 355 { | |
| 356 Node* chain[] = {w1.loop}; | |
| 357 t.CheckNestedLoops(chain, 1); | |
| 358 | |
| 359 Node* header[] = {w1.loop, c1.phi}; | |
| 360 Node* body[] = {w1.branch, w1.if_true, c1.add}; | |
| 361 t.CheckLoop(header, 2, body, 3); | |
| 362 } | |
| 363 | |
| 364 { | |
| 365 Node* chain[] = {w2.loop}; | |
| 366 t.CheckNestedLoops(chain, 1); | |
| 367 | |
| 368 Node* header[] = {w2.loop, c2.phi}; | |
| 369 Node* body[] = {w2.branch, w2.if_true, c2.add}; | |
| 370 t.CheckLoop(header, 2, body, 3); | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 | |
| 375 TEST(LaLoop2cc) { | |
| 376 // One loop following another; second loop uses phi from first. | |
| 377 for (int i = 0; i < 8; i++) { | |
| 378 LoopFinderTester t; | |
| 379 While w1(t, t.p0); | |
| 380 While w2(t, t.p0); | |
| 381 Counter c1(w1, 0, 1); | |
| 382 | |
| 383 // various usage scenarios for the second loop. | |
| 384 Counter c2(w2, i & 1 ? t.p0 : c1.phi, i & 2 ? t.p0 : c1.phi); | |
| 385 if (i & 3) w2.branch->ReplaceInput(0, c1.phi); | |
| 386 | |
| 387 w2.chain(w1.exit); | |
| 388 t.Return(t.graph.NewNode(&kIntAdd, c1.phi, c2.phi), t.start, w2.exit); | |
| 389 | |
| 390 { | |
| 391 Node* chain[] = {w1.loop}; | |
| 392 t.CheckNestedLoops(chain, 1); | |
| 393 | |
| 394 Node* header[] = {w1.loop, c1.phi}; | |
| 395 Node* body[] = {w1.branch, w1.if_true, c1.add}; | |
| 396 t.CheckLoop(header, 2, body, 3); | |
| 397 } | |
| 398 | |
| 399 { | |
| 400 Node* chain[] = {w2.loop}; | |
| 401 t.CheckNestedLoops(chain, 1); | |
| 402 | |
| 403 Node* header[] = {w2.loop, c2.phi}; | |
| 404 Node* body[] = {w2.branch, w2.if_true, c2.add}; | |
| 405 t.CheckLoop(header, 2, body, 3); | |
| 406 } | |
| 407 } | |
| 408 } | |
| 409 | |
| 410 | |
| 411 TEST(LaNestedLoop1) { | |
| 412 // One loop nested in another. | |
| 413 LoopFinderTester t; | |
| 414 While w1(t, t.p0); | |
| 415 While w2(t, t.p0); | |
| 416 w2.nest(w1); | |
| 417 t.Return(t.p0, t.start, w1.exit); | |
| 418 | |
| 419 Node* chain[] = {w1.loop, w2.loop}; | |
| 420 t.CheckNestedLoops(chain, 2); | |
| 421 | |
| 422 Node* h1[] = {w1.loop}; | |
| 423 Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true, w2.exit}; | |
| 424 t.CheckLoop(h1, 1, b1, 6); | |
| 425 | |
| 426 Node* h2[] = {w2.loop}; | |
| 427 Node* b2[] = {w2.branch, w2.if_true}; | |
| 428 t.CheckLoop(h2, 1, b2, 2); | |
| 429 } | |
| 430 | |
| 431 | |
| 432 TEST(LaNestedLoop1c) { | |
| 433 // One loop nested in another, each with a counter. | |
| 434 LoopFinderTester t; | |
| 435 While w1(t, t.p0); | |
| 436 While w2(t, t.p0); | |
| 437 Counter c1(w1, 0, 1); | |
| 438 Counter c2(w2, 0, 1); | |
| 439 w2.branch->ReplaceInput(0, c2.phi); | |
| 440 w2.nest(w1); | |
| 441 t.Return(c1.phi, t.start, w1.exit); | |
| 442 | |
| 443 Node* chain[] = {w1.loop, w2.loop}; | |
| 444 t.CheckNestedLoops(chain, 2); | |
| 445 | |
| 446 Node* h1[] = {w1.loop, c1.phi}; | |
| 447 Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true, | |
| 448 w2.exit, c2.phi, c1.add, c2.add}; | |
| 449 t.CheckLoop(h1, 2, b1, 9); | |
| 450 | |
| 451 Node* h2[] = {w2.loop, c2.phi}; | |
| 452 Node* b2[] = {w2.branch, w2.if_true, c2.add}; | |
| 453 t.CheckLoop(h2, 2, b2, 3); | |
| 454 } | |
| 455 | |
| 456 | |
| 457 TEST(LaNestedLoop2) { | |
| 458 // Two loops nested in an outer loop. | |
| 459 LoopFinderTester t; | |
| 460 While w1(t, t.p0); | |
| 461 While w2(t, t.p0); | |
| 462 While w3(t, t.p0); | |
| 463 w2.nest(w1); | |
| 464 w3.nest(w1); | |
| 465 w3.chain(w2.exit); | |
| 466 t.Return(t.p0, t.start, w1.exit); | |
| 467 | |
| 468 Node* chain1[] = {w1.loop, w2.loop}; | |
| 469 t.CheckNestedLoops(chain1, 2); | |
| 470 | |
| 471 Node* chain2[] = {w1.loop, w3.loop}; | |
| 472 t.CheckNestedLoops(chain2, 2); | |
| 473 | |
| 474 Node* h1[] = {w1.loop}; | |
| 475 Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true, | |
| 476 w2.exit, w3.loop, w3.branch, w3.if_true, w3.exit}; | |
| 477 t.CheckLoop(h1, 1, b1, 10); | |
| 478 | |
| 479 Node* h2[] = {w2.loop}; | |
| 480 Node* b2[] = {w2.branch, w2.if_true}; | |
| 481 t.CheckLoop(h2, 1, b2, 2); | |
| 482 | |
| 483 Node* h3[] = {w3.loop}; | |
| 484 Node* b3[] = {w3.branch, w3.if_true}; | |
| 485 t.CheckLoop(h3, 1, b3, 2); | |
| 486 } | |
| 487 | |
| 488 | |
| 489 TEST(LaNestedLoop3) { | |
| 490 // Three nested loops. | |
| 491 LoopFinderTester t; | |
| 492 While w1(t, t.p0); | |
| 493 While w2(t, t.p0); | |
| 494 While w3(t, t.p0); | |
| 495 w2.loop->ReplaceInput(0, w1.if_true); | |
| 496 w3.loop->ReplaceInput(0, w2.if_true); | |
| 497 w2.loop->ReplaceInput(1, w3.exit); | |
| 498 w1.loop->ReplaceInput(1, w2.exit); | |
| 499 t.Return(t.p0, t.start, w1.exit); | |
| 500 | |
| 501 Node* chain[] = {w1.loop, w2.loop, w3.loop}; | |
| 502 t.CheckNestedLoops(chain, 3); | |
| 503 | |
| 504 Node* h1[] = {w1.loop}; | |
| 505 Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true, | |
| 506 w2.exit, w3.loop, w3.branch, w3.if_true, w3.exit}; | |
| 507 t.CheckLoop(h1, 1, b1, 10); | |
| 508 | |
| 509 Node* h2[] = {w2.loop}; | |
| 510 Node* b2[] = {w2.branch, w2.if_true, w3.loop, w3.branch, w3.if_true, w3.exit}; | |
| 511 t.CheckLoop(h2, 1, b2, 6); | |
| 512 | |
| 513 Node* h3[] = {w3.loop}; | |
| 514 Node* b3[] = {w3.branch, w3.if_true}; | |
| 515 t.CheckLoop(h3, 1, b3, 2); | |
| 516 } | |
| 517 | |
| 518 | |
| 519 TEST(LaNestedLoop3c) { | |
| 520 // Three nested loops with counters. | |
| 521 LoopFinderTester t; | |
| 522 While w1(t, t.p0); | |
| 523 Counter c1(w1, 0, 1); | |
| 524 While w2(t, t.p0); | |
| 525 Counter c2(w2, 0, 1); | |
| 526 While w3(t, t.p0); | |
| 527 Counter c3(w3, 0, 1); | |
| 528 w2.loop->ReplaceInput(0, w1.if_true); | |
| 529 w3.loop->ReplaceInput(0, w2.if_true); | |
| 530 w2.loop->ReplaceInput(1, w3.exit); | |
| 531 w1.loop->ReplaceInput(1, w2.exit); | |
| 532 w1.branch->ReplaceInput(0, c1.phi); | |
| 533 w2.branch->ReplaceInput(0, c2.phi); | |
| 534 w3.branch->ReplaceInput(0, c3.phi); | |
| 535 t.Return(c1.phi, t.start, w1.exit); | |
| 536 | |
| 537 Node* chain[] = {w1.loop, w2.loop, w3.loop}; | |
| 538 t.CheckNestedLoops(chain, 3); | |
| 539 | |
| 540 Node* h1[] = {w1.loop, c1.phi}; | |
| 541 Node* b1[] = {w1.branch, w1.if_true, c1.add, c2.add, c2.add, | |
| 542 c2.phi, c3.phi, w2.loop, w2.branch, w2.if_true, | |
| 543 w2.exit, w3.loop, w3.branch, w3.if_true, w3.exit}; | |
| 544 t.CheckLoop(h1, 2, b1, 15); | |
| 545 | |
| 546 Node* h2[] = {w2.loop, c2.phi}; | |
| 547 Node* b2[] = {w2.branch, w2.if_true, c2.add, c3.add, c3.phi, | |
| 548 w3.loop, w3.branch, w3.if_true, w3.exit}; | |
| 549 t.CheckLoop(h2, 2, b2, 9); | |
| 550 | |
| 551 Node* h3[] = {w3.loop, c3.phi}; | |
| 552 Node* b3[] = {w3.branch, w3.if_true, c3.add}; | |
| 553 t.CheckLoop(h3, 2, b3, 3); | |
| 554 } | |
| 555 | |
| 556 | |
| 557 TEST(LaMultipleExit1) { | |
| 558 const int kMaxExits = 10; | |
| 559 Node* merge[1 + kMaxExits]; | |
| 560 Node* body[2 * kMaxExits]; | |
| 561 | |
| 562 // A single loop with {i} exits. | |
| 563 for (int i = 1; i < kMaxExits; i++) { | |
| 564 LoopFinderTester t; | |
| 565 Node* cond = t.p0; | |
| 566 | |
| 567 int merge_count = 0; | |
| 568 int body_count = 0; | |
| 569 Node* loop = t.graph.NewNode(t.common.Loop(2), t.start, t.start); | |
| 570 Node* last = loop; | |
| 571 | |
| 572 for (int e = 0; e < i; e++) { | |
| 573 Node* branch = t.graph.NewNode(t.common.Branch(), cond, last); | |
| 574 Node* if_true = t.graph.NewNode(t.common.IfTrue(), branch); | |
| 575 Node* exit = t.graph.NewNode(t.common.IfFalse(), branch); | |
| 576 last = if_true; | |
| 577 | |
| 578 body[body_count++] = branch; | |
| 579 body[body_count++] = if_true; | |
| 580 merge[merge_count++] = exit; | |
| 581 } | |
| 582 | |
| 583 loop->ReplaceInput(1, last); // form loop backedge. | |
| 584 Node* end = t.graph.NewNode(t.common.Merge(i), i, merge); // form exit. | |
| 585 t.graph.SetEnd(end); | |
| 586 | |
| 587 Node* h[] = {loop}; | |
| 588 t.CheckLoop(h, 1, body, body_count); | |
| 589 } | |
| 590 } | |
| 591 | |
| 592 | |
| 593 TEST(LaMultipleBackedge1) { | |
| 594 const int kMaxBackedges = 10; | |
| 595 Node* loop_inputs[1 + kMaxBackedges]; | |
| 596 Node* body[3 * kMaxBackedges]; | |
| 597 | |
| 598 // A single loop with {i} backedges. | |
| 599 for (int i = 1; i < kMaxBackedges; i++) { | |
| 600 LoopFinderTester t; | |
| 601 | |
| 602 for (int j = 0; j <= i; j++) loop_inputs[j] = t.start; | |
| 603 Node* loop = t.graph.NewNode(t.common.Loop(1 + i), 1 + i, loop_inputs); | |
| 604 | |
| 605 Node* cond = t.p0; | |
| 606 int body_count = 0; | |
| 607 Node* exit = loop; | |
| 608 | |
| 609 for (int b = 0; b < i; b++) { | |
| 610 Node* branch = t.graph.NewNode(t.common.Branch(), cond, exit); | |
| 611 Node* if_true = t.graph.NewNode(t.common.IfTrue(), branch); | |
| 612 Node* if_false = t.graph.NewNode(t.common.IfFalse(), branch); | |
| 613 exit = if_false; | |
| 614 | |
| 615 body[body_count++] = branch; | |
| 616 body[body_count++] = if_true; | |
| 617 if (b != (i - 1)) body[body_count++] = if_false; | |
| 618 | |
| 619 loop->ReplaceInput(1 + b, if_true); | |
| 620 } | |
| 621 | |
| 622 t.graph.SetEnd(exit); | |
| 623 | |
| 624 Node* h[] = {loop}; | |
| 625 t.CheckLoop(h, 1, body, body_count); | |
| 626 } | |
| 627 } | |
| 628 | |
| 629 | |
| 630 TEST(LaEdgeMatrix1) { | |
| 631 // Test various kinds of extra edges added to a simple loop. | |
| 632 for (int i = 0; i < 3; i++) { | |
| 633 for (int j = 0; j < 3; j++) { | |
| 634 for (int k = 0; k < 3; k++) { | |
| 635 LoopFinderTester t; | |
| 636 | |
| 637 Node* p1 = t.jsgraph.Int32Constant(11); | |
| 638 Node* p2 = t.jsgraph.Int32Constant(22); | |
| 639 Node* p3 = t.jsgraph.Int32Constant(33); | |
| 640 | |
| 641 Node* loop = t.graph.NewNode(t.common.Loop(2), t.start, t.start); | |
| 642 Node* phi = | |
| 643 t.graph.NewNode(t.common.Phi(kMachInt32, 2), t.one, p1, loop); | |
| 644 Node* cond = t.graph.NewNode(&kIntAdd, phi, p2); | |
| 645 Node* branch = t.graph.NewNode(t.common.Branch(), cond, loop); | |
| 646 Node* if_true = t.graph.NewNode(t.common.IfTrue(), branch); | |
| 647 Node* exit = t.graph.NewNode(t.common.IfFalse(), branch); | |
| 648 loop->ReplaceInput(1, if_true); | |
| 649 Node* ret = t.graph.NewNode(t.common.Return(), p3, t.start, exit); | |
| 650 t.graph.SetEnd(ret); | |
| 651 | |
| 652 Node* choices[] = {p1, phi, cond}; | |
| 653 p1->ReplaceUses(choices[i]); | |
| 654 p2->ReplaceUses(choices[j]); | |
| 655 p3->ReplaceUses(choices[k]); | |
| 656 | |
| 657 Node* header[] = {loop, phi}; | |
| 658 Node* body[] = {cond, branch, if_true}; | |
| 659 t.CheckLoop(header, 2, body, 3); | |
| 660 } | |
| 661 } | |
| 662 } | |
| 663 } | |
| 664 | |
| 665 | |
| 666 void RunEdgeMatrix2(int i) { | |
| 667 DCHECK(i >= 0 && i < 5); | |
| 668 for (int j = 0; j < 5; j++) { | |
| 669 for (int k = 0; k < 5; k++) { | |
| 670 LoopFinderTester t; | |
| 671 | |
| 672 Node* p1 = t.jsgraph.Int32Constant(11); | |
| 673 Node* p2 = t.jsgraph.Int32Constant(22); | |
| 674 Node* p3 = t.jsgraph.Int32Constant(33); | |
| 675 | |
| 676 // outer loop. | |
| 677 Node* loop1 = t.graph.NewNode(t.common.Loop(2), t.start, t.start); | |
| 678 Node* phi1 = | |
| 679 t.graph.NewNode(t.common.Phi(kMachInt32, 2), t.one, p1, loop1); | |
| 680 Node* cond1 = t.graph.NewNode(&kIntAdd, phi1, t.one); | |
| 681 Node* branch1 = t.graph.NewNode(t.common.Branch(), cond1, loop1); | |
| 682 Node* if_true1 = t.graph.NewNode(t.common.IfTrue(), branch1); | |
| 683 Node* exit1 = t.graph.NewNode(t.common.IfFalse(), branch1); | |
| 684 | |
| 685 // inner loop. | |
| 686 Node* loop2 = t.graph.NewNode(t.common.Loop(2), if_true1, t.start); | |
| 687 Node* phi2 = | |
| 688 t.graph.NewNode(t.common.Phi(kMachInt32, 2), t.one, p2, loop2); | |
| 689 Node* cond2 = t.graph.NewNode(&kIntAdd, phi2, p3); | |
| 690 Node* branch2 = t.graph.NewNode(t.common.Branch(), cond2, loop2); | |
| 691 Node* if_true2 = t.graph.NewNode(t.common.IfTrue(), branch2); | |
| 692 Node* exit2 = t.graph.NewNode(t.common.IfFalse(), branch2); | |
| 693 loop2->ReplaceInput(1, if_true2); | |
| 694 loop1->ReplaceInput(1, exit2); | |
| 695 | |
| 696 Node* ret = t.graph.NewNode(t.common.Return(), phi1, t.start, exit1); | |
| 697 t.graph.SetEnd(ret); | |
| 698 | |
| 699 Node* choices[] = {p1, phi1, cond1, phi2, cond2}; | |
| 700 p1->ReplaceUses(choices[i]); | |
| 701 p2->ReplaceUses(choices[j]); | |
| 702 p3->ReplaceUses(choices[k]); | |
| 703 | |
| 704 Node* header1[] = {loop1, phi1}; | |
| 705 Node* body1[] = {cond1, branch1, if_true1, exit2, loop2, | |
| 706 phi2, cond2, branch2, if_true2}; | |
| 707 t.CheckLoop(header1, 2, body1, 9); | |
| 708 | |
| 709 Node* header2[] = {loop2, phi2}; | |
| 710 Node* body2[] = {cond2, branch2, if_true2}; | |
| 711 t.CheckLoop(header2, 2, body2, 3); | |
| 712 | |
| 713 Node* chain[] = {loop1, loop2}; | |
| 714 t.CheckNestedLoops(chain, 2); | |
| 715 } | |
| 716 } | |
| 717 } | |
| 718 | |
| 719 | |
| 720 TEST(LaEdgeMatrix2_0) { RunEdgeMatrix2(0); } | |
| 721 | |
| 722 | |
| 723 TEST(LaEdgeMatrix2_1) { RunEdgeMatrix2(1); } | |
| 724 | |
| 725 | |
| 726 TEST(LaEdgeMatrix2_2) { RunEdgeMatrix2(2); } | |
| 727 | |
| 728 | |
| 729 TEST(LaEdgeMatrix2_3) { RunEdgeMatrix2(3); } | |
| 730 | |
| 731 | |
| 732 TEST(LaEdgeMatrix2_4) { RunEdgeMatrix2(4); } | |
| 733 | |
| 734 | |
| 735 // Generates a triply-nested loop with extra edges between the phis and | |
| 736 // conditions according to the edge choice parameters. | |
| 737 void RunEdgeMatrix3(int c1a, int c1b, int c1c, // line break | |
| 738 int c2a, int c2b, int c2c, // line break | |
| 739 int c3a, int c3b, int c3c) { // line break | |
| 740 LoopFinderTester t; | |
| 741 | |
| 742 Node* p1a = t.jsgraph.Int32Constant(11); | |
| 743 Node* p1b = t.jsgraph.Int32Constant(22); | |
| 744 Node* p1c = t.jsgraph.Int32Constant(33); | |
| 745 Node* p2a = t.jsgraph.Int32Constant(44); | |
| 746 Node* p2b = t.jsgraph.Int32Constant(55); | |
| 747 Node* p2c = t.jsgraph.Int32Constant(66); | |
| 748 Node* p3a = t.jsgraph.Int32Constant(77); | |
| 749 Node* p3b = t.jsgraph.Int32Constant(88); | |
| 750 Node* p3c = t.jsgraph.Int32Constant(99); | |
| 751 | |
| 752 // L1 depth = 0 | |
| 753 Node* loop1 = t.graph.NewNode(t.common.Loop(2), t.start, t.start); | |
| 754 Node* phi1 = t.graph.NewNode(t.common.Phi(kMachInt32, 2), p1a, p1c, loop1); | |
| 755 Node* cond1 = t.graph.NewNode(&kIntAdd, phi1, p1b); | |
| 756 Node* branch1 = t.graph.NewNode(t.common.Branch(), cond1, loop1); | |
| 757 Node* if_true1 = t.graph.NewNode(t.common.IfTrue(), branch1); | |
| 758 Node* exit1 = t.graph.NewNode(t.common.IfFalse(), branch1); | |
| 759 | |
| 760 // L2 depth = 1 | |
| 761 Node* loop2 = t.graph.NewNode(t.common.Loop(2), if_true1, t.start); | |
| 762 Node* phi2 = t.graph.NewNode(t.common.Phi(kMachInt32, 2), p2a, p2c, loop2); | |
| 763 Node* cond2 = t.graph.NewNode(&kIntAdd, phi2, p2b); | |
| 764 Node* branch2 = t.graph.NewNode(t.common.Branch(), cond2, loop2); | |
| 765 Node* if_true2 = t.graph.NewNode(t.common.IfTrue(), branch2); | |
| 766 Node* exit2 = t.graph.NewNode(t.common.IfFalse(), branch2); | |
| 767 | |
| 768 // L3 depth = 2 | |
| 769 Node* loop3 = t.graph.NewNode(t.common.Loop(2), if_true2, t.start); | |
| 770 Node* phi3 = t.graph.NewNode(t.common.Phi(kMachInt32, 2), p3a, p3c, loop3); | |
| 771 Node* cond3 = t.graph.NewNode(&kIntAdd, phi3, p3b); | |
| 772 Node* branch3 = t.graph.NewNode(t.common.Branch(), cond3, loop3); | |
| 773 Node* if_true3 = t.graph.NewNode(t.common.IfTrue(), branch3); | |
| 774 Node* exit3 = t.graph.NewNode(t.common.IfFalse(), branch3); | |
| 775 | |
| 776 loop3->ReplaceInput(1, if_true3); | |
| 777 loop2->ReplaceInput(1, exit3); | |
| 778 loop1->ReplaceInput(1, exit2); | |
| 779 | |
| 780 Node* ret = t.graph.NewNode(t.common.Return(), phi1, t.start, exit1); | |
| 781 t.graph.SetEnd(ret); | |
| 782 | |
| 783 // Mutate the graph according to the edge choices. | |
| 784 | |
| 785 Node* o1[] = {t.one}; | |
| 786 Node* o2[] = {t.one, phi1, cond1}; | |
| 787 Node* o3[] = {t.one, phi1, cond1, phi2, cond2}; | |
| 788 | |
| 789 p1a->ReplaceUses(o1[c1a]); | |
| 790 p1b->ReplaceUses(o1[c1b]); | |
| 791 | |
| 792 p2a->ReplaceUses(o2[c2a]); | |
| 793 p2b->ReplaceUses(o2[c2b]); | |
| 794 | |
| 795 p3a->ReplaceUses(o3[c3a]); | |
| 796 p3b->ReplaceUses(o3[c3b]); | |
| 797 | |
| 798 Node* l2[] = {phi1, cond1, phi2, cond2}; | |
| 799 Node* l3[] = {phi1, cond1, phi2, cond2, phi3, cond3}; | |
| 800 | |
| 801 p1c->ReplaceUses(l2[c1c]); | |
| 802 p2c->ReplaceUses(l3[c2c]); | |
| 803 p3c->ReplaceUses(l3[c3c]); | |
| 804 | |
| 805 // Run the tests and verify loop structure. | |
| 806 | |
| 807 Node* chain[] = {loop1, loop2, loop3}; | |
| 808 t.CheckNestedLoops(chain, 3); | |
| 809 | |
| 810 Node* header1[] = {loop1, phi1}; | |
| 811 Node* body1[] = {cond1, branch1, if_true1, exit2, loop2, | |
| 812 phi2, cond2, branch2, if_true2, exit3, | |
| 813 loop3, phi3, cond3, branch3, if_true3}; | |
| 814 t.CheckLoop(header1, 2, body1, 15); | |
| 815 | |
| 816 Node* header2[] = {loop2, phi2}; | |
| 817 Node* body2[] = {cond2, branch2, if_true2, exit3, loop3, | |
| 818 phi3, cond3, branch3, if_true3}; | |
| 819 t.CheckLoop(header2, 2, body2, 9); | |
| 820 | |
| 821 Node* header3[] = {loop3, phi3}; | |
| 822 Node* body3[] = {cond3, branch3, if_true3}; | |
| 823 t.CheckLoop(header3, 2, body3, 3); | |
| 824 } | |
| 825 | |
| 826 | |
| 827 // Runs all combinations with a fixed {i}. | |
| 828 void RunEdgeMatrix3_i(int i) { | |
| 829 for (int a = 0; a < 1; a++) { | |
| 830 for (int b = 0; b < 1; b++) { | |
| 831 for (int c = 0; c < 4; c++) { | |
| 832 for (int d = 0; d < 3; d++) { | |
| 833 for (int e = 0; e < 3; e++) { | |
| 834 for (int f = 0; f < 6; f++) { | |
| 835 for (int g = 0; g < 5; g++) { | |
| 836 for (int h = 0; h < 5; h++) { | |
| 837 RunEdgeMatrix3(a, b, c, d, e, f, g, h, i); | |
| 838 } | |
| 839 } | |
| 840 } | |
| 841 } | |
| 842 } | |
| 843 } | |
| 844 } | |
| 845 } | |
| 846 } | |
| 847 | |
| 848 | |
| 849 // Test all possible legal triply-nested loops with conditions and phis. | |
| 850 TEST(LaEdgeMatrix3_0) { RunEdgeMatrix3_i(0); } | |
| 851 | |
| 852 | |
| 853 TEST(LaEdgeMatrix3_1) { RunEdgeMatrix3_i(1); } | |
| 854 | |
| 855 | |
| 856 TEST(LaEdgeMatrix3_2) { RunEdgeMatrix3_i(2); } | |
| 857 | |
| 858 | |
| 859 TEST(LaEdgeMatrix3_3) { RunEdgeMatrix3_i(3); } | |
| 860 | |
| 861 | |
| 862 TEST(LaEdgeMatrix3_4) { RunEdgeMatrix3_i(4); } | |
| 863 | |
| 864 | |
| 865 TEST(LaEdgeMatrix3_5) { RunEdgeMatrix3_i(5); } | |
| OLD | NEW |