Home | History | Annotate | Download | only in optimizing
      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 "base/arena_allocator.h"
     18 #include "builder.h"
     19 #include "licm.h"
     20 #include "nodes.h"
     21 #include "optimizing_unit_test.h"
     22 #include "side_effects_analysis.h"
     23 
     24 namespace art {
     25 
     26 /**
     27  * Fixture class for the LICM tests.
     28  */
     29 class LICMTest : public CommonCompilerTest {
     30  public:
     31   LICMTest() : pool_(), allocator_(&pool_) {
     32     graph_ = CreateGraph(&allocator_);
     33   }
     34 
     35   ~LICMTest() { }
     36 
     37   // Builds a singly-nested loop structure in CFG. Tests can further populate
     38   // the basic blocks with instructions to set up interesting scenarios.
     39   void BuildLoop() {
     40     entry_ = new (&allocator_) HBasicBlock(graph_);
     41     loop_preheader_ = new (&allocator_) HBasicBlock(graph_);
     42     loop_header_ = new (&allocator_) HBasicBlock(graph_);
     43     loop_body_ = new (&allocator_) HBasicBlock(graph_);
     44     return_ = new (&allocator_) HBasicBlock(graph_);
     45     exit_ = new (&allocator_) HBasicBlock(graph_);
     46 
     47     graph_->AddBlock(entry_);
     48     graph_->AddBlock(loop_preheader_);
     49     graph_->AddBlock(loop_header_);
     50     graph_->AddBlock(loop_body_);
     51     graph_->AddBlock(return_);
     52     graph_->AddBlock(exit_);
     53 
     54     graph_->SetEntryBlock(entry_);
     55     graph_->SetExitBlock(exit_);
     56 
     57     // Set up loop flow in CFG.
     58     entry_->AddSuccessor(loop_preheader_);
     59     loop_preheader_->AddSuccessor(loop_header_);
     60     loop_header_->AddSuccessor(loop_body_);
     61     loop_header_->AddSuccessor(return_);
     62     loop_body_->AddSuccessor(loop_header_);
     63     return_->AddSuccessor(exit_);
     64 
     65     // Provide boiler-plate instructions.
     66     parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot);
     67     entry_->AddInstruction(parameter_);
     68     int_constant_ = graph_->GetIntConstant(42);
     69     float_constant_ = graph_->GetFloatConstant(42.0f);
     70     loop_preheader_->AddInstruction(new (&allocator_) HGoto());
     71     loop_header_->AddInstruction(new (&allocator_) HIf(parameter_));
     72     loop_body_->AddInstruction(new (&allocator_) HGoto());
     73     return_->AddInstruction(new (&allocator_) HReturnVoid());
     74     exit_->AddInstruction(new (&allocator_) HExit());
     75   }
     76 
     77   // Performs LICM optimizations (after proper set up).
     78   void PerformLICM() {
     79     graph_->BuildDominatorTree();
     80     SideEffectsAnalysis side_effects(graph_);
     81     side_effects.Run();
     82     LICM(graph_, side_effects, nullptr).Run();
     83   }
     84 
     85   // General building fields.
     86   ArenaPool pool_;
     87   ArenaAllocator allocator_;
     88   HGraph* graph_;
     89 
     90   // Specific basic blocks.
     91   HBasicBlock* entry_;
     92   HBasicBlock* loop_preheader_;
     93   HBasicBlock* loop_header_;
     94   HBasicBlock* loop_body_;
     95   HBasicBlock* return_;
     96   HBasicBlock* exit_;
     97 
     98   HInstruction* parameter_;  // "this"
     99   HInstruction* int_constant_;
    100   HInstruction* float_constant_;
    101 };
    102 
    103 //
    104 // The actual LICM tests.
    105 //
    106 
    107 TEST_F(LICMTest, FieldHoisting) {
    108   BuildLoop();
    109 
    110   // Populate the loop with instructions: set/get field with different types.
    111   ScopedNullHandle<mirror::DexCache> dex_cache;
    112   HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_,
    113                                                                 Primitive::kPrimLong,
    114                                                                 MemberOffset(10),
    115                                                                 false,
    116                                                                 kUnknownFieldIndex,
    117                                                                 kUnknownClassDefIndex,
    118                                                                 graph_->GetDexFile(),
    119                                                                 dex_cache,
    120                                                                 0);
    121   loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
    122   HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
    123       parameter_, int_constant_, Primitive::kPrimInt, MemberOffset(20),
    124       false, kUnknownFieldIndex, kUnknownClassDefIndex, graph_->GetDexFile(), dex_cache, 0);
    125   loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
    126 
    127   EXPECT_EQ(get_field->GetBlock(), loop_body_);
    128   EXPECT_EQ(set_field->GetBlock(), loop_body_);
    129   PerformLICM();
    130   EXPECT_EQ(get_field->GetBlock(), loop_preheader_);
    131   EXPECT_EQ(set_field->GetBlock(), loop_body_);
    132 }
    133 
    134 TEST_F(LICMTest, NoFieldHoisting) {
    135   BuildLoop();
    136 
    137   // Populate the loop with instructions: set/get field with same types.
    138   ScopedNullHandle<mirror::DexCache> dex_cache;
    139   HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_,
    140                                                                 Primitive::kPrimLong,
    141                                                                 MemberOffset(10),
    142                                                                 false,
    143                                                                 kUnknownFieldIndex,
    144                                                                 kUnknownClassDefIndex,
    145                                                                 graph_->GetDexFile(),
    146                                                                 dex_cache,
    147                                                                 0);
    148   loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
    149   HInstruction* set_field = new (&allocator_) HInstanceFieldSet(parameter_,
    150                                                                 get_field,
    151                                                                 Primitive::kPrimLong,
    152                                                                 MemberOffset(10),
    153                                                                 false,
    154                                                                 kUnknownFieldIndex,
    155                                                                 kUnknownClassDefIndex,
    156                                                                 graph_->GetDexFile(),
    157                                                                 dex_cache,
    158                                                                 0);
    159   loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
    160 
    161   EXPECT_EQ(get_field->GetBlock(), loop_body_);
    162   EXPECT_EQ(set_field->GetBlock(), loop_body_);
    163   PerformLICM();
    164   EXPECT_EQ(get_field->GetBlock(), loop_body_);
    165   EXPECT_EQ(set_field->GetBlock(), loop_body_);
    166 }
    167 
    168 TEST_F(LICMTest, ArrayHoisting) {
    169   BuildLoop();
    170 
    171   // Populate the loop with instructions: set/get array with different types.
    172   // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
    173   // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
    174   HInstruction* get_array = new (&allocator_) HArrayGet(
    175       parameter_, int_constant_, Primitive::kPrimByte, 0);
    176   loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
    177   HInstruction* set_array = new (&allocator_) HArraySet(
    178       parameter_, int_constant_, float_constant_, Primitive::kPrimShort, 0);
    179   loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
    180 
    181   EXPECT_EQ(get_array->GetBlock(), loop_body_);
    182   EXPECT_EQ(set_array->GetBlock(), loop_body_);
    183   PerformLICM();
    184   EXPECT_EQ(get_array->GetBlock(), loop_preheader_);
    185   EXPECT_EQ(set_array->GetBlock(), loop_body_);
    186 }
    187 
    188 TEST_F(LICMTest, NoArrayHoisting) {
    189   BuildLoop();
    190 
    191   // Populate the loop with instructions: set/get array with same types.
    192   // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
    193   // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
    194   HInstruction* get_array = new (&allocator_) HArrayGet(
    195       parameter_, int_constant_, Primitive::kPrimByte, 0);
    196   loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
    197   HInstruction* set_array = new (&allocator_) HArraySet(
    198       parameter_, get_array, float_constant_, Primitive::kPrimByte, 0);
    199   loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
    200 
    201   EXPECT_EQ(get_array->GetBlock(), loop_body_);
    202   EXPECT_EQ(set_array->GetBlock(), loop_body_);
    203   PerformLICM();
    204   EXPECT_EQ(get_array->GetBlock(), loop_body_);
    205   EXPECT_EQ(set_array->GetBlock(), loop_body_);
    206 }
    207 
    208 }  // namespace art
    209