blob: 9516ccb38526153b0122b7937afdb6eaebc1d6cc [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"
Aart Bik30efb4e2015-07-30 12:14:31 -070021#include "induction_var_analysis.h"
22#include "nodes.h"
23#include "optimizing_unit_test.h"
24
25namespace art {
26
27/**
28 * Fixture class for the InductionVarAnalysis tests.
29 */
David Brazdil4833f5a2015-12-16 10:37:39 +000030class InductionVarAnalysisTest : public CommonCompilerTest {
Aart Bik30efb4e2015-07-30 12:14:31 -070031 public:
Andreas Gamped9911ee2017-03-27 13:27:24 -070032 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) {
Aart Bik30efb4e2015-07-30 12:14:31 -070047 graph_ = CreateGraph(&allocator_);
48 }
49
50 ~InductionVarAnalysisTest() { }
51
52 // Builds single for-loop at depth d.
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.
77 void BuildLoopNest(int n) {
78 ASSERT_LE(n, 10);
Aart Bike609b7c2015-08-27 13:46:58 -070079 graph_->SetNumberOfVRegs(n + 3);
Aart Bik30efb4e2015-07-30 12:14:31 -070080
81 // Build basic blocks with entry, nested loop, exit.
82 entry_ = new (&allocator_) HBasicBlock(graph_);
83 graph_->AddBlock(entry_);
84 BuildForLoop(0, n);
David Brazdildb51efb2015-11-06 01:36:20 +000085 return_ = new (&allocator_) HBasicBlock(graph_);
86 graph_->AddBlock(return_);
Aart Bik30efb4e2015-07-30 12:14:31 -070087 exit_ = new (&allocator_) HBasicBlock(graph_);
88 graph_->AddBlock(exit_);
89 entry_->AddSuccessor(loop_preheader_[0]);
David Brazdildb51efb2015-11-06 01:36:20 +000090 loop_header_[0]->AddSuccessor(return_);
91 return_->AddSuccessor(exit_);
Aart Bik30efb4e2015-07-30 12:14:31 -070092 graph_->SetEntryBlock(entry_);
93 graph_->SetExitBlock(exit_);
94
95 // Provide entry and exit instructions.
Calin Juravlee6e3bea2015-10-14 13:53:10 +000096 parameter_ = new (&allocator_) HParameterValue(
Andreas Gampea5b09a62016-11-17 15:21:22 -080097 graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot, true);
Aart Bik30efb4e2015-07-30 12:14:31 -070098 entry_->AddInstruction(parameter_);
Aart Bike609b7c2015-08-27 13:46:58 -070099 constant0_ = graph_->GetIntConstant(0);
100 constant1_ = graph_->GetIntConstant(1);
Aart Bikc071a012016-12-01 10:22:31 -0800101 constant2_ = graph_->GetIntConstant(2);
Aart Bikdf7822e2016-12-06 10:05:30 -0800102 constant7_ = graph_->GetIntConstant(7);
Aart Bike609b7c2015-08-27 13:46:58 -0700103 constant100_ = graph_->GetIntConstant(100);
Aart Bikd0a022d2016-12-13 11:22:31 -0800104 constantm1_ = graph_->GetIntConstant(-1);
David Brazdil15693bf2015-12-16 10:30:45 +0000105 float_constant0_ = graph_->GetFloatConstant(0.0f);
David Brazdildb51efb2015-11-06 01:36:20 +0000106 return_->AddInstruction(new (&allocator_) HReturnVoid());
Aart Bike609b7c2015-08-27 13:46:58 -0700107 exit_->AddInstruction(new (&allocator_) HExit());
Aart Bik30efb4e2015-07-30 12:14:31 -0700108
109 // Provide loop instructions.
110 for (int d = 0; d < n; d++) {
David Brazdilbadd8262016-02-02 16:28:56 +0000111 basic_[d] = new (&allocator_) HPhi(&allocator_, d, 0, Primitive::kPrimInt);
David Brazdil4833f5a2015-12-16 10:37:39 +0000112 loop_preheader_[d]->AddInstruction(new (&allocator_) HGoto());
David Brazdilbadd8262016-02-02 16:28:56 +0000113 loop_header_[d]->AddPhi(basic_[d]);
114 HInstruction* compare = new (&allocator_) HLessThan(basic_[d], constant100_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700115 loop_header_[d]->AddInstruction(compare);
116 loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare));
David Brazdilbadd8262016-02-02 16:28:56 +0000117 increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[d], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700118 loop_body_[d]->AddInstruction(increment_[d]);
Aart Bik30efb4e2015-07-30 12:14:31 -0700119 loop_body_[d]->AddInstruction(new (&allocator_) HGoto());
David Brazdilbadd8262016-02-02 16:28:56 +0000120
121 basic_[d]->AddInput(constant0_);
122 basic_[d]->AddInput(increment_[d]);
Aart Bik30efb4e2015-07-30 12:14:31 -0700123 }
124 }
125
126 // Builds if-statement at depth d.
Aart Bik7dc96932016-10-12 10:01:05 -0700127 HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock** ifF) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700128 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;
David Brazdilbadd8262016-02-02 16:28:56 +0000143
144 HPhi* select_phi = new (&allocator_) HPhi(&allocator_, -1, 0, Primitive::kPrimInt);
145 loop_body_[d]->AddPhi(select_phi);
146 return select_phi;
Aart Bik30efb4e2015-07-30 12:14:31 -0700147 }
148
149 // Inserts instruction right before increment at depth d.
150 HInstruction* InsertInstruction(HInstruction* instruction, int d) {
151 loop_body_[d]->InsertInstructionBefore(instruction, increment_[d]);
152 return instruction;
153 }
154
David Brazdilbadd8262016-02-02 16:28:56 +0000155 // Inserts a phi to loop header at depth d and returns it.
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;
Aart Bik30efb4e2015-07-30 12:14:31 -0700160 }
161
David Brazdilbadd8262016-02-02 16:28:56 +0000162 // Inserts an array store with given `subscript` at depth d to
Aart Bik30efb4e2015-07-30 12:14:31 -0700163 // enable tests to inspect the computed induction at that point easily.
David Brazdilbadd8262016-02-02 16:28:56 +0000164 HInstruction* InsertArrayStore(HInstruction* subscript, int d) {
David Brazdil15693bf2015-12-16 10:30:45 +0000165 // ArraySet is given a float value in order to avoid SsaBuilder typing
166 // it from the array's non-existent reference type info.
Aart Bik30efb4e2015-07-30 12:14:31 -0700167 return InsertInstruction(new (&allocator_) HArraySet(
David Brazdilbadd8262016-02-02 16:28:56 +0000168 parameter_, subscript, float_constant0_, Primitive::kPrimFloat, 0), d);
Aart Bik30efb4e2015-07-30 12:14:31 -0700169 }
170
Aart Bike609b7c2015-08-27 13:46:58 -0700171 // Returns induction information of instruction in loop at depth d.
172 std::string GetInductionInfo(HInstruction* instruction, int d) {
173 return HInductionVarAnalysis::InductionToString(
174 iva_->LookupInfo(loop_body_[d]->GetLoopInformation(), instruction));
Aart Bik30efb4e2015-07-30 12:14:31 -0700175 }
176
Aart Bik009cace2016-09-16 10:15:19 -0700177 // Returns induction information of the trip-count of loop at depth 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
Aart Bik78296912016-03-25 13:14:53 -0700184 // Returns true if instructions have identical induction.
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
Aart Bike6bd0272016-12-16 13:57:52 -0800191 // Returns true for narrowing linear induction.
192 bool IsNarrowingLinear(HInstruction* instruction) {
193 return HInductionVarAnalysis::IsNarrowingLinear(
194 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction));
195 }
196
Aart Bik30efb4e2015-07-30 12:14:31 -0700197 // Performs InductionVarAnalysis (after proper set up).
198 void PerformInductionVarAnalysis() {
David Brazdilbadd8262016-02-02 16:28:56 +0000199 graph_->BuildDominatorTree();
Aart Bik30efb4e2015-07-30 12:14:31 -0700200 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_;
David Brazdildb51efb2015-11-06 01:36:20 +0000212 HBasicBlock* return_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700213 HBasicBlock* exit_;
214 HInstruction* parameter_; // "this"
215 HInstruction* constant0_;
216 HInstruction* constant1_;
Aart Bikc071a012016-12-01 10:22:31 -0800217 HInstruction* constant2_;
Aart Bikdf7822e2016-12-06 10:05:30 -0800218 HInstruction* constant7_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700219 HInstruction* constant100_;
Aart Bikd0a022d2016-12-13 11:22:31 -0800220 HInstruction* constantm1_;
David Brazdil15693bf2015-12-16 10:30:45 +0000221 HInstruction* float_constant0_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700222
223 // Loop specifics.
224 HBasicBlock* loop_preheader_[10];
225 HBasicBlock* loop_header_[10];
226 HBasicBlock* loop_body_[10];
227 HInstruction* increment_[10];
David Brazdilbadd8262016-02-02 16:28:56 +0000228 HPhi* basic_[10]; // "vreg_d", the "i_d"
Aart Bik30efb4e2015-07-30 12:14:31 -0700229};
230
231//
232// The actual InductionVarAnalysis tests.
233//
234
235TEST_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);
David Brazdilbadd8262016-02-02 16:28:56 +0000244 graph_->BuildDominatorTree();
Aart Bik0d345cf2016-03-16 10:49:38 -0700245
Aart Bik30efb4e2015-07-30 12:14:31 -0700246 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
Aart Bike609b7c2015-08-27 13:46:58 -0700259TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700260 // Setup:
261 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700262 // a[i] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700263 // }
264 BuildLoopNest(1);
265 HInstruction* store = InsertArrayStore(basic_[0], 0);
266 PerformInductionVarAnalysis();
267
Aart Bik0d345cf2016-03-16 10:49:38 -0700268 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());
Aart Bikd14c5952015-09-08 15:25:15 -0700270
Aart Bik78296912016-03-25 13:14:53 -0700271 // Offset matters!
272 EXPECT_FALSE(HaveSameInduction(store->InputAt(1), increment_[0]));
273
Aart Bikd14c5952015-09-08 15:25:15 -0700274 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700275 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700276}
277
Aart Bike609b7c2015-08-27 13:46:58 -0700278TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700279 // Setup:
280 // for (int i = 0; i < 100; i++) {
Aart Bikc071a012016-12-01 10:22:31 -0800281 // t = 100 + i;
282 // t = 100 - i;
283 // t = 100 * i;
284 // t = i << 1;
285 // t = - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700286 // }
287 BuildLoopNest(1);
Aart Bik7dc96932016-10-12 10:01:05 -0700288 HInstruction* add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000289 new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700290 HInstruction* sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000291 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700292 HInstruction* mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000293 new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700294 HInstruction* shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000295 new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700296 HInstruction* neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000297 new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700298 PerformInductionVarAnalysis();
299
Aart Bik0d345cf2016-03-16 10:49:38 -0700300 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());
Aart Bik30efb4e2015-07-30 12:14:31 -0700305}
306
307TEST_F(InductionVarAnalysisTest, FindChainInduction) {
308 // Setup:
309 // k = 0;
310 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700311 // k = k + 100;
312 // a[k] = 0;
313 // k = k - 1;
314 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700315 // }
316 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800317 HPhi* k_header = InsertLoopPhi(0, 0);
318 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000319
Aart Bik7dc96932016-10-12 10:01:05 -0700320 HInstruction* add = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800321 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
David Brazdilbadd8262016-02-02 16:28:56 +0000322 HInstruction* store1 = InsertArrayStore(add, 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700323 HInstruction* sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000324 new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
325 HInstruction* store2 = InsertArrayStore(sub, 0);
Aart Bikc071a012016-12-01 10:22:31 -0800326 k_header->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700327 PerformInductionVarAnalysis();
328
Aart Bikc071a012016-12-01 10:22:31 -0800329 EXPECT_STREQ("(((100) - (1)) * i + (0)):PrimInt",
330 GetInductionInfo(k_header, 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700331 EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700332 GetInductionInfo(store1->InputAt(1), 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700333 EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700334 GetInductionInfo(store2->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700335}
336
337TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
338 // Setup:
339 // k = 0;
340 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700341 // if () k = k + 1;
342 // else k = k + 1;
343 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700344 // }
345 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000346 HPhi* k_header = InsertLoopPhi(0, 0);
347 k_header->AddInput(constant0_);
348
Aart Bik30efb4e2015-07-30 12:14:31 -0700349 HBasicBlock* ifTrue;
350 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000351 HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse);
352
Aart Bik30efb4e2015-07-30 12:14:31 -0700353 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000354 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700355 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000356 k_body->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700357 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000358 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700359 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000360 k_body->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700361 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000362 HInstruction* store = InsertArrayStore(k_body, 0);
363 k_header->AddInput(k_body);
Aart Bik30efb4e2015-07-30 12:14:31 -0700364 PerformInductionVarAnalysis();
365
Aart Bikc071a012016-12-01 10:22:31 -0800366 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700367 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik78296912016-03-25 13:14:53 -0700368
369 // Both increments get same induction.
370 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
371 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
Aart Bik30efb4e2015-07-30 12:14:31 -0700372}
373
374TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
375 // Setup:
376 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700377 // if () k = i + 1;
378 // else k = i + 1;
379 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700380 // }
381 BuildLoopNest(1);
382 HBasicBlock* ifTrue;
383 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000384 HPhi* k = BuildIf(0, &ifTrue, &ifFalse);
385
Aart Bik30efb4e2015-07-30 12:14:31 -0700386 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000387 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700388 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000389 k->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700390 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000391 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700392 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000393 k->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700394 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000395 HInstruction* store = InsertArrayStore(k, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700396 PerformInductionVarAnalysis();
397
Aart Bik0d345cf2016-03-16 10:49:38 -0700398 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bikc071a012016-12-01 10:22:31 -0800399
400 // Both increments get same induction.
401 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
402 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
403}
404
Aart Bikdf7822e2016-12-06 10:05:30 -0800405TEST_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
428TEST_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
456TEST_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
502TEST_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
Aart Bikc071a012016-12-01 10:22:31 -0800534TEST_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
553TEST_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
597TEST_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
Aart Bikd0a022d2016-12-13 11:22:31 -0800637TEST_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
657TEST_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
Aart Bikdf7822e2016-12-06 10:05:30 -0800676TEST_F(InductionVarAnalysisTest, FindRemWrapAroundInductionAndDerived) {
Aart Bikc071a012016-12-01 10:22:31 -0800677 // Setup:
Aart Bikdf7822e2016-12-06 10:05:30 -0800678 // k = 100;
Aart Bikc071a012016-12-01 10:22:31 -0800679 // 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;
Aart Bikdf7822e2016-12-06 10:05:30 -0800685 // k = k % 7;
Aart Bikc071a012016-12-01 10:22:31 -0800686 // }
687 BuildLoopNest(1);
688 HPhi* k_header = InsertLoopPhi(0, 0);
Aart Bikdf7822e2016-12-06 10:05:30 -0800689 k_header->AddInput(constant100_);
Aart Bikc071a012016-12-01 10:22:31 -0800690
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(
Aart Bikdf7822e2016-12-06 10:05:30 -0800702 new (&allocator_) HRem(Primitive::kPrimInt, k_header, constant7_, kNoDexPc), 0);
Aart Bikc071a012016-12-01 10:22:31 -0800703 k_header->AddInput(rem);
704 PerformInductionVarAnalysis();
705
Aart Bikdf7822e2016-12-06 10:05:30 -0800706 // 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());
Aart Bikc071a012016-12-01 10:22:31 -0800713 EXPECT_STREQ("", GetInductionInfo(rem, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700714}
715
716TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
717 // Setup:
718 // k = 0;
719 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700720 // a[k] = 0;
721 // k = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700722 // }
723 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800724 HPhi* k_header = InsertLoopPhi(0, 0);
725 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000726
Aart Bikc071a012016-12-01 10:22:31 -0800727 HInstruction* store = InsertArrayStore(k_header, 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700728 HInstruction* sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000729 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bikc071a012016-12-01 10:22:31 -0800730 k_header->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700731 PerformInductionVarAnalysis();
732
Aart Bik0d345cf2016-03-16 10:49:38 -0700733 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
Aart Bikc071a012016-12-01 10:22:31 -0800734 GetInductionInfo(k_header, 0).c_str());
735 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700736 GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bikc071a012016-12-01 10:22:31 -0800737 EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700738}
739
740TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
741 // Setup:
742 // k = 0;
743 // t = 100;
744 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700745 // a[k] = 0;
746 // k = t;
747 // t = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700748 // }
749 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800750 HPhi* k_header = InsertLoopPhi(0, 0);
751 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000752 HPhi* t = InsertLoopPhi(1, 0);
753 t->AddInput(constant100_);
754
Aart Bikc071a012016-12-01 10:22:31 -0800755 HInstruction* store = InsertArrayStore(k_header, 0);
756 k_header->AddInput(t);
Aart Bik7dc96932016-10-12 10:01:05 -0700757 HInstruction* sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000758 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
759 t->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700760 PerformInductionVarAnalysis();
761
Aart Bik0d345cf2016-03-16 10:49:38 -0700762 EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700763 GetInductionInfo(store->InputAt(1), 0).c_str());
764}
765
766TEST_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;
Aart Bikc071a012016-12-01 10:22:31 -0800776 // t = - k;
Aart Bike609b7c2015-08-27 13:46:58 -0700777 // }
778 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800779 HPhi* k_header = InsertLoopPhi(0, 0);
780 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000781
Aart Bik7dc96932016-10-12 10:01:05 -0700782 HInstruction* add = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800783 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700784 HInstruction* sub = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800785 new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700786 HInstruction* mul = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800787 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);
Aart Bike609b7c2015-08-27 13:46:58 -0700797 PerformInductionVarAnalysis();
798
Aart Bik0d345cf2016-03-16 10:49:38 -0700799 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",
Aart Bikc071a012016-12-01 10:22:31 -0800806 GetInductionInfo(shl1, 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700807 EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
Aart Bikc071a012016-12-01 10:22:31 -0800808 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());
Aart Bike609b7c2015-08-27 13:46:58 -0700811}
812
813TEST_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);
Aart Bikc071a012016-12-01 10:22:31 -0800826 HPhi* k_header = InsertLoopPhi(0, 0);
827 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000828 HPhi* t = InsertLoopPhi(1, 0);
829 t->AddInput(constant100_);
830
Aart Bikc071a012016-12-01 10:22:31 -0800831 HInstruction* store1 = InsertArrayStore(k_header, 0);
David Brazdilbadd8262016-02-02 16:28:56 +0000832 HInstruction* store2 = InsertArrayStore(t, 0);
Aart Bikc071a012016-12-01 10:22:31 -0800833 k_header->AddInput(t);
834 t->AddInput(k_header);
Aart Bike609b7c2015-08-27 13:46:58 -0700835 PerformInductionVarAnalysis();
836
Aart Bik0d345cf2016-03-16 10:49:38 -0700837 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());
Aart Bike609b7c2015-08-27 13:46:58 -0700839}
840
841TEST_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);
Aart Bikc071a012016-12-01 10:22:31 -0800849 HPhi* k_header = InsertLoopPhi(0, 0);
850 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000851
Aart Bikc071a012016-12-01 10:22:31 -0800852 HInstruction* store = InsertArrayStore(k_header, 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700853 HInstruction* sub = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800854 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
855 k_header->AddInput(sub);
Aart Bike609b7c2015-08-27 13:46:58 -0700856 PerformInductionVarAnalysis();
857
Aart Bik0d345cf2016-03-16 10:49:38 -0700858 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());
Aart Bike609b7c2015-08-27 13:46:58 -0700860}
861
Aart Bik7dc96932016-10-12 10:01:05 -0700862TEST_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);
Aart Bikc071a012016-12-01 10:22:31 -0800870 HPhi* k_header = InsertLoopPhi(0, 0);
871 k_header->AddInput(constant0_);
Aart Bik7dc96932016-10-12 10:01:05 -0700872
Aart Bikc071a012016-12-01 10:22:31 -0800873 HInstruction* store = InsertArrayStore(k_header, 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700874 HInstruction* x = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800875 new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant1_), 0);
876 k_header->AddInput(x);
Aart Bik7dc96932016-10-12 10:01:05 -0700877 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
Aart Bik639cc8c2016-10-18 13:03:31 -0700883TEST_F(InductionVarAnalysisTest, FindXorConstantLeftPeriodicInduction) {
884 // Setup:
885 // k = 1;
886 // for (int i = 0; i < 100; i++) {
887 // k = 1 ^ k;
888 // }
889 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800890 HPhi* k_header = InsertLoopPhi(0, 0);
891 k_header->AddInput(constant1_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700892
893 HInstruction* x = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800894 new (&allocator_) HXor(Primitive::kPrimInt, constant1_, k_header), 0);
895 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700896 PerformInductionVarAnalysis();
897
Aart Bikc071a012016-12-01 10:22:31 -0800898 EXPECT_STREQ("periodic((1), ((1) ^ (1))):PrimInt", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700899 EXPECT_STREQ("periodic(((1) ^ (1)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
900}
901
Aart Bik7dc96932016-10-12 10:01:05 -0700902TEST_F(InductionVarAnalysisTest, FindXor100PeriodicInduction) {
903 // Setup:
Aart Bik639cc8c2016-10-18 13:03:31 -0700904 // k = 1;
Aart Bik7dc96932016-10-12 10:01:05 -0700905 // for (int i = 0; i < 100; i++) {
906 // k = k ^ 100;
907 // }
908 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800909 HPhi* k_header = InsertLoopPhi(0, 0);
910 k_header->AddInput(constant1_);
Aart Bik7dc96932016-10-12 10:01:05 -0700911
912 HInstruction* x = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800913 new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant100_), 0);
914 k_header->AddInput(x);
Aart Bik7dc96932016-10-12 10:01:05 -0700915 PerformInductionVarAnalysis();
916
Aart Bikc071a012016-12-01 10:22:31 -0800917 EXPECT_STREQ("periodic((1), ((1) ^ (100))):PrimInt", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700918 EXPECT_STREQ("periodic(((1) ^ (100)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
919}
920
921TEST_F(InductionVarAnalysisTest, FindBooleanEqPeriodicInduction) {
922 // Setup:
923 // k = 0;
924 // for (int i = 0; i < 100; i++) {
925 // k = (k == 0);
926 // }
927 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800928 HPhi* k_header = InsertLoopPhi(0, 0);
929 k_header->AddInput(constant0_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700930
Aart Bikc071a012016-12-01 10:22:31 -0800931 HInstruction* x = InsertInstruction(new (&allocator_) HEqual(k_header, constant0_), 0);
932 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700933 PerformInductionVarAnalysis();
934
Aart Bikc071a012016-12-01 10:22:31 -0800935 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700936 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
937}
938
939TEST_F(InductionVarAnalysisTest, FindBooleanEqConstantLeftPeriodicInduction) {
940 // Setup:
941 // k = 0;
942 // for (int i = 0; i < 100; i++) {
943 // k = (0 == k);
944 // }
945 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800946 HPhi* k_header = InsertLoopPhi(0, 0);
947 k_header->AddInput(constant0_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700948
Aart Bikc071a012016-12-01 10:22:31 -0800949 HInstruction* x = InsertInstruction(new (&allocator_) HEqual(constant0_, k_header), 0);
950 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700951 PerformInductionVarAnalysis();
952
Aart Bikc071a012016-12-01 10:22:31 -0800953 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700954 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
955}
956
957TEST_F(InductionVarAnalysisTest, FindBooleanNePeriodicInduction) {
958 // Setup:
959 // k = 0;
960 // for (int i = 0; i < 100; i++) {
961 // k = (k != 1);
962 // }
963 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800964 HPhi* k_header = InsertLoopPhi(0, 0);
965 k_header->AddInput(constant0_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700966
Aart Bikc071a012016-12-01 10:22:31 -0800967 HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(k_header, constant1_), 0);
968 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700969 PerformInductionVarAnalysis();
970
Aart Bikc071a012016-12-01 10:22:31 -0800971 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700972 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
973}
974
975TEST_F(InductionVarAnalysisTest, FindBooleanNeConstantLeftPeriodicInduction) {
976 // Setup:
977 // k = 0;
978 // for (int i = 0; i < 100; i++) {
979 // k = (1 != k);
980 // }
981 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800982 HPhi* k_header = InsertLoopPhi(0, 0);
983 k_header->AddInput(constant0_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700984
Aart Bikc071a012016-12-01 10:22:31 -0800985 HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(constant1_, k_header), 0);
986 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700987 PerformInductionVarAnalysis();
988
Aart Bikc071a012016-12-01 10:22:31 -0800989 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700990 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
Aart Bik7dc96932016-10-12 10:01:05 -0700991}
992
Aart Bike609b7c2015-08-27 13:46:58 -0700993TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
994 // Setup:
995 // k = 0;
996 // for (int i = 0; i < 100; i++) {
Aart Bikc071a012016-12-01 10:22:31 -0800997 // t = - k;
Aart Bike609b7c2015-08-27 13:46:58 -0700998 // 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);
David Brazdilbadd8262016-02-02 16:28:56 +00001006 HPhi* k_header = InsertLoopPhi(0, 0);
1007 k_header->AddInput(constant0_);
1008
Aart Bikc071a012016-12-01 10:22:31 -08001009 HInstruction* neg1 = InsertInstruction(
1010 new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
1011 HInstruction* idiom = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +00001012 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
Aart Bik7dc96932016-10-12 10:01:05 -07001013 HInstruction* add = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -08001014 new (&allocator_) HAdd(Primitive::kPrimInt, idiom, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -07001015 HInstruction* sub = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -08001016 new (&allocator_) HSub(Primitive::kPrimInt, idiom, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -07001017 HInstruction* mul = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -08001018 new (&allocator_) HMul(Primitive::kPrimInt, idiom, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -07001019 HInstruction* shl = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -08001020 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);
Aart Bike609b7c2015-08-27 13:46:58 -07001024 PerformInductionVarAnalysis();
1025
Aart Bikc071a012016-12-01 10:22:31 -08001026 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());
Aart Bik0d345cf2016-03-16 10:49:38 -07001029 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());
Aart Bikc071a012016-12-01 10:22:31 -08001033 EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -07001034}
1035
1036TEST_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);
David Brazdilbadd8262016-02-02 16:28:56 +00001048
Aart Bikc071a012016-12-01 10:22:31 -08001049 HPhi* k_header[10];
David Brazdilbadd8262016-02-02 16:28:56 +00001050 for (int d = 0; d < 10; d++) {
Aart Bikc071a012016-12-01 10:22:31 -08001051 k_header[d] = InsertLoopPhi(0, d);
David Brazdilbadd8262016-02-02 16:28:56 +00001052 }
1053
Aart Bik7dc96932016-10-12 10:01:05 -07001054 HInstruction* inc = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -08001055 new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k_header[9]), 9);
David Brazdilbadd8262016-02-02 16:28:56 +00001056 HInstruction* store = InsertArrayStore(inc, 9);
1057
1058 for (int d = 0; d < 10; d++) {
Aart Bikc071a012016-12-01 10:22:31 -08001059 k_header[d]->AddInput((d != 0) ? k_header[d - 1] : constant0_);
1060 k_header[d]->AddInput((d != 9) ? k_header[d + 1] : inc);
David Brazdilbadd8262016-02-02 16:28:56 +00001061 }
Aart Bik30efb4e2015-07-30 12:14:31 -07001062 PerformInductionVarAnalysis();
1063
Aart Bike609b7c2015-08-27 13:46:58 -07001064 // Avoid exact phi number, since that depends on the SSA building phase.
1065 std::regex r("\\(\\(1\\) \\* i \\+ "
Aart Bik0d345cf2016-03-16 10:49:38 -07001066 "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt");
Aart Bik30efb4e2015-07-30 12:14:31 -07001067
1068 for (int d = 0; d < 10; d++) {
1069 if (d == 9) {
Aart Bike609b7c2015-08-27 13:46:58 -07001070 EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r));
Aart Bik30efb4e2015-07-30 12:14:31 -07001071 } else {
Aart Bike609b7c2015-08-27 13:46:58 -07001072 EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -07001073 }
Aart Bik0d345cf2016-03-16 10:49:38 -07001074 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str());
Aart Bikd14c5952015-09-08 15:25:15 -07001075 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -07001076 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -07001077 }
1078}
1079
Aart Bik78296912016-03-25 13:14:53 -07001080TEST_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);
Aart Bik7dc96932016-10-12 10:01:05 -07001088 HInstruction* conv = InsertInstruction(
Aart Bike6bd0272016-12-16 13:57:52 -08001089 new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
Aart Bik78296912016-03-25 13:14:53 -07001090 HInstruction* store1 = InsertArrayStore(conv, 0);
1091 HInstruction* store2 = InsertArrayStore(basic_[0], 0);
1092 PerformInductionVarAnalysis();
1093
Aart Bike6bd0272016-12-16 13:57:52 -08001094 // Regular int induction (i) is transferred over conversion into byte induction (k).
Aart Bik78296912016-03-25 13:14:53 -07001095 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
Aart Bike6bd0272016-12-16 13:57:52 -08001099 // Narrowing detected.
1100 EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
1101 EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1)));
1102
Aart Bik78296912016-03-25 13:14:53 -07001103 // Type matters!
1104 EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
1105
1106 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -07001107 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
Aart Bik78296912016-03-25 13:14:53 -07001108}
1109
Aart Bikcc42be02016-10-20 16:14:16 -07001110TEST_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(
Aart Bike6bd0272016-12-16 13:57:52 -08001120 new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
Aart Bikcc42be02016-10-20 16:14:16 -07001121 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
Aart Bike6bd0272016-12-16 13:57:52 -08001128 // Byte induction (k) is detected, but it does not transfer over the addition,
1129 // since this may yield out-of-type values.
Aart Bikcc42be02016-10-20 16:14:16 -07001130 EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
Aart Bike6bd0272016-12-16 13:57:52 -08001131 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
1138TEST_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
1166TEST_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
1188TEST_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());
Aart Bikcc42be02016-10-20 16:14:16 -07001208}
1209
Aart Bik0d345cf2016-03-16 10:49:38 -07001210TEST_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);
Aart Bike6bd0272016-12-16 13:57:52 -08001218 HInstruction* conv =
1219 new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
Aart Bik0d345cf2016-03-16 10:49:38 -07001220 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1221 basic_[0]->ReplaceInput(conv, 1);
1222 PerformInductionVarAnalysis();
1223
Aart Bike6bd0272016-12-16 13:57:52 -08001224 // 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
Aart Bik0d345cf2016-03-16 10:49:38 -07001232 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -07001233 EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -07001234}
1235
1236TEST_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);
Aart Bike6bd0272016-12-16 13:57:52 -08001244 HInstruction* conv =
1245 new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
Aart Bik0d345cf2016-03-16 10:49:38 -07001246 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1247 basic_[0]->ReplaceInput(conv, 1);
1248 PerformInductionVarAnalysis();
1249
Aart Bike6bd0272016-12-16 13:57:52 -08001250 // 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
Aart Bik0d345cf2016-03-16 10:49:38 -07001258 // Trip-count undefined.
Aart Bik009cace2016-09-16 10:15:19 -07001259 EXPECT_STREQ("", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -07001260}
1261
1262TEST_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);
Aart Bike6bd0272016-12-16 13:57:52 -08001270 HInstruction* conv =
1271 new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
Aart Bik0d345cf2016-03-16 10:49:38 -07001272 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1273 basic_[0]->ReplaceInput(conv, 1);
1274 PerformInductionVarAnalysis();
1275
Aart Bike6bd0272016-12-16 13:57:52 -08001276 // 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
Aart Bik0d345cf2016-03-16 10:49:38 -07001284 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -07001285 EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -07001286}
1287
1288TEST_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);
Aart Bike6bd0272016-12-16 13:57:52 -08001296 HInstruction* conv =
1297 new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
Aart Bik0d345cf2016-03-16 10:49:38 -07001298 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1299 basic_[0]->ReplaceInput(conv, 1);
1300 PerformInductionVarAnalysis();
1301
Aart Bike6bd0272016-12-16 13:57:52 -08001302 // 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
Aart Bik0d345cf2016-03-16 10:49:38 -07001310 // Trip-count undefined.
Aart Bik009cace2016-09-16 10:15:19 -07001311 EXPECT_STREQ("", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -07001312}
1313
1314TEST_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);
Aart Bike6bd0272016-12-16 13:57:52 -08001321 HInstruction* conv =
1322 new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
Aart Bik0d345cf2016-03-16 10:49:38 -07001323 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1324 basic_[0]->ReplaceInput(conv, 1);
1325 PerformInductionVarAnalysis();
1326
Aart Bike6bd0272016-12-16 13:57:52 -08001327 // 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
Aart Bik0d345cf2016-03-16 10:49:38 -07001335 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -07001336 EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -07001337}
1338
1339TEST_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);
Aart Bike6bd0272016-12-16 13:57:52 -08001346 HInstruction* conv =
1347 new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
Aart Bik0d345cf2016-03-16 10:49:38 -07001348 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1349 basic_[0]->ReplaceInput(conv, 1);
1350 PerformInductionVarAnalysis();
1351
Aart Bike6bd0272016-12-16 13:57:52 -08001352 // 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
Aart Bik0d345cf2016-03-16 10:49:38 -07001360 // Trip-count undefined.
Aart Bik009cace2016-09-16 10:15:19 -07001361 EXPECT_STREQ("", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -07001362}
1363
Aart Bik30efb4e2015-07-30 12:14:31 -07001364} // namespace art