blob: 9db490560dded5fcbb9c1af2f0a5907871d13de6 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/compiler/access-builder.h"
6#include "src/compiler/graph.h"
7#include "src/compiler/graph-visualizer.h"
8#include "src/compiler/js-graph.h"
9#include "src/compiler/loop-peeling.h"
10#include "src/compiler/machine-operator.h"
11#include "src/compiler/node.h"
12#include "src/compiler/node-properties.h"
13#include "test/unittests/compiler/compiler-test-utils.h"
14#include "test/unittests/compiler/graph-unittest.h"
15#include "test/unittests/compiler/node-test-utils.h"
16#include "testing/gmock-support.h"
17
18using testing::AllOf;
19using testing::BitEq;
20using testing::Capture;
21using testing::CaptureEq;
22
23namespace v8 {
24namespace internal {
25namespace compiler {
26
27struct While {
28 Node* loop;
29 Node* branch;
30 Node* if_true;
31 Node* exit;
32};
33
34
35// A helper for building branches.
36struct Branch {
37 Node* branch;
38 Node* if_true;
39 Node* if_false;
40};
41
42
43// A helper for building counters attached to loops.
44struct Counter {
45 Node* base;
46 Node* inc;
47 Node* phi;
48 Node* add;
49};
50
51
52class LoopPeelingTest : public GraphTest {
53 public:
54 LoopPeelingTest() : GraphTest(1), machine_(zone()) {}
55 ~LoopPeelingTest() override {}
56
57 protected:
58 MachineOperatorBuilder machine_;
59
60 MachineOperatorBuilder* machine() { return &machine_; }
61
62 LoopTree* GetLoopTree() {
63 if (FLAG_trace_turbo_graph) {
64 OFStream os(stdout);
65 os << AsRPO(*graph());
66 }
Ben Murdochda12d292016-06-02 14:46:10 +010067 Zone zone(isolate()->allocator());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000068 return LoopFinder::BuildLoopTree(graph(), &zone);
69 }
70
71
72 PeeledIteration* PeelOne() {
73 LoopTree* loop_tree = GetLoopTree();
74 LoopTree::Loop* loop = loop_tree->outer_loops()[0];
75 EXPECT_TRUE(LoopPeeler::CanPeel(loop_tree, loop));
76 return Peel(loop_tree, loop);
77 }
78
79 PeeledIteration* Peel(LoopTree* loop_tree, LoopTree::Loop* loop) {
80 EXPECT_TRUE(LoopPeeler::CanPeel(loop_tree, loop));
81 PeeledIteration* peeled =
82 LoopPeeler::Peel(graph(), common(), loop_tree, loop, zone());
83 if (FLAG_trace_turbo_graph) {
84 OFStream os(stdout);
85 os << AsRPO(*graph());
86 }
87 return peeled;
88 }
89
90 Node* InsertReturn(Node* val, Node* effect, Node* control) {
91 Node* r = graph()->NewNode(common()->Return(), val, effect, control);
92 graph()->SetEnd(r);
93 return r;
94 }
95
96 Node* ExpectPeeled(Node* node, PeeledIteration* iter) {
97 Node* p = iter->map(node);
98 EXPECT_NE(node, p);
99 return p;
100 }
101
102 void ExpectNotPeeled(Node* node, PeeledIteration* iter) {
103 EXPECT_EQ(node, iter->map(node));
104 }
105
106 While NewWhile(Node* cond, Node* control = nullptr) {
107 if (control == nullptr) control = start();
108 Node* loop = graph()->NewNode(common()->Loop(2), control, control);
109 Node* branch = graph()->NewNode(common()->Branch(), cond, loop);
110 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
111 Node* exit = graph()->NewNode(common()->IfFalse(), branch);
112 loop->ReplaceInput(1, if_true);
113 return {loop, branch, if_true, exit};
114 }
115
116 void Chain(While* a, Node* control) { a->loop->ReplaceInput(0, control); }
117 void Nest(While* a, While* b) {
118 b->loop->ReplaceInput(1, a->exit);
119 a->loop->ReplaceInput(0, b->if_true);
120 }
121 Node* NewPhi(While* w, Node* a, Node* b) {
122 return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), a,
123 b, w->loop);
124 }
125
126 Branch NewBranch(Node* cond, Node* control = nullptr) {
127 if (control == nullptr) control = start();
128 Node* branch = graph()->NewNode(common()->Branch(), cond, control);
129 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
130 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
131 return {branch, if_true, if_false};
132 }
133
134 Counter NewCounter(While* w, int32_t b, int32_t k) {
135 Node* base = Int32Constant(b);
136 Node* inc = Int32Constant(k);
137 Node* phi = graph()->NewNode(
138 common()->Phi(MachineRepresentation::kTagged, 2), base, base, w->loop);
139 Node* add = graph()->NewNode(machine()->Int32Add(), phi, inc);
140 phi->ReplaceInput(1, add);
141 return {base, inc, phi, add};
142 }
143};
144
145
146TEST_F(LoopPeelingTest, SimpleLoop) {
147 Node* p0 = Parameter(0);
148 While w = NewWhile(p0);
149 Node* r = InsertReturn(p0, start(), w.exit);
150
151 PeeledIteration* peeled = PeelOne();
152
153 Node* br1 = ExpectPeeled(w.branch, peeled);
154 Node* if_true1 = ExpectPeeled(w.if_true, peeled);
155 Node* if_false1 = ExpectPeeled(w.exit, peeled);
156
157 EXPECT_THAT(br1, IsBranch(p0, start()));
158 EXPECT_THAT(if_true1, IsIfTrue(br1));
159 EXPECT_THAT(if_false1, IsIfFalse(br1));
160
161 EXPECT_THAT(w.loop, IsLoop(if_true1, w.if_true));
162 EXPECT_THAT(r, IsReturn(p0, start(), IsMerge(w.exit, if_false1)));
163}
164
165
166TEST_F(LoopPeelingTest, SimpleLoopWithCounter) {
167 Node* p0 = Parameter(0);
168 While w = NewWhile(p0);
169 Counter c = NewCounter(&w, 0, 1);
170 Node* r = InsertReturn(c.phi, start(), w.exit);
171
172 PeeledIteration* peeled = PeelOne();
173
174 Node* br1 = ExpectPeeled(w.branch, peeled);
175 Node* if_true1 = ExpectPeeled(w.if_true, peeled);
176 Node* if_false1 = ExpectPeeled(w.exit, peeled);
177
178 EXPECT_THAT(br1, IsBranch(p0, start()));
179 EXPECT_THAT(if_true1, IsIfTrue(br1));
180 EXPECT_THAT(if_false1, IsIfFalse(br1));
181 EXPECT_THAT(w.loop, IsLoop(if_true1, w.if_true));
182
183 EXPECT_THAT(peeled->map(c.add), IsInt32Add(c.base, c.inc));
184
185 Capture<Node*> merge;
186 EXPECT_THAT(
187 r, IsReturn(IsPhi(MachineRepresentation::kTagged, c.phi, c.base,
188 AllOf(CaptureEq(&merge), IsMerge(w.exit, if_false1))),
189 start(), CaptureEq(&merge)));
190}
191
192
193TEST_F(LoopPeelingTest, SimpleNestedLoopWithCounter_peel_outer) {
194 Node* p0 = Parameter(0);
195 While outer = NewWhile(p0);
196 While inner = NewWhile(p0);
197 Nest(&inner, &outer);
198
199 Counter c = NewCounter(&outer, 0, 1);
200 Node* r = InsertReturn(c.phi, start(), outer.exit);
201
202 PeeledIteration* peeled = PeelOne();
203
204 Node* bro = ExpectPeeled(outer.branch, peeled);
205 Node* if_trueo = ExpectPeeled(outer.if_true, peeled);
206 Node* if_falseo = ExpectPeeled(outer.exit, peeled);
207
208 EXPECT_THAT(bro, IsBranch(p0, start()));
209 EXPECT_THAT(if_trueo, IsIfTrue(bro));
210 EXPECT_THAT(if_falseo, IsIfFalse(bro));
211
212 Node* bri = ExpectPeeled(inner.branch, peeled);
213 Node* if_truei = ExpectPeeled(inner.if_true, peeled);
214 Node* if_falsei = ExpectPeeled(inner.exit, peeled);
215
216 EXPECT_THAT(bri, IsBranch(p0, ExpectPeeled(inner.loop, peeled)));
217 EXPECT_THAT(if_truei, IsIfTrue(bri));
218 EXPECT_THAT(if_falsei, IsIfFalse(bri));
219
220 EXPECT_THAT(outer.loop, IsLoop(if_falsei, inner.exit));
221 EXPECT_THAT(peeled->map(c.add), IsInt32Add(c.base, c.inc));
222
223 Capture<Node*> merge;
224 EXPECT_THAT(
225 r,
226 IsReturn(IsPhi(MachineRepresentation::kTagged, c.phi, c.base,
227 AllOf(CaptureEq(&merge), IsMerge(outer.exit, if_falseo))),
228 start(), CaptureEq(&merge)));
229}
230
231
232TEST_F(LoopPeelingTest, SimpleNestedLoopWithCounter_peel_inner) {
233 Node* p0 = Parameter(0);
234 While outer = NewWhile(p0);
235 While inner = NewWhile(p0);
236 Nest(&inner, &outer);
237
238 Counter c = NewCounter(&outer, 0, 1);
239 Node* r = InsertReturn(c.phi, start(), outer.exit);
240
241 LoopTree* loop_tree = GetLoopTree();
242 LoopTree::Loop* loop = loop_tree->ContainingLoop(inner.loop);
243 EXPECT_NE(nullptr, loop);
244 EXPECT_EQ(1u, loop->depth());
245
246 PeeledIteration* peeled = Peel(loop_tree, loop);
247
248 ExpectNotPeeled(outer.loop, peeled);
249 ExpectNotPeeled(outer.branch, peeled);
250 ExpectNotPeeled(outer.if_true, peeled);
251 ExpectNotPeeled(outer.exit, peeled);
252
253 Node* bri = ExpectPeeled(inner.branch, peeled);
254 Node* if_truei = ExpectPeeled(inner.if_true, peeled);
255 Node* if_falsei = ExpectPeeled(inner.exit, peeled);
256
257 EXPECT_THAT(bri, IsBranch(p0, ExpectPeeled(inner.loop, peeled)));
258 EXPECT_THAT(if_truei, IsIfTrue(bri));
259 EXPECT_THAT(if_falsei, IsIfFalse(bri));
260
261 EXPECT_THAT(outer.loop, IsLoop(start(), IsMerge(inner.exit, if_falsei)));
262 ExpectNotPeeled(c.add, peeled);
263
264 EXPECT_THAT(r, IsReturn(c.phi, start(), outer.exit));
265}
266
267
268TEST_F(LoopPeelingTest, SimpleInnerCounter_peel_inner) {
269 Node* p0 = Parameter(0);
270 While outer = NewWhile(p0);
271 While inner = NewWhile(p0);
272 Nest(&inner, &outer);
273 Counter c = NewCounter(&inner, 0, 1);
274 Node* phi = NewPhi(&outer, Int32Constant(11), c.phi);
275
276 Node* r = InsertReturn(phi, start(), outer.exit);
277
278 LoopTree* loop_tree = GetLoopTree();
279 LoopTree::Loop* loop = loop_tree->ContainingLoop(inner.loop);
280 EXPECT_NE(nullptr, loop);
281 EXPECT_EQ(1u, loop->depth());
282
283 PeeledIteration* peeled = Peel(loop_tree, loop);
284
285 ExpectNotPeeled(outer.loop, peeled);
286 ExpectNotPeeled(outer.branch, peeled);
287 ExpectNotPeeled(outer.if_true, peeled);
288 ExpectNotPeeled(outer.exit, peeled);
289
290 Node* bri = ExpectPeeled(inner.branch, peeled);
291 Node* if_truei = ExpectPeeled(inner.if_true, peeled);
292 Node* if_falsei = ExpectPeeled(inner.exit, peeled);
293
294 EXPECT_THAT(bri, IsBranch(p0, ExpectPeeled(inner.loop, peeled)));
295 EXPECT_THAT(if_truei, IsIfTrue(bri));
296 EXPECT_THAT(if_falsei, IsIfFalse(bri));
297
298 EXPECT_THAT(outer.loop, IsLoop(start(), IsMerge(inner.exit, if_falsei)));
299 EXPECT_THAT(peeled->map(c.add), IsInt32Add(c.base, c.inc));
300
301 Node* back = phi->InputAt(1);
302 EXPECT_THAT(back, IsPhi(MachineRepresentation::kTagged, c.phi, c.base,
303 IsMerge(inner.exit, if_falsei)));
304
305 EXPECT_THAT(phi, IsPhi(MachineRepresentation::kTagged, IsInt32Constant(11),
306 back, outer.loop));
307
308 EXPECT_THAT(r, IsReturn(phi, start(), outer.exit));
309}
310
311
312TEST_F(LoopPeelingTest, TwoBackedgeLoop) {
313 Node* p0 = Parameter(0);
314 Node* loop = graph()->NewNode(common()->Loop(3), start(), start(), start());
315 Branch b1 = NewBranch(p0, loop);
316 Branch b2 = NewBranch(p0, b1.if_true);
317
318 loop->ReplaceInput(1, b2.if_true);
319 loop->ReplaceInput(2, b2.if_false);
320
321 Node* r = InsertReturn(p0, start(), b1.if_false);
322
323 PeeledIteration* peeled = PeelOne();
324
325 Node* b1b = ExpectPeeled(b1.branch, peeled);
326 Node* b1t = ExpectPeeled(b1.if_true, peeled);
327 Node* b1f = ExpectPeeled(b1.if_false, peeled);
328
329 EXPECT_THAT(b1b, IsBranch(p0, start()));
330 EXPECT_THAT(ExpectPeeled(b1.if_true, peeled), IsIfTrue(b1b));
331 EXPECT_THAT(b1f, IsIfFalse(b1b));
332
333 Node* b2b = ExpectPeeled(b2.branch, peeled);
334 Node* b2t = ExpectPeeled(b2.if_true, peeled);
335 Node* b2f = ExpectPeeled(b2.if_false, peeled);
336
337 EXPECT_THAT(b2b, IsBranch(p0, b1t));
338 EXPECT_THAT(b2t, IsIfTrue(b2b));
339 EXPECT_THAT(b2f, IsIfFalse(b2b));
340
341 EXPECT_THAT(loop, IsLoop(IsMerge(b2t, b2f), b2.if_true, b2.if_false));
342 EXPECT_THAT(r, IsReturn(p0, start(), IsMerge(b1.if_false, b1f)));
343}
344
345
346TEST_F(LoopPeelingTest, TwoBackedgeLoopWithPhi) {
347 Node* p0 = Parameter(0);
348 Node* loop = graph()->NewNode(common()->Loop(3), start(), start(), start());
349 Branch b1 = NewBranch(p0, loop);
350 Branch b2 = NewBranch(p0, b1.if_true);
351 Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 3),
352 Int32Constant(0), Int32Constant(1),
353 Int32Constant(2), loop);
354
355 loop->ReplaceInput(1, b2.if_true);
356 loop->ReplaceInput(2, b2.if_false);
357
358 Node* r = InsertReturn(phi, start(), b1.if_false);
359
360 PeeledIteration* peeled = PeelOne();
361
362 Node* b1b = ExpectPeeled(b1.branch, peeled);
363 Node* b1t = ExpectPeeled(b1.if_true, peeled);
364 Node* b1f = ExpectPeeled(b1.if_false, peeled);
365
366 EXPECT_THAT(b1b, IsBranch(p0, start()));
367 EXPECT_THAT(ExpectPeeled(b1.if_true, peeled), IsIfTrue(b1b));
368 EXPECT_THAT(b1f, IsIfFalse(b1b));
369
370 Node* b2b = ExpectPeeled(b2.branch, peeled);
371 Node* b2t = ExpectPeeled(b2.if_true, peeled);
372 Node* b2f = ExpectPeeled(b2.if_false, peeled);
373
374 EXPECT_THAT(b2b, IsBranch(p0, b1t));
375 EXPECT_THAT(b2t, IsIfTrue(b2b));
376 EXPECT_THAT(b2f, IsIfFalse(b2b));
377
378 EXPECT_THAT(loop, IsLoop(IsMerge(b2t, b2f), b2.if_true, b2.if_false));
379
380 EXPECT_THAT(phi,
381 IsPhi(MachineRepresentation::kTagged,
382 IsPhi(MachineRepresentation::kTagged, IsInt32Constant(1),
383 IsInt32Constant(2), IsMerge(b2t, b2f)),
384 IsInt32Constant(1), IsInt32Constant(2), loop));
385
386 Capture<Node*> merge;
387 EXPECT_THAT(
388 r, IsReturn(IsPhi(MachineRepresentation::kTagged, phi, IsInt32Constant(0),
389 AllOf(CaptureEq(&merge), IsMerge(b1.if_false, b1f))),
390 start(), CaptureEq(&merge)));
391}
392
393
394TEST_F(LoopPeelingTest, TwoBackedgeLoopWithCounter) {
395 Node* p0 = Parameter(0);
396 Node* loop = graph()->NewNode(common()->Loop(3), start(), start(), start());
397 Branch b1 = NewBranch(p0, loop);
398 Branch b2 = NewBranch(p0, b1.if_true);
399 Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 3),
400 Int32Constant(0), Int32Constant(1),
401 Int32Constant(2), loop);
402
403 phi->ReplaceInput(
404 1, graph()->NewNode(machine()->Int32Add(), phi, Int32Constant(1)));
405 phi->ReplaceInput(
406 2, graph()->NewNode(machine()->Int32Add(), phi, Int32Constant(2)));
407
408 loop->ReplaceInput(1, b2.if_true);
409 loop->ReplaceInput(2, b2.if_false);
410
411 Node* r = InsertReturn(phi, start(), b1.if_false);
412
413 PeeledIteration* peeled = PeelOne();
414
415 Node* b1b = ExpectPeeled(b1.branch, peeled);
416 Node* b1t = ExpectPeeled(b1.if_true, peeled);
417 Node* b1f = ExpectPeeled(b1.if_false, peeled);
418
419 EXPECT_THAT(b1b, IsBranch(p0, start()));
420 EXPECT_THAT(ExpectPeeled(b1.if_true, peeled), IsIfTrue(b1b));
421 EXPECT_THAT(b1f, IsIfFalse(b1b));
422
423 Node* b2b = ExpectPeeled(b2.branch, peeled);
424 Node* b2t = ExpectPeeled(b2.if_true, peeled);
425 Node* b2f = ExpectPeeled(b2.if_false, peeled);
426
427 EXPECT_THAT(b2b, IsBranch(p0, b1t));
428 EXPECT_THAT(b2t, IsIfTrue(b2b));
429 EXPECT_THAT(b2f, IsIfFalse(b2b));
430
431 Capture<Node*> entry;
432 EXPECT_THAT(loop, IsLoop(AllOf(CaptureEq(&entry), IsMerge(b2t, b2f)),
433 b2.if_true, b2.if_false));
434
435 Node* eval = phi->InputAt(0);
436
437 EXPECT_THAT(eval, IsPhi(MachineRepresentation::kTagged,
438 IsInt32Add(IsInt32Constant(0), IsInt32Constant(1)),
439 IsInt32Add(IsInt32Constant(0), IsInt32Constant(2)),
440 CaptureEq(&entry)));
441
442 EXPECT_THAT(phi, IsPhi(MachineRepresentation::kTagged, eval,
443 IsInt32Add(phi, IsInt32Constant(1)),
444 IsInt32Add(phi, IsInt32Constant(2)), loop));
445
446 Capture<Node*> merge;
447 EXPECT_THAT(
448 r, IsReturn(IsPhi(MachineRepresentation::kTagged, phi, IsInt32Constant(0),
449 AllOf(CaptureEq(&merge), IsMerge(b1.if_false, b1f))),
450 start(), CaptureEq(&merge)));
451}
452
453
454TEST_F(LoopPeelingTest, TwoExitLoop_nope) {
455 Node* p0 = Parameter(0);
456 Node* loop = graph()->NewNode(common()->Loop(2), start(), start());
457 Branch b1 = NewBranch(p0, loop);
458 Branch b2 = NewBranch(p0, b1.if_true);
459
460 loop->ReplaceInput(1, b2.if_true);
461 Node* merge = graph()->NewNode(common()->Merge(2), b1.if_false, b2.if_false);
462 InsertReturn(p0, start(), merge);
463
464 {
465 LoopTree* loop_tree = GetLoopTree();
466 LoopTree::Loop* loop = loop_tree->outer_loops()[0];
467 EXPECT_FALSE(LoopPeeler::CanPeel(loop_tree, loop));
468 }
469}
470
471
472const Operator kMockCall(IrOpcode::kCall, Operator::kNoProperties, "MockCall",
473 0, 0, 1, 1, 1, 2);
474
475
476TEST_F(LoopPeelingTest, TwoExitLoopWithCall_nope) {
477 Node* p0 = Parameter(0);
478 Node* loop = graph()->NewNode(common()->Loop(2), start(), start());
479 Branch b1 = NewBranch(p0, loop);
480
481 Node* call = graph()->NewNode(&kMockCall, b1.if_true);
482 Node* if_success = graph()->NewNode(common()->IfSuccess(), call);
483 Node* if_exception = graph()->NewNode(
484 common()->IfException(IfExceptionHint::kLocallyUncaught), call, call);
485
486 loop->ReplaceInput(1, if_success);
487 Node* merge = graph()->NewNode(common()->Merge(2), b1.if_false, if_exception);
488 InsertReturn(p0, start(), merge);
489
490 {
491 LoopTree* loop_tree = GetLoopTree();
492 LoopTree::Loop* loop = loop_tree->outer_loops()[0];
493 EXPECT_FALSE(LoopPeeler::CanPeel(loop_tree, loop));
494 }
495}
496
497
498} // namespace compiler
499} // namespace internal
500} // namespace v8