Home | History | Annotate | Download | only in optimizing
      1 /*
      2  * Copyright (C) 2014 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 "gvn.h"
     20 #include "nodes.h"
     21 #include "optimizing_unit_test.h"
     22 #include "side_effects_analysis.h"
     23 
     24 #include "gtest/gtest.h"
     25 
     26 namespace art {
     27 
     28 TEST(GVNTest, LocalFieldElimination) {
     29   ArenaPool pool;
     30   ArenaAllocator allocator(&pool);
     31 
     32   HGraph* graph = CreateGraph(&allocator);
     33   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
     34   graph->AddBlock(entry);
     35   graph->SetEntryBlock(entry);
     36   HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
     37   entry->AddInstruction(parameter);
     38 
     39   HBasicBlock* block = new (&allocator) HBasicBlock(graph);
     40   graph->AddBlock(block);
     41   entry->AddSuccessor(block);
     42 
     43   block->AddInstruction(
     44       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
     45           MemberOffset(42), false));
     46   block->AddInstruction(
     47       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
     48           MemberOffset(42), false));
     49   HInstruction* to_remove = block->GetLastInstruction();
     50   block->AddInstruction(
     51       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
     52           MemberOffset(43), false));
     53   HInstruction* different_offset = block->GetLastInstruction();
     54   // Kill the value.
     55   block->AddInstruction(new (&allocator) HInstanceFieldSet(
     56       parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
     57   block->AddInstruction(
     58       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
     59           MemberOffset(42), false));
     60   HInstruction* use_after_kill = block->GetLastInstruction();
     61   block->AddInstruction(new (&allocator) HExit());
     62 
     63   ASSERT_EQ(to_remove->GetBlock(), block);
     64   ASSERT_EQ(different_offset->GetBlock(), block);
     65   ASSERT_EQ(use_after_kill->GetBlock(), block);
     66 
     67   graph->TryBuildingSsa();
     68   SideEffectsAnalysis side_effects(graph);
     69   side_effects.Run();
     70   GVNOptimization(graph, side_effects).Run();
     71 
     72   ASSERT_TRUE(to_remove->GetBlock() == nullptr);
     73   ASSERT_EQ(different_offset->GetBlock(), block);
     74   ASSERT_EQ(use_after_kill->GetBlock(), block);
     75 }
     76 
     77 TEST(GVNTest, GlobalFieldElimination) {
     78   ArenaPool pool;
     79   ArenaAllocator allocator(&pool);
     80 
     81   HGraph* graph = CreateGraph(&allocator);
     82   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
     83   graph->AddBlock(entry);
     84   graph->SetEntryBlock(entry);
     85   HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
     86   entry->AddInstruction(parameter);
     87 
     88   HBasicBlock* block = new (&allocator) HBasicBlock(graph);
     89   graph->AddBlock(block);
     90   entry->AddSuccessor(block);
     91   block->AddInstruction(
     92       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
     93           MemberOffset(42), false));
     94 
     95   block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
     96   HBasicBlock* then = new (&allocator) HBasicBlock(graph);
     97   HBasicBlock* else_ = new (&allocator) HBasicBlock(graph);
     98   HBasicBlock* join = new (&allocator) HBasicBlock(graph);
     99   graph->AddBlock(then);
    100   graph->AddBlock(else_);
    101   graph->AddBlock(join);
    102 
    103   block->AddSuccessor(then);
    104   block->AddSuccessor(else_);
    105   then->AddSuccessor(join);
    106   else_->AddSuccessor(join);
    107 
    108   then->AddInstruction(
    109       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
    110           MemberOffset(42), false));
    111   then->AddInstruction(new (&allocator) HGoto());
    112   else_->AddInstruction(
    113       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
    114           MemberOffset(42), false));
    115   else_->AddInstruction(new (&allocator) HGoto());
    116   join->AddInstruction(
    117       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
    118           MemberOffset(42), false));
    119   join->AddInstruction(new (&allocator) HExit());
    120 
    121   graph->TryBuildingSsa();
    122   SideEffectsAnalysis side_effects(graph);
    123   side_effects.Run();
    124   GVNOptimization(graph, side_effects).Run();
    125 
    126   // Check that all field get instructions have been GVN'ed.
    127   ASSERT_TRUE(then->GetFirstInstruction()->IsGoto());
    128   ASSERT_TRUE(else_->GetFirstInstruction()->IsGoto());
    129   ASSERT_TRUE(join->GetFirstInstruction()->IsExit());
    130 }
    131 
    132 TEST(GVNTest, LoopFieldElimination) {
    133   ArenaPool pool;
    134   ArenaAllocator allocator(&pool);
    135 
    136   HGraph* graph = CreateGraph(&allocator);
    137   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
    138   graph->AddBlock(entry);
    139   graph->SetEntryBlock(entry);
    140 
    141   HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
    142   entry->AddInstruction(parameter);
    143 
    144   HBasicBlock* block = new (&allocator) HBasicBlock(graph);
    145   graph->AddBlock(block);
    146   entry->AddSuccessor(block);
    147   block->AddInstruction(
    148       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
    149           MemberOffset(42), false));
    150   block->AddInstruction(new (&allocator) HGoto());
    151 
    152   HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
    153   HBasicBlock* loop_body = new (&allocator) HBasicBlock(graph);
    154   HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
    155 
    156   graph->AddBlock(loop_header);
    157   graph->AddBlock(loop_body);
    158   graph->AddBlock(exit);
    159   block->AddSuccessor(loop_header);
    160   loop_header->AddSuccessor(loop_body);
    161   loop_header->AddSuccessor(exit);
    162   loop_body->AddSuccessor(loop_header);
    163 
    164   loop_header->AddInstruction(
    165       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
    166           MemberOffset(42), false));
    167   HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
    168   loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
    169 
    170   // Kill inside the loop body to prevent field gets inside the loop header
    171   // and the body to be GVN'ed.
    172   loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(
    173       parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
    174   HInstruction* field_set = loop_body->GetLastInstruction();
    175   loop_body->AddInstruction(
    176       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
    177           MemberOffset(42), false));
    178   HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
    179   loop_body->AddInstruction(new (&allocator) HGoto());
    180 
    181   exit->AddInstruction(
    182       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
    183           MemberOffset(42), false));
    184   HInstruction* field_get_in_exit = exit->GetLastInstruction();
    185   exit->AddInstruction(new (&allocator) HExit());
    186 
    187   ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
    188   ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
    189   ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
    190 
    191   graph->TryBuildingSsa();
    192   {
    193     SideEffectsAnalysis side_effects(graph);
    194     side_effects.Run();
    195     GVNOptimization(graph, side_effects).Run();
    196   }
    197 
    198   // Check that all field get instructions are still there.
    199   ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
    200   ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
    201   // The exit block is dominated by the loop header, whose field get
    202   // does not get killed by the loop flags.
    203   ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
    204 
    205   // Now remove the field set, and check that all field get instructions have been GVN'ed.
    206   loop_body->RemoveInstruction(field_set);
    207   {
    208     SideEffectsAnalysis side_effects(graph);
    209     side_effects.Run();
    210     GVNOptimization(graph, side_effects).Run();
    211   }
    212 
    213   ASSERT_TRUE(field_get_in_loop_header->GetBlock() == nullptr);
    214   ASSERT_TRUE(field_get_in_loop_body->GetBlock() == nullptr);
    215   ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
    216 }
    217 
    218 // Test that inner loops affect the side effects of the outer loop.
    219 TEST(GVNTest, LoopSideEffects) {
    220   ArenaPool pool;
    221   ArenaAllocator allocator(&pool);
    222 
    223   HGraph* graph = CreateGraph(&allocator);
    224   HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
    225   graph->AddBlock(entry);
    226   graph->SetEntryBlock(entry);
    227 
    228   HBasicBlock* outer_loop_header = new (&allocator) HBasicBlock(graph);
    229   HBasicBlock* outer_loop_body = new (&allocator) HBasicBlock(graph);
    230   HBasicBlock* outer_loop_exit = new (&allocator) HBasicBlock(graph);
    231   HBasicBlock* inner_loop_header = new (&allocator) HBasicBlock(graph);
    232   HBasicBlock* inner_loop_body = new (&allocator) HBasicBlock(graph);
    233   HBasicBlock* inner_loop_exit = new (&allocator) HBasicBlock(graph);
    234 
    235   graph->AddBlock(outer_loop_header);
    236   graph->AddBlock(outer_loop_body);
    237   graph->AddBlock(outer_loop_exit);
    238   graph->AddBlock(inner_loop_header);
    239   graph->AddBlock(inner_loop_body);
    240   graph->AddBlock(inner_loop_exit);
    241 
    242   entry->AddSuccessor(outer_loop_header);
    243   outer_loop_header->AddSuccessor(outer_loop_body);
    244   outer_loop_header->AddSuccessor(outer_loop_exit);
    245   outer_loop_body->AddSuccessor(inner_loop_header);
    246   inner_loop_header->AddSuccessor(inner_loop_body);
    247   inner_loop_header->AddSuccessor(inner_loop_exit);
    248   inner_loop_body->AddSuccessor(inner_loop_header);
    249   inner_loop_exit->AddSuccessor(outer_loop_header);
    250 
    251   HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimBoolean);
    252   entry->AddInstruction(parameter);
    253   entry->AddInstruction(new (&allocator) HGoto());
    254   outer_loop_header->AddInstruction(new (&allocator) HIf(parameter));
    255   outer_loop_body->AddInstruction(new (&allocator) HGoto());
    256   inner_loop_header->AddInstruction(new (&allocator) HIf(parameter));
    257   inner_loop_body->AddInstruction(new (&allocator) HGoto());
    258   inner_loop_exit->AddInstruction(new (&allocator) HGoto());
    259   outer_loop_exit->AddInstruction(new (&allocator) HExit());
    260 
    261   graph->TryBuildingSsa();
    262 
    263   ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
    264       *outer_loop_header->GetLoopInformation()));
    265 
    266   // Check that the loops don't have side effects.
    267   {
    268     // Make one block with a side effect.
    269     entry->AddInstruction(new (&allocator) HInstanceFieldSet(
    270         parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
    271 
    272     SideEffectsAnalysis side_effects(graph);
    273     side_effects.Run();
    274 
    275     ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
    276     ASSERT_FALSE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
    277     ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
    278   }
    279 
    280   // Check that the side effects of the outer loop does not affect the inner loop.
    281   {
    282     outer_loop_body->InsertInstructionBefore(
    283         new (&allocator) HInstanceFieldSet(
    284             parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false),
    285         outer_loop_body->GetLastInstruction());
    286 
    287     SideEffectsAnalysis side_effects(graph);
    288     side_effects.Run();
    289 
    290     ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
    291     ASSERT_TRUE(side_effects.GetBlockEffects(outer_loop_body).HasSideEffects());
    292     ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
    293     ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
    294   }
    295 
    296   // Check that the side effects of the inner loop affects the outer loop.
    297   {
    298     outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction());
    299     inner_loop_body->InsertInstructionBefore(
    300         new (&allocator) HInstanceFieldSet(
    301             parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false),
    302         inner_loop_body->GetLastInstruction());
    303 
    304     SideEffectsAnalysis side_effects(graph);
    305     side_effects.Run();
    306 
    307     ASSERT_TRUE(side_effects.GetBlockEffects(entry).HasSideEffects());
    308     ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).HasSideEffects());
    309     ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).HasSideEffects());
    310     ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).HasSideEffects());
    311   }
    312 }
    313 }  // namespace art
    314