blob: 0fbb67d0d965d8f52070f2e1972260ac2486b19a [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
160 // Performs InductionVarAnalysis (after proper set up).
161 void PerformInductionVarAnalysis() {
David Brazdilbadd8262016-02-02 16:28:56 +0000162 graph_->BuildDominatorTree();
Aart Bik30efb4e2015-07-30 12:14:31 -0700163 iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
164 iva_->Run();
165 }
166
167 // General building fields.
168 ArenaPool pool_;
169 ArenaAllocator allocator_;
170 HGraph* graph_;
171 HInductionVarAnalysis* iva_;
172
173 // Fixed basic blocks and instructions.
174 HBasicBlock* entry_;
David Brazdildb51efb2015-11-06 01:36:20 +0000175 HBasicBlock* return_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700176 HBasicBlock* exit_;
177 HInstruction* parameter_; // "this"
178 HInstruction* constant0_;
179 HInstruction* constant1_;
180 HInstruction* constant100_;
David Brazdil15693bf2015-12-16 10:30:45 +0000181 HInstruction* float_constant0_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700182
183 // Loop specifics.
184 HBasicBlock* loop_preheader_[10];
185 HBasicBlock* loop_header_[10];
186 HBasicBlock* loop_body_[10];
187 HInstruction* increment_[10];
David Brazdilbadd8262016-02-02 16:28:56 +0000188 HPhi* basic_[10]; // "vreg_d", the "i_d"
Aart Bik30efb4e2015-07-30 12:14:31 -0700189};
190
191//
192// The actual InductionVarAnalysis tests.
193//
194
195TEST_F(InductionVarAnalysisTest, ProperLoopSetup) {
196 // Setup:
197 // for (int i_0 = 0; i_0 < 100; i_0++) {
198 // ..
199 // for (int i_9 = 0; i_9 < 100; i_9++) {
200 // }
201 // ..
202 // }
203 BuildLoopNest(10);
David Brazdilbadd8262016-02-02 16:28:56 +0000204 graph_->BuildDominatorTree();
Aart Bik0d345cf2016-03-16 10:49:38 -0700205
Aart Bik30efb4e2015-07-30 12:14:31 -0700206 ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
207 for (int d = 0; d < 1; d++) {
208 ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
209 (d == 0) ? nullptr
210 : loop_header_[d - 1]->GetLoopInformation());
211 ASSERT_NE(loop_header_[d]->GetLoopInformation(), nullptr);
212 ASSERT_NE(loop_body_[d]->GetLoopInformation(), nullptr);
213 ASSERT_EQ(loop_header_[d]->GetLoopInformation(),
214 loop_body_[d]->GetLoopInformation());
215 }
216 ASSERT_EQ(exit_->GetLoopInformation(), nullptr);
217}
218
Aart Bike609b7c2015-08-27 13:46:58 -0700219TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700220 // Setup:
221 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700222 // a[i] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700223 // }
224 BuildLoopNest(1);
225 HInstruction* store = InsertArrayStore(basic_[0], 0);
226 PerformInductionVarAnalysis();
227
Aart Bik0d345cf2016-03-16 10:49:38 -0700228 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
229 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
Aart Bikd14c5952015-09-08 15:25:15 -0700230
231 // Trip-count.
Aart Bik22f05872015-10-27 15:56:28 -0700232 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
Aart Bik9401f532015-09-28 16:25:56 -0700233 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700234}
235
Aart Bike609b7c2015-08-27 13:46:58 -0700236TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700237 // Setup:
238 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700239 // k = 100 + i;
240 // k = 100 - i;
241 // k = 100 * i;
242 // k = i << 1;
243 // k = - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700244 // }
245 BuildLoopNest(1);
246 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000247 new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700248 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000249 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700250 HInstruction *mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000251 new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700252 HInstruction *shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000253 new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700254 HInstruction *neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000255 new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700256 PerformInductionVarAnalysis();
257
Aart Bik0d345cf2016-03-16 10:49:38 -0700258 EXPECT_STREQ("((1) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
259 EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
260 EXPECT_STREQ("((100) * i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
261 EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
262 EXPECT_STREQ("(( - (1)) * i + (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700263}
264
265TEST_F(InductionVarAnalysisTest, FindChainInduction) {
266 // Setup:
267 // k = 0;
268 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700269 // k = k + 100;
270 // a[k] = 0;
271 // k = k - 1;
272 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700273 // }
274 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000275 HPhi* k = InsertLoopPhi(0, 0);
276 k->AddInput(constant0_);
277
Aart Bik30efb4e2015-07-30 12:14:31 -0700278 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000279 new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
280 HInstruction* store1 = InsertArrayStore(add, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700281 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000282 new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
283 HInstruction* store2 = InsertArrayStore(sub, 0);
284 k->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700285 PerformInductionVarAnalysis();
286
Aart Bik0d345cf2016-03-16 10:49:38 -0700287 EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700288 GetInductionInfo(store1->InputAt(1), 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700289 EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700290 GetInductionInfo(store2->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700291}
292
293TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
294 // Setup:
295 // k = 0;
296 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700297 // if () k = k + 1;
298 // else k = k + 1;
299 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700300 // }
301 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000302 HPhi* k_header = InsertLoopPhi(0, 0);
303 k_header->AddInput(constant0_);
304
Aart Bik30efb4e2015-07-30 12:14:31 -0700305 HBasicBlock* ifTrue;
306 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000307 HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse);
308
Aart Bik30efb4e2015-07-30 12:14:31 -0700309 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000310 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700311 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000312 k_body->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700313 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000314 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700315 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000316 k_body->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700317 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000318 HInstruction* store = InsertArrayStore(k_body, 0);
319 k_header->AddInput(k_body);
Aart Bik30efb4e2015-07-30 12:14:31 -0700320 PerformInductionVarAnalysis();
321
Aart Bik0d345cf2016-03-16 10:49:38 -0700322 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700323}
324
325TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
326 // Setup:
327 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700328 // if () k = i + 1;
329 // else k = i + 1;
330 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700331 // }
332 BuildLoopNest(1);
333 HBasicBlock* ifTrue;
334 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000335 HPhi* k = BuildIf(0, &ifTrue, &ifFalse);
336
Aart Bik30efb4e2015-07-30 12:14:31 -0700337 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000338 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700339 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000340 k->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700341 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000342 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700343 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000344 k->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700345 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000346 HInstruction* store = InsertArrayStore(k, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700347 PerformInductionVarAnalysis();
348
Aart Bik0d345cf2016-03-16 10:49:38 -0700349 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700350}
351
352TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
353 // Setup:
354 // k = 0;
355 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700356 // a[k] = 0;
357 // k = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700358 // }
359 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000360 HPhi* k = InsertLoopPhi(0, 0);
361 k->AddInput(constant0_);
362
363 HInstruction* store = InsertArrayStore(k, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700364 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000365 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
366 k->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700367 PerformInductionVarAnalysis();
368
Aart Bik0d345cf2016-03-16 10:49:38 -0700369 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700370 GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700371}
372
373TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
374 // Setup:
375 // k = 0;
376 // t = 100;
377 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700378 // a[k] = 0;
379 // k = t;
380 // t = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700381 // }
382 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000383 HPhi* k = InsertLoopPhi(0, 0);
384 k->AddInput(constant0_);
385 HPhi* t = InsertLoopPhi(1, 0);
386 t->AddInput(constant100_);
387
388 HInstruction* store = InsertArrayStore(k, 0);
389 k->AddInput(t);
Aart Bik30efb4e2015-07-30 12:14:31 -0700390 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000391 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
392 t->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700393 PerformInductionVarAnalysis();
394
Aart Bik0d345cf2016-03-16 10:49:38 -0700395 EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700396 GetInductionInfo(store->InputAt(1), 0).c_str());
397}
398
399TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) {
400 // Setup:
401 // k = 0;
402 // for (int i = 0; i < 100; i++) {
403 // t = k + 100;
404 // t = k - 100;
405 // t = k * 100;
406 // t = k << 1;
407 // t = - k;
408 // k = i << 1;
409 // }
410 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000411 HPhi* k = InsertLoopPhi(0, 0);
412 k->AddInput(constant0_);
413
Aart Bike609b7c2015-08-27 13:46:58 -0700414 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000415 new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700416 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000417 new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700418 HInstruction *mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000419 new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700420 HInstruction *shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000421 new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700422 HInstruction *neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000423 new (&allocator_) HNeg(Primitive::kPrimInt, k), 0);
424 k->AddInput(
425 InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
Aart Bike609b7c2015-08-27 13:46:58 -0700426 PerformInductionVarAnalysis();
427
Aart Bik0d345cf2016-03-16 10:49:38 -0700428 EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
429 GetInductionInfo(add, 0).c_str());
430 EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):PrimInt):PrimInt",
431 GetInductionInfo(sub, 0).c_str());
432 EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt",
433 GetInductionInfo(mul, 0).c_str());
434 EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt",
435 GetInductionInfo(shl, 0).c_str());
436 EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
437 GetInductionInfo(neg, 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700438}
439
440TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
441 // Setup:
442 // k = 0;
443 // t = 100;
444 // for (int i = 0; i < 100; i++) {
445 // a[k] = 0;
446 // a[t] = 0;
447 // // Swap t <-> k.
448 // d = t;
449 // t = k;
450 // k = d;
451 // }
452 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000453 HPhi* k = InsertLoopPhi(0, 0);
454 k->AddInput(constant0_);
455 HPhi* t = InsertLoopPhi(1, 0);
456 t->AddInput(constant100_);
457
458 HInstruction* store1 = InsertArrayStore(k, 0);
459 HInstruction* store2 = InsertArrayStore(t, 0);
460 k->AddInput(t);
461 t->AddInput(k);
Aart Bike609b7c2015-08-27 13:46:58 -0700462 PerformInductionVarAnalysis();
463
Aart Bik0d345cf2016-03-16 10:49:38 -0700464 EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
465 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700466}
467
468TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
469 // Setup:
470 // k = 0;
471 // for (int i = 0; i < 100; i++) {
472 // a[k] = 0;
473 // k = 1 - k;
474 // }
475 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000476 HPhi* k = InsertLoopPhi(0, 0);
477 k->AddInput(constant0_);
478
479 HInstruction* store = InsertArrayStore(k, 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700480 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000481 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0);
482 k->AddInput(sub);
Aart Bike609b7c2015-08-27 13:46:58 -0700483 PerformInductionVarAnalysis();
484
Aart Bik0d345cf2016-03-16 10:49:38 -0700485 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
486 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700487}
488
489TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
490 // Setup:
491 // k = 0;
492 // for (int i = 0; i < 100; i++) {
493 // k = 1 - k;
494 // t = k + 100;
495 // t = k - 100;
496 // t = k * 100;
497 // t = k << 1;
498 // t = - k;
499 // }
500 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000501 HPhi* k_header = InsertLoopPhi(0, 0);
502 k_header->AddInput(constant0_);
503
504 HInstruction* k_body = InsertInstruction(
505 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
506 k_header->AddInput(k_body);
507
Aart Bike609b7c2015-08-27 13:46:58 -0700508 // Derived expressions.
509 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000510 new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700511 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000512 new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700513 HInstruction *mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000514 new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700515 HInstruction *shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000516 new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700517 HInstruction *neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000518 new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700519 PerformInductionVarAnalysis();
520
Aart Bik0d345cf2016-03-16 10:49:38 -0700521 EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str());
522 EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str());
523 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
524 EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
525 EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700526}
527
528TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
529 // Setup:
530 // k = 0;
531 // for (int i_0 = 0; i_0 < 100; i_0++) {
532 // ..
533 // for (int i_9 = 0; i_9 < 100; i_9++) {
534 // k = 1 + k;
535 // a[k] = 0;
536 // }
537 // ..
538 // }
539 BuildLoopNest(10);
David Brazdilbadd8262016-02-02 16:28:56 +0000540
541 HPhi* k[10];
542 for (int d = 0; d < 10; d++) {
543 k[d] = InsertLoopPhi(0, d);
544 }
545
Aart Bik30efb4e2015-07-30 12:14:31 -0700546 HInstruction *inc = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000547 new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9);
548 HInstruction* store = InsertArrayStore(inc, 9);
549
550 for (int d = 0; d < 10; d++) {
David Brazdil6dd10fa2016-02-15 17:14:31 +0000551 k[d]->AddInput((d != 0) ? k[d - 1] : constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000552 k[d]->AddInput((d != 9) ? k[d + 1] : inc);
553 }
Aart Bik30efb4e2015-07-30 12:14:31 -0700554 PerformInductionVarAnalysis();
555
Aart Bike609b7c2015-08-27 13:46:58 -0700556 // Avoid exact phi number, since that depends on the SSA building phase.
557 std::regex r("\\(\\(1\\) \\* i \\+ "
Aart Bik0d345cf2016-03-16 10:49:38 -0700558 "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt");
Aart Bik30efb4e2015-07-30 12:14:31 -0700559
560 for (int d = 0; d < 10; d++) {
561 if (d == 9) {
Aart Bike609b7c2015-08-27 13:46:58 -0700562 EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r));
Aart Bik30efb4e2015-07-30 12:14:31 -0700563 } else {
Aart Bike609b7c2015-08-27 13:46:58 -0700564 EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700565 }
Aart Bik0d345cf2016-03-16 10:49:38 -0700566 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str());
Aart Bikd14c5952015-09-08 15:25:15 -0700567 // Trip-count.
Aart Bik22f05872015-10-27 15:56:28 -0700568 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
Aart Bik9401f532015-09-28 16:25:56 -0700569 GetInductionInfo(loop_header_[d]->GetLastInstruction(), d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700570 }
571}
572
Aart Bik0d345cf2016-03-16 10:49:38 -0700573TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
574 // Setup:
575 // for (byte i = -128; i < 127; i++) { // just fits!
576 // }
577 BuildLoopNest(1);
578 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
579 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
580 ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
581 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
582 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
583 basic_[0]->ReplaceInput(conv, 1);
584 PerformInductionVarAnalysis();
585
586 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
587 // Trip-count.
588 EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))",
589 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
590}
591
592TEST_F(InductionVarAnalysisTest, ByteLoopControl2) {
593 // Setup:
594 // for (byte i = -128; i < 128; i++) { // infinite loop!
595 // }
596 BuildLoopNest(1);
597 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
598 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
599 ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
600 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
601 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
602 basic_[0]->ReplaceInput(conv, 1);
603 PerformInductionVarAnalysis();
604
605 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
606 // Trip-count undefined.
607 EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
608}
609
610TEST_F(InductionVarAnalysisTest, ShortLoopControl1) {
611 // Setup:
612 // for (short i = -32768; i < 32767; i++) { // just fits!
613 // }
614 BuildLoopNest(1);
615 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
616 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
617 ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
618 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
619 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
620 basic_[0]->ReplaceInput(conv, 1);
621 PerformInductionVarAnalysis();
622
623 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
624 GetInductionInfo(increment_[0], 0).c_str());
625 // Trip-count.
626 EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))",
627 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
628}
629
630TEST_F(InductionVarAnalysisTest, ShortLoopControl2) {
631 // Setup:
632 // for (short i = -32768; i < 32768; i++) { // infinite loop!
633 // }
634 BuildLoopNest(1);
635 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
636 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
637 ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
638 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
639 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
640 basic_[0]->ReplaceInput(conv, 1);
641 PerformInductionVarAnalysis();
642
643 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
644 GetInductionInfo(increment_[0], 0).c_str());
645 // Trip-count undefined.
646 EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
647}
648
649TEST_F(InductionVarAnalysisTest, CharLoopControl1) {
650 // Setup:
651 // for (char i = 0; i < 65535; i++) { // just fits!
652 // }
653 BuildLoopNest(1);
654 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
655 ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
656 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
657 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
658 basic_[0]->ReplaceInput(conv, 1);
659 PerformInductionVarAnalysis();
660
661 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
662 // Trip-count.
663 EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))",
664 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
665}
666
667TEST_F(InductionVarAnalysisTest, CharLoopControl2) {
668 // Setup:
669 // for (char i = 0; i < 65536; i++) { // infinite loop!
670 // }
671 BuildLoopNest(1);
672 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
673 ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
674 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
675 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
676 basic_[0]->ReplaceInput(conv, 1);
677 PerformInductionVarAnalysis();
678
679 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
680 // Trip-count undefined.
681 EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
682}
683
Aart Bik30efb4e2015-07-30 12:14:31 -0700684} // namespace art