| /* |
| * Copyright (C) 2016 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 "loop_optimization.h" |
| #include "optimizing_unit_test.h" |
| |
| namespace art { |
| |
| /** |
| * Fixture class for the loop optimization tests. These unit tests focus |
| * constructing the loop hierarchy. Actual optimizations are tested |
| * through the checker tests. |
| */ |
| class LoopOptimizationTest : public CommonCompilerTest { |
| public: |
| LoopOptimizationTest() |
| : pool_(), |
| allocator_(&pool_), |
| graph_(CreateGraph(&allocator_)), |
| iva_(new (&allocator_) HInductionVarAnalysis(graph_)), |
| loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_)) { |
| BuildGraph(); |
| } |
| |
| ~LoopOptimizationTest() { } |
| |
| /** Constructs bare minimum graph. */ |
| void BuildGraph() { |
| graph_->SetNumberOfVRegs(1); |
| entry_block_ = new (&allocator_) HBasicBlock(graph_); |
| return_block_ = new (&allocator_) HBasicBlock(graph_); |
| exit_block_ = new (&allocator_) HBasicBlock(graph_); |
| graph_->AddBlock(entry_block_); |
| graph_->AddBlock(return_block_); |
| graph_->AddBlock(exit_block_); |
| graph_->SetEntryBlock(entry_block_); |
| graph_->SetExitBlock(exit_block_); |
| parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), |
| dex::TypeIndex(0), |
| 0, |
| Primitive::kPrimInt); |
| entry_block_->AddInstruction(parameter_); |
| return_block_->AddInstruction(new (&allocator_) HReturnVoid()); |
| exit_block_->AddInstruction(new (&allocator_) HExit()); |
| entry_block_->AddSuccessor(return_block_); |
| return_block_->AddSuccessor(exit_block_); |
| } |
| |
| /** Adds a loop nest at given position before successor. */ |
| HBasicBlock* AddLoop(HBasicBlock* position, HBasicBlock* successor) { |
| HBasicBlock* header = new (&allocator_) HBasicBlock(graph_); |
| HBasicBlock* body = new (&allocator_) HBasicBlock(graph_); |
| graph_->AddBlock(header); |
| graph_->AddBlock(body); |
| // Control flow. |
| position->ReplaceSuccessor(successor, header); |
| header->AddSuccessor(body); |
| header->AddSuccessor(successor); |
| header->AddInstruction(new (&allocator_) HIf(parameter_)); |
| body->AddSuccessor(header); |
| body->AddInstruction(new (&allocator_) HGoto()); |
| return header; |
| } |
| |
| /** Performs analysis. */ |
| void PerformAnalysis() { |
| graph_->BuildDominatorTree(); |
| iva_->Run(); |
| // Do not release the loop hierarchy. |
| loop_opt_->loop_allocator_ = &allocator_; |
| loop_opt_->LocalRun(); |
| } |
| |
| /** Constructs string representation of computed loop hierarchy. */ |
| std::string LoopStructure() { |
| return LoopStructureRecurse(loop_opt_->top_loop_); |
| } |
| |
| // Helper method |
| std::string LoopStructureRecurse(HLoopOptimization::LoopNode* node) { |
| std::string s; |
| for ( ; node != nullptr; node = node->next) { |
| s.append("["); |
| s.append(LoopStructureRecurse(node->inner)); |
| s.append("]"); |
| } |
| return s; |
| } |
| |
| // General building fields. |
| ArenaPool pool_; |
| ArenaAllocator allocator_; |
| HGraph* graph_; |
| HInductionVarAnalysis* iva_; |
| HLoopOptimization* loop_opt_; |
| |
| HBasicBlock* entry_block_; |
| HBasicBlock* return_block_; |
| HBasicBlock* exit_block_; |
| |
| HInstruction* parameter_; |
| }; |
| |
| // |
| // The actual tests. |
| // |
| |
| TEST_F(LoopOptimizationTest, NoLoops) { |
| PerformAnalysis(); |
| EXPECT_EQ("", LoopStructure()); |
| } |
| |
| TEST_F(LoopOptimizationTest, SingleLoop) { |
| AddLoop(entry_block_, return_block_); |
| PerformAnalysis(); |
| EXPECT_EQ("[]", LoopStructure()); |
| } |
| |
| TEST_F(LoopOptimizationTest, LoopNest10) { |
| HBasicBlock* b = entry_block_; |
| HBasicBlock* s = return_block_; |
| for (int i = 0; i < 10; i++) { |
| s = AddLoop(b, s); |
| b = s->GetSuccessors()[0]; |
| } |
| PerformAnalysis(); |
| EXPECT_EQ("[[[[[[[[[[]]]]]]]]]]", LoopStructure()); |
| } |
| |
| TEST_F(LoopOptimizationTest, LoopSequence10) { |
| HBasicBlock* b = entry_block_; |
| HBasicBlock* s = return_block_; |
| for (int i = 0; i < 10; i++) { |
| b = AddLoop(b, s); |
| s = b->GetSuccessors()[1]; |
| } |
| PerformAnalysis(); |
| EXPECT_EQ("[][][][][][][][][][]", LoopStructure()); |
| } |
| |
| TEST_F(LoopOptimizationTest, LoopSequenceOfNests) { |
| HBasicBlock* b = entry_block_; |
| HBasicBlock* s = return_block_; |
| for (int i = 0; i < 10; i++) { |
| b = AddLoop(b, s); |
| s = b->GetSuccessors()[1]; |
| HBasicBlock* bi = b->GetSuccessors()[0]; |
| HBasicBlock* si = b; |
| for (int j = 0; j < i; j++) { |
| si = AddLoop(bi, si); |
| bi = si->GetSuccessors()[0]; |
| } |
| } |
| PerformAnalysis(); |
| EXPECT_EQ("[]" |
| "[[]]" |
| "[[[]]]" |
| "[[[[]]]]" |
| "[[[[[]]]]]" |
| "[[[[[[]]]]]]" |
| "[[[[[[[]]]]]]]" |
| "[[[[[[[[]]]]]]]]" |
| "[[[[[[[[[]]]]]]]]]" |
| "[[[[[[[[[[]]]]]]]]]]", |
| LoopStructure()); |
| } |
| |
| TEST_F(LoopOptimizationTest, LoopNestWithSequence) { |
| HBasicBlock* b = entry_block_; |
| HBasicBlock* s = return_block_; |
| for (int i = 0; i < 10; i++) { |
| s = AddLoop(b, s); |
| b = s->GetSuccessors()[0]; |
| } |
| b = s; |
| s = b->GetSuccessors()[1]; |
| for (int i = 0; i < 9; i++) { |
| b = AddLoop(b, s); |
| s = b->GetSuccessors()[1]; |
| } |
| PerformAnalysis(); |
| EXPECT_EQ("[[[[[[[[[[][][][][][][][][][]]]]]]]]]]", LoopStructure()); |
| } |
| |
| } // namespace art |