| /* |
| * 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 "base/arena_allocator.h" |
| #include "builder.h" |
| #include "licm.h" |
| #include "nodes.h" |
| #include "optimizing_unit_test.h" |
| #include "side_effects_analysis.h" |
| |
| namespace art { |
| |
| /** |
| * Fixture class for the LICM tests. |
| */ |
| class LICMTest : public CommonCompilerTest { |
| public: |
| LICMTest() : pool_(), allocator_(&pool_) { |
| graph_ = CreateGraph(&allocator_); |
| } |
| |
| ~LICMTest() { } |
| |
| // Builds a singly-nested loop structure in CFG. Tests can further populate |
| // the basic blocks with instructions to set up interesting scenarios. |
| void BuildLoop() { |
| entry_ = new (&allocator_) HBasicBlock(graph_); |
| loop_preheader_ = new (&allocator_) HBasicBlock(graph_); |
| loop_header_ = new (&allocator_) HBasicBlock(graph_); |
| loop_body_ = new (&allocator_) HBasicBlock(graph_); |
| return_ = new (&allocator_) HBasicBlock(graph_); |
| exit_ = new (&allocator_) HBasicBlock(graph_); |
| |
| graph_->AddBlock(entry_); |
| graph_->AddBlock(loop_preheader_); |
| graph_->AddBlock(loop_header_); |
| graph_->AddBlock(loop_body_); |
| graph_->AddBlock(return_); |
| graph_->AddBlock(exit_); |
| |
| graph_->SetEntryBlock(entry_); |
| graph_->SetExitBlock(exit_); |
| |
| // Set up loop flow in CFG. |
| entry_->AddSuccessor(loop_preheader_); |
| loop_preheader_->AddSuccessor(loop_header_); |
| loop_header_->AddSuccessor(loop_body_); |
| loop_header_->AddSuccessor(return_); |
| loop_body_->AddSuccessor(loop_header_); |
| return_->AddSuccessor(exit_); |
| |
| // Provide boiler-plate instructions. |
| parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimNot); |
| entry_->AddInstruction(parameter_); |
| int_constant_ = graph_->GetIntConstant(42); |
| float_constant_ = graph_->GetFloatConstant(42.0f); |
| loop_preheader_->AddInstruction(new (&allocator_) HGoto()); |
| loop_header_->AddInstruction(new (&allocator_) HIf(parameter_)); |
| loop_body_->AddInstruction(new (&allocator_) HGoto()); |
| return_->AddInstruction(new (&allocator_) HReturnVoid()); |
| exit_->AddInstruction(new (&allocator_) HExit()); |
| } |
| |
| // Performs LICM optimizations (after proper set up). |
| void PerformLICM() { |
| graph_->BuildDominatorTree(); |
| SideEffectsAnalysis side_effects(graph_); |
| side_effects.Run(); |
| LICM(graph_, side_effects, nullptr).Run(); |
| } |
| |
| // General building fields. |
| ArenaPool pool_; |
| ArenaAllocator allocator_; |
| HGraph* graph_; |
| |
| // Specific basic blocks. |
| HBasicBlock* entry_; |
| HBasicBlock* loop_preheader_; |
| HBasicBlock* loop_header_; |
| HBasicBlock* loop_body_; |
| HBasicBlock* return_; |
| HBasicBlock* exit_; |
| |
| HInstruction* parameter_; // "this" |
| HInstruction* int_constant_; |
| HInstruction* float_constant_; |
| }; |
| |
| // |
| // The actual LICM tests. |
| // |
| |
| TEST_F(LICMTest, FieldHoisting) { |
| BuildLoop(); |
| |
| // Populate the loop with instructions: set/get field with different types. |
| ScopedNullHandle<mirror::DexCache> dex_cache; |
| HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_, |
| Primitive::kPrimLong, |
| MemberOffset(10), |
| false, |
| kUnknownFieldIndex, |
| kUnknownClassDefIndex, |
| graph_->GetDexFile(), |
| dex_cache, |
| 0); |
| loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction()); |
| HInstruction* set_field = new (&allocator_) HInstanceFieldSet( |
| parameter_, int_constant_, Primitive::kPrimInt, MemberOffset(20), |
| false, kUnknownFieldIndex, kUnknownClassDefIndex, graph_->GetDexFile(), dex_cache, 0); |
| loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction()); |
| |
| EXPECT_EQ(get_field->GetBlock(), loop_body_); |
| EXPECT_EQ(set_field->GetBlock(), loop_body_); |
| PerformLICM(); |
| EXPECT_EQ(get_field->GetBlock(), loop_preheader_); |
| EXPECT_EQ(set_field->GetBlock(), loop_body_); |
| } |
| |
| TEST_F(LICMTest, NoFieldHoisting) { |
| BuildLoop(); |
| |
| // Populate the loop with instructions: set/get field with same types. |
| ScopedNullHandle<mirror::DexCache> dex_cache; |
| HInstruction* get_field = new (&allocator_) HInstanceFieldGet(parameter_, |
| Primitive::kPrimLong, |
| MemberOffset(10), |
| false, |
| kUnknownFieldIndex, |
| kUnknownClassDefIndex, |
| graph_->GetDexFile(), |
| dex_cache, |
| 0); |
| loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction()); |
| HInstruction* set_field = new (&allocator_) HInstanceFieldSet(parameter_, |
| get_field, |
| Primitive::kPrimLong, |
| MemberOffset(10), |
| false, |
| kUnknownFieldIndex, |
| kUnknownClassDefIndex, |
| graph_->GetDexFile(), |
| dex_cache, |
| 0); |
| loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction()); |
| |
| EXPECT_EQ(get_field->GetBlock(), loop_body_); |
| EXPECT_EQ(set_field->GetBlock(), loop_body_); |
| PerformLICM(); |
| EXPECT_EQ(get_field->GetBlock(), loop_body_); |
| EXPECT_EQ(set_field->GetBlock(), loop_body_); |
| } |
| |
| TEST_F(LICMTest, ArrayHoisting) { |
| BuildLoop(); |
| |
| // Populate the loop with instructions: set/get array with different types. |
| // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to |
| // avoid SsaBuilder's typing of ambiguous array operations from reference type info. |
| HInstruction* get_array = new (&allocator_) HArrayGet( |
| parameter_, int_constant_, Primitive::kPrimByte, 0); |
| loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); |
| HInstruction* set_array = new (&allocator_) HArraySet( |
| parameter_, int_constant_, float_constant_, Primitive::kPrimShort, 0); |
| loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); |
| |
| EXPECT_EQ(get_array->GetBlock(), loop_body_); |
| EXPECT_EQ(set_array->GetBlock(), loop_body_); |
| PerformLICM(); |
| EXPECT_EQ(get_array->GetBlock(), loop_preheader_); |
| EXPECT_EQ(set_array->GetBlock(), loop_body_); |
| } |
| |
| TEST_F(LICMTest, NoArrayHoisting) { |
| BuildLoop(); |
| |
| // Populate the loop with instructions: set/get array with same types. |
| // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to |
| // avoid SsaBuilder's typing of ambiguous array operations from reference type info. |
| HInstruction* get_array = new (&allocator_) HArrayGet( |
| parameter_, int_constant_, Primitive::kPrimByte, 0); |
| loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); |
| HInstruction* set_array = new (&allocator_) HArraySet( |
| parameter_, get_array, float_constant_, Primitive::kPrimByte, 0); |
| loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); |
| |
| EXPECT_EQ(get_array->GetBlock(), loop_body_); |
| EXPECT_EQ(set_array->GetBlock(), loop_body_); |
| PerformLICM(); |
| EXPECT_EQ(get_array->GetBlock(), loop_body_); |
| EXPECT_EQ(set_array->GetBlock(), loop_body_); |
| } |
| |
| } // namespace art |