1 /*
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 "induction_var_analysis.h"
22 #include "nodes.h"
23 #include "optimizing_unit_test.h"
24
25 namespace art {
26
27 /**
28 * Fixture class for the InductionVarAnalysis tests.
29 */
30 class InductionVarAnalysisTest : public CommonCompilerTest {
31 public:
InductionVarAnalysisTest()32 InductionVarAnalysisTest()
33 : pool_(),
34 allocator_(&pool_),
35 iva_(nullptr),
36 entry_(nullptr),
37 return_(nullptr),
38 exit_(nullptr),
39 parameter_(nullptr),
40 constant0_(nullptr),
41 constant1_(nullptr),
42 constant2_(nullptr),
43 constant7_(nullptr),
44 constant100_(nullptr),
45 constantm1_(nullptr),
46 float_constant0_(nullptr) {
47 graph_ = CreateGraph(&allocator_);
48 }
49
~InductionVarAnalysisTest()50 ~InductionVarAnalysisTest() { }
51
52 // Builds single for-loop at depth d.
BuildForLoop(int d,int n)53 void BuildForLoop(int d, int n) {
54 ASSERT_LT(d, n);
55 loop_preheader_[d] = new (&allocator_) HBasicBlock(graph_);
56 graph_->AddBlock(loop_preheader_[d]);
57 loop_header_[d] = new (&allocator_) HBasicBlock(graph_);
58 graph_->AddBlock(loop_header_[d]);
59 loop_preheader_[d]->AddSuccessor(loop_header_[d]);
60 if (d < (n - 1)) {
61 BuildForLoop(d + 1, n);
62 }
63 loop_body_[d] = new (&allocator_) HBasicBlock(graph_);
64 graph_->AddBlock(loop_body_[d]);
65 loop_body_[d]->AddSuccessor(loop_header_[d]);
66 if (d < (n - 1)) {
67 loop_header_[d]->AddSuccessor(loop_preheader_[d + 1]);
68 loop_header_[d + 1]->AddSuccessor(loop_body_[d]);
69 } else {
70 loop_header_[d]->AddSuccessor(loop_body_[d]);
71 }
72 }
73
74 // Builds a n-nested loop in CFG where each loop at depth 0 <= d < n
75 // is defined as "for (int i_d = 0; i_d < 100; i_d++)". Tests can further
76 // populate the loop with instructions to set up interesting scenarios.
BuildLoopNest(int n)77 void BuildLoopNest(int n) {
78 ASSERT_LE(n, 10);
79 graph_->SetNumberOfVRegs(n + 3);
80
81 // Build basic blocks with entry, nested loop, exit.
82 entry_ = new (&allocator_) HBasicBlock(graph_);
83 graph_->AddBlock(entry_);
84 BuildForLoop(0, n);
85 return_ = new (&allocator_) HBasicBlock(graph_);
86 graph_->AddBlock(return_);
87 exit_ = new (&allocator_) HBasicBlock(graph_);
88 graph_->AddBlock(exit_);
89 entry_->AddSuccessor(loop_preheader_[0]);
90 loop_header_[0]->AddSuccessor(return_);
91 return_->AddSuccessor(exit_);
92 graph_->SetEntryBlock(entry_);
93 graph_->SetExitBlock(exit_);
94
95 // Provide entry and exit instructions.
96 parameter_ = new (&allocator_) HParameterValue(
97 graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot, true);
98 entry_->AddInstruction(parameter_);
99 constant0_ = graph_->GetIntConstant(0);
100 constant1_ = graph_->GetIntConstant(1);
101 constant2_ = graph_->GetIntConstant(2);
102 constant7_ = graph_->GetIntConstant(7);
103 constant100_ = graph_->GetIntConstant(100);
104 constantm1_ = graph_->GetIntConstant(-1);
105 float_constant0_ = graph_->GetFloatConstant(0.0f);
106 return_->AddInstruction(new (&allocator_) HReturnVoid());
107 exit_->AddInstruction(new (&allocator_) HExit());
108
109 // Provide loop instructions.
110 for (int d = 0; d < n; d++) {
111 basic_[d] = new (&allocator_) HPhi(&allocator_, d, 0, Primitive::kPrimInt);
112 loop_preheader_[d]->AddInstruction(new (&allocator_) HGoto());
113 loop_header_[d]->AddPhi(basic_[d]);
114 HInstruction* compare = new (&allocator_) HLessThan(basic_[d], constant100_);
115 loop_header_[d]->AddInstruction(compare);
116 loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare));
117 increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[d], constant1_);
118 loop_body_[d]->AddInstruction(increment_[d]);
119 loop_body_[d]->AddInstruction(new (&allocator_) HGoto());
120
121 basic_[d]->AddInput(constant0_);
122 basic_[d]->AddInput(increment_[d]);
123 }
124 }
125
126 // Builds if-statement at depth d.
BuildIf(int d,HBasicBlock ** ifT,HBasicBlock ** ifF)127 HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock** ifF) {
128 HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_);
129 HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_);
130 HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_);
131 graph_->AddBlock(cond);
132 graph_->AddBlock(ifTrue);
133 graph_->AddBlock(ifFalse);
134 // Conditional split.
135 loop_header_[d]->ReplaceSuccessor(loop_body_[d], cond);
136 cond->AddSuccessor(ifTrue);
137 cond->AddSuccessor(ifFalse);
138 ifTrue->AddSuccessor(loop_body_[d]);
139 ifFalse->AddSuccessor(loop_body_[d]);
140 cond->AddInstruction(new (&allocator_) HIf(parameter_));
141 *ifT = ifTrue;
142 *ifF = ifFalse;
143
144 HPhi* select_phi = new (&allocator_) HPhi(&allocator_, -1, 0, Primitive::kPrimInt);
145 loop_body_[d]->AddPhi(select_phi);
146 return select_phi;
147 }
148
149 // Inserts instruction right before increment at depth d.
InsertInstruction(HInstruction * instruction,int d)150 HInstruction* InsertInstruction(HInstruction* instruction, int d) {
151 loop_body_[d]->InsertInstructionBefore(instruction, increment_[d]);
152 return instruction;
153 }
154
155 // Inserts a phi to loop header at depth d and returns it.
InsertLoopPhi(int vreg,int d)156 HPhi* InsertLoopPhi(int vreg, int d) {
157 HPhi* phi = new (&allocator_) HPhi(&allocator_, vreg, 0, Primitive::kPrimInt);
158 loop_header_[d]->AddPhi(phi);
159 return phi;
160 }
161
162 // Inserts an array store with given `subscript` at depth d to
163 // enable tests to inspect the computed induction at that point easily.
InsertArrayStore(HInstruction * subscript,int d)164 HInstruction* InsertArrayStore(HInstruction* subscript, int d) {
165 // ArraySet is given a float value in order to avoid SsaBuilder typing
166 // it from the array's non-existent reference type info.
167 return InsertInstruction(new (&allocator_) HArraySet(
168 parameter_, subscript, float_constant0_, Primitive::kPrimFloat, 0), d);
169 }
170
171 // Returns induction information of instruction in loop at depth d.
GetInductionInfo(HInstruction * instruction,int d)172 std::string GetInductionInfo(HInstruction* instruction, int d) {
173 return HInductionVarAnalysis::InductionToString(
174 iva_->LookupInfo(loop_body_[d]->GetLoopInformation(), instruction));
175 }
176
177 // Returns induction information of the trip-count of loop at depth d.
GetTripCount(int d)178 std::string GetTripCount(int d) {
179 HInstruction* control = loop_header_[d]->GetLastInstruction();
180 DCHECK(control->IsIf());
181 return GetInductionInfo(control, d);
182 }
183
184 // Returns true if instructions have identical induction.
HaveSameInduction(HInstruction * instruction1,HInstruction * instruction2)185 bool HaveSameInduction(HInstruction* instruction1, HInstruction* instruction2) {
186 return HInductionVarAnalysis::InductionEqual(
187 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction1),
188 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
189 }
190
191 // Returns true for narrowing linear induction.
IsNarrowingLinear(HInstruction * instruction)192 bool IsNarrowingLinear(HInstruction* instruction) {
193 return HInductionVarAnalysis::IsNarrowingLinear(
194 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction));
195 }
196
197 // Performs InductionVarAnalysis (after proper set up).
PerformInductionVarAnalysis()198 void PerformInductionVarAnalysis() {
199 graph_->BuildDominatorTree();
200 iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
201 iva_->Run();
202 }
203
204 // General building fields.
205 ArenaPool pool_;
206 ArenaAllocator allocator_;
207 HGraph* graph_;
208 HInductionVarAnalysis* iva_;
209
210 // Fixed basic blocks and instructions.
211 HBasicBlock* entry_;
212 HBasicBlock* return_;
213 HBasicBlock* exit_;
214 HInstruction* parameter_; // "this"
215 HInstruction* constant0_;
216 HInstruction* constant1_;
217 HInstruction* constant2_;
218 HInstruction* constant7_;
219 HInstruction* constant100_;
220 HInstruction* constantm1_;
221 HInstruction* float_constant0_;
222
223 // Loop specifics.
224 HBasicBlock* loop_preheader_[10];
225 HBasicBlock* loop_header_[10];
226 HBasicBlock* loop_body_[10];
227 HInstruction* increment_[10];
228 HPhi* basic_[10]; // "vreg_d", the "i_d"
229 };
230
231 //
232 // The actual InductionVarAnalysis tests.
233 //
234
TEST_F(InductionVarAnalysisTest,ProperLoopSetup)235 TEST_F(InductionVarAnalysisTest, ProperLoopSetup) {
236 // Setup:
237 // for (int i_0 = 0; i_0 < 100; i_0++) {
238 // ..
239 // for (int i_9 = 0; i_9 < 100; i_9++) {
240 // }
241 // ..
242 // }
243 BuildLoopNest(10);
244 graph_->BuildDominatorTree();
245
246 ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
247 for (int d = 0; d < 1; d++) {
248 ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
249 (d == 0) ? nullptr
250 : loop_header_[d - 1]->GetLoopInformation());
251 ASSERT_NE(loop_header_[d]->GetLoopInformation(), nullptr);
252 ASSERT_NE(loop_body_[d]->GetLoopInformation(), nullptr);
253 ASSERT_EQ(loop_header_[d]->GetLoopInformation(),
254 loop_body_[d]->GetLoopInformation());
255 }
256 ASSERT_EQ(exit_->GetLoopInformation(), nullptr);
257 }
258
TEST_F(InductionVarAnalysisTest,FindBasicInduction)259 TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
260 // Setup:
261 // for (int i = 0; i < 100; i++) {
262 // a[i] = 0;
263 // }
264 BuildLoopNest(1);
265 HInstruction* store = InsertArrayStore(basic_[0], 0);
266 PerformInductionVarAnalysis();
267
268 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
269 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
270
271 // Offset matters!
272 EXPECT_FALSE(HaveSameInduction(store->InputAt(1), increment_[0]));
273
274 // Trip-count.
275 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
276 }
277
TEST_F(InductionVarAnalysisTest,FindDerivedInduction)278 TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
279 // Setup:
280 // for (int i = 0; i < 100; i++) {
281 // t = 100 + i;
282 // t = 100 - i;
283 // t = 100 * i;
284 // t = i << 1;
285 // t = - i;
286 // }
287 BuildLoopNest(1);
288 HInstruction* add = InsertInstruction(
289 new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0);
290 HInstruction* sub = InsertInstruction(
291 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
292 HInstruction* mul = InsertInstruction(
293 new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0);
294 HInstruction* shl = InsertInstruction(
295 new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
296 HInstruction* neg = InsertInstruction(
297 new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
298 PerformInductionVarAnalysis();
299
300 EXPECT_STREQ("((1) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
301 EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
302 EXPECT_STREQ("((100) * i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
303 EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
304 EXPECT_STREQ("(( - (1)) * i + (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
305 }
306
TEST_F(InductionVarAnalysisTest,FindChainInduction)307 TEST_F(InductionVarAnalysisTest, FindChainInduction) {
308 // Setup:
309 // k = 0;
310 // for (int i = 0; i < 100; i++) {
311 // k = k + 100;
312 // a[k] = 0;
313 // k = k - 1;
314 // a[k] = 0;
315 // }
316 BuildLoopNest(1);
317 HPhi* k_header = InsertLoopPhi(0, 0);
318 k_header->AddInput(constant0_);
319
320 HInstruction* add = InsertInstruction(
321 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
322 HInstruction* store1 = InsertArrayStore(add, 0);
323 HInstruction* sub = InsertInstruction(
324 new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
325 HInstruction* store2 = InsertArrayStore(sub, 0);
326 k_header->AddInput(sub);
327 PerformInductionVarAnalysis();
328
329 EXPECT_STREQ("(((100) - (1)) * i + (0)):PrimInt",
330 GetInductionInfo(k_header, 0).c_str());
331 EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
332 GetInductionInfo(store1->InputAt(1), 0).c_str());
333 EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
334 GetInductionInfo(store2->InputAt(1), 0).c_str());
335 }
336
TEST_F(InductionVarAnalysisTest,FindTwoWayBasicInduction)337 TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
338 // Setup:
339 // k = 0;
340 // for (int i = 0; i < 100; i++) {
341 // if () k = k + 1;
342 // else k = k + 1;
343 // a[k] = 0;
344 // }
345 BuildLoopNest(1);
346 HPhi* k_header = InsertLoopPhi(0, 0);
347 k_header->AddInput(constant0_);
348
349 HBasicBlock* ifTrue;
350 HBasicBlock* ifFalse;
351 HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse);
352
353 // True-branch.
354 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
355 ifTrue->AddInstruction(inc1);
356 k_body->AddInput(inc1);
357 // False-branch.
358 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
359 ifFalse->AddInstruction(inc2);
360 k_body->AddInput(inc2);
361 // Merge over a phi.
362 HInstruction* store = InsertArrayStore(k_body, 0);
363 k_header->AddInput(k_body);
364 PerformInductionVarAnalysis();
365
366 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
367 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
368
369 // Both increments get same induction.
370 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
371 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
372 }
373
TEST_F(InductionVarAnalysisTest,FindTwoWayDerivedInduction)374 TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
375 // Setup:
376 // for (int i = 0; i < 100; i++) {
377 // if () k = i + 1;
378 // else k = i + 1;
379 // a[k] = 0;
380 // }
381 BuildLoopNest(1);
382 HBasicBlock* ifTrue;
383 HBasicBlock* ifFalse;
384 HPhi* k = BuildIf(0, &ifTrue, &ifFalse);
385
386 // True-branch.
387 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
388 ifTrue->AddInstruction(inc1);
389 k->AddInput(inc1);
390 // False-branch.
391 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
392 ifFalse->AddInstruction(inc2);
393 k->AddInput(inc2);
394 // Merge over a phi.
395 HInstruction* store = InsertArrayStore(k, 0);
396 PerformInductionVarAnalysis();
397
398 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
399
400 // Both increments get same induction.
401 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
402 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
403 }
404
TEST_F(InductionVarAnalysisTest,AddLinear)405 TEST_F(InductionVarAnalysisTest, AddLinear) {
406 // Setup:
407 // for (int i = 0; i < 100; i++) {
408 // t1 = i + i;
409 // t2 = 7 + i;
410 // t3 = t1 + t2;
411 // }
412 BuildLoopNest(1);
413
414 HInstruction* add1 = InsertInstruction(
415 new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], basic_[0]), 0);
416 HInstruction* add2 = InsertInstruction(
417 new (&allocator_) HAdd(Primitive::kPrimInt, constant7_, basic_[0]), 0);
418 HInstruction* add3 = InsertInstruction(
419 new (&allocator_) HAdd(Primitive::kPrimInt, add1, add2), 0);
420 PerformInductionVarAnalysis();
421
422 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(basic_[0], 0).c_str());
423 EXPECT_STREQ("(((1) + (1)) * i + (0)):PrimInt", GetInductionInfo(add1, 0).c_str());
424 EXPECT_STREQ("((1) * i + (7)):PrimInt", GetInductionInfo(add2, 0).c_str());
425 EXPECT_STREQ("((((1) + (1)) + (1)) * i + (7)):PrimInt", GetInductionInfo(add3, 0).c_str());
426 }
427
TEST_F(InductionVarAnalysisTest,FindPolynomialInduction)428 TEST_F(InductionVarAnalysisTest, FindPolynomialInduction) {
429 // Setup:
430 // k = 1;
431 // for (int i = 0; i < 100; i++) {
432 // t = i * 2;
433 // t = 100 + t
434 // k = t + k; // polynomial
435 // }
436 BuildLoopNest(1);
437 HPhi* k_header = InsertLoopPhi(0, 0);
438 k_header->AddInput(constant1_);
439
440 HInstruction* mul = InsertInstruction(
441 new (&allocator_) HMul(Primitive::kPrimInt, basic_[0], constant2_), 0);
442 HInstruction* add = InsertInstruction(
443 new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, mul), 0);
444 HInstruction* pol = InsertInstruction(
445 new (&allocator_) HAdd(Primitive::kPrimInt, add, k_header), 0);
446 k_header->AddInput(pol);
447 PerformInductionVarAnalysis();
448
449 // Note, only the phi in the cycle and the base linear induction are classified.
450 EXPECT_STREQ("poly(sum_lt(((2) * i + (100)):PrimInt) + (1)):PrimInt",
451 GetInductionInfo(k_header, 0).c_str());
452 EXPECT_STREQ("((2) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
453 EXPECT_STREQ("", GetInductionInfo(pol, 0).c_str());
454 }
455
TEST_F(InductionVarAnalysisTest,FindPolynomialInductionAndDerived)456 TEST_F(InductionVarAnalysisTest, FindPolynomialInductionAndDerived) {
457 // Setup:
458 // k = 1;
459 // for (int i = 0; i < 100; i++) {
460 // t = k + 100;
461 // t = k - 1;
462 // t = - t
463 // t = k * 2;
464 // t = k << 2;
465 // k = k + i; // polynomial
466 // }
467 BuildLoopNest(1);
468 HPhi* k_header = InsertLoopPhi(0, 0);
469 k_header->AddInput(constant1_);
470
471 HInstruction* add = InsertInstruction(
472 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
473 HInstruction* sub = InsertInstruction(
474 new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0);
475 HInstruction* neg = InsertInstruction(
476 new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
477 HInstruction* mul = InsertInstruction(
478 new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0);
479 HInstruction* shl = InsertInstruction(
480 new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0);
481 HInstruction* pol = InsertInstruction(
482 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, basic_[0]), 0);
483 k_header->AddInput(pol);
484 PerformInductionVarAnalysis();
485
486 // Note, only the phi in the cycle and derived are classified.
487 EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):PrimInt) + (1)):PrimInt",
488 GetInductionInfo(k_header, 0).c_str());
489 EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):PrimInt) + ((1) + (100))):PrimInt",
490 GetInductionInfo(add, 0).c_str());
491 EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):PrimInt) + ((1) - (1))):PrimInt",
492 GetInductionInfo(sub, 0).c_str());
493 EXPECT_STREQ("poly(sum_lt((( - (1)) * i + (0)):PrimInt) + ((1) - (1))):PrimInt",
494 GetInductionInfo(neg, 0).c_str());
495 EXPECT_STREQ("poly(sum_lt(((2) * i + (0)):PrimInt) + (2)):PrimInt",
496 GetInductionInfo(mul, 0).c_str());
497 EXPECT_STREQ("poly(sum_lt(((4) * i + (0)):PrimInt) + (4)):PrimInt",
498 GetInductionInfo(shl, 0).c_str());
499 EXPECT_STREQ("", GetInductionInfo(pol, 0).c_str());
500 }
501
TEST_F(InductionVarAnalysisTest,AddPolynomial)502 TEST_F(InductionVarAnalysisTest, AddPolynomial) {
503 // Setup:
504 // k = 7;
505 // for (int i = 0; i < 100; i++) {
506 // t = k + k;
507 // t = t + k;
508 // k = k + i
509 // }
510 BuildLoopNest(1);
511 HPhi* k_header = InsertLoopPhi(0, 0);
512 k_header->AddInput(constant7_);
513
514 HInstruction* add1 = InsertInstruction(
515 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, k_header), 0);
516 HInstruction* add2 = InsertInstruction(
517 new (&allocator_) HAdd(Primitive::kPrimInt, add1, k_header), 0);
518 HInstruction* add3 = InsertInstruction(
519 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, basic_[0]), 0);
520 k_header->AddInput(add3);
521 PerformInductionVarAnalysis();
522
523 // Note, only the phi in the cycle and added-derived are classified.
524 EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):PrimInt) + (7)):PrimInt",
525 GetInductionInfo(k_header, 0).c_str());
526 EXPECT_STREQ("poly(sum_lt((((1) + (1)) * i + (0)):PrimInt) + ((7) + (7))):PrimInt",
527 GetInductionInfo(add1, 0).c_str());
528 EXPECT_STREQ(
529 "poly(sum_lt(((((1) + (1)) + (1)) * i + (0)):PrimInt) + (((7) + (7)) + (7))):PrimInt",
530 GetInductionInfo(add2, 0).c_str());
531 EXPECT_STREQ("", GetInductionInfo(add3, 0).c_str());
532 }
533
TEST_F(InductionVarAnalysisTest,FindGeometricMulInduction)534 TEST_F(InductionVarAnalysisTest, FindGeometricMulInduction) {
535 // Setup:
536 // k = 1;
537 // for (int i = 0; i < 100; i++) {
538 // k = k * 100; // geometric (x 100)
539 // }
540 BuildLoopNest(1);
541 HPhi* k_header = InsertLoopPhi(0, 0);
542 k_header->AddInput(constant1_);
543
544 HInstruction* mul = InsertInstruction(
545 new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
546 k_header->AddInput(mul);
547 PerformInductionVarAnalysis();
548
549 EXPECT_STREQ("geo((1) * 100 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
550 EXPECT_STREQ("geo((100) * 100 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
551 }
552
TEST_F(InductionVarAnalysisTest,FindGeometricShlInductionAndDerived)553 TEST_F(InductionVarAnalysisTest, FindGeometricShlInductionAndDerived) {
554 // Setup:
555 // k = 1;
556 // for (int i = 0; i < 100; i++) {
557 // t = k + 1;
558 // k = k << 1; // geometric (x 2)
559 // t = k + 100;
560 // t = k - 1;
561 // t = - t;
562 // t = k * 2;
563 // t = k << 2;
564 // }
565 BuildLoopNest(1);
566 HPhi* k_header = InsertLoopPhi(0, 0);
567 k_header->AddInput(constant1_);
568
569 HInstruction* add1 = InsertInstruction(
570 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
571 HInstruction* shl1 = InsertInstruction(
572 new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
573 HInstruction* add2 = InsertInstruction(
574 new (&allocator_) HAdd(Primitive::kPrimInt, shl1, constant100_), 0);
575 HInstruction* sub = InsertInstruction(
576 new (&allocator_) HSub(Primitive::kPrimInt, shl1, constant1_), 0);
577 HInstruction* neg = InsertInstruction(
578 new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
579 HInstruction* mul = InsertInstruction(
580 new (&allocator_) HMul(Primitive::kPrimInt, shl1, constant2_), 0);
581 HInstruction* shl2 = InsertInstruction(
582 new (&allocator_) HShl(Primitive::kPrimInt, shl1, constant2_), 0);
583 k_header->AddInput(shl1);
584 PerformInductionVarAnalysis();
585
586 EXPECT_STREQ("geo((1) * 2 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
587 EXPECT_STREQ("geo((1) * 2 ^ i + (1)):PrimInt", GetInductionInfo(add1, 0).c_str());
588 EXPECT_STREQ("geo((2) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl1, 0).c_str());
589 EXPECT_STREQ("geo((2) * 2 ^ i + (100)):PrimInt", GetInductionInfo(add2, 0).c_str());
590 EXPECT_STREQ("geo((2) * 2 ^ i + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
591 EXPECT_STREQ("geo(( - (2)) * 2 ^ i + ( - ((0) - (1)))):PrimInt",
592 GetInductionInfo(neg, 0).c_str());
593 EXPECT_STREQ("geo(((2) * (2)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
594 EXPECT_STREQ("geo(((2) * (4)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
595 }
596
TEST_F(InductionVarAnalysisTest,FindGeometricDivInductionAndDerived)597 TEST_F(InductionVarAnalysisTest, FindGeometricDivInductionAndDerived) {
598 // Setup:
599 // k = 1;
600 // for (int i = 0; i < 100; i++) {
601 // t = k + 100;
602 // t = k - 1;
603 // t = - t
604 // t = k * 2;
605 // t = k << 2;
606 // k = k / 100; // geometric (/ 100)
607 // }
608 BuildLoopNest(1);
609 HPhi* k_header = InsertLoopPhi(0, 0);
610 k_header->AddInput(constant1_);
611
612 HInstruction* add = InsertInstruction(
613 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
614 HInstruction* sub = InsertInstruction(
615 new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0);
616 HInstruction* neg = InsertInstruction(
617 new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
618 HInstruction* mul = InsertInstruction(
619 new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0);
620 HInstruction* shl = InsertInstruction(
621 new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0);
622 HInstruction* div = InsertInstruction(
623 new (&allocator_) HDiv(Primitive::kPrimInt, k_header, constant100_, kNoDexPc), 0);
624 k_header->AddInput(div);
625 PerformInductionVarAnalysis();
626
627 // Note, only the phi in the cycle and direct additive derived are classified.
628 EXPECT_STREQ("geo((1) * 100 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
629 EXPECT_STREQ("geo((1) * 100 ^ -i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
630 EXPECT_STREQ("geo((1) * 100 ^ -i + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
631 EXPECT_STREQ("", GetInductionInfo(neg, 0).c_str());
632 EXPECT_STREQ("", GetInductionInfo(mul, 0).c_str());
633 EXPECT_STREQ("", GetInductionInfo(shl, 0).c_str());
634 EXPECT_STREQ("", GetInductionInfo(div, 0).c_str());
635 }
636
TEST_F(InductionVarAnalysisTest,FindGeometricShrInduction)637 TEST_F(InductionVarAnalysisTest, FindGeometricShrInduction) {
638 // Setup:
639 // k = 100;
640 // for (int i = 0; i < 100; i++) {
641 // k = k >> 1; // geometric (/ 2)
642 // }
643 BuildLoopNest(1);
644 HPhi* k_header = InsertLoopPhi(0, 0);
645 k_header->AddInput(constant100_);
646
647 HInstruction* shr = InsertInstruction(
648 new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0);
649 k_header->AddInput(shr);
650 PerformInductionVarAnalysis();
651
652 // Note, only the phi in the cycle is classified.
653 EXPECT_STREQ("geo((100) * 2 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
654 EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
655 }
656
TEST_F(InductionVarAnalysisTest,FindNotGeometricShrInduction)657 TEST_F(InductionVarAnalysisTest, FindNotGeometricShrInduction) {
658 // Setup:
659 // k = -1;
660 // for (int i = 0; i < 100; i++) {
661 // k = k >> 1; // initial value is negative
662 // }
663 BuildLoopNest(1);
664 HPhi* k_header = InsertLoopPhi(0, 0);
665 k_header->AddInput(constantm1_);
666
667 HInstruction* shr = InsertInstruction(
668 new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0);
669 k_header->AddInput(shr);
670 PerformInductionVarAnalysis();
671
672 EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
673 EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
674 }
675
TEST_F(InductionVarAnalysisTest,FindRemWrapAroundInductionAndDerived)676 TEST_F(InductionVarAnalysisTest, FindRemWrapAroundInductionAndDerived) {
677 // Setup:
678 // k = 100;
679 // for (int i = 0; i < 100; i++) {
680 // t = k + 100;
681 // t = k - 1;
682 // t = -t
683 // t = k * 2;
684 // t = k << 2;
685 // k = k % 7;
686 // }
687 BuildLoopNest(1);
688 HPhi* k_header = InsertLoopPhi(0, 0);
689 k_header->AddInput(constant100_);
690
691 HInstruction* add = InsertInstruction(
692 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
693 HInstruction* sub = InsertInstruction(
694 new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0);
695 HInstruction* neg = InsertInstruction(
696 new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
697 HInstruction* mul = InsertInstruction(
698 new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0);
699 HInstruction* shl = InsertInstruction(
700 new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0);
701 HInstruction* rem = InsertInstruction(
702 new (&allocator_) HRem(Primitive::kPrimInt, k_header, constant7_, kNoDexPc), 0);
703 k_header->AddInput(rem);
704 PerformInductionVarAnalysis();
705
706 // Note, only the phi in the cycle and derived are classified.
707 EXPECT_STREQ("wrap((100), ((100) % (7))):PrimInt", GetInductionInfo(k_header, 0).c_str());
708 EXPECT_STREQ("wrap(((100) + (100)), (((100) % (7)) + (100))):PrimInt", GetInductionInfo(add, 0).c_str());
709 EXPECT_STREQ("wrap(((100) - (1)), (((100) % (7)) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
710 EXPECT_STREQ("wrap(( - ((100) - (1))), ( - (((100) % (7)) - (1)))):PrimInt", GetInductionInfo(neg, 0).c_str());
711 EXPECT_STREQ("wrap(((100) * (2)), (((100) % (7)) * (2))):PrimInt", GetInductionInfo(mul, 0).c_str());
712 EXPECT_STREQ("wrap(((100) * (4)), (((100) % (7)) * (4))):PrimInt", GetInductionInfo(shl, 0).c_str());
713 EXPECT_STREQ("", GetInductionInfo(rem, 0).c_str());
714 }
715
TEST_F(InductionVarAnalysisTest,FindFirstOrderWrapAroundInduction)716 TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
717 // Setup:
718 // k = 0;
719 // for (int i = 0; i < 100; i++) {
720 // a[k] = 0;
721 // k = 100 - i;
722 // }
723 BuildLoopNest(1);
724 HPhi* k_header = InsertLoopPhi(0, 0);
725 k_header->AddInput(constant0_);
726
727 HInstruction* store = InsertArrayStore(k_header, 0);
728 HInstruction* sub = InsertInstruction(
729 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
730 k_header->AddInput(sub);
731 PerformInductionVarAnalysis();
732
733 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
734 GetInductionInfo(k_header, 0).c_str());
735 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
736 GetInductionInfo(store->InputAt(1), 0).c_str());
737 EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
738 }
739
TEST_F(InductionVarAnalysisTest,FindSecondOrderWrapAroundInduction)740 TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
741 // Setup:
742 // k = 0;
743 // t = 100;
744 // for (int i = 0; i < 100; i++) {
745 // a[k] = 0;
746 // k = t;
747 // t = 100 - i;
748 // }
749 BuildLoopNest(1);
750 HPhi* k_header = InsertLoopPhi(0, 0);
751 k_header->AddInput(constant0_);
752 HPhi* t = InsertLoopPhi(1, 0);
753 t->AddInput(constant100_);
754
755 HInstruction* store = InsertArrayStore(k_header, 0);
756 k_header->AddInput(t);
757 HInstruction* sub = InsertInstruction(
758 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
759 t->AddInput(sub);
760 PerformInductionVarAnalysis();
761
762 EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt",
763 GetInductionInfo(store->InputAt(1), 0).c_str());
764 }
765
TEST_F(InductionVarAnalysisTest,FindWrapAroundDerivedInduction)766 TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) {
767 // Setup:
768 // k = 0;
769 // for (int i = 0; i < 100; i++) {
770 // t = k + 100;
771 // t = k - 100;
772 // t = k * 100;
773 // t = k << 1;
774 // t = - k;
775 // k = i << 1;
776 // t = - k;
777 // }
778 BuildLoopNest(1);
779 HPhi* k_header = InsertLoopPhi(0, 0);
780 k_header->AddInput(constant0_);
781
782 HInstruction* add = InsertInstruction(
783 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
784 HInstruction* sub = InsertInstruction(
785 new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant100_), 0);
786 HInstruction* mul = InsertInstruction(
787 new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
788 HInstruction* shl1 = InsertInstruction(
789 new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
790 HInstruction* neg1 = InsertInstruction(
791 new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
792 HInstruction* shl2 = InsertInstruction(
793 new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
794 HInstruction* neg2 = InsertInstruction(
795 new (&allocator_) HNeg(Primitive::kPrimInt, shl2), 0);
796 k_header->AddInput(shl2);
797 PerformInductionVarAnalysis();
798
799 EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
800 GetInductionInfo(add, 0).c_str());
801 EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):PrimInt):PrimInt",
802 GetInductionInfo(sub, 0).c_str());
803 EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt",
804 GetInductionInfo(mul, 0).c_str());
805 EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt",
806 GetInductionInfo(shl1, 0).c_str());
807 EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
808 GetInductionInfo(neg1, 0).c_str());
809 EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
810 EXPECT_STREQ("(( - (2)) * i + (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
811 }
812
TEST_F(InductionVarAnalysisTest,FindPeriodicInduction)813 TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
814 // Setup:
815 // k = 0;
816 // t = 100;
817 // for (int i = 0; i < 100; i++) {
818 // a[k] = 0;
819 // a[t] = 0;
820 // // Swap t <-> k.
821 // d = t;
822 // t = k;
823 // k = d;
824 // }
825 BuildLoopNest(1);
826 HPhi* k_header = InsertLoopPhi(0, 0);
827 k_header->AddInput(constant0_);
828 HPhi* t = InsertLoopPhi(1, 0);
829 t->AddInput(constant100_);
830
831 HInstruction* store1 = InsertArrayStore(k_header, 0);
832 HInstruction* store2 = InsertArrayStore(t, 0);
833 k_header->AddInput(t);
834 t->AddInput(k_header);
835 PerformInductionVarAnalysis();
836
837 EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
838 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
839 }
840
TEST_F(InductionVarAnalysisTest,FindIdiomaticPeriodicInduction)841 TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
842 // Setup:
843 // k = 0;
844 // for (int i = 0; i < 100; i++) {
845 // a[k] = 0;
846 // k = 1 - k;
847 // }
848 BuildLoopNest(1);
849 HPhi* k_header = InsertLoopPhi(0, 0);
850 k_header->AddInput(constant0_);
851
852 HInstruction* store = InsertArrayStore(k_header, 0);
853 HInstruction* sub = InsertInstruction(
854 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
855 k_header->AddInput(sub);
856 PerformInductionVarAnalysis();
857
858 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
859 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str());
860 }
861
TEST_F(InductionVarAnalysisTest,FindXorPeriodicInduction)862 TEST_F(InductionVarAnalysisTest, FindXorPeriodicInduction) {
863 // Setup:
864 // k = 0;
865 // for (int i = 0; i < 100; i++) {
866 // a[k] = 0;
867 // k = k ^ 1;
868 // }
869 BuildLoopNest(1);
870 HPhi* k_header = InsertLoopPhi(0, 0);
871 k_header->AddInput(constant0_);
872
873 HInstruction* store = InsertArrayStore(k_header, 0);
874 HInstruction* x = InsertInstruction(
875 new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant1_), 0);
876 k_header->AddInput(x);
877 PerformInductionVarAnalysis();
878
879 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
880 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(x, 0).c_str());
881 }
882
TEST_F(InductionVarAnalysisTest,FindXorConstantLeftPeriodicInduction)883 TEST_F(InductionVarAnalysisTest, FindXorConstantLeftPeriodicInduction) {
884 // Setup:
885 // k = 1;
886 // for (int i = 0; i < 100; i++) {
887 // k = 1 ^ k;
888 // }
889 BuildLoopNest(1);
890 HPhi* k_header = InsertLoopPhi(0, 0);
891 k_header->AddInput(constant1_);
892
893 HInstruction* x = InsertInstruction(
894 new (&allocator_) HXor(Primitive::kPrimInt, constant1_, k_header), 0);
895 k_header->AddInput(x);
896 PerformInductionVarAnalysis();
897
898 EXPECT_STREQ("periodic((1), ((1) ^ (1))):PrimInt", GetInductionInfo(k_header, 0).c_str());
899 EXPECT_STREQ("periodic(((1) ^ (1)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
900 }
901
TEST_F(InductionVarAnalysisTest,FindXor100PeriodicInduction)902 TEST_F(InductionVarAnalysisTest, FindXor100PeriodicInduction) {
903 // Setup:
904 // k = 1;
905 // for (int i = 0; i < 100; i++) {
906 // k = k ^ 100;
907 // }
908 BuildLoopNest(1);
909 HPhi* k_header = InsertLoopPhi(0, 0);
910 k_header->AddInput(constant1_);
911
912 HInstruction* x = InsertInstruction(
913 new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant100_), 0);
914 k_header->AddInput(x);
915 PerformInductionVarAnalysis();
916
917 EXPECT_STREQ("periodic((1), ((1) ^ (100))):PrimInt", GetInductionInfo(k_header, 0).c_str());
918 EXPECT_STREQ("periodic(((1) ^ (100)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
919 }
920
TEST_F(InductionVarAnalysisTest,FindBooleanEqPeriodicInduction)921 TEST_F(InductionVarAnalysisTest, FindBooleanEqPeriodicInduction) {
922 // Setup:
923 // k = 0;
924 // for (int i = 0; i < 100; i++) {
925 // k = (k == 0);
926 // }
927 BuildLoopNest(1);
928 HPhi* k_header = InsertLoopPhi(0, 0);
929 k_header->AddInput(constant0_);
930
931 HInstruction* x = InsertInstruction(new (&allocator_) HEqual(k_header, constant0_), 0);
932 k_header->AddInput(x);
933 PerformInductionVarAnalysis();
934
935 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
936 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
937 }
938
TEST_F(InductionVarAnalysisTest,FindBooleanEqConstantLeftPeriodicInduction)939 TEST_F(InductionVarAnalysisTest, FindBooleanEqConstantLeftPeriodicInduction) {
940 // Setup:
941 // k = 0;
942 // for (int i = 0; i < 100; i++) {
943 // k = (0 == k);
944 // }
945 BuildLoopNest(1);
946 HPhi* k_header = InsertLoopPhi(0, 0);
947 k_header->AddInput(constant0_);
948
949 HInstruction* x = InsertInstruction(new (&allocator_) HEqual(constant0_, k_header), 0);
950 k_header->AddInput(x);
951 PerformInductionVarAnalysis();
952
953 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
954 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
955 }
956
TEST_F(InductionVarAnalysisTest,FindBooleanNePeriodicInduction)957 TEST_F(InductionVarAnalysisTest, FindBooleanNePeriodicInduction) {
958 // Setup:
959 // k = 0;
960 // for (int i = 0; i < 100; i++) {
961 // k = (k != 1);
962 // }
963 BuildLoopNest(1);
964 HPhi* k_header = InsertLoopPhi(0, 0);
965 k_header->AddInput(constant0_);
966
967 HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(k_header, constant1_), 0);
968 k_header->AddInput(x);
969 PerformInductionVarAnalysis();
970
971 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
972 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
973 }
974
TEST_F(InductionVarAnalysisTest,FindBooleanNeConstantLeftPeriodicInduction)975 TEST_F(InductionVarAnalysisTest, FindBooleanNeConstantLeftPeriodicInduction) {
976 // Setup:
977 // k = 0;
978 // for (int i = 0; i < 100; i++) {
979 // k = (1 != k);
980 // }
981 BuildLoopNest(1);
982 HPhi* k_header = InsertLoopPhi(0, 0);
983 k_header->AddInput(constant0_);
984
985 HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(constant1_, k_header), 0);
986 k_header->AddInput(x);
987 PerformInductionVarAnalysis();
988
989 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
990 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
991 }
992
TEST_F(InductionVarAnalysisTest,FindDerivedPeriodicInduction)993 TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
994 // Setup:
995 // k = 0;
996 // for (int i = 0; i < 100; i++) {
997 // t = - k;
998 // k = 1 - k;
999 // t = k + 100;
1000 // t = k - 100;
1001 // t = k * 100;
1002 // t = k << 1;
1003 // t = - k;
1004 // }
1005 BuildLoopNest(1);
1006 HPhi* k_header = InsertLoopPhi(0, 0);
1007 k_header->AddInput(constant0_);
1008
1009 HInstruction* neg1 = InsertInstruction(
1010 new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
1011 HInstruction* idiom = InsertInstruction(
1012 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
1013 HInstruction* add = InsertInstruction(
1014 new (&allocator_) HAdd(Primitive::kPrimInt, idiom, constant100_), 0);
1015 HInstruction* sub = InsertInstruction(
1016 new (&allocator_) HSub(Primitive::kPrimInt, idiom, constant100_), 0);
1017 HInstruction* mul = InsertInstruction(
1018 new (&allocator_) HMul(Primitive::kPrimInt, idiom, constant100_), 0);
1019 HInstruction* shl = InsertInstruction(
1020 new (&allocator_) HShl(Primitive::kPrimInt, idiom, constant1_), 0);
1021 HInstruction* neg2 = InsertInstruction(
1022 new (&allocator_) HNeg(Primitive::kPrimInt, idiom), 0);
1023 k_header->AddInput(idiom);
1024 PerformInductionVarAnalysis();
1025
1026 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(k_header, 0).c_str());
1027 EXPECT_STREQ("periodic((0), ( - (1))):PrimInt", GetInductionInfo(neg1, 0).c_str());
1028 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(idiom, 0).c_str());
1029 EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str());
1030 EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str());
1031 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
1032 EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
1033 EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
1034 }
1035
TEST_F(InductionVarAnalysisTest,FindDeepLoopInduction)1036 TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
1037 // Setup:
1038 // k = 0;
1039 // for (int i_0 = 0; i_0 < 100; i_0++) {
1040 // ..
1041 // for (int i_9 = 0; i_9 < 100; i_9++) {
1042 // k = 1 + k;
1043 // a[k] = 0;
1044 // }
1045 // ..
1046 // }
1047 BuildLoopNest(10);
1048
1049 HPhi* k_header[10];
1050 for (int d = 0; d < 10; d++) {
1051 k_header[d] = InsertLoopPhi(0, d);
1052 }
1053
1054 HInstruction* inc = InsertInstruction(
1055 new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k_header[9]), 9);
1056 HInstruction* store = InsertArrayStore(inc, 9);
1057
1058 for (int d = 0; d < 10; d++) {
1059 k_header[d]->AddInput((d != 0) ? k_header[d - 1] : constant0_);
1060 k_header[d]->AddInput((d != 9) ? k_header[d + 1] : inc);
1061 }
1062 PerformInductionVarAnalysis();
1063
1064 // Avoid exact phi number, since that depends on the SSA building phase.
1065 std::regex r("\\(\\(1\\) \\* i \\+ "
1066 "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt");
1067
1068 for (int d = 0; d < 10; d++) {
1069 if (d == 9) {
1070 EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r));
1071 } else {
1072 EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
1073 }
1074 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str());
1075 // Trip-count.
1076 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(d).c_str());
1077 }
1078 }
1079
TEST_F(InductionVarAnalysisTest,ByteInductionIntLoopControl)1080 TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) {
1081 // Setup:
1082 // for (int i = 0; i < 100; i++) {
1083 // k = (byte) i;
1084 // a[k] = 0;
1085 // a[i] = 0;
1086 // }
1087 BuildLoopNest(1);
1088 HInstruction* conv = InsertInstruction(
1089 new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
1090 HInstruction* store1 = InsertArrayStore(conv, 0);
1091 HInstruction* store2 = InsertArrayStore(basic_[0], 0);
1092 PerformInductionVarAnalysis();
1093
1094 // Regular int induction (i) is transferred over conversion into byte induction (k).
1095 EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
1096 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
1097 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
1098
1099 // Narrowing detected.
1100 EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
1101 EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1)));
1102
1103 // Type matters!
1104 EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
1105
1106 // Trip-count.
1107 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
1108 }
1109
TEST_F(InductionVarAnalysisTest,ByteInductionDerivedIntLoopControl)1110 TEST_F(InductionVarAnalysisTest, ByteInductionDerivedIntLoopControl) {
1111 // Setup:
1112 // for (int i = 0; i < 100; i++) {
1113 // k = (byte) i;
1114 // a[k] = 0;
1115 // k = k + 1
1116 // a[k] = 0;
1117 // }
1118 BuildLoopNest(1);
1119 HInstruction* conv = InsertInstruction(
1120 new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
1121 HInstruction* store1 = InsertArrayStore(conv, 0);
1122 HInstruction* add = InsertInstruction(
1123 new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
1124 HInstruction* store2 = InsertArrayStore(add, 0);
1125
1126 PerformInductionVarAnalysis();
1127
1128 // Byte induction (k) is detected, but it does not transfer over the addition,
1129 // since this may yield out-of-type values.
1130 EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
1131 EXPECT_STREQ("", GetInductionInfo(store2->InputAt(1), 0).c_str());
1132
1133 // Narrowing detected.
1134 EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
1135 EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1))); // works for null
1136 }
1137
TEST_F(InductionVarAnalysisTest,ByteInduction)1138 TEST_F(InductionVarAnalysisTest, ByteInduction) {
1139 // Setup:
1140 // k = -128;
1141 // for (int i = 0; i < 100; i++) {
1142 // k = k + 1;
1143 // k = (byte) k;
1144 // }
1145 BuildLoopNest(1);
1146 HPhi* k_header = InsertLoopPhi(0, 0);
1147 k_header->AddInput(graph_->GetIntConstant(-128));
1148
1149 HInstruction* add = InsertInstruction(
1150 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
1151 HInstruction* conv = InsertInstruction(
1152 new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
1153 k_header->AddInput(conv);
1154 PerformInductionVarAnalysis();
1155
1156 // Byte induction (k) is detected, but it does not transfer over the addition,
1157 // since this may yield out-of-type values.
1158 EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(k_header, 0).c_str());
1159 EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
1160
1161 // Narrowing detected.
1162 EXPECT_TRUE(IsNarrowingLinear(k_header));
1163 EXPECT_FALSE(IsNarrowingLinear(add)); // works for null
1164 }
1165
TEST_F(InductionVarAnalysisTest,NoByteInduction1)1166 TEST_F(InductionVarAnalysisTest, NoByteInduction1) {
1167 // Setup:
1168 // k = -129; / does not fit!
1169 // for (int i = 0; i < 100; i++) {
1170 // k = k + 1;
1171 // k = (byte) k;
1172 // }
1173 BuildLoopNest(1);
1174 HPhi* k_header = InsertLoopPhi(0, 0);
1175 k_header->AddInput(graph_->GetIntConstant(-129));
1176
1177 HInstruction* add = InsertInstruction(
1178 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
1179 HInstruction* conv = InsertInstruction(
1180 new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
1181 k_header->AddInput(conv);
1182 PerformInductionVarAnalysis();
1183
1184 EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
1185 EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
1186 }
1187
TEST_F(InductionVarAnalysisTest,NoByteInduction2)1188 TEST_F(InductionVarAnalysisTest, NoByteInduction2) {
1189 // Setup:
1190 // k = 0;
1191 // for (int i = 0; i < 100; i++) {
1192 // k = (byte) k; // conversion not done last!
1193 // k = k + 1;
1194 // }
1195 BuildLoopNest(1);
1196 HPhi* k_header = InsertLoopPhi(0, 0);
1197 k_header->AddInput(constant0_);
1198
1199 HInstruction* conv = InsertInstruction(
1200 new (&allocator_) HTypeConversion(Primitive::kPrimByte, k_header, kNoDexPc), 0);
1201 HInstruction* add = InsertInstruction(
1202 new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
1203 k_header->AddInput(add);
1204 PerformInductionVarAnalysis();
1205
1206 EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
1207 EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
1208 }
1209
TEST_F(InductionVarAnalysisTest,ByteLoopControl1)1210 TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
1211 // Setup:
1212 // for (byte i = -128; i < 127; i++) { // just fits!
1213 // }
1214 BuildLoopNest(1);
1215 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
1216 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
1217 ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
1218 HInstruction* conv =
1219 new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
1220 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1221 basic_[0]->ReplaceInput(conv, 1);
1222 PerformInductionVarAnalysis();
1223
1224 // Recorded at the phi, but not transferred to increment.
1225 EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
1226 EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
1227
1228 // Narrowing detected.
1229 EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
1230 EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
1231
1232 // Trip-count.
1233 EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str());
1234 }
1235
TEST_F(InductionVarAnalysisTest,ByteLoopControl2)1236 TEST_F(InductionVarAnalysisTest, ByteLoopControl2) {
1237 // Setup:
1238 // for (byte i = -128; i < 128; i++) { // infinite loop!
1239 // }
1240 BuildLoopNest(1);
1241 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
1242 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
1243 ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
1244 HInstruction* conv =
1245 new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
1246 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1247 basic_[0]->ReplaceInput(conv, 1);
1248 PerformInductionVarAnalysis();
1249
1250 // Recorded at the phi, but not transferred to increment.
1251 EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
1252 EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
1253
1254 // Narrowing detected.
1255 EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
1256 EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
1257
1258 // Trip-count undefined.
1259 EXPECT_STREQ("", GetTripCount(0).c_str());
1260 }
1261
TEST_F(InductionVarAnalysisTest,ShortLoopControl1)1262 TEST_F(InductionVarAnalysisTest, ShortLoopControl1) {
1263 // Setup:
1264 // for (short i = -32768; i < 32767; i++) { // just fits!
1265 // }
1266 BuildLoopNest(1);
1267 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
1268 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
1269 ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
1270 HInstruction* conv =
1271 new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
1272 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1273 basic_[0]->ReplaceInput(conv, 1);
1274 PerformInductionVarAnalysis();
1275
1276 // Recorded at the phi, but not transferred to increment.
1277 EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
1278 EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
1279
1280 // Narrowing detected.
1281 EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
1282 EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
1283
1284 // Trip-count.
1285 EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str());
1286 }
1287
TEST_F(InductionVarAnalysisTest,ShortLoopControl2)1288 TEST_F(InductionVarAnalysisTest, ShortLoopControl2) {
1289 // Setup:
1290 // for (short i = -32768; i < 32768; i++) { // infinite loop!
1291 // }
1292 BuildLoopNest(1);
1293 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
1294 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
1295 ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
1296 HInstruction* conv =
1297 new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
1298 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1299 basic_[0]->ReplaceInput(conv, 1);
1300 PerformInductionVarAnalysis();
1301
1302 // Recorded at the phi, but not transferred to increment.
1303 EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
1304 EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
1305
1306 // Narrowing detected.
1307 EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
1308 EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
1309
1310 // Trip-count undefined.
1311 EXPECT_STREQ("", GetTripCount(0).c_str());
1312 }
1313
TEST_F(InductionVarAnalysisTest,CharLoopControl1)1314 TEST_F(InductionVarAnalysisTest, CharLoopControl1) {
1315 // Setup:
1316 // for (char i = 0; i < 65535; i++) { // just fits!
1317 // }
1318 BuildLoopNest(1);
1319 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
1320 ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
1321 HInstruction* conv =
1322 new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
1323 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1324 basic_[0]->ReplaceInput(conv, 1);
1325 PerformInductionVarAnalysis();
1326
1327 // Recorded at the phi, but not transferred to increment.
1328 EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
1329 EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
1330
1331 // Narrowing detected.
1332 EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
1333 EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
1334
1335 // Trip-count.
1336 EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str());
1337 }
1338
TEST_F(InductionVarAnalysisTest,CharLoopControl2)1339 TEST_F(InductionVarAnalysisTest, CharLoopControl2) {
1340 // Setup:
1341 // for (char i = 0; i < 65536; i++) { // infinite loop!
1342 // }
1343 BuildLoopNest(1);
1344 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
1345 ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
1346 HInstruction* conv =
1347 new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
1348 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1349 basic_[0]->ReplaceInput(conv, 1);
1350 PerformInductionVarAnalysis();
1351
1352 // Recorded at the phi, but not transferred to increment.
1353 EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
1354 EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
1355
1356 // Narrowing detected.
1357 EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
1358 EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
1359
1360 // Trip-count undefined.
1361 EXPECT_STREQ("", GetTripCount(0).c_str());
1362 }
1363
1364 } // namespace art
1365