blob: 2093e3355d251b66dd46111cf76773865cf3e7e3 [file] [log] [blame]
Aart Bik30efb4e2015-07-30 12:14:31 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <regex>
18
19#include "base/arena_allocator.h"
20#include "builder.h"
21#include "gtest/gtest.h"
22#include "induction_var_analysis.h"
23#include "nodes.h"
24#include "optimizing_unit_test.h"
25
26namespace art {
27
28/**
29 * Fixture class for the InductionVarAnalysis tests.
30 */
31class InductionVarAnalysisTest : public testing::Test {
32 public:
33 InductionVarAnalysisTest() : pool_(), allocator_(&pool_) {
34 graph_ = CreateGraph(&allocator_);
35 }
36
37 ~InductionVarAnalysisTest() { }
38
39 // Builds single for-loop at depth d.
40 void BuildForLoop(int d, int n) {
41 ASSERT_LT(d, n);
42 loop_preheader_[d] = new (&allocator_) HBasicBlock(graph_);
43 graph_->AddBlock(loop_preheader_[d]);
44 loop_header_[d] = new (&allocator_) HBasicBlock(graph_);
45 graph_->AddBlock(loop_header_[d]);
46 loop_preheader_[d]->AddSuccessor(loop_header_[d]);
47 if (d < (n - 1)) {
48 BuildForLoop(d + 1, n);
49 }
50 loop_body_[d] = new (&allocator_) HBasicBlock(graph_);
51 graph_->AddBlock(loop_body_[d]);
52 loop_body_[d]->AddSuccessor(loop_header_[d]);
53 if (d < (n - 1)) {
54 loop_header_[d]->AddSuccessor(loop_preheader_[d + 1]);
55 loop_header_[d + 1]->AddSuccessor(loop_body_[d]);
56 } else {
57 loop_header_[d]->AddSuccessor(loop_body_[d]);
58 }
59 }
60
61 // Builds a n-nested loop in CFG where each loop at depth 0 <= d < n
62 // is defined as "for (int i_d = 0; i_d < 100; i_d++)". Tests can further
63 // populate the loop with instructions to set up interesting scenarios.
64 void BuildLoopNest(int n) {
65 ASSERT_LE(n, 10);
66 graph_->SetNumberOfVRegs(n + 2);
67
68 // Build basic blocks with entry, nested loop, exit.
69 entry_ = new (&allocator_) HBasicBlock(graph_);
70 graph_->AddBlock(entry_);
71 BuildForLoop(0, n);
72 exit_ = new (&allocator_) HBasicBlock(graph_);
73 graph_->AddBlock(exit_);
74 entry_->AddSuccessor(loop_preheader_[0]);
75 loop_header_[0]->AddSuccessor(exit_);
76 graph_->SetEntryBlock(entry_);
77 graph_->SetExitBlock(exit_);
78
79 // Provide entry and exit instructions.
80 // 0 : parameter
81 // 1 : constant 0
82 // 2 : constant 1
83 // 3 : constant 100
84 parameter_ = new (&allocator_)
85 HParameterValue(0, Primitive::kPrimNot, true);
86 entry_->AddInstruction(parameter_);
87 constant0_ = new (&allocator_) HConstant(Primitive::kPrimInt);
88 entry_->AddInstruction(constant0_);
89 constant1_ = new (&allocator_) HConstant(Primitive::kPrimInt);
90 entry_->AddInstruction(constant1_);
91 constant100_ = new (&allocator_) HConstant(Primitive::kPrimInt);
92 entry_->AddInstruction(constant100_);
93 exit_->AddInstruction(new (&allocator_) HExit());
94 induc_ = new (&allocator_) HLocal(n);
95 entry_->AddInstruction(induc_);
96 entry_->AddInstruction(new (&allocator_) HStoreLocal(induc_, constant0_));
97 tmp_ = new (&allocator_) HLocal(n + 1);
98 entry_->AddInstruction(tmp_);
99 entry_->AddInstruction(new (&allocator_) HStoreLocal(tmp_, constant100_));
100
101 // Provide loop instructions.
102 for (int d = 0; d < n; d++) {
103 basic_[d] = new (&allocator_) HLocal(d);
104 entry_->AddInstruction(basic_[d]);
105 loop_preheader_[d]->AddInstruction(
106 new (&allocator_) HStoreLocal(basic_[d], constant0_));
107 HInstruction* load = new (&allocator_)
108 HLoadLocal(basic_[d], Primitive::kPrimInt);
109 loop_header_[d]->AddInstruction(load);
110 HInstruction* compare = new (&allocator_)
111 HGreaterThanOrEqual(load, constant100_);
112 loop_header_[d]->AddInstruction(compare);
113 loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare));
114 load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt);
115 loop_body_[d]->AddInstruction(load);
116 increment_[d] = new (&allocator_)
117 HAdd(Primitive::kPrimInt, load, constant1_);
118 loop_body_[d]->AddInstruction(increment_[d]);
119 loop_body_[d]->AddInstruction(
120 new (&allocator_) HStoreLocal(basic_[d], increment_[d]));
121 loop_body_[d]->AddInstruction(new (&allocator_) HGoto());
122 }
123 }
124
125 // Builds if-statement at depth d.
126 void BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) {
127 HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_);
128 HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_);
129 HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_);
130 graph_->AddBlock(cond);
131 graph_->AddBlock(ifTrue);
132 graph_->AddBlock(ifFalse);
133 // Conditional split.
134 loop_header_[d]->ReplaceSuccessor(loop_body_[d], cond);
135 cond->AddSuccessor(ifTrue);
136 cond->AddSuccessor(ifFalse);
137 ifTrue->AddSuccessor(loop_body_[d]);
138 ifFalse->AddSuccessor(loop_body_[d]);
139 cond->AddInstruction(new (&allocator_) HIf(parameter_));
140 *ifT = ifTrue;
141 *ifF = ifFalse;
142 }
143
144 // Inserts instruction right before increment at depth d.
145 HInstruction* InsertInstruction(HInstruction* instruction, int d) {
146 loop_body_[d]->InsertInstructionBefore(instruction, increment_[d]);
147 return instruction;
148 }
149
150 // Inserts local load at depth d.
151 HInstruction* InsertLocalLoad(HLocal* local, int d) {
152 return InsertInstruction(
153 new (&allocator_) HLoadLocal(local, Primitive::kPrimInt), d);
154 }
155
156 // Inserts local store at depth d.
157 HInstruction* InsertLocalStore(HLocal* local, HInstruction* rhs, int d) {
158 return InsertInstruction(new (&allocator_) HStoreLocal(local, rhs), d);
159 }
160
161 // Inserts an array store with given local as subscript at depth d to
162 // enable tests to inspect the computed induction at that point easily.
163 HInstruction* InsertArrayStore(HLocal* subscript, int d) {
164 HInstruction* load = InsertInstruction(
165 new (&allocator_) HLoadLocal(subscript, Primitive::kPrimInt), d);
166 return InsertInstruction(new (&allocator_) HArraySet(
167 parameter_, load, constant0_, Primitive::kPrimInt, 0), d);
168 }
169
170 // Returns loop information of loop at depth d.
171 HLoopInformation* GetLoopInfo(int d) {
172 return loop_body_[d]->GetLoopInformation();
173 }
174
175 // Performs InductionVarAnalysis (after proper set up).
176 void PerformInductionVarAnalysis() {
177 ASSERT_TRUE(graph_->TryBuildingSsa());
178 iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
179 iva_->Run();
180 }
181
182 // General building fields.
183 ArenaPool pool_;
184 ArenaAllocator allocator_;
185 HGraph* graph_;
186 HInductionVarAnalysis* iva_;
187
188 // Fixed basic blocks and instructions.
189 HBasicBlock* entry_;
190 HBasicBlock* exit_;
191 HInstruction* parameter_; // "this"
192 HInstruction* constant0_;
193 HInstruction* constant1_;
194 HInstruction* constant100_;
195 HLocal* induc_; // "vreg_n", the "k"
196 HLocal* tmp_; // "vreg_n+1"
197
198 // Loop specifics.
199 HBasicBlock* loop_preheader_[10];
200 HBasicBlock* loop_header_[10];
201 HBasicBlock* loop_body_[10];
202 HInstruction* increment_[10];
203 HLocal* basic_[10]; // "vreg_d", the "i_d"
204};
205
206//
207// The actual InductionVarAnalysis tests.
208//
209
210TEST_F(InductionVarAnalysisTest, ProperLoopSetup) {
211 // Setup:
212 // for (int i_0 = 0; i_0 < 100; i_0++) {
213 // ..
214 // for (int i_9 = 0; i_9 < 100; i_9++) {
215 // }
216 // ..
217 // }
218 BuildLoopNest(10);
219 ASSERT_TRUE(graph_->TryBuildingSsa());
220 ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
221 for (int d = 0; d < 1; d++) {
222 ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
223 (d == 0) ? nullptr
224 : loop_header_[d - 1]->GetLoopInformation());
225 ASSERT_NE(loop_header_[d]->GetLoopInformation(), nullptr);
226 ASSERT_NE(loop_body_[d]->GetLoopInformation(), nullptr);
227 ASSERT_EQ(loop_header_[d]->GetLoopInformation(),
228 loop_body_[d]->GetLoopInformation());
229 }
230 ASSERT_EQ(exit_->GetLoopInformation(), nullptr);
231}
232
233TEST_F(InductionVarAnalysisTest, FindBasicInductionVar) {
234 // Setup:
235 // for (int i = 0; i < 100; i++) {
236 // a[i] = 0;
237 // }
238 BuildLoopNest(1);
239 HInstruction* store = InsertArrayStore(basic_[0], 0);
240 PerformInductionVarAnalysis();
241
242 EXPECT_STREQ(
243 "((2:Constant) * i + (1:Constant))",
244 iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
245 EXPECT_STREQ(
246 "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
247 iva_->InductionToString(GetLoopInfo(0), increment_[0]).c_str());
248}
249
250TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarAdd) {
251 // Setup:
252 // for (int i = 0; i < 100; i++) {
253 // k = 100 + i;
254 // a[k] = 0;
255 // }
256 BuildLoopNest(1);
257 HInstruction *add = InsertInstruction(
258 new (&allocator_) HAdd(
259 Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
260 InsertLocalStore(induc_, add, 0);
261 HInstruction* store = InsertArrayStore(induc_, 0);
262 PerformInductionVarAnalysis();
263
264 EXPECT_STREQ(
265 "((2:Constant) * i + ((3:Constant) + (1:Constant)))",
266 iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
267}
268
269TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarSub) {
270 // Setup:
271 // for (int i = 0; i < 100; i++) {
272 // k = 100 - i;
273 // a[k] = 0;
274 // }
275 BuildLoopNest(1);
276 HInstruction *sub = InsertInstruction(
277 new (&allocator_) HSub(
278 Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
279 InsertLocalStore(induc_, sub, 0);
280 HInstruction* store = InsertArrayStore(induc_, 0);
281 PerformInductionVarAnalysis();
282
283 EXPECT_STREQ(
284 "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant)))",
285 iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
286}
287
288TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarMul) {
289 // Setup:
290 // for (int i = 0; i < 100; i++) {
291 // k = 100 * i;
292 // a[k] = 0;
293 // }
294 BuildLoopNest(1);
295 HInstruction *mul = InsertInstruction(
296 new (&allocator_) HMul(
297 Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
298 InsertLocalStore(induc_, mul, 0);
299 HInstruction* store = InsertArrayStore(induc_, 0);
300 PerformInductionVarAnalysis();
301
302 EXPECT_STREQ(
303 "(((3:Constant) * (2:Constant)) * i + ((3:Constant) * (1:Constant)))",
304 iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
305}
306
307TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarNeg) {
308 // Setup:
309 // for (int i = 0; i < 100; i++) {
310 // k = - i;
311 // a[k] = 0;
312 // }
313 BuildLoopNest(1);
314 HInstruction *neg = InsertInstruction(
315 new (&allocator_) HNeg(
316 Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0)), 0);
317 InsertLocalStore(induc_, neg, 0);
318 HInstruction* store = InsertArrayStore(induc_, 0);
319 PerformInductionVarAnalysis();
320
321 EXPECT_STREQ(
322 "(( - (2:Constant)) * i + ( - (1:Constant)))",
323 iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
324}
325
326TEST_F(InductionVarAnalysisTest, FindChainInduction) {
327 // Setup:
328 // k = 0;
329 // for (int i = 0; i < 100; i++) {
330 // k = k + 100;
331 // a[k] = 0;
332 // k = k - 1;
333 // a[k] = 0;
334 // }
335 BuildLoopNest(1);
336 HInstruction *add = InsertInstruction(
337 new (&allocator_) HAdd(
338 Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
339 InsertLocalStore(induc_, add, 0);
340 HInstruction* store1 = InsertArrayStore(induc_, 0);
341 HInstruction *sub = InsertInstruction(
342 new (&allocator_) HSub(
343 Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
344 InsertLocalStore(induc_, sub, 0);
345 HInstruction* store2 = InsertArrayStore(induc_, 0);
346 PerformInductionVarAnalysis();
347
348 EXPECT_STREQ(
349 "(((3:Constant) - (2:Constant)) * i + ((1:Constant) + (3:Constant)))",
350 iva_->InductionToString(GetLoopInfo(0), store1->InputAt(1)).c_str());
351 EXPECT_STREQ(
352 "(((3:Constant) - (2:Constant)) * i + "
353 "(((1:Constant) + (3:Constant)) - (2:Constant)))",
354 iva_->InductionToString(GetLoopInfo(0), store2->InputAt(1)).c_str());
355}
356
357TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
358 // Setup:
359 // k = 0;
360 // for (int i = 0; i < 100; i++) {
361 // if () k = k + 1;
362 // else k = k + 1;
363 // a[k] = 0;
364 // }
365 BuildLoopNest(1);
366 HBasicBlock* ifTrue;
367 HBasicBlock* ifFalse;
368 BuildIf(0, &ifTrue, &ifFalse);
369 // True-branch.
370 HInstruction* load1 = new (&allocator_)
371 HLoadLocal(induc_, Primitive::kPrimInt);
372 ifTrue->AddInstruction(load1);
373 HInstruction* inc1 = new (&allocator_)
374 HAdd(Primitive::kPrimInt, load1, constant1_);
375 ifTrue->AddInstruction(inc1);
376 ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1));
377 // False-branch.
378 HInstruction* load2 = new (&allocator_)
379 HLoadLocal(induc_, Primitive::kPrimInt);
380 ifFalse->AddInstruction(load2);
381 HInstruction* inc2 = new (&allocator_)
382 HAdd(Primitive::kPrimInt, load2, constant1_);
383 ifFalse->AddInstruction(inc2);
384 ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2));
385 // Merge over a phi.
386 HInstruction* store = InsertArrayStore(induc_, 0);
387 PerformInductionVarAnalysis();
388
389 EXPECT_STREQ(
390 "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
391 iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
392}
393
394TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
395 // Setup:
396 // for (int i = 0; i < 100; i++) {
397 // if () k = i + 1;
398 // else k = i + 1;
399 // a[k] = 0;
400 // }
401 BuildLoopNest(1);
402 HBasicBlock* ifTrue;
403 HBasicBlock* ifFalse;
404 BuildIf(0, &ifTrue, &ifFalse);
405 // True-branch.
406 HInstruction* load1 = new (&allocator_)
407 HLoadLocal(basic_[0], Primitive::kPrimInt);
408 ifTrue->AddInstruction(load1);
409 HInstruction* inc1 = new (&allocator_)
410 HAdd(Primitive::kPrimInt, load1, constant1_);
411 ifTrue->AddInstruction(inc1);
412 ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1));
413 // False-branch.
414 HInstruction* load2 = new (&allocator_)
415 HLoadLocal(basic_[0], Primitive::kPrimInt);
416 ifFalse->AddInstruction(load2);
417 HInstruction* inc2 = new (&allocator_)
418 HAdd(Primitive::kPrimInt, load2, constant1_);
419 ifFalse->AddInstruction(inc2);
420 ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2));
421 // Merge over a phi.
422 HInstruction* store = InsertArrayStore(induc_, 0);
423 PerformInductionVarAnalysis();
424
425 EXPECT_STREQ(
426 "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
427 iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
428}
429
430TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
431 // Setup:
432 // k = 0;
433 // for (int i = 0; i < 100; i++) {
434 // a[k] = 0;
435 // k = 100 - i;
436 // }
437 BuildLoopNest(1);
438 HInstruction* store = InsertArrayStore(induc_, 0);
439 HInstruction *sub = InsertInstruction(
440 new (&allocator_) HSub(
441 Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
442 InsertLocalStore(induc_, sub, 0);
443 PerformInductionVarAnalysis();
444
445 EXPECT_STREQ(
446 "wrap((1:Constant), "
447 "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant))))",
448 iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
449}
450
451TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
452 // Setup:
453 // k = 0;
454 // t = 100;
455 // for (int i = 0; i < 100; i++) {
456 // a[k] = 0;
457 // k = t;
458 // t = 100 - i;
459 // }
460 BuildLoopNest(1);
461 HInstruction* store = InsertArrayStore(induc_, 0);
462 InsertLocalStore(induc_, InsertLocalLoad(tmp_, 0), 0);
463 HInstruction *sub = InsertInstruction(
464 new (&allocator_) HSub(
465 Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
466 InsertLocalStore(tmp_, sub, 0);
467 PerformInductionVarAnalysis();
468
469 EXPECT_STREQ(
470 "wrap((1:Constant), wrap((3:Constant), "
471 "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant)))))",
472 iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
473}
474
475TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
476 // Setup:
477 // k = 0;
478 // for (int i_0 = 0; i_0 < 100; i_0++) {
479 // ..
480 // for (int i_9 = 0; i_9 < 100; i_9++) {
481 // k = 1 + k;
482 // a[k] = 0;
483 // }
484 // ..
485 // }
486 BuildLoopNest(10);
487 HInstruction *inc = InsertInstruction(
488 new (&allocator_) HAdd(
489 Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 9)), 9);
490 InsertLocalStore(induc_, inc, 9);
491 HInstruction* store = InsertArrayStore(induc_, 9);
492 PerformInductionVarAnalysis();
493
494 // Match exact number of constants, but be less strict on phi number,
495 // since that depends on the SSA building phase.
496 std::regex r("\\(\\(2:Constant\\) \\* i \\+ "
497 "\\(\\(2:Constant\\) \\+ \\(\\d+:Phi\\)\\)\\)");
498
499 for (int d = 0; d < 10; d++) {
500 if (d == 9) {
501 EXPECT_TRUE(std::regex_match(
502 iva_->InductionToString(GetLoopInfo(d), store->InputAt(1)), r));
503 } else {
504 EXPECT_STREQ(
505 "",
506 iva_->InductionToString(GetLoopInfo(d), store->InputAt(1)).c_str());
507 }
508 EXPECT_STREQ(
509 "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
510 iva_->InductionToString(GetLoopInfo(d), increment_[d]).c_str());
511 }
512}
513
514} // namespace art