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 |