blob: c0f25fa1a29acab55df7f55f0305fc0106acdbaa [file]
/*
* 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 "licm.h"
#include "base/arena_allocator.h"
#include "base/macros.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
namespace art HIDDEN {
/**
* Fixture class for the LICM tests.
*/
class LICMTest : public OptimizingUnitTest {
public:
LICMTest()
: loop_preheader_(nullptr),
loop_header_(nullptr),
loop_body_(nullptr),
parameter_(nullptr),
int_constant_(nullptr),
float_constant_(nullptr) {
graph_ = CreateGraph();
}
~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() {
HBasicBlock* return_block = InitEntryMainExitGraphWithReturnVoid();
std::tie(loop_preheader_, loop_header_, loop_body_) = CreateWhileLoop(return_block);
loop_header_->SwapSuccessors(); // Move the loop exit to the "else" successor.
// Provide boiler-plate instructions.
parameter_ = MakeParam(DataType::Type::kReference);
int_constant_ = graph_->GetIntConstant(42);
float_constant_ = graph_->GetFloatConstant(42.0f);
MakeIf(loop_header_, parameter_);
}
// Performs LICM optimizations (after proper set up).
void PerformLICM() {
graph_->BuildDominatorTree();
LICM(graph_, nullptr).Run();
}
// Specific basic blocks.
HBasicBlock* loop_preheader_;
HBasicBlock* loop_header_;
HBasicBlock* loop_body_;
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.
HInstruction* get_field =
MakeIFieldGet(loop_body_, parameter_, DataType::Type::kInt64, MemberOffset(10));
HInstruction* set_field =
MakeIFieldSet(loop_body_, parameter_, int_constant_, DataType::Type::kInt32, MemberOffset(20));
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 =
MakeIFieldGet(loop_body_, parameter_, DataType::Type::kInt64, MemberOffset(10));
HInstruction* set_field = MakeIFieldSet(loop_body_, parameter_, get_field, MemberOffset(10));
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.
HInstruction* get_array =
MakeArrayGet(loop_body_, parameter_, int_constant_, DataType::Type::kInt32);
HInstruction* set_array = MakeArraySet(
loop_body_, parameter_, int_constant_, float_constant_, DataType::Type::kFloat32);
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.
HInstruction* get_array =
MakeArrayGet(loop_body_, parameter_, int_constant_, DataType::Type::kFloat32);
HInstruction* set_array =
MakeArraySet(loop_body_, parameter_, get_array, float_constant_, DataType::Type::kFloat32);
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_);
}
TEST_F(LICMTest, NoConditionalBlockHoisting) {
HBasicBlock* return_block = InitEntryMainExitGraph();
HInstruction* param = MakeParam(DataType::Type::kInt32);
HInstruction* bool_param = MakeParam(DataType::Type::kBool);
HInstruction* const1 = graph_->GetIntConstant(1);
HInstruction* const100 = graph_->GetIntConstant(100);
auto [pre_header, loop_header, body_end] = CreateWhileLoop(return_block);
auto [body_start, body_left, body_right] = CreateDiamondPattern(body_end, bool_param);
auto [loop_var_phi, loop_var_add] =
MakeLinearLoopVar(loop_header, body_end, /*initial=*/ 0, /*increment=*/ 1);
HCondition* ge = MakeCondition(loop_header, kCondGE, loop_var_phi, const100);
MakeIf(loop_header, ge);
HPhi* phi1 = MakePhi(loop_header, {param, /* placeholder */ param});
HAdd* add = MakeBinOp<HAdd>(body_right, DataType::Type::kInt32, param, const1);
ASSERT_EQ(1, body_end->GetPredecessorIndexOf(body_right));
HPhi* phi2 = MakePhi(body_end, {phi1, add});
phi1->ReplaceInput(phi2, 1u); // Update back-edge input.
HReturn* ret = MakeReturn(return_block, phi1);
PerformLICM();
// The loop-invariant `add` should not be moved from a conditional block.
ASSERT_TRUE(add->GetBlock() == body_right);
}
} // namespace art