blob: 292bc4e06e6eb0ea392465b3fd31cf24f08f5e1e [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 Bik009cace2016-09-16 10:15:19 -0700160 // Returns induction information of the trip-count of loop at depth d.
161 std::string GetTripCount(int d) {
162 HInstruction* control = loop_header_[d]->GetLastInstruction();
163 DCHECK(control->IsIf());
164 return GetInductionInfo(control, d);
165 }
166
Aart Bik78296912016-03-25 13:14:53 -0700167 // Returns true if instructions have identical induction.
168 bool HaveSameInduction(HInstruction* instruction1, HInstruction* instruction2) {
169 return HInductionVarAnalysis::InductionEqual(
170 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction1),
171 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
172 }
173
Aart Bik30efb4e2015-07-30 12:14:31 -0700174 // Performs InductionVarAnalysis (after proper set up).
175 void PerformInductionVarAnalysis() {
David Brazdilbadd8262016-02-02 16:28:56 +0000176 graph_->BuildDominatorTree();
Aart Bik30efb4e2015-07-30 12:14:31 -0700177 iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
178 iva_->Run();
179 }
180
181 // General building fields.
182 ArenaPool pool_;
183 ArenaAllocator allocator_;
184 HGraph* graph_;
185 HInductionVarAnalysis* iva_;
186
187 // Fixed basic blocks and instructions.
188 HBasicBlock* entry_;
David Brazdildb51efb2015-11-06 01:36:20 +0000189 HBasicBlock* return_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700190 HBasicBlock* exit_;
191 HInstruction* parameter_; // "this"
192 HInstruction* constant0_;
193 HInstruction* constant1_;
194 HInstruction* constant100_;
David Brazdil15693bf2015-12-16 10:30:45 +0000195 HInstruction* float_constant0_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700196
197 // Loop specifics.
198 HBasicBlock* loop_preheader_[10];
199 HBasicBlock* loop_header_[10];
200 HBasicBlock* loop_body_[10];
201 HInstruction* increment_[10];
David Brazdilbadd8262016-02-02 16:28:56 +0000202 HPhi* basic_[10]; // "vreg_d", the "i_d"
Aart Bik30efb4e2015-07-30 12:14:31 -0700203};
204
205//
206// The actual InductionVarAnalysis tests.
207//
208
209TEST_F(InductionVarAnalysisTest, ProperLoopSetup) {
210 // Setup:
211 // for (int i_0 = 0; i_0 < 100; i_0++) {
212 // ..
213 // for (int i_9 = 0; i_9 < 100; i_9++) {
214 // }
215 // ..
216 // }
217 BuildLoopNest(10);
David Brazdilbadd8262016-02-02 16:28:56 +0000218 graph_->BuildDominatorTree();
Aart Bik0d345cf2016-03-16 10:49:38 -0700219
Aart Bik30efb4e2015-07-30 12:14:31 -0700220 ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
221 for (int d = 0; d < 1; d++) {
222 ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
223 (d == 0) ? nullptr
224 : loop_header_[d - 1]->GetLoopInformation());
225 ASSERT_NE(loop_header_[d]->GetLoopInformation(), nullptr);
226 ASSERT_NE(loop_body_[d]->GetLoopInformation(), nullptr);
227 ASSERT_EQ(loop_header_[d]->GetLoopInformation(),
228 loop_body_[d]->GetLoopInformation());
229 }
230 ASSERT_EQ(exit_->GetLoopInformation(), nullptr);
231}
232
Aart Bike609b7c2015-08-27 13:46:58 -0700233TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700234 // Setup:
235 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700236 // a[i] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700237 // }
238 BuildLoopNest(1);
239 HInstruction* store = InsertArrayStore(basic_[0], 0);
240 PerformInductionVarAnalysis();
241
Aart Bik0d345cf2016-03-16 10:49:38 -0700242 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
243 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
Aart Bikd14c5952015-09-08 15:25:15 -0700244
Aart Bik78296912016-03-25 13:14:53 -0700245 // Offset matters!
246 EXPECT_FALSE(HaveSameInduction(store->InputAt(1), increment_[0]));
247
Aart Bikd14c5952015-09-08 15:25:15 -0700248 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700249 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700250}
251
Aart Bike609b7c2015-08-27 13:46:58 -0700252TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700253 // Setup:
254 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700255 // k = 100 + i;
256 // k = 100 - i;
257 // k = 100 * i;
258 // k = i << 1;
259 // k = - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700260 // }
261 BuildLoopNest(1);
262 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000263 new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700264 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000265 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700266 HInstruction *mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000267 new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700268 HInstruction *shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000269 new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700270 HInstruction *neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000271 new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700272 PerformInductionVarAnalysis();
273
Aart Bik0d345cf2016-03-16 10:49:38 -0700274 EXPECT_STREQ("((1) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
275 EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
276 EXPECT_STREQ("((100) * i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
277 EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
278 EXPECT_STREQ("(( - (1)) * i + (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700279}
280
281TEST_F(InductionVarAnalysisTest, FindChainInduction) {
282 // Setup:
283 // k = 0;
284 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700285 // k = k + 100;
286 // a[k] = 0;
287 // k = k - 1;
288 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700289 // }
290 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000291 HPhi* k = InsertLoopPhi(0, 0);
292 k->AddInput(constant0_);
293
Aart Bik30efb4e2015-07-30 12:14:31 -0700294 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000295 new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
296 HInstruction* store1 = InsertArrayStore(add, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700297 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000298 new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
299 HInstruction* store2 = InsertArrayStore(sub, 0);
300 k->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700301 PerformInductionVarAnalysis();
302
Aart Bik0d345cf2016-03-16 10:49:38 -0700303 EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700304 GetInductionInfo(store1->InputAt(1), 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700305 EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700306 GetInductionInfo(store2->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700307}
308
309TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
310 // Setup:
311 // k = 0;
312 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700313 // if () k = k + 1;
314 // else k = k + 1;
315 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700316 // }
317 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000318 HPhi* k_header = InsertLoopPhi(0, 0);
319 k_header->AddInput(constant0_);
320
Aart Bik30efb4e2015-07-30 12:14:31 -0700321 HBasicBlock* ifTrue;
322 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000323 HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse);
324
Aart Bik30efb4e2015-07-30 12:14:31 -0700325 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000326 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700327 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000328 k_body->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700329 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000330 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700331 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000332 k_body->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700333 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000334 HInstruction* store = InsertArrayStore(k_body, 0);
335 k_header->AddInput(k_body);
Aart Bik30efb4e2015-07-30 12:14:31 -0700336 PerformInductionVarAnalysis();
337
Aart Bik0d345cf2016-03-16 10:49:38 -0700338 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik78296912016-03-25 13:14:53 -0700339
340 // Both increments get same induction.
341 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
342 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
Aart Bik30efb4e2015-07-30 12:14:31 -0700343}
344
345TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
346 // Setup:
347 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700348 // if () k = i + 1;
349 // else k = i + 1;
350 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700351 // }
352 BuildLoopNest(1);
353 HBasicBlock* ifTrue;
354 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000355 HPhi* k = BuildIf(0, &ifTrue, &ifFalse);
356
Aart Bik30efb4e2015-07-30 12:14:31 -0700357 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000358 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700359 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000360 k->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700361 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000362 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700363 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000364 k->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700365 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000366 HInstruction* store = InsertArrayStore(k, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700367 PerformInductionVarAnalysis();
368
Aart Bik0d345cf2016-03-16 10:49:38 -0700369 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700370}
371
372TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
373 // Setup:
374 // k = 0;
375 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700376 // a[k] = 0;
377 // k = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700378 // }
379 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000380 HPhi* k = InsertLoopPhi(0, 0);
381 k->AddInput(constant0_);
382
383 HInstruction* store = InsertArrayStore(k, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700384 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000385 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
386 k->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700387 PerformInductionVarAnalysis();
388
Aart Bik0d345cf2016-03-16 10:49:38 -0700389 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700390 GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700391}
392
393TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
394 // Setup:
395 // k = 0;
396 // t = 100;
397 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700398 // a[k] = 0;
399 // k = t;
400 // t = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700401 // }
402 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000403 HPhi* k = InsertLoopPhi(0, 0);
404 k->AddInput(constant0_);
405 HPhi* t = InsertLoopPhi(1, 0);
406 t->AddInput(constant100_);
407
408 HInstruction* store = InsertArrayStore(k, 0);
409 k->AddInput(t);
Aart Bik30efb4e2015-07-30 12:14:31 -0700410 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000411 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
412 t->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700413 PerformInductionVarAnalysis();
414
Aart Bik0d345cf2016-03-16 10:49:38 -0700415 EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700416 GetInductionInfo(store->InputAt(1), 0).c_str());
417}
418
419TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) {
420 // Setup:
421 // k = 0;
422 // for (int i = 0; i < 100; i++) {
423 // t = k + 100;
424 // t = k - 100;
425 // t = k * 100;
426 // t = k << 1;
427 // t = - k;
428 // k = i << 1;
429 // }
430 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000431 HPhi* k = InsertLoopPhi(0, 0);
432 k->AddInput(constant0_);
433
Aart Bike609b7c2015-08-27 13:46:58 -0700434 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000435 new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700436 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000437 new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700438 HInstruction *mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000439 new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700440 HInstruction *shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000441 new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700442 HInstruction *neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000443 new (&allocator_) HNeg(Primitive::kPrimInt, k), 0);
444 k->AddInput(
445 InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
Aart Bike609b7c2015-08-27 13:46:58 -0700446 PerformInductionVarAnalysis();
447
Aart Bik0d345cf2016-03-16 10:49:38 -0700448 EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
449 GetInductionInfo(add, 0).c_str());
450 EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):PrimInt):PrimInt",
451 GetInductionInfo(sub, 0).c_str());
452 EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt",
453 GetInductionInfo(mul, 0).c_str());
454 EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt",
455 GetInductionInfo(shl, 0).c_str());
456 EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
457 GetInductionInfo(neg, 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700458}
459
460TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
461 // Setup:
462 // k = 0;
463 // t = 100;
464 // for (int i = 0; i < 100; i++) {
465 // a[k] = 0;
466 // a[t] = 0;
467 // // Swap t <-> k.
468 // d = t;
469 // t = k;
470 // k = d;
471 // }
472 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000473 HPhi* k = InsertLoopPhi(0, 0);
474 k->AddInput(constant0_);
475 HPhi* t = InsertLoopPhi(1, 0);
476 t->AddInput(constant100_);
477
478 HInstruction* store1 = InsertArrayStore(k, 0);
479 HInstruction* store2 = InsertArrayStore(t, 0);
480 k->AddInput(t);
481 t->AddInput(k);
Aart Bike609b7c2015-08-27 13:46:58 -0700482 PerformInductionVarAnalysis();
483
Aart Bik0d345cf2016-03-16 10:49:38 -0700484 EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
485 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700486}
487
488TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
489 // Setup:
490 // k = 0;
491 // for (int i = 0; i < 100; i++) {
492 // a[k] = 0;
493 // k = 1 - k;
494 // }
495 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000496 HPhi* k = InsertLoopPhi(0, 0);
497 k->AddInput(constant0_);
498
499 HInstruction* store = InsertArrayStore(k, 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700500 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000501 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0);
502 k->AddInput(sub);
Aart Bike609b7c2015-08-27 13:46:58 -0700503 PerformInductionVarAnalysis();
504
Aart Bik0d345cf2016-03-16 10:49:38 -0700505 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
506 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700507}
508
509TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
510 // Setup:
511 // k = 0;
512 // for (int i = 0; i < 100; i++) {
513 // k = 1 - k;
514 // t = k + 100;
515 // t = k - 100;
516 // t = k * 100;
517 // t = k << 1;
518 // t = - k;
519 // }
520 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000521 HPhi* k_header = InsertLoopPhi(0, 0);
522 k_header->AddInput(constant0_);
523
524 HInstruction* k_body = InsertInstruction(
525 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
526 k_header->AddInput(k_body);
527
Aart Bike609b7c2015-08-27 13:46:58 -0700528 // Derived expressions.
529 HInstruction *add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000530 new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700531 HInstruction *sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000532 new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700533 HInstruction *mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000534 new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700535 HInstruction *shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000536 new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700537 HInstruction *neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000538 new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
Aart Bike609b7c2015-08-27 13:46:58 -0700539 PerformInductionVarAnalysis();
540
Aart Bik0d345cf2016-03-16 10:49:38 -0700541 EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str());
542 EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str());
543 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
544 EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
545 EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700546}
547
548TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
549 // Setup:
550 // k = 0;
551 // for (int i_0 = 0; i_0 < 100; i_0++) {
552 // ..
553 // for (int i_9 = 0; i_9 < 100; i_9++) {
554 // k = 1 + k;
555 // a[k] = 0;
556 // }
557 // ..
558 // }
559 BuildLoopNest(10);
David Brazdilbadd8262016-02-02 16:28:56 +0000560
561 HPhi* k[10];
562 for (int d = 0; d < 10; d++) {
563 k[d] = InsertLoopPhi(0, d);
564 }
565
Aart Bik30efb4e2015-07-30 12:14:31 -0700566 HInstruction *inc = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000567 new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9);
568 HInstruction* store = InsertArrayStore(inc, 9);
569
570 for (int d = 0; d < 10; d++) {
David Brazdil6dd10fa2016-02-15 17:14:31 +0000571 k[d]->AddInput((d != 0) ? k[d - 1] : constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000572 k[d]->AddInput((d != 9) ? k[d + 1] : inc);
573 }
Aart Bik30efb4e2015-07-30 12:14:31 -0700574 PerformInductionVarAnalysis();
575
Aart Bike609b7c2015-08-27 13:46:58 -0700576 // Avoid exact phi number, since that depends on the SSA building phase.
577 std::regex r("\\(\\(1\\) \\* i \\+ "
Aart Bik0d345cf2016-03-16 10:49:38 -0700578 "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt");
Aart Bik30efb4e2015-07-30 12:14:31 -0700579
580 for (int d = 0; d < 10; d++) {
581 if (d == 9) {
Aart Bike609b7c2015-08-27 13:46:58 -0700582 EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r));
Aart Bik30efb4e2015-07-30 12:14:31 -0700583 } else {
Aart Bike609b7c2015-08-27 13:46:58 -0700584 EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700585 }
Aart Bik0d345cf2016-03-16 10:49:38 -0700586 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str());
Aart Bikd14c5952015-09-08 15:25:15 -0700587 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700588 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700589 }
590}
591
Aart Bik78296912016-03-25 13:14:53 -0700592TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) {
593 // Setup:
594 // for (int i = 0; i < 100; i++) {
595 // k = (byte) i;
596 // a[k] = 0;
597 // a[i] = 0;
598 // }
599 BuildLoopNest(1);
600 HInstruction *conv = InsertInstruction(
601 new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
602 HInstruction* store1 = InsertArrayStore(conv, 0);
603 HInstruction* store2 = InsertArrayStore(basic_[0], 0);
604 PerformInductionVarAnalysis();
605
606 // Regular int induction (i) is "transferred" over conversion into byte induction (k).
607 EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
608 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
609 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
610
611 // Type matters!
612 EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
613
614 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700615 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
Aart Bik78296912016-03-25 13:14:53 -0700616}
617
Aart Bik0d345cf2016-03-16 10:49:38 -0700618TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
619 // Setup:
620 // for (byte i = -128; i < 127; i++) { // just fits!
621 // }
622 BuildLoopNest(1);
623 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
624 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
625 ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
626 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
627 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
628 basic_[0]->ReplaceInput(conv, 1);
629 PerformInductionVarAnalysis();
630
631 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
632 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700633 EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700634}
635
636TEST_F(InductionVarAnalysisTest, ByteLoopControl2) {
637 // Setup:
638 // for (byte i = -128; i < 128; i++) { // infinite loop!
639 // }
640 BuildLoopNest(1);
641 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
642 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
643 ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
644 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
645 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
646 basic_[0]->ReplaceInput(conv, 1);
647 PerformInductionVarAnalysis();
648
649 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
650 // Trip-count undefined.
Aart Bik009cace2016-09-16 10:15:19 -0700651 EXPECT_STREQ("", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700652}
653
654TEST_F(InductionVarAnalysisTest, ShortLoopControl1) {
655 // Setup:
656 // for (short i = -32768; i < 32767; i++) { // just fits!
657 // }
658 BuildLoopNest(1);
659 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
660 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
661 ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
662 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
663 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
664 basic_[0]->ReplaceInput(conv, 1);
665 PerformInductionVarAnalysis();
666
667 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
668 GetInductionInfo(increment_[0], 0).c_str());
669 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700670 EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700671}
672
673TEST_F(InductionVarAnalysisTest, ShortLoopControl2) {
674 // Setup:
675 // for (short i = -32768; i < 32768; i++) { // infinite loop!
676 // }
677 BuildLoopNest(1);
678 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
679 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
680 ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
681 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
682 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
683 basic_[0]->ReplaceInput(conv, 1);
684 PerformInductionVarAnalysis();
685
686 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
687 GetInductionInfo(increment_[0], 0).c_str());
688 // Trip-count undefined.
Aart Bik009cace2016-09-16 10:15:19 -0700689 EXPECT_STREQ("", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700690}
691
692TEST_F(InductionVarAnalysisTest, CharLoopControl1) {
693 // Setup:
694 // for (char i = 0; i < 65535; i++) { // just fits!
695 // }
696 BuildLoopNest(1);
697 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
698 ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
699 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
700 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
701 basic_[0]->ReplaceInput(conv, 1);
702 PerformInductionVarAnalysis();
703
704 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
705 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700706 EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700707}
708
709TEST_F(InductionVarAnalysisTest, CharLoopControl2) {
710 // Setup:
711 // for (char i = 0; i < 65536; i++) { // infinite loop!
712 // }
713 BuildLoopNest(1);
714 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
715 ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
716 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
717 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
718 basic_[0]->ReplaceInput(conv, 1);
719 PerformInductionVarAnalysis();
720
721 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
722 // Trip-count undefined.
Aart Bik009cace2016-09-16 10:15:19 -0700723 EXPECT_STREQ("", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700724}
725
Aart Bik30efb4e2015-07-30 12:14:31 -0700726} // namespace art