Home | History | Annotate | Download | only in optimizing
      1 /*
      2  * Copyright (C) 2016 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 "loop_optimization.h"
     18 #include "optimizing_unit_test.h"
     19 
     20 namespace art {
     21 
     22 /**
     23  * Fixture class for the loop optimization tests. These unit tests focus
     24  * constructing the loop hierarchy. Actual optimizations are tested
     25  * through the checker tests.
     26  */
     27 class LoopOptimizationTest : public OptimizingUnitTest {
     28  public:
     29   LoopOptimizationTest()
     30       : graph_(CreateGraph()),
     31         iva_(new (GetAllocator()) HInductionVarAnalysis(graph_)),
     32         loop_opt_(new (GetAllocator()) HLoopOptimization(graph_, nullptr, iva_, nullptr)) {
     33     BuildGraph();
     34   }
     35 
     36   ~LoopOptimizationTest() { }
     37 
     38   /** Constructs bare minimum graph. */
     39   void BuildGraph() {
     40     graph_->SetNumberOfVRegs(1);
     41     entry_block_ = new (GetAllocator()) HBasicBlock(graph_);
     42     return_block_ = new (GetAllocator()) HBasicBlock(graph_);
     43     exit_block_ = new (GetAllocator()) HBasicBlock(graph_);
     44     graph_->AddBlock(entry_block_);
     45     graph_->AddBlock(return_block_);
     46     graph_->AddBlock(exit_block_);
     47     graph_->SetEntryBlock(entry_block_);
     48     graph_->SetExitBlock(exit_block_);
     49     parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
     50                                                       dex::TypeIndex(0),
     51                                                       0,
     52                                                       DataType::Type::kInt32);
     53     entry_block_->AddInstruction(parameter_);
     54     return_block_->AddInstruction(new (GetAllocator()) HReturnVoid());
     55     exit_block_->AddInstruction(new (GetAllocator()) HExit());
     56     entry_block_->AddSuccessor(return_block_);
     57     return_block_->AddSuccessor(exit_block_);
     58   }
     59 
     60   /** Adds a loop nest at given position before successor. */
     61   HBasicBlock* AddLoop(HBasicBlock* position, HBasicBlock* successor) {
     62     HBasicBlock* header = new (GetAllocator()) HBasicBlock(graph_);
     63     HBasicBlock* body = new (GetAllocator()) HBasicBlock(graph_);
     64     graph_->AddBlock(header);
     65     graph_->AddBlock(body);
     66     // Control flow.
     67     position->ReplaceSuccessor(successor, header);
     68     header->AddSuccessor(body);
     69     header->AddSuccessor(successor);
     70     header->AddInstruction(new (GetAllocator()) HIf(parameter_));
     71     body->AddSuccessor(header);
     72     body->AddInstruction(new (GetAllocator()) HGoto());
     73     return header;
     74   }
     75 
     76   /** Performs analysis. */
     77   void PerformAnalysis() {
     78     graph_->BuildDominatorTree();
     79     iva_->Run();
     80     // Do not release the loop hierarchy.
     81     ScopedArenaAllocator loop_allocator(GetArenaStack());
     82     loop_opt_->loop_allocator_ = &loop_allocator;
     83     loop_opt_->LocalRun();
     84   }
     85 
     86   /** Constructs string representation of computed loop hierarchy. */
     87   std::string LoopStructure() {
     88     return LoopStructureRecurse(loop_opt_->top_loop_);
     89   }
     90 
     91   // Helper method
     92   std::string LoopStructureRecurse(HLoopOptimization::LoopNode* node) {
     93     std::string s;
     94     for ( ; node != nullptr; node = node->next) {
     95       s.append("[");
     96       s.append(LoopStructureRecurse(node->inner));
     97       s.append("]");
     98     }
     99     return s;
    100   }
    101 
    102   // General building fields.
    103   HGraph* graph_;
    104   HInductionVarAnalysis* iva_;
    105   HLoopOptimization* loop_opt_;
    106 
    107   HBasicBlock* entry_block_;
    108   HBasicBlock* return_block_;
    109   HBasicBlock* exit_block_;
    110 
    111   HInstruction* parameter_;
    112 };
    113 
    114 //
    115 // The actual tests.
    116 //
    117 
    118 TEST_F(LoopOptimizationTest, NoLoops) {
    119   PerformAnalysis();
    120   EXPECT_EQ("", LoopStructure());
    121 }
    122 
    123 TEST_F(LoopOptimizationTest, SingleLoop) {
    124   AddLoop(entry_block_, return_block_);
    125   PerformAnalysis();
    126   EXPECT_EQ("[]", LoopStructure());
    127 }
    128 
    129 TEST_F(LoopOptimizationTest, LoopNest10) {
    130   HBasicBlock* b = entry_block_;
    131   HBasicBlock* s = return_block_;
    132   for (int i = 0; i < 10; i++) {
    133     s = AddLoop(b, s);
    134     b = s->GetSuccessors()[0];
    135   }
    136   PerformAnalysis();
    137   EXPECT_EQ("[[[[[[[[[[]]]]]]]]]]", LoopStructure());
    138 }
    139 
    140 TEST_F(LoopOptimizationTest, LoopSequence10) {
    141   HBasicBlock* b = entry_block_;
    142   HBasicBlock* s = return_block_;
    143   for (int i = 0; i < 10; i++) {
    144     b = AddLoop(b, s);
    145     s = b->GetSuccessors()[1];
    146   }
    147   PerformAnalysis();
    148   EXPECT_EQ("[][][][][][][][][][]", LoopStructure());
    149 }
    150 
    151 TEST_F(LoopOptimizationTest, LoopSequenceOfNests) {
    152   HBasicBlock* b = entry_block_;
    153   HBasicBlock* s = return_block_;
    154   for (int i = 0; i < 10; i++) {
    155     b = AddLoop(b, s);
    156     s = b->GetSuccessors()[1];
    157     HBasicBlock* bi = b->GetSuccessors()[0];
    158     HBasicBlock* si = b;
    159     for (int j = 0; j < i; j++) {
    160       si = AddLoop(bi, si);
    161       bi = si->GetSuccessors()[0];
    162     }
    163   }
    164   PerformAnalysis();
    165   EXPECT_EQ("[]"
    166             "[[]]"
    167             "[[[]]]"
    168             "[[[[]]]]"
    169             "[[[[[]]]]]"
    170             "[[[[[[]]]]]]"
    171             "[[[[[[[]]]]]]]"
    172             "[[[[[[[[]]]]]]]]"
    173             "[[[[[[[[[]]]]]]]]]"
    174             "[[[[[[[[[[]]]]]]]]]]",
    175             LoopStructure());
    176 }
    177 
    178 TEST_F(LoopOptimizationTest, LoopNestWithSequence) {
    179   HBasicBlock* b = entry_block_;
    180   HBasicBlock* s = return_block_;
    181   for (int i = 0; i < 10; i++) {
    182     s = AddLoop(b, s);
    183     b = s->GetSuccessors()[0];
    184   }
    185   b = s;
    186   s = b->GetSuccessors()[1];
    187   for (int i = 0; i < 9; i++) {
    188     b = AddLoop(b, s);
    189     s = b->GetSuccessors()[1];
    190   }
    191   PerformAnalysis();
    192   EXPECT_EQ("[[[[[[[[[[][][][][][][][][][]]]]]]]]]]", LoopStructure());
    193 }
    194 
    195 // Check that SimplifyLoop() doesn't invalidate data flow when ordering loop headers'
    196 // predecessors.
    197 //
    198 // This is a test for nodes.cc functionality - HGraph::SimplifyLoop.
    199 TEST_F(LoopOptimizationTest, SimplifyLoopReoderPredecessors) {
    200   // Can't use AddLoop as we want special order for blocks predecessors.
    201   HBasicBlock* header = new (GetAllocator()) HBasicBlock(graph_);
    202   HBasicBlock* body = new (GetAllocator()) HBasicBlock(graph_);
    203   graph_->AddBlock(header);
    204   graph_->AddBlock(body);
    205 
    206   // Control flow: make a loop back edge first in the list of predecessors.
    207   entry_block_->RemoveSuccessor(return_block_);
    208   body->AddSuccessor(header);
    209   entry_block_->AddSuccessor(header);
    210   header->AddSuccessor(body);
    211   header->AddSuccessor(return_block_);
    212   DCHECK(header->GetSuccessors()[1] == return_block_);
    213 
    214   // Data flow.
    215   header->AddInstruction(new (GetAllocator()) HIf(parameter_));
    216   body->AddInstruction(new (GetAllocator()) HGoto());
    217 
    218   HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
    219   HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, parameter_);
    220   header->AddPhi(phi);
    221   body->AddInstruction(add);
    222 
    223   phi->AddInput(add);
    224   phi->AddInput(parameter_);
    225 
    226   graph_->ClearLoopInformation();
    227   graph_->ClearDominanceInformation();
    228   graph_->BuildDominatorTree();
    229 
    230   // Check that after optimizations in BuildDominatorTree()/SimplifyCFG() phi inputs
    231   // are still mapped correctly to the block predecessors.
    232   for (size_t i = 0, e = phi->InputCount(); i < e; i++) {
    233     HInstruction* input = phi->InputAt(i);
    234     ASSERT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i]));
    235   }
    236 }
    237 
    238 // Test that SimplifyLoop() processes the multiple-preheaders loops correctly.
    239 //
    240 // This is a test for nodes.cc functionality - HGraph::SimplifyLoop.
    241 TEST_F(LoopOptimizationTest, SimplifyLoopSinglePreheader) {
    242   HBasicBlock* header = AddLoop(entry_block_, return_block_);
    243 
    244   header->InsertInstructionBefore(
    245       new (GetAllocator()) HSuspendCheck(), header->GetLastInstruction());
    246 
    247   // Insert an if construct before the loop so it will have two preheaders.
    248   HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_);
    249   HBasicBlock* preheader0 = new (GetAllocator()) HBasicBlock(graph_);
    250   HBasicBlock* preheader1 = new (GetAllocator()) HBasicBlock(graph_);
    251 
    252   graph_->AddBlock(if_block);
    253   graph_->AddBlock(preheader0);
    254   graph_->AddBlock(preheader1);
    255 
    256   // Fix successors/predecessors.
    257   entry_block_->ReplaceSuccessor(header, if_block);
    258   if_block->AddSuccessor(preheader0);
    259   if_block->AddSuccessor(preheader1);
    260   preheader0->AddSuccessor(header);
    261   preheader1->AddSuccessor(header);
    262 
    263   if_block->AddInstruction(new (GetAllocator()) HIf(parameter_));
    264   preheader0->AddInstruction(new (GetAllocator()) HGoto());
    265   preheader1->AddInstruction(new (GetAllocator()) HGoto());
    266 
    267   HBasicBlock* body = header->GetSuccessors()[0];
    268   DCHECK(body != return_block_);
    269 
    270   // Add some data flow.
    271   HIntConstant* const_0 = graph_->GetIntConstant(0);
    272   HIntConstant* const_1 = graph_->GetIntConstant(1);
    273   HIntConstant* const_2 = graph_->GetIntConstant(2);
    274 
    275   HAdd* preheader0_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_0);
    276   preheader0->AddInstruction(preheader0_add);
    277   HAdd* preheader1_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_1);
    278   preheader1->AddInstruction(preheader1_add);
    279 
    280   HPhi* header_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
    281   header->AddPhi(header_phi);
    282 
    283   HAdd* body_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_2);
    284   body->AddInstruction(body_add);
    285 
    286   DCHECK(header->GetPredecessors()[0] == body);
    287   DCHECK(header->GetPredecessors()[1] == preheader0);
    288   DCHECK(header->GetPredecessors()[2] == preheader1);
    289 
    290   header_phi->AddInput(body_add);
    291   header_phi->AddInput(preheader0_add);
    292   header_phi->AddInput(preheader1_add);
    293 
    294   graph_->ClearLoopInformation();
    295   graph_->ClearDominanceInformation();
    296   graph_->BuildDominatorTree();
    297 
    298   EXPECT_EQ(header->GetPredecessors().size(), 2u);
    299   EXPECT_EQ(header->GetPredecessors()[1], body);
    300 
    301   HBasicBlock* new_preheader = header->GetLoopInformation()->GetPreHeader();
    302   EXPECT_EQ(preheader0->GetSingleSuccessor(), new_preheader);
    303   EXPECT_EQ(preheader1->GetSingleSuccessor(), new_preheader);
    304 
    305   EXPECT_EQ(new_preheader->GetPhis().CountSize(), 1u);
    306   HPhi* new_preheader_phi = new_preheader->GetFirstPhi()->AsPhi();
    307   EXPECT_EQ(new_preheader_phi->InputCount(), 2u);
    308   EXPECT_EQ(new_preheader_phi->InputAt(0), preheader0_add);
    309   EXPECT_EQ(new_preheader_phi->InputAt(1), preheader1_add);
    310 
    311   EXPECT_EQ(header_phi->InputCount(), 2u);
    312   EXPECT_EQ(header_phi->InputAt(0), new_preheader_phi);
    313   EXPECT_EQ(header_phi->InputAt(1), body_add);
    314 }
    315 
    316 }  // namespace art
    317