blob: 580d24b74b946d6b907b15270f2cb8e7e48c95f8 [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:
32 InductionVarAnalysisTest() : pool_(), allocator_(&pool_) {
33 graph_ = CreateGraph(&allocator_);
34 }
35
36 ~InductionVarAnalysisTest() { }
37
38 // Builds single for-loop at depth d.
39 void BuildForLoop(int d, int n) {
40 ASSERT_LT(d, n);
41 loop_preheader_[d] = new (&allocator_) HBasicBlock(graph_);
42 graph_->AddBlock(loop_preheader_[d]);
43 loop_header_[d] = new (&allocator_) HBasicBlock(graph_);
44 graph_->AddBlock(loop_header_[d]);
45 loop_preheader_[d]->AddSuccessor(loop_header_[d]);
46 if (d < (n - 1)) {
47 BuildForLoop(d + 1, n);
48 }
49 loop_body_[d] = new (&allocator_) HBasicBlock(graph_);
50 graph_->AddBlock(loop_body_[d]);
51 loop_body_[d]->AddSuccessor(loop_header_[d]);
52 if (d < (n - 1)) {
53 loop_header_[d]->AddSuccessor(loop_preheader_[d + 1]);
54 loop_header_[d + 1]->AddSuccessor(loop_body_[d]);
55 } else {
56 loop_header_[d]->AddSuccessor(loop_body_[d]);
57 }
58 }
59
60 // Builds a n-nested loop in CFG where each loop at depth 0 <= d < n
61 // is defined as "for (int i_d = 0; i_d < 100; i_d++)". Tests can further
62 // populate the loop with instructions to set up interesting scenarios.
63 void BuildLoopNest(int n) {
64 ASSERT_LE(n, 10);
Aart Bike609b7c2015-08-27 13:46:58 -070065 graph_->SetNumberOfVRegs(n + 3);
Aart Bik30efb4e2015-07-30 12:14:31 -070066
67 // Build basic blocks with entry, nested loop, exit.
68 entry_ = new (&allocator_) HBasicBlock(graph_);
69 graph_->AddBlock(entry_);
70 BuildForLoop(0, n);
David Brazdildb51efb2015-11-06 01:36:20 +000071 return_ = new (&allocator_) HBasicBlock(graph_);
72 graph_->AddBlock(return_);
Aart Bik30efb4e2015-07-30 12:14:31 -070073 exit_ = new (&allocator_) HBasicBlock(graph_);
74 graph_->AddBlock(exit_);
75 entry_->AddSuccessor(loop_preheader_[0]);
David Brazdildb51efb2015-11-06 01:36:20 +000076 loop_header_[0]->AddSuccessor(return_);
77 return_->AddSuccessor(exit_);
Aart Bik30efb4e2015-07-30 12:14:31 -070078 graph_->SetEntryBlock(entry_);
79 graph_->SetExitBlock(exit_);
80
81 // Provide entry and exit instructions.
Calin Juravlee6e3bea2015-10-14 13:53:10 +000082 parameter_ = new (&allocator_) HParameterValue(
83 graph_->GetDexFile(), 0, 0, Primitive::kPrimNot, true);
Aart Bik30efb4e2015-07-30 12:14:31 -070084 entry_->AddInstruction(parameter_);
Aart Bike609b7c2015-08-27 13:46:58 -070085 constant0_ = graph_->GetIntConstant(0);
86 constant1_ = graph_->GetIntConstant(1);
87 constant100_ = graph_->GetIntConstant(100);
David Brazdil15693bf2015-12-16 10:30:45 +000088 float_constant0_ = graph_->GetFloatConstant(0.0f);
David Brazdildb51efb2015-11-06 01:36:20 +000089 return_->AddInstruction(new (&allocator_) HReturnVoid());
Aart Bike609b7c2015-08-27 13:46:58 -070090 exit_->AddInstruction(new (&allocator_) HExit());
Aart Bik30efb4e2015-07-30 12:14:31 -070091
92 // Provide loop instructions.
93 for (int d = 0; d < n; d++) {
David Brazdilbadd8262016-02-02 16:28:56 +000094 basic_[d] = new (&allocator_) HPhi(&allocator_, d, 0, Primitive::kPrimInt);
David Brazdil4833f5a2015-12-16 10:37:39 +000095 loop_preheader_[d]->AddInstruction(new (&allocator_) HGoto());
David Brazdilbadd8262016-02-02 16:28:56 +000096 loop_header_[d]->AddPhi(basic_[d]);
97 HInstruction* compare = new (&allocator_) HLessThan(basic_[d], constant100_);
Aart Bik30efb4e2015-07-30 12:14:31 -070098 loop_header_[d]->AddInstruction(compare);
99 loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare));
David Brazdilbadd8262016-02-02 16:28:56 +0000100 increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[d], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700101 loop_body_[d]->AddInstruction(increment_[d]);
Aart Bik30efb4e2015-07-30 12:14:31 -0700102 loop_body_[d]->AddInstruction(new (&allocator_) HGoto());
David Brazdilbadd8262016-02-02 16:28:56 +0000103
104 basic_[d]->AddInput(constant0_);
105 basic_[d]->AddInput(increment_[d]);
Aart Bik30efb4e2015-07-30 12:14:31 -0700106 }
107 }
108
109 // Builds if-statement at depth d.
David Brazdilbadd8262016-02-02 16:28:56 +0000110 HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700111 HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_);
112 HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_);
113 HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_);
114 graph_->AddBlock(cond);
115 graph_->AddBlock(ifTrue);
116 graph_->AddBlock(ifFalse);
117 // Conditional split.
118 loop_header_[d]->ReplaceSuccessor(loop_body_[d], cond);
119 cond->AddSuccessor(ifTrue);
120 cond->AddSuccessor(ifFalse);
121 ifTrue->AddSuccessor(loop_body_[d]);
122 ifFalse->AddSuccessor(loop_body_[d]);
123 cond->AddInstruction(new (&allocator_) HIf(parameter_));
124 *ifT = ifTrue;
125 *ifF = ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000126
127 HPhi* select_phi = new (&allocator_) HPhi(&allocator_, -1, 0, Primitive::kPrimInt);
128 loop_body_[d]->AddPhi(select_phi);
129 return select_phi;
Aart Bik30efb4e2015-07-30 12:14:31 -0700130 }
131
132 // Inserts instruction right before increment at depth d.
133 HInstruction* InsertInstruction(HInstruction* instruction, int d) {
134 loop_body_[d]->InsertInstructionBefore(instruction, increment_[d]);
135 return instruction;
136 }
137
David Brazdilbadd8262016-02-02 16:28:56 +0000138 // Inserts a phi to loop header at depth d and returns it.
139 HPhi* InsertLoopPhi(int vreg, int d) {
140 HPhi* phi = new (&allocator_) HPhi(&allocator_, vreg, 0, Primitive::kPrimInt);
141 loop_header_[d]->AddPhi(phi);
142 return phi;
Aart Bik30efb4e2015-07-30 12:14:31 -0700143 }
144
David Brazdilbadd8262016-02-02 16:28:56 +0000145 // Inserts an array store with given `subscript` at depth d to
Aart Bik30efb4e2015-07-30 12:14:31 -0700146 // enable tests to inspect the computed induction at that point easily.
David Brazdilbadd8262016-02-02 16:28:56 +0000147 HInstruction* InsertArrayStore(HInstruction* subscript, int d) {
David Brazdil15693bf2015-12-16 10:30:45 +0000148 // ArraySet is given a float value in order to avoid SsaBuilder typing
149 // it from the array's non-existent reference type info.
Aart Bik30efb4e2015-07-30 12:14:31 -0700150 return InsertInstruction(new (&allocator_) HArraySet(
David Brazdilbadd8262016-02-02 16:28:56 +0000151 parameter_, subscript, float_constant0_, Primitive::kPrimFloat, 0), d);
Aart Bik30efb4e2015-07-30 12:14:31 -0700152 }
153
Aart Bike609b7c2015-08-27 13:46:58 -0700154 // Returns induction information of instruction in loop at depth d.
155 std::string GetInductionInfo(HInstruction* instruction, int d) {
156 return HInductionVarAnalysis::InductionToString(
157 iva_->LookupInfo(loop_body_[d]->GetLoopInformation(), instruction));
Aart Bik30efb4e2015-07-30 12:14:31 -0700158 }
159
Aart Bik78296912016-03-25 13:14:53 -0700160 // Returns true if instructions have identical induction.
161 bool HaveSameInduction(HInstruction* instruction1, HInstruction* instruction2) {
162 return HInductionVarAnalysis::InductionEqual(
163 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction1),
164 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
165 }
166
Aart Bik30efb4e2015-07-30 12:14:31 -0700167 // Performs InductionVarAnalysis (after proper set up).
168 void PerformInductionVarAnalysis() {
David Brazdilbadd8262016-02-02 16:28:56 +0000169 graph_->BuildDominatorTree();
Aart Bik30efb4e2015-07-30 12:14:31 -0700170 iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
171 iva_->Run();
172 }
173
174 // General building fields.
175 ArenaPool pool_;
176 ArenaAllocator allocator_;
177 HGraph* graph_;
178 HInductionVarAnalysis* iva_;
179
180 // Fixed basic blocks and instructions.
181 HBasicBlock* entry_;
David Brazdildb51efb2015-11-06 01:36:20 +0000182 HBasicBlock* return_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700183 HBasicBlock* exit_;
184 HInstruction* parameter_; // "this"
185 HInstruction* constant0_;
186 HInstruction* constant1_;
187 HInstruction* constant100_;
David Brazdil15693bf2015-12-16 10:30:45 +0000188 HInstruction* float_constant0_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700189
190 // Loop specifics.
191 HBasicBlock* loop_preheader_[10];
192 HBasicBlock* loop_header_[10];
193 HBasicBlock* loop_body_[10];
194 HInstruction* increment_[10];
David Brazdilbadd8262016-02-02 16:28:56 +0000195 HPhi* basic_[10]; // "vreg_d", the "i_d"
Aart Bik30efb4e2015-07-30 12:14:31 -0700196};
197
198//
199// The actual InductionVarAnalysis tests.
200//
201
202TEST_F(InductionVarAnalysisTest, ProperLoopSetup) {
203 // Setup:
204 // for (int i_0 = 0; i_0 < 100; i_0++) {
205 // ..
206 // for (int i_9 = 0; i_9 < 100; i_9++) {
207 // }
208 // ..
209 // }
210 BuildLoopNest(10);
David Brazdilbadd8262016-02-02 16:28:56 +0000211 graph_->BuildDominatorTree();
Aart Bik0d345cf2016-03-16 10:49:38 -0700212
Aart Bik30efb4e2015-07-30 12:14:31 -0700213 ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
214 for (int d = 0; d < 1; d++) {
215 ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
216 (d == 0) ? nullptr
217 : loop_header_[d - 1]->GetLoopInformation());
218 ASSERT_NE(loop_header_[d]->GetLoopInformation(), nullptr);
219 ASSERT_NE(loop_body_[d]->GetLoopInformation(), nullptr);
220 ASSERT_EQ(loop_header_[d]->GetLoopInformation(),
221 loop_body_[d]->GetLoopInformation());
222 }
223 ASSERT_EQ(exit_->GetLoopInformation(), nullptr);
224}
225
Aart Bike609b7c2015-08-27 13:46:58 -0700226TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700227 // Setup:
228 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700229 // a[i] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700230 // }
231 BuildLoopNest(1);
232 HInstruction* store = InsertArrayStore(basic_[0], 0);
233 PerformInductionVarAnalysis();
234
Aart Bik0d345cf2016-03-16 10:49:38 -0700235 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
236 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
Aart Bikd14c5952015-09-08 15:25:15 -0700237
Aart Bik78296912016-03-25 13:14:53 -0700238 // Offset matters!
239 EXPECT_FALSE(HaveSameInduction(store->InputAt(1), increment_[0]));
240
Aart Bikd14c5952015-09-08 15:25:15 -0700241 // Trip-count.
Aart Bik22f05872015-10-27 15:56:28 -0700242 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
Aart Bik9401f532015-09-28 16:25:56 -0700243 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700244}
245
Aart Bike609b7c2015-08-27 13:46:58 -0700246TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700247 // Setup:
248 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700249 // k = 100 + i;
250 // k = 100 - i;
251 // k = 100 * i;
252 // k = i << 1;
253 // k = - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700254 // }
255 BuildLoopNest(1);
256 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000257 new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700258 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000259 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700260 HInstruction *mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000261 new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700262 HInstruction *shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000263 new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700264 HInstruction *neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000265 new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700266 PerformInductionVarAnalysis();
267
Aart Bik0d345cf2016-03-16 10:49:38 -0700268 EXPECT_STREQ("((1) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
269 EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
270 EXPECT_STREQ("((100) * i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
271 EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
272 EXPECT_STREQ("(( - (1)) * i + (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700273}
274
275TEST_F(InductionVarAnalysisTest, FindChainInduction) {
276 // Setup:
277 // k = 0;
278 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700279 // k = k + 100;
280 // a[k] = 0;
281 // k = k - 1;
282 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700283 // }
284 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000285 HPhi* k = InsertLoopPhi(0, 0);
286 k->AddInput(constant0_);
287
Aart Bik30efb4e2015-07-30 12:14:31 -0700288 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000289 new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
290 HInstruction* store1 = InsertArrayStore(add, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700291 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000292 new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
293 HInstruction* store2 = InsertArrayStore(sub, 0);
294 k->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700295 PerformInductionVarAnalysis();
296
Aart Bik0d345cf2016-03-16 10:49:38 -0700297 EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700298 GetInductionInfo(store1->InputAt(1), 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700299 EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700300 GetInductionInfo(store2->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700301}
302
303TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
304 // Setup:
305 // k = 0;
306 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700307 // if () k = k + 1;
308 // else k = k + 1;
309 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700310 // }
311 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000312 HPhi* k_header = InsertLoopPhi(0, 0);
313 k_header->AddInput(constant0_);
314
Aart Bik30efb4e2015-07-30 12:14:31 -0700315 HBasicBlock* ifTrue;
316 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000317 HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse);
318
Aart Bik30efb4e2015-07-30 12:14:31 -0700319 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000320 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700321 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000322 k_body->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700323 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000324 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700325 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000326 k_body->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700327 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000328 HInstruction* store = InsertArrayStore(k_body, 0);
329 k_header->AddInput(k_body);
Aart Bik30efb4e2015-07-30 12:14:31 -0700330 PerformInductionVarAnalysis();
331
Aart Bik0d345cf2016-03-16 10:49:38 -0700332 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik78296912016-03-25 13:14:53 -0700333
334 // Both increments get same induction.
335 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
336 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
Aart Bik30efb4e2015-07-30 12:14:31 -0700337}
338
339TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
340 // Setup:
341 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700342 // if () k = i + 1;
343 // else k = i + 1;
344 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700345 // }
346 BuildLoopNest(1);
347 HBasicBlock* ifTrue;
348 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000349 HPhi* k = BuildIf(0, &ifTrue, &ifFalse);
350
Aart Bik30efb4e2015-07-30 12:14:31 -0700351 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000352 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700353 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000354 k->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700355 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000356 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700357 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000358 k->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700359 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000360 HInstruction* store = InsertArrayStore(k, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700361 PerformInductionVarAnalysis();
362
Aart Bik0d345cf2016-03-16 10:49:38 -0700363 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700364}
365
366TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
367 // Setup:
368 // k = 0;
369 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700370 // a[k] = 0;
371 // k = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700372 // }
373 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000374 HPhi* k = InsertLoopPhi(0, 0);
375 k->AddInput(constant0_);
376
377 HInstruction* store = InsertArrayStore(k, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700378 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000379 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
380 k->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700381 PerformInductionVarAnalysis();
382
Aart Bik0d345cf2016-03-16 10:49:38 -0700383 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700384 GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700385}
386
387TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
388 // Setup:
389 // k = 0;
390 // t = 100;
391 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700392 // a[k] = 0;
393 // k = t;
394 // t = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700395 // }
396 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000397 HPhi* k = InsertLoopPhi(0, 0);
398 k->AddInput(constant0_);
399 HPhi* t = InsertLoopPhi(1, 0);
400 t->AddInput(constant100_);
401
402 HInstruction* store = InsertArrayStore(k, 0);
403 k->AddInput(t);
Aart Bik30efb4e2015-07-30 12:14:31 -0700404 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000405 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
406 t->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700407 PerformInductionVarAnalysis();
408
Aart Bik0d345cf2016-03-16 10:49:38 -0700409 EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700410 GetInductionInfo(store->InputAt(1), 0).c_str());
411}
412
413TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) {
414 // Setup:
415 // k = 0;
416 // for (int i = 0; i < 100; i++) {
417 // t = k + 100;
418 // t = k - 100;
419 // t = k * 100;
420 // t = k << 1;
421 // t = - k;
422 // k = i << 1;
423 // }
424 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000425 HPhi* k = InsertLoopPhi(0, 0);
426 k->AddInput(constant0_);
427
Aart Bike609b7c2015-08-27 13:46:58 -0700428 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000429 new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700430 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000431 new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700432 HInstruction *mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000433 new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700434 HInstruction *shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000435 new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700436 HInstruction *neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000437 new (&allocator_) HNeg(Primitive::kPrimInt, k), 0);
438 k->AddInput(
439 InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
Aart Bike609b7c2015-08-27 13:46:58 -0700440 PerformInductionVarAnalysis();
441
Aart Bik0d345cf2016-03-16 10:49:38 -0700442 EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
443 GetInductionInfo(add, 0).c_str());
444 EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):PrimInt):PrimInt",
445 GetInductionInfo(sub, 0).c_str());
446 EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt",
447 GetInductionInfo(mul, 0).c_str());
448 EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt",
449 GetInductionInfo(shl, 0).c_str());
450 EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
451 GetInductionInfo(neg, 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700452}
453
454TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
455 // Setup:
456 // k = 0;
457 // t = 100;
458 // for (int i = 0; i < 100; i++) {
459 // a[k] = 0;
460 // a[t] = 0;
461 // // Swap t <-> k.
462 // d = t;
463 // t = k;
464 // k = d;
465 // }
466 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000467 HPhi* k = InsertLoopPhi(0, 0);
468 k->AddInput(constant0_);
469 HPhi* t = InsertLoopPhi(1, 0);
470 t->AddInput(constant100_);
471
472 HInstruction* store1 = InsertArrayStore(k, 0);
473 HInstruction* store2 = InsertArrayStore(t, 0);
474 k->AddInput(t);
475 t->AddInput(k);
Aart Bike609b7c2015-08-27 13:46:58 -0700476 PerformInductionVarAnalysis();
477
Aart Bik0d345cf2016-03-16 10:49:38 -0700478 EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
479 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700480}
481
482TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
483 // Setup:
484 // k = 0;
485 // for (int i = 0; i < 100; i++) {
486 // a[k] = 0;
487 // k = 1 - k;
488 // }
489 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000490 HPhi* k = InsertLoopPhi(0, 0);
491 k->AddInput(constant0_);
492
493 HInstruction* store = InsertArrayStore(k, 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700494 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000495 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0);
496 k->AddInput(sub);
Aart Bike609b7c2015-08-27 13:46:58 -0700497 PerformInductionVarAnalysis();
498
Aart Bik0d345cf2016-03-16 10:49:38 -0700499 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
500 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700501}
502
503TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
504 // Setup:
505 // k = 0;
506 // for (int i = 0; i < 100; i++) {
507 // k = 1 - k;
508 // t = k + 100;
509 // t = k - 100;
510 // t = k * 100;
511 // t = k << 1;
512 // t = - k;
513 // }
514 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000515 HPhi* k_header = InsertLoopPhi(0, 0);
516 k_header->AddInput(constant0_);
517
518 HInstruction* k_body = InsertInstruction(
519 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
520 k_header->AddInput(k_body);
521
Aart Bike609b7c2015-08-27 13:46:58 -0700522 // Derived expressions.
523 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000524 new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700525 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000526 new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700527 HInstruction *mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000528 new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700529 HInstruction *shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000530 new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700531 HInstruction *neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000532 new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700533 PerformInductionVarAnalysis();
534
Aart Bik0d345cf2016-03-16 10:49:38 -0700535 EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str());
536 EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str());
537 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
538 EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
539 EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700540}
541
542TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
543 // Setup:
544 // k = 0;
545 // for (int i_0 = 0; i_0 < 100; i_0++) {
546 // ..
547 // for (int i_9 = 0; i_9 < 100; i_9++) {
548 // k = 1 + k;
549 // a[k] = 0;
550 // }
551 // ..
552 // }
553 BuildLoopNest(10);
David Brazdilbadd8262016-02-02 16:28:56 +0000554
555 HPhi* k[10];
556 for (int d = 0; d < 10; d++) {
557 k[d] = InsertLoopPhi(0, d);
558 }
559
Aart Bik30efb4e2015-07-30 12:14:31 -0700560 HInstruction *inc = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000561 new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9);
562 HInstruction* store = InsertArrayStore(inc, 9);
563
564 for (int d = 0; d < 10; d++) {
David Brazdil6dd10fa2016-02-15 17:14:31 +0000565 k[d]->AddInput((d != 0) ? k[d - 1] : constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000566 k[d]->AddInput((d != 9) ? k[d + 1] : inc);
567 }
Aart Bik30efb4e2015-07-30 12:14:31 -0700568 PerformInductionVarAnalysis();
569
Aart Bike609b7c2015-08-27 13:46:58 -0700570 // Avoid exact phi number, since that depends on the SSA building phase.
571 std::regex r("\\(\\(1\\) \\* i \\+ "
Aart Bik0d345cf2016-03-16 10:49:38 -0700572 "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt");
Aart Bik30efb4e2015-07-30 12:14:31 -0700573
574 for (int d = 0; d < 10; d++) {
575 if (d == 9) {
Aart Bike609b7c2015-08-27 13:46:58 -0700576 EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r));
Aart Bik30efb4e2015-07-30 12:14:31 -0700577 } else {
Aart Bike609b7c2015-08-27 13:46:58 -0700578 EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700579 }
Aart Bik0d345cf2016-03-16 10:49:38 -0700580 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str());
Aart Bikd14c5952015-09-08 15:25:15 -0700581 // Trip-count.
Aart Bik22f05872015-10-27 15:56:28 -0700582 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
Aart Bik9401f532015-09-28 16:25:56 -0700583 GetInductionInfo(loop_header_[d]->GetLastInstruction(), d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700584 }
585}
586
Aart Bik78296912016-03-25 13:14:53 -0700587TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) {
588 // Setup:
589 // for (int i = 0; i < 100; i++) {
590 // k = (byte) i;
591 // a[k] = 0;
592 // a[i] = 0;
593 // }
594 BuildLoopNest(1);
595 HInstruction *conv = InsertInstruction(
596 new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
597 HInstruction* store1 = InsertArrayStore(conv, 0);
598 HInstruction* store2 = InsertArrayStore(basic_[0], 0);
599 PerformInductionVarAnalysis();
600
601 // Regular int induction (i) is "transferred" over conversion into byte induction (k).
602 EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
603 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
604 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
605
606 // Type matters!
607 EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
608
609 // Trip-count.
610 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
611 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
612}
613
Aart Bik0d345cf2016-03-16 10:49:38 -0700614TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
615 // Setup:
616 // for (byte i = -128; i < 127; i++) { // just fits!
617 // }
618 BuildLoopNest(1);
619 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
620 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
621 ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
622 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
623 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
624 basic_[0]->ReplaceInput(conv, 1);
625 PerformInductionVarAnalysis();
626
627 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
628 // Trip-count.
629 EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))",
630 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
631}
632
633TEST_F(InductionVarAnalysisTest, ByteLoopControl2) {
634 // Setup:
635 // for (byte i = -128; i < 128; i++) { // infinite loop!
636 // }
637 BuildLoopNest(1);
638 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
639 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
640 ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
641 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
642 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
643 basic_[0]->ReplaceInput(conv, 1);
644 PerformInductionVarAnalysis();
645
646 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
647 // Trip-count undefined.
648 EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
649}
650
651TEST_F(InductionVarAnalysisTest, ShortLoopControl1) {
652 // Setup:
653 // for (short i = -32768; i < 32767; i++) { // just fits!
654 // }
655 BuildLoopNest(1);
656 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
657 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
658 ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
659 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
660 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
661 basic_[0]->ReplaceInput(conv, 1);
662 PerformInductionVarAnalysis();
663
664 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
665 GetInductionInfo(increment_[0], 0).c_str());
666 // Trip-count.
667 EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))",
668 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
669}
670
671TEST_F(InductionVarAnalysisTest, ShortLoopControl2) {
672 // Setup:
673 // for (short i = -32768; i < 32768; i++) { // infinite loop!
674 // }
675 BuildLoopNest(1);
676 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
677 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
678 ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
679 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
680 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
681 basic_[0]->ReplaceInput(conv, 1);
682 PerformInductionVarAnalysis();
683
684 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
685 GetInductionInfo(increment_[0], 0).c_str());
686 // Trip-count undefined.
687 EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
688}
689
690TEST_F(InductionVarAnalysisTest, CharLoopControl1) {
691 // Setup:
692 // for (char i = 0; i < 65535; i++) { // just fits!
693 // }
694 BuildLoopNest(1);
695 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
696 ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
697 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
698 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
699 basic_[0]->ReplaceInput(conv, 1);
700 PerformInductionVarAnalysis();
701
702 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
703 // Trip-count.
704 EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))",
705 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
706}
707
708TEST_F(InductionVarAnalysisTest, CharLoopControl2) {
709 // Setup:
710 // for (char i = 0; i < 65536; i++) { // infinite loop!
711 // }
712 BuildLoopNest(1);
713 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
714 ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
715 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
716 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
717 basic_[0]->ReplaceInput(conv, 1);
718 PerformInductionVarAnalysis();
719
720 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
721 // Trip-count undefined.
722 EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
723}
724
Aart Bik30efb4e2015-07-30 12:14:31 -0700725} // namespace art