diff options
| author | Aart Bik <ajcbik@google.com> | 2015-07-30 12:14:31 -0700 |
|---|---|---|
| committer | Aart Bik <ajcbik@google.com> | 2015-08-26 17:20:28 +0000 |
| commit | 30efb4e00c2a9aa318d44486b5eacaa7178d20ef (patch) | |
| tree | 90c4e84b5473def182fa443191ba8de80fa62d63 /compiler/optimizing/induction_var_analysis_test.cc | |
| parent | 574d75597013cb12a961c0d4365d1618d8ef6977 (diff) | |
| download | art-30efb4e00c2a9aa318d44486b5eacaa7178d20ef.tar.gz art-30efb4e00c2a9aa318d44486b5eacaa7178d20ef.tar.bz2 art-30efb4e00c2a9aa318d44486b5eacaa7178d20ef.zip | |
Induction variable analysis (with unit tests).
Rationale:
Induction variable analysis forms the basis of a wide
variety of compiler optimizations. This implementation
finds induction variables using the elegant SSA-based
algorithm defined by [Gerlek et al.].
Change-Id: I79b8dce33ffb8b283c179699a8dff5bd196f75b2
Diffstat (limited to 'compiler/optimizing/induction_var_analysis_test.cc')
| -rw-r--r-- | compiler/optimizing/induction_var_analysis_test.cc | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc new file mode 100644 index 0000000000..2093e3355d --- /dev/null +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <regex> + +#include "base/arena_allocator.h" +#include "builder.h" +#include "gtest/gtest.h" +#include "induction_var_analysis.h" +#include "nodes.h" +#include "optimizing_unit_test.h" + +namespace art { + +/** + * Fixture class for the InductionVarAnalysis tests. + */ +class InductionVarAnalysisTest : public testing::Test { + public: + InductionVarAnalysisTest() : pool_(), allocator_(&pool_) { + graph_ = CreateGraph(&allocator_); + } + + ~InductionVarAnalysisTest() { } + + // Builds single for-loop at depth d. + void BuildForLoop(int d, int n) { + ASSERT_LT(d, n); + loop_preheader_[d] = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(loop_preheader_[d]); + loop_header_[d] = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(loop_header_[d]); + loop_preheader_[d]->AddSuccessor(loop_header_[d]); + if (d < (n - 1)) { + BuildForLoop(d + 1, n); + } + loop_body_[d] = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(loop_body_[d]); + loop_body_[d]->AddSuccessor(loop_header_[d]); + if (d < (n - 1)) { + loop_header_[d]->AddSuccessor(loop_preheader_[d + 1]); + loop_header_[d + 1]->AddSuccessor(loop_body_[d]); + } else { + loop_header_[d]->AddSuccessor(loop_body_[d]); + } + } + + // Builds a n-nested loop in CFG where each loop at depth 0 <= d < n + // is defined as "for (int i_d = 0; i_d < 100; i_d++)". Tests can further + // populate the loop with instructions to set up interesting scenarios. + void BuildLoopNest(int n) { + ASSERT_LE(n, 10); + graph_->SetNumberOfVRegs(n + 2); + + // Build basic blocks with entry, nested loop, exit. + entry_ = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(entry_); + BuildForLoop(0, n); + exit_ = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(exit_); + entry_->AddSuccessor(loop_preheader_[0]); + loop_header_[0]->AddSuccessor(exit_); + graph_->SetEntryBlock(entry_); + graph_->SetExitBlock(exit_); + + // Provide entry and exit instructions. + // 0 : parameter + // 1 : constant 0 + // 2 : constant 1 + // 3 : constant 100 + parameter_ = new (&allocator_) + HParameterValue(0, Primitive::kPrimNot, true); + entry_->AddInstruction(parameter_); + constant0_ = new (&allocator_) HConstant(Primitive::kPrimInt); + entry_->AddInstruction(constant0_); + constant1_ = new (&allocator_) HConstant(Primitive::kPrimInt); + entry_->AddInstruction(constant1_); + constant100_ = new (&allocator_) HConstant(Primitive::kPrimInt); + entry_->AddInstruction(constant100_); + exit_->AddInstruction(new (&allocator_) HExit()); + induc_ = new (&allocator_) HLocal(n); + entry_->AddInstruction(induc_); + entry_->AddInstruction(new (&allocator_) HStoreLocal(induc_, constant0_)); + tmp_ = new (&allocator_) HLocal(n + 1); + entry_->AddInstruction(tmp_); + entry_->AddInstruction(new (&allocator_) HStoreLocal(tmp_, constant100_)); + + // Provide loop instructions. + for (int d = 0; d < n; d++) { + basic_[d] = new (&allocator_) HLocal(d); + entry_->AddInstruction(basic_[d]); + loop_preheader_[d]->AddInstruction( + new (&allocator_) HStoreLocal(basic_[d], constant0_)); + HInstruction* load = new (&allocator_) + HLoadLocal(basic_[d], Primitive::kPrimInt); + loop_header_[d]->AddInstruction(load); + HInstruction* compare = new (&allocator_) + HGreaterThanOrEqual(load, constant100_); + loop_header_[d]->AddInstruction(compare); + loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare)); + load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt); + loop_body_[d]->AddInstruction(load); + increment_[d] = new (&allocator_) + HAdd(Primitive::kPrimInt, load, constant1_); + loop_body_[d]->AddInstruction(increment_[d]); + loop_body_[d]->AddInstruction( + new (&allocator_) HStoreLocal(basic_[d], increment_[d])); + loop_body_[d]->AddInstruction(new (&allocator_) HGoto()); + } + } + + // Builds if-statement at depth d. + void BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) { + HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_); + HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_); + HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(cond); + graph_->AddBlock(ifTrue); + graph_->AddBlock(ifFalse); + // Conditional split. + loop_header_[d]->ReplaceSuccessor(loop_body_[d], cond); + cond->AddSuccessor(ifTrue); + cond->AddSuccessor(ifFalse); + ifTrue->AddSuccessor(loop_body_[d]); + ifFalse->AddSuccessor(loop_body_[d]); + cond->AddInstruction(new (&allocator_) HIf(parameter_)); + *ifT = ifTrue; + *ifF = ifFalse; + } + + // Inserts instruction right before increment at depth d. + HInstruction* InsertInstruction(HInstruction* instruction, int d) { + loop_body_[d]->InsertInstructionBefore(instruction, increment_[d]); + return instruction; + } + + // Inserts local load at depth d. + HInstruction* InsertLocalLoad(HLocal* local, int d) { + return InsertInstruction( + new (&allocator_) HLoadLocal(local, Primitive::kPrimInt), d); + } + + // Inserts local store at depth d. + HInstruction* InsertLocalStore(HLocal* local, HInstruction* rhs, int d) { + return InsertInstruction(new (&allocator_) HStoreLocal(local, rhs), d); + } + + // Inserts an array store with given local as subscript at depth d to + // enable tests to inspect the computed induction at that point easily. + HInstruction* InsertArrayStore(HLocal* subscript, int d) { + HInstruction* load = InsertInstruction( + new (&allocator_) HLoadLocal(subscript, Primitive::kPrimInt), d); + return InsertInstruction(new (&allocator_) HArraySet( + parameter_, load, constant0_, Primitive::kPrimInt, 0), d); + } + + // Returns loop information of loop at depth d. + HLoopInformation* GetLoopInfo(int d) { + return loop_body_[d]->GetLoopInformation(); + } + + // Performs InductionVarAnalysis (after proper set up). + void PerformInductionVarAnalysis() { + ASSERT_TRUE(graph_->TryBuildingSsa()); + iva_ = new (&allocator_) HInductionVarAnalysis(graph_); + iva_->Run(); + } + + // General building fields. + ArenaPool pool_; + ArenaAllocator allocator_; + HGraph* graph_; + HInductionVarAnalysis* iva_; + + // Fixed basic blocks and instructions. + HBasicBlock* entry_; + HBasicBlock* exit_; + HInstruction* parameter_; // "this" + HInstruction* constant0_; + HInstruction* constant1_; + HInstruction* constant100_; + HLocal* induc_; // "vreg_n", the "k" + HLocal* tmp_; // "vreg_n+1" + + // Loop specifics. + HBasicBlock* loop_preheader_[10]; + HBasicBlock* loop_header_[10]; + HBasicBlock* loop_body_[10]; + HInstruction* increment_[10]; + HLocal* basic_[10]; // "vreg_d", the "i_d" +}; + +// +// The actual InductionVarAnalysis tests. +// + +TEST_F(InductionVarAnalysisTest, ProperLoopSetup) { + // Setup: + // for (int i_0 = 0; i_0 < 100; i_0++) { + // .. + // for (int i_9 = 0; i_9 < 100; i_9++) { + // } + // .. + // } + BuildLoopNest(10); + ASSERT_TRUE(graph_->TryBuildingSsa()); + ASSERT_EQ(entry_->GetLoopInformation(), nullptr); + for (int d = 0; d < 1; d++) { + ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(), + (d == 0) ? nullptr + : loop_header_[d - 1]->GetLoopInformation()); + ASSERT_NE(loop_header_[d]->GetLoopInformation(), nullptr); + ASSERT_NE(loop_body_[d]->GetLoopInformation(), nullptr); + ASSERT_EQ(loop_header_[d]->GetLoopInformation(), + loop_body_[d]->GetLoopInformation()); + } + ASSERT_EQ(exit_->GetLoopInformation(), nullptr); +} + +TEST_F(InductionVarAnalysisTest, FindBasicInductionVar) { + // Setup: + // for (int i = 0; i < 100; i++) { + // a[i] = 0; + // } + BuildLoopNest(1); + HInstruction* store = InsertArrayStore(basic_[0], 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "((2:Constant) * i + (1:Constant))", + iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str()); + EXPECT_STREQ( + "((2:Constant) * i + ((1:Constant) + (2:Constant)))", + iva_->InductionToString(GetLoopInfo(0), increment_[0]).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarAdd) { + // Setup: + // for (int i = 0; i < 100; i++) { + // k = 100 + i; + // a[k] = 0; + // } + BuildLoopNest(1); + HInstruction *add = InsertInstruction( + new (&allocator_) HAdd( + Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); + InsertLocalStore(induc_, add, 0); + HInstruction* store = InsertArrayStore(induc_, 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "((2:Constant) * i + ((3:Constant) + (1:Constant)))", + iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarSub) { + // Setup: + // for (int i = 0; i < 100; i++) { + // k = 100 - i; + // a[k] = 0; + // } + BuildLoopNest(1); + HInstruction *sub = InsertInstruction( + new (&allocator_) HSub( + Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); + InsertLocalStore(induc_, sub, 0); + HInstruction* store = InsertArrayStore(induc_, 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant)))", + iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarMul) { + // Setup: + // for (int i = 0; i < 100; i++) { + // k = 100 * i; + // a[k] = 0; + // } + BuildLoopNest(1); + HInstruction *mul = InsertInstruction( + new (&allocator_) HMul( + Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); + InsertLocalStore(induc_, mul, 0); + HInstruction* store = InsertArrayStore(induc_, 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "(((3:Constant) * (2:Constant)) * i + ((3:Constant) * (1:Constant)))", + iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarNeg) { + // Setup: + // for (int i = 0; i < 100; i++) { + // k = - i; + // a[k] = 0; + // } + BuildLoopNest(1); + HInstruction *neg = InsertInstruction( + new (&allocator_) HNeg( + Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0)), 0); + InsertLocalStore(induc_, neg, 0); + HInstruction* store = InsertArrayStore(induc_, 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "(( - (2:Constant)) * i + ( - (1:Constant)))", + iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindChainInduction) { + // Setup: + // k = 0; + // for (int i = 0; i < 100; i++) { + // k = k + 100; + // a[k] = 0; + // k = k - 1; + // a[k] = 0; + // } + BuildLoopNest(1); + HInstruction *add = InsertInstruction( + new (&allocator_) HAdd( + Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0); + InsertLocalStore(induc_, add, 0); + HInstruction* store1 = InsertArrayStore(induc_, 0); + HInstruction *sub = InsertInstruction( + new (&allocator_) HSub( + Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0); + InsertLocalStore(induc_, sub, 0); + HInstruction* store2 = InsertArrayStore(induc_, 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "(((3:Constant) - (2:Constant)) * i + ((1:Constant) + (3:Constant)))", + iva_->InductionToString(GetLoopInfo(0), store1->InputAt(1)).c_str()); + EXPECT_STREQ( + "(((3:Constant) - (2:Constant)) * i + " + "(((1:Constant) + (3:Constant)) - (2:Constant)))", + iva_->InductionToString(GetLoopInfo(0), store2->InputAt(1)).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) { + // Setup: + // k = 0; + // for (int i = 0; i < 100; i++) { + // if () k = k + 1; + // else k = k + 1; + // a[k] = 0; + // } + BuildLoopNest(1); + HBasicBlock* ifTrue; + HBasicBlock* ifFalse; + BuildIf(0, &ifTrue, &ifFalse); + // True-branch. + HInstruction* load1 = new (&allocator_) + HLoadLocal(induc_, Primitive::kPrimInt); + ifTrue->AddInstruction(load1); + HInstruction* inc1 = new (&allocator_) + HAdd(Primitive::kPrimInt, load1, constant1_); + ifTrue->AddInstruction(inc1); + ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1)); + // False-branch. + HInstruction* load2 = new (&allocator_) + HLoadLocal(induc_, Primitive::kPrimInt); + ifFalse->AddInstruction(load2); + HInstruction* inc2 = new (&allocator_) + HAdd(Primitive::kPrimInt, load2, constant1_); + ifFalse->AddInstruction(inc2); + ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2)); + // Merge over a phi. + HInstruction* store = InsertArrayStore(induc_, 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "((2:Constant) * i + ((1:Constant) + (2:Constant)))", + iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) { + // Setup: + // for (int i = 0; i < 100; i++) { + // if () k = i + 1; + // else k = i + 1; + // a[k] = 0; + // } + BuildLoopNest(1); + HBasicBlock* ifTrue; + HBasicBlock* ifFalse; + BuildIf(0, &ifTrue, &ifFalse); + // True-branch. + HInstruction* load1 = new (&allocator_) + HLoadLocal(basic_[0], Primitive::kPrimInt); + ifTrue->AddInstruction(load1); + HInstruction* inc1 = new (&allocator_) + HAdd(Primitive::kPrimInt, load1, constant1_); + ifTrue->AddInstruction(inc1); + ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1)); + // False-branch. + HInstruction* load2 = new (&allocator_) + HLoadLocal(basic_[0], Primitive::kPrimInt); + ifFalse->AddInstruction(load2); + HInstruction* inc2 = new (&allocator_) + HAdd(Primitive::kPrimInt, load2, constant1_); + ifFalse->AddInstruction(inc2); + ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2)); + // Merge over a phi. + HInstruction* store = InsertArrayStore(induc_, 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "((2:Constant) * i + ((1:Constant) + (2:Constant)))", + iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) { + // Setup: + // k = 0; + // for (int i = 0; i < 100; i++) { + // a[k] = 0; + // k = 100 - i; + // } + BuildLoopNest(1); + HInstruction* store = InsertArrayStore(induc_, 0); + HInstruction *sub = InsertInstruction( + new (&allocator_) HSub( + Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); + InsertLocalStore(induc_, sub, 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "wrap((1:Constant), " + "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant))))", + iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) { + // Setup: + // k = 0; + // t = 100; + // for (int i = 0; i < 100; i++) { + // a[k] = 0; + // k = t; + // t = 100 - i; + // } + BuildLoopNest(1); + HInstruction* store = InsertArrayStore(induc_, 0); + InsertLocalStore(induc_, InsertLocalLoad(tmp_, 0), 0); + HInstruction *sub = InsertInstruction( + new (&allocator_) HSub( + Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); + InsertLocalStore(tmp_, sub, 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ( + "wrap((1:Constant), wrap((3:Constant), " + "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant)))))", + iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) { + // Setup: + // k = 0; + // for (int i_0 = 0; i_0 < 100; i_0++) { + // .. + // for (int i_9 = 0; i_9 < 100; i_9++) { + // k = 1 + k; + // a[k] = 0; + // } + // .. + // } + BuildLoopNest(10); + HInstruction *inc = InsertInstruction( + new (&allocator_) HAdd( + Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 9)), 9); + InsertLocalStore(induc_, inc, 9); + HInstruction* store = InsertArrayStore(induc_, 9); + PerformInductionVarAnalysis(); + + // Match exact number of constants, but be less strict on phi number, + // since that depends on the SSA building phase. + std::regex r("\\(\\(2:Constant\\) \\* i \\+ " + "\\(\\(2:Constant\\) \\+ \\(\\d+:Phi\\)\\)\\)"); + + for (int d = 0; d < 10; d++) { + if (d == 9) { + EXPECT_TRUE(std::regex_match( + iva_->InductionToString(GetLoopInfo(d), store->InputAt(1)), r)); + } else { + EXPECT_STREQ( + "", + iva_->InductionToString(GetLoopInfo(d), store->InputAt(1)).c_str()); + } + EXPECT_STREQ( + "((2:Constant) * i + ((1:Constant) + (2:Constant)))", + iva_->InductionToString(GetLoopInfo(d), increment_[d]).c_str()); + } +} + +} // namespace art |
