/*
* 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 .0 f);
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, 1 u); // 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
Messung V0.5 in Prozent C=89 H=97 G=93
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet am 2026-06-29)
¤
*© Formatika GbR, Deutschland