blob: 2199c8e105afd279d2db5c4aee67f19d2373f868 [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(
Andreas Gampea5b09a62016-11-17 15:21:22 -080083 graph_->GetDexFile(), dex::TypeIndex(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);
Aart Bikc071a012016-12-01 10:22:31 -080087 constant2_ = graph_->GetIntConstant(2);
Aart Bike609b7c2015-08-27 13:46:58 -070088 constant100_ = graph_->GetIntConstant(100);
David Brazdil15693bf2015-12-16 10:30:45 +000089 float_constant0_ = graph_->GetFloatConstant(0.0f);
David Brazdildb51efb2015-11-06 01:36:20 +000090 return_->AddInstruction(new (&allocator_) HReturnVoid());
Aart Bike609b7c2015-08-27 13:46:58 -070091 exit_->AddInstruction(new (&allocator_) HExit());
Aart Bik30efb4e2015-07-30 12:14:31 -070092
93 // Provide loop instructions.
94 for (int d = 0; d < n; d++) {
David Brazdilbadd8262016-02-02 16:28:56 +000095 basic_[d] = new (&allocator_) HPhi(&allocator_, d, 0, Primitive::kPrimInt);
David Brazdil4833f5a2015-12-16 10:37:39 +000096 loop_preheader_[d]->AddInstruction(new (&allocator_) HGoto());
David Brazdilbadd8262016-02-02 16:28:56 +000097 loop_header_[d]->AddPhi(basic_[d]);
98 HInstruction* compare = new (&allocator_) HLessThan(basic_[d], constant100_);
Aart Bik30efb4e2015-07-30 12:14:31 -070099 loop_header_[d]->AddInstruction(compare);
100 loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare));
David Brazdilbadd8262016-02-02 16:28:56 +0000101 increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[d], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700102 loop_body_[d]->AddInstruction(increment_[d]);
Aart Bik30efb4e2015-07-30 12:14:31 -0700103 loop_body_[d]->AddInstruction(new (&allocator_) HGoto());
David Brazdilbadd8262016-02-02 16:28:56 +0000104
105 basic_[d]->AddInput(constant0_);
106 basic_[d]->AddInput(increment_[d]);
Aart Bik30efb4e2015-07-30 12:14:31 -0700107 }
108 }
109
110 // Builds if-statement at depth d.
Aart Bik7dc96932016-10-12 10:01:05 -0700111 HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock** ifF) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700112 HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_);
113 HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_);
114 HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_);
115 graph_->AddBlock(cond);
116 graph_->AddBlock(ifTrue);
117 graph_->AddBlock(ifFalse);
118 // Conditional split.
119 loop_header_[d]->ReplaceSuccessor(loop_body_[d], cond);
120 cond->AddSuccessor(ifTrue);
121 cond->AddSuccessor(ifFalse);
122 ifTrue->AddSuccessor(loop_body_[d]);
123 ifFalse->AddSuccessor(loop_body_[d]);
124 cond->AddInstruction(new (&allocator_) HIf(parameter_));
125 *ifT = ifTrue;
126 *ifF = ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000127
128 HPhi* select_phi = new (&allocator_) HPhi(&allocator_, -1, 0, Primitive::kPrimInt);
129 loop_body_[d]->AddPhi(select_phi);
130 return select_phi;
Aart Bik30efb4e2015-07-30 12:14:31 -0700131 }
132
133 // Inserts instruction right before increment at depth d.
134 HInstruction* InsertInstruction(HInstruction* instruction, int d) {
135 loop_body_[d]->InsertInstructionBefore(instruction, increment_[d]);
136 return instruction;
137 }
138
David Brazdilbadd8262016-02-02 16:28:56 +0000139 // Inserts a phi to loop header at depth d and returns it.
140 HPhi* InsertLoopPhi(int vreg, int d) {
141 HPhi* phi = new (&allocator_) HPhi(&allocator_, vreg, 0, Primitive::kPrimInt);
142 loop_header_[d]->AddPhi(phi);
143 return phi;
Aart Bik30efb4e2015-07-30 12:14:31 -0700144 }
145
David Brazdilbadd8262016-02-02 16:28:56 +0000146 // Inserts an array store with given `subscript` at depth d to
Aart Bik30efb4e2015-07-30 12:14:31 -0700147 // enable tests to inspect the computed induction at that point easily.
David Brazdilbadd8262016-02-02 16:28:56 +0000148 HInstruction* InsertArrayStore(HInstruction* subscript, int d) {
David Brazdil15693bf2015-12-16 10:30:45 +0000149 // ArraySet is given a float value in order to avoid SsaBuilder typing
150 // it from the array's non-existent reference type info.
Aart Bik30efb4e2015-07-30 12:14:31 -0700151 return InsertInstruction(new (&allocator_) HArraySet(
David Brazdilbadd8262016-02-02 16:28:56 +0000152 parameter_, subscript, float_constant0_, Primitive::kPrimFloat, 0), d);
Aart Bik30efb4e2015-07-30 12:14:31 -0700153 }
154
Aart Bike609b7c2015-08-27 13:46:58 -0700155 // Returns induction information of instruction in loop at depth d.
156 std::string GetInductionInfo(HInstruction* instruction, int d) {
157 return HInductionVarAnalysis::InductionToString(
158 iva_->LookupInfo(loop_body_[d]->GetLoopInformation(), instruction));
Aart Bik30efb4e2015-07-30 12:14:31 -0700159 }
160
Aart Bik009cace2016-09-16 10:15:19 -0700161 // Returns induction information of the trip-count of loop at depth d.
162 std::string GetTripCount(int d) {
163 HInstruction* control = loop_header_[d]->GetLastInstruction();
164 DCHECK(control->IsIf());
165 return GetInductionInfo(control, d);
166 }
167
Aart Bik78296912016-03-25 13:14:53 -0700168 // Returns true if instructions have identical induction.
169 bool HaveSameInduction(HInstruction* instruction1, HInstruction* instruction2) {
170 return HInductionVarAnalysis::InductionEqual(
171 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction1),
172 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
173 }
174
Aart Bik30efb4e2015-07-30 12:14:31 -0700175 // Performs InductionVarAnalysis (after proper set up).
176 void PerformInductionVarAnalysis() {
David Brazdilbadd8262016-02-02 16:28:56 +0000177 graph_->BuildDominatorTree();
Aart Bik30efb4e2015-07-30 12:14:31 -0700178 iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
179 iva_->Run();
180 }
181
182 // General building fields.
183 ArenaPool pool_;
184 ArenaAllocator allocator_;
185 HGraph* graph_;
186 HInductionVarAnalysis* iva_;
187
188 // Fixed basic blocks and instructions.
189 HBasicBlock* entry_;
David Brazdildb51efb2015-11-06 01:36:20 +0000190 HBasicBlock* return_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700191 HBasicBlock* exit_;
192 HInstruction* parameter_; // "this"
193 HInstruction* constant0_;
194 HInstruction* constant1_;
Aart Bikc071a012016-12-01 10:22:31 -0800195 HInstruction* constant2_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700196 HInstruction* constant100_;
David Brazdil15693bf2015-12-16 10:30:45 +0000197 HInstruction* float_constant0_;
Aart Bik30efb4e2015-07-30 12:14:31 -0700198
199 // Loop specifics.
200 HBasicBlock* loop_preheader_[10];
201 HBasicBlock* loop_header_[10];
202 HBasicBlock* loop_body_[10];
203 HInstruction* increment_[10];
David Brazdilbadd8262016-02-02 16:28:56 +0000204 HPhi* basic_[10]; // "vreg_d", the "i_d"
Aart Bik30efb4e2015-07-30 12:14:31 -0700205};
206
207//
208// The actual InductionVarAnalysis tests.
209//
210
211TEST_F(InductionVarAnalysisTest, ProperLoopSetup) {
212 // Setup:
213 // for (int i_0 = 0; i_0 < 100; i_0++) {
214 // ..
215 // for (int i_9 = 0; i_9 < 100; i_9++) {
216 // }
217 // ..
218 // }
219 BuildLoopNest(10);
David Brazdilbadd8262016-02-02 16:28:56 +0000220 graph_->BuildDominatorTree();
Aart Bik0d345cf2016-03-16 10:49:38 -0700221
Aart Bik30efb4e2015-07-30 12:14:31 -0700222 ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
223 for (int d = 0; d < 1; d++) {
224 ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
225 (d == 0) ? nullptr
226 : loop_header_[d - 1]->GetLoopInformation());
227 ASSERT_NE(loop_header_[d]->GetLoopInformation(), nullptr);
228 ASSERT_NE(loop_body_[d]->GetLoopInformation(), nullptr);
229 ASSERT_EQ(loop_header_[d]->GetLoopInformation(),
230 loop_body_[d]->GetLoopInformation());
231 }
232 ASSERT_EQ(exit_->GetLoopInformation(), nullptr);
233}
234
Aart Bike609b7c2015-08-27 13:46:58 -0700235TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700236 // Setup:
237 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700238 // a[i] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700239 // }
240 BuildLoopNest(1);
241 HInstruction* store = InsertArrayStore(basic_[0], 0);
242 PerformInductionVarAnalysis();
243
Aart Bik0d345cf2016-03-16 10:49:38 -0700244 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
245 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
Aart Bikd14c5952015-09-08 15:25:15 -0700246
Aart Bik78296912016-03-25 13:14:53 -0700247 // Offset matters!
248 EXPECT_FALSE(HaveSameInduction(store->InputAt(1), increment_[0]));
249
Aart Bikd14c5952015-09-08 15:25:15 -0700250 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700251 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700252}
253
Aart Bike609b7c2015-08-27 13:46:58 -0700254TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
Aart Bik30efb4e2015-07-30 12:14:31 -0700255 // Setup:
256 // for (int i = 0; i < 100; i++) {
Aart Bikc071a012016-12-01 10:22:31 -0800257 // t = 100 + i;
258 // t = 100 - i;
259 // t = 100 * i;
260 // t = i << 1;
261 // t = - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700262 // }
263 BuildLoopNest(1);
Aart Bik7dc96932016-10-12 10:01:05 -0700264 HInstruction* add = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000265 new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700266 HInstruction* sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000267 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700268 HInstruction* mul = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000269 new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700270 HInstruction* shl = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000271 new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700272 HInstruction* neg = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000273 new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700274 PerformInductionVarAnalysis();
275
Aart Bik0d345cf2016-03-16 10:49:38 -0700276 EXPECT_STREQ("((1) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
277 EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
278 EXPECT_STREQ("((100) * i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
279 EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
280 EXPECT_STREQ("(( - (1)) * i + (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700281}
282
283TEST_F(InductionVarAnalysisTest, FindChainInduction) {
284 // Setup:
285 // k = 0;
286 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700287 // k = k + 100;
288 // a[k] = 0;
289 // k = k - 1;
290 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700291 // }
292 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800293 HPhi* k_header = InsertLoopPhi(0, 0);
294 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000295
Aart Bik7dc96932016-10-12 10:01:05 -0700296 HInstruction* add = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800297 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
David Brazdilbadd8262016-02-02 16:28:56 +0000298 HInstruction* store1 = InsertArrayStore(add, 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700299 HInstruction* sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000300 new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
301 HInstruction* store2 = InsertArrayStore(sub, 0);
Aart Bikc071a012016-12-01 10:22:31 -0800302 k_header->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700303 PerformInductionVarAnalysis();
304
Aart Bikc071a012016-12-01 10:22:31 -0800305 EXPECT_STREQ("(((100) - (1)) * i + (0)):PrimInt",
306 GetInductionInfo(k_header, 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700307 EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700308 GetInductionInfo(store1->InputAt(1), 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700309 EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700310 GetInductionInfo(store2->InputAt(1), 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700311}
312
313TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
314 // Setup:
315 // k = 0;
316 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700317 // if () k = k + 1;
318 // else k = k + 1;
319 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700320 // }
321 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000322 HPhi* k_header = InsertLoopPhi(0, 0);
323 k_header->AddInput(constant0_);
324
Aart Bik30efb4e2015-07-30 12:14:31 -0700325 HBasicBlock* ifTrue;
326 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000327 HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse);
328
Aart Bik30efb4e2015-07-30 12:14:31 -0700329 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000330 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700331 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000332 k_body->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700333 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000334 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700335 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000336 k_body->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700337 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000338 HInstruction* store = InsertArrayStore(k_body, 0);
339 k_header->AddInput(k_body);
Aart Bik30efb4e2015-07-30 12:14:31 -0700340 PerformInductionVarAnalysis();
341
Aart Bikc071a012016-12-01 10:22:31 -0800342 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700343 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bik78296912016-03-25 13:14:53 -0700344
345 // Both increments get same induction.
346 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
347 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
Aart Bik30efb4e2015-07-30 12:14:31 -0700348}
349
350TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
351 // Setup:
352 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700353 // if () k = i + 1;
354 // else k = i + 1;
355 // a[k] = 0;
Aart Bik30efb4e2015-07-30 12:14:31 -0700356 // }
357 BuildLoopNest(1);
358 HBasicBlock* ifTrue;
359 HBasicBlock* ifFalse;
David Brazdilbadd8262016-02-02 16:28:56 +0000360 HPhi* k = BuildIf(0, &ifTrue, &ifFalse);
361
Aart Bik30efb4e2015-07-30 12:14:31 -0700362 // True-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000363 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700364 ifTrue->AddInstruction(inc1);
David Brazdilbadd8262016-02-02 16:28:56 +0000365 k->AddInput(inc1);
Aart Bik30efb4e2015-07-30 12:14:31 -0700366 // False-branch.
David Brazdilbadd8262016-02-02 16:28:56 +0000367 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
Aart Bik30efb4e2015-07-30 12:14:31 -0700368 ifFalse->AddInstruction(inc2);
David Brazdilbadd8262016-02-02 16:28:56 +0000369 k->AddInput(inc2);
Aart Bik30efb4e2015-07-30 12:14:31 -0700370 // Merge over a phi.
David Brazdilbadd8262016-02-02 16:28:56 +0000371 HInstruction* store = InsertArrayStore(k, 0);
Aart Bik30efb4e2015-07-30 12:14:31 -0700372 PerformInductionVarAnalysis();
373
Aart Bik0d345cf2016-03-16 10:49:38 -0700374 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bikc071a012016-12-01 10:22:31 -0800375
376 // Both increments get same induction.
377 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
378 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
379}
380
381TEST_F(InductionVarAnalysisTest, FindGeometricMulInduction) {
382 // Setup:
383 // k = 1;
384 // for (int i = 0; i < 100; i++) {
385 // k = k * 100; // geometric (x 100)
386 // }
387 BuildLoopNest(1);
388 HPhi* k_header = InsertLoopPhi(0, 0);
389 k_header->AddInput(constant1_);
390
391 HInstruction* mul = InsertInstruction(
392 new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
393 k_header->AddInput(mul);
394 PerformInductionVarAnalysis();
395
396 EXPECT_STREQ("geo((1) * 100 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
397 EXPECT_STREQ("geo((100) * 100 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
398}
399
400TEST_F(InductionVarAnalysisTest, FindGeometricShlInductionAndDerived) {
401 // Setup:
402 // k = 1;
403 // for (int i = 0; i < 100; i++) {
404 // t = k + 1;
405 // k = k << 1; // geometric (x 2)
406 // t = k + 100;
407 // t = k - 1;
408 // t = - t;
409 // t = k * 2;
410 // t = k << 2;
411 // }
412 BuildLoopNest(1);
413 HPhi* k_header = InsertLoopPhi(0, 0);
414 k_header->AddInput(constant1_);
415
416 HInstruction* add1 = InsertInstruction(
417 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
418 HInstruction* shl1 = InsertInstruction(
419 new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
420 HInstruction* add2 = InsertInstruction(
421 new (&allocator_) HAdd(Primitive::kPrimInt, shl1, constant100_), 0);
422 HInstruction* sub = InsertInstruction(
423 new (&allocator_) HSub(Primitive::kPrimInt, shl1, constant1_), 0);
424 HInstruction* neg = InsertInstruction(
425 new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
426 HInstruction* mul = InsertInstruction(
427 new (&allocator_) HMul(Primitive::kPrimInt, shl1, constant2_), 0);
428 HInstruction* shl2 = InsertInstruction(
429 new (&allocator_) HShl(Primitive::kPrimInt, shl1, constant2_), 0);
430 k_header->AddInput(shl1);
431 PerformInductionVarAnalysis();
432
433 EXPECT_STREQ("geo((1) * 2 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
434 EXPECT_STREQ("geo((1) * 2 ^ i + (1)):PrimInt", GetInductionInfo(add1, 0).c_str());
435 EXPECT_STREQ("geo((2) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl1, 0).c_str());
436 EXPECT_STREQ("geo((2) * 2 ^ i + (100)):PrimInt", GetInductionInfo(add2, 0).c_str());
437 EXPECT_STREQ("geo((2) * 2 ^ i + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
438 EXPECT_STREQ("geo(( - (2)) * 2 ^ i + ( - ((0) - (1)))):PrimInt",
439 GetInductionInfo(neg, 0).c_str());
440 EXPECT_STREQ("geo(((2) * (2)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
441 EXPECT_STREQ("geo(((2) * (4)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
442}
443
444TEST_F(InductionVarAnalysisTest, FindGeometricDivInductionAndDerived) {
445 // Setup:
446 // k = 1;
447 // for (int i = 0; i < 100; i++) {
448 // t = k + 100;
449 // t = k - 1;
450 // t = - t
451 // t = k * 2;
452 // t = k << 2;
453 // k = k / 100; // geometric (/ 100)
454 // }
455 BuildLoopNest(1);
456 HPhi* k_header = InsertLoopPhi(0, 0);
457 k_header->AddInput(constant1_);
458
459 HInstruction* add = InsertInstruction(
460 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
461 HInstruction* sub = InsertInstruction(
462 new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0);
463 HInstruction* neg = InsertInstruction(
464 new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
465 HInstruction* mul = InsertInstruction(
466 new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0);
467 HInstruction* shl = InsertInstruction(
468 new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0);
469 HInstruction* div = InsertInstruction(
470 new (&allocator_) HDiv(Primitive::kPrimInt, k_header, constant100_, kNoDexPc), 0);
471 k_header->AddInput(div);
472 PerformInductionVarAnalysis();
473
474 // Note, only the phi in the cycle and direct additive derived are classified.
475 EXPECT_STREQ("geo((1) * 100 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
476 EXPECT_STREQ("geo((1) * 100 ^ -i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
477 EXPECT_STREQ("geo((1) * 100 ^ -i + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
478 EXPECT_STREQ("", GetInductionInfo(neg, 0).c_str());
479 EXPECT_STREQ("", GetInductionInfo(mul, 0).c_str());
480 EXPECT_STREQ("", GetInductionInfo(shl, 0).c_str());
481 EXPECT_STREQ("", GetInductionInfo(div, 0).c_str());
482}
483
484TEST_F(InductionVarAnalysisTest, FindGeometricRemInductionAndDerived) {
485 // Setup:
486 // k = 1;
487 // for (int i = 0; i < 100; i++) {
488 // t = k + 100;
489 // t = k - 1;
490 // t = -t
491 // t = k * 2;
492 // t = k << 2;
493 // k = k % 100; // geometric (% 100)
494 // }
495 BuildLoopNest(1);
496 HPhi* k_header = InsertLoopPhi(0, 0);
497 k_header->AddInput(constant1_);
498
499 HInstruction* add = InsertInstruction(
500 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
501 HInstruction* sub = InsertInstruction(
502 new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0);
503 HInstruction* neg = InsertInstruction(
504 new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
505 HInstruction* mul = InsertInstruction(
506 new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0);
507 HInstruction* shl = InsertInstruction(
508 new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0);
509 HInstruction* rem = InsertInstruction(
510 new (&allocator_) HRem(Primitive::kPrimInt, k_header, constant100_, kNoDexPc), 0);
511 k_header->AddInput(rem);
512 PerformInductionVarAnalysis();
513
514 // Note, only the phi in the cycle and direct additive derived are classified.
515 EXPECT_STREQ("geo((1) mod_i 100 + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
516 EXPECT_STREQ("geo((1) mod_i 100 + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
517 EXPECT_STREQ("geo((1) mod_i 100 + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
518 EXPECT_STREQ("", GetInductionInfo(neg, 0).c_str());
519 EXPECT_STREQ("", GetInductionInfo(mul, 0).c_str());
520 EXPECT_STREQ("", GetInductionInfo(shl, 0).c_str());
521 EXPECT_STREQ("", GetInductionInfo(rem, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700522}
523
524TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
525 // Setup:
526 // k = 0;
527 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700528 // a[k] = 0;
529 // k = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700530 // }
531 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800532 HPhi* k_header = InsertLoopPhi(0, 0);
533 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000534
Aart Bikc071a012016-12-01 10:22:31 -0800535 HInstruction* store = InsertArrayStore(k_header, 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700536 HInstruction* sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000537 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
Aart Bikc071a012016-12-01 10:22:31 -0800538 k_header->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700539 PerformInductionVarAnalysis();
540
Aart Bik0d345cf2016-03-16 10:49:38 -0700541 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
Aart Bikc071a012016-12-01 10:22:31 -0800542 GetInductionInfo(k_header, 0).c_str());
543 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700544 GetInductionInfo(store->InputAt(1), 0).c_str());
Aart Bikc071a012016-12-01 10:22:31 -0800545 EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700546}
547
548TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
549 // Setup:
550 // k = 0;
551 // t = 100;
552 // for (int i = 0; i < 100; i++) {
Aart Bike609b7c2015-08-27 13:46:58 -0700553 // a[k] = 0;
554 // k = t;
555 // t = 100 - i;
Aart Bik30efb4e2015-07-30 12:14:31 -0700556 // }
557 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800558 HPhi* k_header = InsertLoopPhi(0, 0);
559 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000560 HPhi* t = InsertLoopPhi(1, 0);
561 t->AddInput(constant100_);
562
Aart Bikc071a012016-12-01 10:22:31 -0800563 HInstruction* store = InsertArrayStore(k_header, 0);
564 k_header->AddInput(t);
Aart Bik7dc96932016-10-12 10:01:05 -0700565 HInstruction* sub = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000566 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
567 t->AddInput(sub);
Aart Bik30efb4e2015-07-30 12:14:31 -0700568 PerformInductionVarAnalysis();
569
Aart Bik0d345cf2016-03-16 10:49:38 -0700570 EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt",
Aart Bike609b7c2015-08-27 13:46:58 -0700571 GetInductionInfo(store->InputAt(1), 0).c_str());
572}
573
574TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) {
575 // Setup:
576 // k = 0;
577 // for (int i = 0; i < 100; i++) {
578 // t = k + 100;
579 // t = k - 100;
580 // t = k * 100;
581 // t = k << 1;
582 // t = - k;
583 // k = i << 1;
Aart Bikc071a012016-12-01 10:22:31 -0800584 // t = - k;
Aart Bike609b7c2015-08-27 13:46:58 -0700585 // }
586 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800587 HPhi* k_header = InsertLoopPhi(0, 0);
588 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000589
Aart Bik7dc96932016-10-12 10:01:05 -0700590 HInstruction* add = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800591 new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700592 HInstruction* sub = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800593 new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700594 HInstruction* mul = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800595 new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
596 HInstruction* shl1 = InsertInstruction(
597 new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
598 HInstruction* neg1 = InsertInstruction(
599 new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
600 HInstruction* shl2 = InsertInstruction(
601 new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
602 HInstruction* neg2 = InsertInstruction(
603 new (&allocator_) HNeg(Primitive::kPrimInt, shl2), 0);
604 k_header->AddInput(shl2);
Aart Bike609b7c2015-08-27 13:46:58 -0700605 PerformInductionVarAnalysis();
606
Aart Bik0d345cf2016-03-16 10:49:38 -0700607 EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
608 GetInductionInfo(add, 0).c_str());
609 EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):PrimInt):PrimInt",
610 GetInductionInfo(sub, 0).c_str());
611 EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt",
612 GetInductionInfo(mul, 0).c_str());
613 EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt",
Aart Bikc071a012016-12-01 10:22:31 -0800614 GetInductionInfo(shl1, 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700615 EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
Aart Bikc071a012016-12-01 10:22:31 -0800616 GetInductionInfo(neg1, 0).c_str());
617 EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
618 EXPECT_STREQ("(( - (2)) * i + (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700619}
620
621TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
622 // Setup:
623 // k = 0;
624 // t = 100;
625 // for (int i = 0; i < 100; i++) {
626 // a[k] = 0;
627 // a[t] = 0;
628 // // Swap t <-> k.
629 // d = t;
630 // t = k;
631 // k = d;
632 // }
633 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800634 HPhi* k_header = InsertLoopPhi(0, 0);
635 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000636 HPhi* t = InsertLoopPhi(1, 0);
637 t->AddInput(constant100_);
638
Aart Bikc071a012016-12-01 10:22:31 -0800639 HInstruction* store1 = InsertArrayStore(k_header, 0);
David Brazdilbadd8262016-02-02 16:28:56 +0000640 HInstruction* store2 = InsertArrayStore(t, 0);
Aart Bikc071a012016-12-01 10:22:31 -0800641 k_header->AddInput(t);
642 t->AddInput(k_header);
Aart Bike609b7c2015-08-27 13:46:58 -0700643 PerformInductionVarAnalysis();
644
Aart Bik0d345cf2016-03-16 10:49:38 -0700645 EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
646 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700647}
648
649TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
650 // Setup:
651 // k = 0;
652 // for (int i = 0; i < 100; i++) {
653 // a[k] = 0;
654 // k = 1 - k;
655 // }
656 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800657 HPhi* k_header = InsertLoopPhi(0, 0);
658 k_header->AddInput(constant0_);
David Brazdilbadd8262016-02-02 16:28:56 +0000659
Aart Bikc071a012016-12-01 10:22:31 -0800660 HInstruction* store = InsertArrayStore(k_header, 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700661 HInstruction* sub = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800662 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
663 k_header->AddInput(sub);
Aart Bike609b7c2015-08-27 13:46:58 -0700664 PerformInductionVarAnalysis();
665
Aart Bik0d345cf2016-03-16 10:49:38 -0700666 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
667 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str());
Aart Bike609b7c2015-08-27 13:46:58 -0700668}
669
Aart Bik7dc96932016-10-12 10:01:05 -0700670TEST_F(InductionVarAnalysisTest, FindXorPeriodicInduction) {
671 // Setup:
672 // k = 0;
673 // for (int i = 0; i < 100; i++) {
674 // a[k] = 0;
675 // k = k ^ 1;
676 // }
677 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800678 HPhi* k_header = InsertLoopPhi(0, 0);
679 k_header->AddInput(constant0_);
Aart Bik7dc96932016-10-12 10:01:05 -0700680
Aart Bikc071a012016-12-01 10:22:31 -0800681 HInstruction* store = InsertArrayStore(k_header, 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700682 HInstruction* x = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800683 new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant1_), 0);
684 k_header->AddInput(x);
Aart Bik7dc96932016-10-12 10:01:05 -0700685 PerformInductionVarAnalysis();
686
687 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
688 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(x, 0).c_str());
689}
690
Aart Bik639cc8c2016-10-18 13:03:31 -0700691TEST_F(InductionVarAnalysisTest, FindXorConstantLeftPeriodicInduction) {
692 // Setup:
693 // k = 1;
694 // for (int i = 0; i < 100; i++) {
695 // k = 1 ^ k;
696 // }
697 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800698 HPhi* k_header = InsertLoopPhi(0, 0);
699 k_header->AddInput(constant1_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700700
701 HInstruction* x = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800702 new (&allocator_) HXor(Primitive::kPrimInt, constant1_, k_header), 0);
703 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700704 PerformInductionVarAnalysis();
705
Aart Bikc071a012016-12-01 10:22:31 -0800706 EXPECT_STREQ("periodic((1), ((1) ^ (1))):PrimInt", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700707 EXPECT_STREQ("periodic(((1) ^ (1)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
708}
709
Aart Bik7dc96932016-10-12 10:01:05 -0700710TEST_F(InductionVarAnalysisTest, FindXor100PeriodicInduction) {
711 // Setup:
Aart Bik639cc8c2016-10-18 13:03:31 -0700712 // k = 1;
Aart Bik7dc96932016-10-12 10:01:05 -0700713 // for (int i = 0; i < 100; i++) {
714 // k = k ^ 100;
715 // }
716 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800717 HPhi* k_header = InsertLoopPhi(0, 0);
718 k_header->AddInput(constant1_);
Aart Bik7dc96932016-10-12 10:01:05 -0700719
720 HInstruction* x = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800721 new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant100_), 0);
722 k_header->AddInput(x);
Aart Bik7dc96932016-10-12 10:01:05 -0700723 PerformInductionVarAnalysis();
724
Aart Bikc071a012016-12-01 10:22:31 -0800725 EXPECT_STREQ("periodic((1), ((1) ^ (100))):PrimInt", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700726 EXPECT_STREQ("periodic(((1) ^ (100)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
727}
728
729TEST_F(InductionVarAnalysisTest, FindBooleanEqPeriodicInduction) {
730 // Setup:
731 // k = 0;
732 // for (int i = 0; i < 100; i++) {
733 // k = (k == 0);
734 // }
735 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800736 HPhi* k_header = InsertLoopPhi(0, 0);
737 k_header->AddInput(constant0_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700738
Aart Bikc071a012016-12-01 10:22:31 -0800739 HInstruction* x = InsertInstruction(new (&allocator_) HEqual(k_header, constant0_), 0);
740 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700741 PerformInductionVarAnalysis();
742
Aart Bikc071a012016-12-01 10:22:31 -0800743 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700744 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
745}
746
747TEST_F(InductionVarAnalysisTest, FindBooleanEqConstantLeftPeriodicInduction) {
748 // Setup:
749 // k = 0;
750 // for (int i = 0; i < 100; i++) {
751 // k = (0 == k);
752 // }
753 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800754 HPhi* k_header = InsertLoopPhi(0, 0);
755 k_header->AddInput(constant0_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700756
Aart Bikc071a012016-12-01 10:22:31 -0800757 HInstruction* x = InsertInstruction(new (&allocator_) HEqual(constant0_, k_header), 0);
758 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700759 PerformInductionVarAnalysis();
760
Aart Bikc071a012016-12-01 10:22:31 -0800761 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700762 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
763}
764
765TEST_F(InductionVarAnalysisTest, FindBooleanNePeriodicInduction) {
766 // Setup:
767 // k = 0;
768 // for (int i = 0; i < 100; i++) {
769 // k = (k != 1);
770 // }
771 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800772 HPhi* k_header = InsertLoopPhi(0, 0);
773 k_header->AddInput(constant0_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700774
Aart Bikc071a012016-12-01 10:22:31 -0800775 HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(k_header, constant1_), 0);
776 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700777 PerformInductionVarAnalysis();
778
Aart Bikc071a012016-12-01 10:22:31 -0800779 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700780 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
781}
782
783TEST_F(InductionVarAnalysisTest, FindBooleanNeConstantLeftPeriodicInduction) {
784 // Setup:
785 // k = 0;
786 // for (int i = 0; i < 100; i++) {
787 // k = (1 != k);
788 // }
789 BuildLoopNest(1);
Aart Bikc071a012016-12-01 10:22:31 -0800790 HPhi* k_header = InsertLoopPhi(0, 0);
791 k_header->AddInput(constant0_);
Aart Bik639cc8c2016-10-18 13:03:31 -0700792
Aart Bikc071a012016-12-01 10:22:31 -0800793 HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(constant1_, k_header), 0);
794 k_header->AddInput(x);
Aart Bik639cc8c2016-10-18 13:03:31 -0700795 PerformInductionVarAnalysis();
796
Aart Bikc071a012016-12-01 10:22:31 -0800797 EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
Aart Bik639cc8c2016-10-18 13:03:31 -0700798 EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
Aart Bik7dc96932016-10-12 10:01:05 -0700799}
800
Aart Bike609b7c2015-08-27 13:46:58 -0700801TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
802 // Setup:
803 // k = 0;
804 // for (int i = 0; i < 100; i++) {
Aart Bikc071a012016-12-01 10:22:31 -0800805 // t = - k;
Aart Bike609b7c2015-08-27 13:46:58 -0700806 // k = 1 - k;
807 // t = k + 100;
808 // t = k - 100;
809 // t = k * 100;
810 // t = k << 1;
811 // t = - k;
812 // }
813 BuildLoopNest(1);
David Brazdilbadd8262016-02-02 16:28:56 +0000814 HPhi* k_header = InsertLoopPhi(0, 0);
815 k_header->AddInput(constant0_);
816
Aart Bikc071a012016-12-01 10:22:31 -0800817 HInstruction* neg1 = InsertInstruction(
818 new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
819 HInstruction* idiom = InsertInstruction(
David Brazdilbadd8262016-02-02 16:28:56 +0000820 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700821 HInstruction* add = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800822 new (&allocator_) HAdd(Primitive::kPrimInt, idiom, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700823 HInstruction* sub = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800824 new (&allocator_) HSub(Primitive::kPrimInt, idiom, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700825 HInstruction* mul = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800826 new (&allocator_) HMul(Primitive::kPrimInt, idiom, constant100_), 0);
Aart Bik7dc96932016-10-12 10:01:05 -0700827 HInstruction* shl = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800828 new (&allocator_) HShl(Primitive::kPrimInt, idiom, constant1_), 0);
829 HInstruction* neg2 = InsertInstruction(
830 new (&allocator_) HNeg(Primitive::kPrimInt, idiom), 0);
831 k_header->AddInput(idiom);
Aart Bike609b7c2015-08-27 13:46:58 -0700832 PerformInductionVarAnalysis();
833
Aart Bikc071a012016-12-01 10:22:31 -0800834 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(k_header, 0).c_str());
835 EXPECT_STREQ("periodic((0), ( - (1))):PrimInt", GetInductionInfo(neg1, 0).c_str());
836 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(idiom, 0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700837 EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str());
838 EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str());
839 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
840 EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
Aart Bikc071a012016-12-01 10:22:31 -0800841 EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700842}
843
844TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
845 // Setup:
846 // k = 0;
847 // for (int i_0 = 0; i_0 < 100; i_0++) {
848 // ..
849 // for (int i_9 = 0; i_9 < 100; i_9++) {
850 // k = 1 + k;
851 // a[k] = 0;
852 // }
853 // ..
854 // }
855 BuildLoopNest(10);
David Brazdilbadd8262016-02-02 16:28:56 +0000856
Aart Bikc071a012016-12-01 10:22:31 -0800857 HPhi* k_header[10];
David Brazdilbadd8262016-02-02 16:28:56 +0000858 for (int d = 0; d < 10; d++) {
Aart Bikc071a012016-12-01 10:22:31 -0800859 k_header[d] = InsertLoopPhi(0, d);
David Brazdilbadd8262016-02-02 16:28:56 +0000860 }
861
Aart Bik7dc96932016-10-12 10:01:05 -0700862 HInstruction* inc = InsertInstruction(
Aart Bikc071a012016-12-01 10:22:31 -0800863 new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k_header[9]), 9);
David Brazdilbadd8262016-02-02 16:28:56 +0000864 HInstruction* store = InsertArrayStore(inc, 9);
865
866 for (int d = 0; d < 10; d++) {
Aart Bikc071a012016-12-01 10:22:31 -0800867 k_header[d]->AddInput((d != 0) ? k_header[d - 1] : constant0_);
868 k_header[d]->AddInput((d != 9) ? k_header[d + 1] : inc);
David Brazdilbadd8262016-02-02 16:28:56 +0000869 }
Aart Bik30efb4e2015-07-30 12:14:31 -0700870 PerformInductionVarAnalysis();
871
Aart Bike609b7c2015-08-27 13:46:58 -0700872 // Avoid exact phi number, since that depends on the SSA building phase.
873 std::regex r("\\(\\(1\\) \\* i \\+ "
Aart Bik0d345cf2016-03-16 10:49:38 -0700874 "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt");
Aart Bik30efb4e2015-07-30 12:14:31 -0700875
876 for (int d = 0; d < 10; d++) {
877 if (d == 9) {
Aart Bike609b7c2015-08-27 13:46:58 -0700878 EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r));
Aart Bik30efb4e2015-07-30 12:14:31 -0700879 } else {
Aart Bike609b7c2015-08-27 13:46:58 -0700880 EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700881 }
Aart Bik0d345cf2016-03-16 10:49:38 -0700882 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str());
Aart Bikd14c5952015-09-08 15:25:15 -0700883 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700884 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(d).c_str());
Aart Bik30efb4e2015-07-30 12:14:31 -0700885 }
886}
887
Aart Bik78296912016-03-25 13:14:53 -0700888TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) {
889 // Setup:
890 // for (int i = 0; i < 100; i++) {
891 // k = (byte) i;
892 // a[k] = 0;
893 // a[i] = 0;
894 // }
895 BuildLoopNest(1);
Aart Bik7dc96932016-10-12 10:01:05 -0700896 HInstruction* conv = InsertInstruction(
Aart Bik78296912016-03-25 13:14:53 -0700897 new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
898 HInstruction* store1 = InsertArrayStore(conv, 0);
899 HInstruction* store2 = InsertArrayStore(basic_[0], 0);
900 PerformInductionVarAnalysis();
901
902 // Regular int induction (i) is "transferred" over conversion into byte induction (k).
903 EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
904 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
905 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
906
907 // Type matters!
908 EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
909
910 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700911 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetTripCount(0).c_str());
Aart Bik78296912016-03-25 13:14:53 -0700912}
913
Aart Bikcc42be02016-10-20 16:14:16 -0700914TEST_F(InductionVarAnalysisTest, ByteInductionDerivedIntLoopControl) {
915 // Setup:
916 // for (int i = 0; i < 100; i++) {
917 // k = (byte) i;
918 // a[k] = 0;
919 // k = k + 1
920 // a[k] = 0;
921 // }
922 BuildLoopNest(1);
923 HInstruction* conv = InsertInstruction(
924 new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
925 HInstruction* store1 = InsertArrayStore(conv, 0);
926 HInstruction* add = InsertInstruction(
927 new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
928 HInstruction* store2 = InsertArrayStore(add, 0);
929
930 PerformInductionVarAnalysis();
931
932 // Byte induction (k) is "transferred" over conversion into addition (k + 1).
933 // This means only values within byte range can be trusted (even though
934 // addition can jump out of the range of course).
935 EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
936 EXPECT_STREQ("((1) * i + (1)):PrimByte", GetInductionInfo(store2->InputAt(1), 0).c_str());
937}
938
Aart Bik0d345cf2016-03-16 10:49:38 -0700939TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
940 // Setup:
941 // for (byte i = -128; i < 127; i++) { // just fits!
942 // }
943 BuildLoopNest(1);
944 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
945 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
946 ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
947 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
948 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
949 basic_[0]->ReplaceInput(conv, 1);
950 PerformInductionVarAnalysis();
951
952 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
953 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700954 EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700955}
956
957TEST_F(InductionVarAnalysisTest, ByteLoopControl2) {
958 // Setup:
959 // for (byte i = -128; i < 128; i++) { // infinite loop!
960 // }
961 BuildLoopNest(1);
962 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
963 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
964 ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
965 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
966 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
967 basic_[0]->ReplaceInput(conv, 1);
968 PerformInductionVarAnalysis();
969
970 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
971 // Trip-count undefined.
Aart Bik009cace2016-09-16 10:15:19 -0700972 EXPECT_STREQ("", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700973}
974
975TEST_F(InductionVarAnalysisTest, ShortLoopControl1) {
976 // Setup:
977 // for (short i = -32768; i < 32767; i++) { // just fits!
978 // }
979 BuildLoopNest(1);
980 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
981 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
982 ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
983 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
984 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
985 basic_[0]->ReplaceInput(conv, 1);
986 PerformInductionVarAnalysis();
987
988 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
989 GetInductionInfo(increment_[0], 0).c_str());
990 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -0700991 EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -0700992}
993
994TEST_F(InductionVarAnalysisTest, ShortLoopControl2) {
995 // Setup:
996 // for (short i = -32768; i < 32768; i++) { // infinite loop!
997 // }
998 BuildLoopNest(1);
999 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
1000 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
1001 ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
1002 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
1003 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1004 basic_[0]->ReplaceInput(conv, 1);
1005 PerformInductionVarAnalysis();
1006
1007 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
1008 GetInductionInfo(increment_[0], 0).c_str());
1009 // Trip-count undefined.
Aart Bik009cace2016-09-16 10:15:19 -07001010 EXPECT_STREQ("", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -07001011}
1012
1013TEST_F(InductionVarAnalysisTest, CharLoopControl1) {
1014 // Setup:
1015 // for (char i = 0; i < 65535; i++) { // just fits!
1016 // }
1017 BuildLoopNest(1);
1018 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
1019 ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
1020 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
1021 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1022 basic_[0]->ReplaceInput(conv, 1);
1023 PerformInductionVarAnalysis();
1024
1025 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
1026 // Trip-count.
Aart Bik009cace2016-09-16 10:15:19 -07001027 EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -07001028}
1029
1030TEST_F(InductionVarAnalysisTest, CharLoopControl2) {
1031 // Setup:
1032 // for (char i = 0; i < 65536; i++) { // infinite loop!
1033 // }
1034 BuildLoopNest(1);
1035 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
1036 ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
1037 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
1038 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
1039 basic_[0]->ReplaceInput(conv, 1);
1040 PerformInductionVarAnalysis();
1041
1042 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
1043 // Trip-count undefined.
Aart Bik009cace2016-09-16 10:15:19 -07001044 EXPECT_STREQ("", GetTripCount(0).c_str());
Aart Bik0d345cf2016-03-16 10:49:38 -07001045}
1046
Aart Bik30efb4e2015-07-30 12:14:31 -07001047} // namespace art