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