1 /* 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" 21 #include "induction_var_analysis.h" 22 #include "nodes.h" 23 #include "optimizing_unit_test.h" 24 25 namespace art { 26 27 /** 28 * Fixture class for the InductionVarAnalysis tests. 29 */ 30 class InductionVarAnalysisTest : public CommonCompilerTest { 31 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); 65 graph_->SetNumberOfVRegs(n + 3); 66 67 // Build basic blocks with entry, nested loop, exit. 68 entry_ = new (&allocator_) HBasicBlock(graph_); 69 graph_->AddBlock(entry_); 70 BuildForLoop(0, n); 71 return_ = new (&allocator_) HBasicBlock(graph_); 72 graph_->AddBlock(return_); 73 exit_ = new (&allocator_) HBasicBlock(graph_); 74 graph_->AddBlock(exit_); 75 entry_->AddSuccessor(loop_preheader_[0]); 76 loop_header_[0]->AddSuccessor(return_); 77 return_->AddSuccessor(exit_); 78 graph_->SetEntryBlock(entry_); 79 graph_->SetExitBlock(exit_); 80 81 // Provide entry and exit instructions. 82 parameter_ = new (&allocator_) HParameterValue( 83 graph_->GetDexFile(), 0, 0, Primitive::kPrimNot, true); 84 entry_->AddInstruction(parameter_); 85 constant0_ = graph_->GetIntConstant(0); 86 constant1_ = graph_->GetIntConstant(1); 87 constant100_ = graph_->GetIntConstant(100); 88 float_constant0_ = graph_->GetFloatConstant(0.0f); 89 return_->AddInstruction(new (&allocator_) HReturnVoid()); 90 exit_->AddInstruction(new (&allocator_) HExit()); 91 92 // Provide loop instructions. 93 for (int d = 0; d < n; d++) { 94 basic_[d] = new (&allocator_) HPhi(&allocator_, d, 0, Primitive::kPrimInt); 95 loop_preheader_[d]->AddInstruction(new (&allocator_) HGoto()); 96 loop_header_[d]->AddPhi(basic_[d]); 97 HInstruction* compare = new (&allocator_) HLessThan(basic_[d], constant100_); 98 loop_header_[d]->AddInstruction(compare); 99 loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare)); 100 increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[d], constant1_); 101 loop_body_[d]->AddInstruction(increment_[d]); 102 loop_body_[d]->AddInstruction(new (&allocator_) HGoto()); 103 104 basic_[d]->AddInput(constant0_); 105 basic_[d]->AddInput(increment_[d]); 106 } 107 } 108 109 // Builds if-statement at depth d. 110 HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) { 111 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; 126 127 HPhi* select_phi = new (&allocator_) HPhi(&allocator_, -1, 0, Primitive::kPrimInt); 128 loop_body_[d]->AddPhi(select_phi); 129 return select_phi; 130 } 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 138 // 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; 143 } 144 145 // Inserts an array store with given `subscript` at depth d to 146 // enable tests to inspect the computed induction at that point easily. 147 HInstruction* InsertArrayStore(HInstruction* subscript, int d) { 148 // ArraySet is given a float value in order to avoid SsaBuilder typing 149 // it from the array's non-existent reference type info. 150 return InsertInstruction(new (&allocator_) HArraySet( 151 parameter_, subscript, float_constant0_, Primitive::kPrimFloat, 0), d); 152 } 153 154 // 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)); 158 } 159 160 // Returns true if instructions have identical induction. 161 bool HaveSameInduction(HInstruction* instruction1, HInstruction* instruction2) { 162 return HInductionVarAnalysis::InductionEqual( 163 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction1), 164 iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2)); 165 } 166 167 // Performs InductionVarAnalysis (after proper set up). 168 void PerformInductionVarAnalysis() { 169 graph_->BuildDominatorTree(); 170 iva_ = new (&allocator_) HInductionVarAnalysis(graph_); 171 iva_->Run(); 172 } 173 174 // General building fields. 175 ArenaPool pool_; 176 ArenaAllocator allocator_; 177 HGraph* graph_; 178 HInductionVarAnalysis* iva_; 179 180 // Fixed basic blocks and instructions. 181 HBasicBlock* entry_; 182 HBasicBlock* return_; 183 HBasicBlock* exit_; 184 HInstruction* parameter_; // "this" 185 HInstruction* constant0_; 186 HInstruction* constant1_; 187 HInstruction* constant100_; 188 HInstruction* float_constant0_; 189 190 // Loop specifics. 191 HBasicBlock* loop_preheader_[10]; 192 HBasicBlock* loop_header_[10]; 193 HBasicBlock* loop_body_[10]; 194 HInstruction* increment_[10]; 195 HPhi* basic_[10]; // "vreg_d", the "i_d" 196 }; 197 198 // 199 // The actual InductionVarAnalysis tests. 200 // 201 202 TEST_F(InductionVarAnalysisTest, ProperLoopSetup) { 203 // Setup: 204 // for (int i_0 = 0; i_0 < 100; i_0++) { 205 // .. 206 // for (int i_9 = 0; i_9 < 100; i_9++) { 207 // } 208 // .. 209 // } 210 BuildLoopNest(10); 211 graph_->BuildDominatorTree(); 212 213 ASSERT_EQ(entry_->GetLoopInformation(), nullptr); 214 for (int d = 0; d < 1; d++) { 215 ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(), 216 (d == 0) ? nullptr 217 : loop_header_[d - 1]->GetLoopInformation()); 218 ASSERT_NE(loop_header_[d]->GetLoopInformation(), nullptr); 219 ASSERT_NE(loop_body_[d]->GetLoopInformation(), nullptr); 220 ASSERT_EQ(loop_header_[d]->GetLoopInformation(), 221 loop_body_[d]->GetLoopInformation()); 222 } 223 ASSERT_EQ(exit_->GetLoopInformation(), nullptr); 224 } 225 226 TEST_F(InductionVarAnalysisTest, FindBasicInduction) { 227 // Setup: 228 // for (int i = 0; i < 100; i++) { 229 // a[i] = 0; 230 // } 231 BuildLoopNest(1); 232 HInstruction* store = InsertArrayStore(basic_[0], 0); 233 PerformInductionVarAnalysis(); 234 235 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); 236 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str()); 237 238 // Offset matters! 239 EXPECT_FALSE(HaveSameInduction(store->InputAt(1), increment_[0])); 240 241 // Trip-count. 242 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", 243 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); 244 } 245 246 TEST_F(InductionVarAnalysisTest, FindDerivedInduction) { 247 // Setup: 248 // for (int i = 0; i < 100; i++) { 249 // k = 100 + i; 250 // k = 100 - i; 251 // k = 100 * i; 252 // k = i << 1; 253 // k = - i; 254 // } 255 BuildLoopNest(1); 256 HInstruction *add = InsertInstruction( 257 new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0); 258 HInstruction *sub = InsertInstruction( 259 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0); 260 HInstruction *mul = InsertInstruction( 261 new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0); 262 HInstruction *shl = InsertInstruction( 263 new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0); 264 HInstruction *neg = InsertInstruction( 265 new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0); 266 PerformInductionVarAnalysis(); 267 268 EXPECT_STREQ("((1) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str()); 269 EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str()); 270 EXPECT_STREQ("((100) * i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str()); 271 EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl, 0).c_str()); 272 EXPECT_STREQ("(( - (1)) * i + (0)):PrimInt", GetInductionInfo(neg, 0).c_str()); 273 } 274 275 TEST_F(InductionVarAnalysisTest, FindChainInduction) { 276 // Setup: 277 // k = 0; 278 // for (int i = 0; i < 100; i++) { 279 // k = k + 100; 280 // a[k] = 0; 281 // k = k - 1; 282 // a[k] = 0; 283 // } 284 BuildLoopNest(1); 285 HPhi* k = InsertLoopPhi(0, 0); 286 k->AddInput(constant0_); 287 288 HInstruction *add = InsertInstruction( 289 new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0); 290 HInstruction* store1 = InsertArrayStore(add, 0); 291 HInstruction *sub = InsertInstruction( 292 new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0); 293 HInstruction* store2 = InsertArrayStore(sub, 0); 294 k->AddInput(sub); 295 PerformInductionVarAnalysis(); 296 297 EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt", 298 GetInductionInfo(store1->InputAt(1), 0).c_str()); 299 EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt", 300 GetInductionInfo(store2->InputAt(1), 0).c_str()); 301 } 302 303 TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) { 304 // Setup: 305 // k = 0; 306 // for (int i = 0; i < 100; i++) { 307 // if () k = k + 1; 308 // else k = k + 1; 309 // a[k] = 0; 310 // } 311 BuildLoopNest(1); 312 HPhi* k_header = InsertLoopPhi(0, 0); 313 k_header->AddInput(constant0_); 314 315 HBasicBlock* ifTrue; 316 HBasicBlock* ifFalse; 317 HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse); 318 319 // True-branch. 320 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_); 321 ifTrue->AddInstruction(inc1); 322 k_body->AddInput(inc1); 323 // False-branch. 324 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_); 325 ifFalse->AddInstruction(inc2); 326 k_body->AddInput(inc2); 327 // Merge over a phi. 328 HInstruction* store = InsertArrayStore(k_body, 0); 329 k_header->AddInput(k_body); 330 PerformInductionVarAnalysis(); 331 332 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); 333 334 // Both increments get same induction. 335 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1)); 336 EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2)); 337 } 338 339 TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) { 340 // Setup: 341 // for (int i = 0; i < 100; i++) { 342 // if () k = i + 1; 343 // else k = i + 1; 344 // a[k] = 0; 345 // } 346 BuildLoopNest(1); 347 HBasicBlock* ifTrue; 348 HBasicBlock* ifFalse; 349 HPhi* k = BuildIf(0, &ifTrue, &ifFalse); 350 351 // True-branch. 352 HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_); 353 ifTrue->AddInstruction(inc1); 354 k->AddInput(inc1); 355 // False-branch. 356 HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_); 357 ifFalse->AddInstruction(inc2); 358 k->AddInput(inc2); 359 // Merge over a phi. 360 HInstruction* store = InsertArrayStore(k, 0); 361 PerformInductionVarAnalysis(); 362 363 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); 364 } 365 366 TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) { 367 // Setup: 368 // k = 0; 369 // for (int i = 0; i < 100; i++) { 370 // a[k] = 0; 371 // k = 100 - i; 372 // } 373 BuildLoopNest(1); 374 HPhi* k = InsertLoopPhi(0, 0); 375 k->AddInput(constant0_); 376 377 HInstruction* store = InsertArrayStore(k, 0); 378 HInstruction *sub = InsertInstruction( 379 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0); 380 k->AddInput(sub); 381 PerformInductionVarAnalysis(); 382 383 EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt", 384 GetInductionInfo(store->InputAt(1), 0).c_str()); 385 } 386 387 TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) { 388 // Setup: 389 // k = 0; 390 // t = 100; 391 // for (int i = 0; i < 100; i++) { 392 // a[k] = 0; 393 // k = t; 394 // t = 100 - i; 395 // } 396 BuildLoopNest(1); 397 HPhi* k = InsertLoopPhi(0, 0); 398 k->AddInput(constant0_); 399 HPhi* t = InsertLoopPhi(1, 0); 400 t->AddInput(constant100_); 401 402 HInstruction* store = InsertArrayStore(k, 0); 403 k->AddInput(t); 404 HInstruction *sub = InsertInstruction( 405 new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0); 406 t->AddInput(sub); 407 PerformInductionVarAnalysis(); 408 409 EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt", 410 GetInductionInfo(store->InputAt(1), 0).c_str()); 411 } 412 413 TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) { 414 // Setup: 415 // k = 0; 416 // for (int i = 0; i < 100; i++) { 417 // t = k + 100; 418 // t = k - 100; 419 // t = k * 100; 420 // t = k << 1; 421 // t = - k; 422 // k = i << 1; 423 // } 424 BuildLoopNest(1); 425 HPhi* k = InsertLoopPhi(0, 0); 426 k->AddInput(constant0_); 427 428 HInstruction *add = InsertInstruction( 429 new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0); 430 HInstruction *sub = InsertInstruction( 431 new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0); 432 HInstruction *mul = InsertInstruction( 433 new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0); 434 HInstruction *shl = InsertInstruction( 435 new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0); 436 HInstruction *neg = InsertInstruction( 437 new (&allocator_) HNeg(Primitive::kPrimInt, k), 0); 438 k->AddInput( 439 InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0)); 440 PerformInductionVarAnalysis(); 441 442 EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt", 443 GetInductionInfo(add, 0).c_str()); 444 EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):PrimInt):PrimInt", 445 GetInductionInfo(sub, 0).c_str()); 446 EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt", 447 GetInductionInfo(mul, 0).c_str()); 448 EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt", 449 GetInductionInfo(shl, 0).c_str()); 450 EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt", 451 GetInductionInfo(neg, 0).c_str()); 452 } 453 454 TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) { 455 // Setup: 456 // k = 0; 457 // t = 100; 458 // for (int i = 0; i < 100; i++) { 459 // a[k] = 0; 460 // a[t] = 0; 461 // // Swap t <-> k. 462 // d = t; 463 // t = k; 464 // k = d; 465 // } 466 BuildLoopNest(1); 467 HPhi* k = InsertLoopPhi(0, 0); 468 k->AddInput(constant0_); 469 HPhi* t = InsertLoopPhi(1, 0); 470 t->AddInput(constant100_); 471 472 HInstruction* store1 = InsertArrayStore(k, 0); 473 HInstruction* store2 = InsertArrayStore(t, 0); 474 k->AddInput(t); 475 t->AddInput(k); 476 PerformInductionVarAnalysis(); 477 478 EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str()); 479 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str()); 480 } 481 482 TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) { 483 // Setup: 484 // k = 0; 485 // for (int i = 0; i < 100; i++) { 486 // a[k] = 0; 487 // k = 1 - k; 488 // } 489 BuildLoopNest(1); 490 HPhi* k = InsertLoopPhi(0, 0); 491 k->AddInput(constant0_); 492 493 HInstruction* store = InsertArrayStore(k, 0); 494 HInstruction *sub = InsertInstruction( 495 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0); 496 k->AddInput(sub); 497 PerformInductionVarAnalysis(); 498 499 EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); 500 EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str()); 501 } 502 503 TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) { 504 // Setup: 505 // k = 0; 506 // for (int i = 0; i < 100; i++) { 507 // k = 1 - k; 508 // t = k + 100; 509 // t = k - 100; 510 // t = k * 100; 511 // t = k << 1; 512 // t = - k; 513 // } 514 BuildLoopNest(1); 515 HPhi* k_header = InsertLoopPhi(0, 0); 516 k_header->AddInput(constant0_); 517 518 HInstruction* k_body = InsertInstruction( 519 new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0); 520 k_header->AddInput(k_body); 521 522 // Derived expressions. 523 HInstruction *add = InsertInstruction( 524 new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0); 525 HInstruction *sub = InsertInstruction( 526 new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0); 527 HInstruction *mul = InsertInstruction( 528 new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0); 529 HInstruction *shl = InsertInstruction( 530 new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0); 531 HInstruction *neg = InsertInstruction( 532 new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0); 533 PerformInductionVarAnalysis(); 534 535 EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str()); 536 EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str()); 537 EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str()); 538 EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str()); 539 EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg, 0).c_str()); 540 } 541 542 TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) { 543 // Setup: 544 // k = 0; 545 // for (int i_0 = 0; i_0 < 100; i_0++) { 546 // .. 547 // for (int i_9 = 0; i_9 < 100; i_9++) { 548 // k = 1 + k; 549 // a[k] = 0; 550 // } 551 // .. 552 // } 553 BuildLoopNest(10); 554 555 HPhi* k[10]; 556 for (int d = 0; d < 10; d++) { 557 k[d] = InsertLoopPhi(0, d); 558 } 559 560 HInstruction *inc = InsertInstruction( 561 new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9); 562 HInstruction* store = InsertArrayStore(inc, 9); 563 564 for (int d = 0; d < 10; d++) { 565 k[d]->AddInput((d != 0) ? k[d - 1] : constant0_); 566 k[d]->AddInput((d != 9) ? k[d + 1] : inc); 567 } 568 PerformInductionVarAnalysis(); 569 570 // Avoid exact phi number, since that depends on the SSA building phase. 571 std::regex r("\\(\\(1\\) \\* i \\+ " 572 "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt"); 573 574 for (int d = 0; d < 10; d++) { 575 if (d == 9) { 576 EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r)); 577 } else { 578 EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str()); 579 } 580 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str()); 581 // Trip-count. 582 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", 583 GetInductionInfo(loop_header_[d]->GetLastInstruction(), d).c_str()); 584 } 585 } 586 587 TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) { 588 // Setup: 589 // for (int i = 0; i < 100; i++) { 590 // k = (byte) i; 591 // a[k] = 0; 592 // a[i] = 0; 593 // } 594 BuildLoopNest(1); 595 HInstruction *conv = InsertInstruction( 596 new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0); 597 HInstruction* store1 = InsertArrayStore(conv, 0); 598 HInstruction* store2 = InsertArrayStore(basic_[0], 0); 599 PerformInductionVarAnalysis(); 600 601 // Regular int induction (i) is "transferred" over conversion into byte induction (k). 602 EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str()); 603 EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str()); 604 EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str()); 605 606 // Type matters! 607 EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1))); 608 609 // Trip-count. 610 EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", 611 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); 612 } 613 614 TEST_F(InductionVarAnalysisTest, ByteLoopControl1) { 615 // Setup: 616 // for (byte i = -128; i < 127; i++) { // just fits! 617 // } 618 BuildLoopNest(1); 619 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0); 620 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); 621 ifs->ReplaceInput(graph_->GetIntConstant(127), 1); 622 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1); 623 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); 624 basic_[0]->ReplaceInput(conv, 1); 625 PerformInductionVarAnalysis(); 626 627 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str()); 628 // Trip-count. 629 EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", 630 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); 631 } 632 633 TEST_F(InductionVarAnalysisTest, ByteLoopControl2) { 634 // Setup: 635 // for (byte i = -128; i < 128; i++) { // infinite loop! 636 // } 637 BuildLoopNest(1); 638 basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0); 639 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); 640 ifs->ReplaceInput(graph_->GetIntConstant(128), 1); 641 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1); 642 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); 643 basic_[0]->ReplaceInput(conv, 1); 644 PerformInductionVarAnalysis(); 645 646 EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str()); 647 // Trip-count undefined. 648 EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); 649 } 650 651 TEST_F(InductionVarAnalysisTest, ShortLoopControl1) { 652 // Setup: 653 // for (short i = -32768; i < 32767; i++) { // just fits! 654 // } 655 BuildLoopNest(1); 656 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0); 657 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); 658 ifs->ReplaceInput(graph_->GetIntConstant(32767), 1); 659 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1); 660 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); 661 basic_[0]->ReplaceInput(conv, 1); 662 PerformInductionVarAnalysis(); 663 664 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort", 665 GetInductionInfo(increment_[0], 0).c_str()); 666 // Trip-count. 667 EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", 668 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); 669 } 670 671 TEST_F(InductionVarAnalysisTest, ShortLoopControl2) { 672 // Setup: 673 // for (short i = -32768; i < 32768; i++) { // infinite loop! 674 // } 675 BuildLoopNest(1); 676 basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0); 677 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); 678 ifs->ReplaceInput(graph_->GetIntConstant(32768), 1); 679 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1); 680 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); 681 basic_[0]->ReplaceInput(conv, 1); 682 PerformInductionVarAnalysis(); 683 684 EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort", 685 GetInductionInfo(increment_[0], 0).c_str()); 686 // Trip-count undefined. 687 EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); 688 } 689 690 TEST_F(InductionVarAnalysisTest, CharLoopControl1) { 691 // Setup: 692 // for (char i = 0; i < 65535; i++) { // just fits! 693 // } 694 BuildLoopNest(1); 695 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); 696 ifs->ReplaceInput(graph_->GetIntConstant(65535), 1); 697 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1); 698 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); 699 basic_[0]->ReplaceInput(conv, 1); 700 PerformInductionVarAnalysis(); 701 702 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str()); 703 // Trip-count. 704 EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", 705 GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); 706 } 707 708 TEST_F(InductionVarAnalysisTest, CharLoopControl2) { 709 // Setup: 710 // for (char i = 0; i < 65536; i++) { // infinite loop! 711 // } 712 BuildLoopNest(1); 713 HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); 714 ifs->ReplaceInput(graph_->GetIntConstant(65536), 1); 715 HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1); 716 loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); 717 basic_[0]->ReplaceInput(conv, 1); 718 PerformInductionVarAnalysis(); 719 720 EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str()); 721 // Trip-count undefined. 722 EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); 723 } 724 725 } // namespace art 726