/* * Copyright (C) 2014 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 "dex_file.h" #include "dex_instruction.h" #include "nodes.h" #include "optimizing_unit_test.h" #include "ssa_liveness_analysis.h" #include "pretty_printer.h" #include "gtest/gtest.h" namespace art { class FindLoopsTest : public CommonCompilerTest {}; TEST_F(FindLoopsTest, CFG1) { // Constant is not used. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN_VOID); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } } TEST_F(FindLoopsTest, CFG2) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } } TEST_F(FindLoopsTest, CFG3) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::ADD_INT_2ADDR | 1 << 12, Instruction::GOTO | 0x100, Instruction::RETURN); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } } TEST_F(FindLoopsTest, CFG4) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, Instruction::GOTO | 0x200, Instruction::CONST_4 | 5 << 12 | 0, Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } } TEST_F(FindLoopsTest, CFG5) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::CONST_4 | 4 << 12 | 0, Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } } static void TestBlock(HGraph* graph, uint32_t block_id, bool is_loop_header, uint32_t parent_loop_header_id, const int* blocks_in_loop = nullptr, size_t number_of_blocks = 0) { HBasicBlock* block = graph->GetBlocks()[block_id]; ASSERT_EQ(block->IsLoopHeader(), is_loop_header); if (parent_loop_header_id == kInvalidBlockId) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } else { ASSERT_EQ(block->GetLoopInformation()->GetHeader()->GetBlockId(), parent_loop_header_id); } if (blocks_in_loop != nullptr) { HLoopInformation* info = block->GetLoopInformation(); const BitVector& blocks = info->GetBlocks(); ASSERT_EQ(blocks.NumSetBits(), number_of_blocks); for (size_t i = 0; i < number_of_blocks; ++i) { ASSERT_TRUE(blocks.IsBitSet(blocks_in_loop[i])); } } else { ASSERT_FALSE(block->IsLoopHeader()); } } TEST_F(FindLoopsTest, Loop1) { // Simple loop with one preheader and one back edge. // var a = 0; // while (a == a) { // } // return; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0xFE00, Instruction::RETURN_VOID); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header const int blocks2[] = {2, 3}; TestBlock(graph, 2, true, 2, blocks2, 2); // loop header TestBlock(graph, 3, false, 2); // block in loop TestBlock(graph, 4, false, kInvalidBlockId); // return block TestBlock(graph, 5, false, kInvalidBlockId); // exit block } TEST_F(FindLoopsTest, Loop2) { // Make sure we support a preheader of a loop not being the first predecessor // in the predecessor list of the header. // var a = 0; // while (a == a) { // } // return a; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::GOTO | 0x400, Instruction::IF_EQ, 4, Instruction::GOTO | 0xFE00, Instruction::GOTO | 0xFD00, Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // goto block const int blocks2[] = {2, 3}; TestBlock(graph, 2, true, 2, blocks2, 2); // loop header TestBlock(graph, 3, false, 2); // block in loop TestBlock(graph, 4, false, kInvalidBlockId); // pre header TestBlock(graph, 5, false, kInvalidBlockId); // return block TestBlock(graph, 6, false, kInvalidBlockId); // exit block } TEST_F(FindLoopsTest, Loop3) { // Make sure we create a preheader of a loop when a header originally has two // incoming blocks and one back edge. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, Instruction::IF_EQ, 3, Instruction::GOTO | 0xFE00, Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // goto block TestBlock(graph, 2, false, kInvalidBlockId); const int blocks2[] = {3, 4}; TestBlock(graph, 3, true, 3, blocks2, 2); // loop header TestBlock(graph, 4, false, 3); // block in loop TestBlock(graph, 5, false, kInvalidBlockId); // pre header TestBlock(graph, 6, false, kInvalidBlockId); // return block TestBlock(graph, 7, false, kInvalidBlockId); // exit block TestBlock(graph, 8, false, kInvalidBlockId); // synthesized pre header } TEST_F(FindLoopsTest, Loop4) { // Test loop with originally two back edges. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, Instruction::IF_EQ, 3, Instruction::GOTO | 0xFC00, Instruction::GOTO | 0xFB00, Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header const int blocks2[] = {2, 3, 4, 5}; TestBlock(graph, 2, true, 2, blocks2, arraysize(blocks2)); // loop header TestBlock(graph, 3, false, 2); // block in loop TestBlock(graph, 4, false, 2); // back edge TestBlock(graph, 5, false, 2); // back edge TestBlock(graph, 6, false, kInvalidBlockId); // return block TestBlock(graph, 7, false, kInvalidBlockId); // exit block } TEST_F(FindLoopsTest, Loop5) { // Test loop with two exit edges. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, Instruction::IF_EQ, 3, Instruction::GOTO | 0x0200, Instruction::GOTO | 0xFB00, Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header const int blocks2[] = {2, 3, 5}; TestBlock(graph, 2, true, 2, blocks2, 3); // loop header TestBlock(graph, 3, false, 2); // block in loop TestBlock(graph, 4, false, kInvalidBlockId); // loop exit TestBlock(graph, 5, false, 2); // back edge TestBlock(graph, 6, false, kInvalidBlockId); // return block TestBlock(graph, 7, false, kInvalidBlockId); // exit block TestBlock(graph, 8, false, kInvalidBlockId); // synthesized block at the loop exit } TEST_F(FindLoopsTest, InnerLoop) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, Instruction::IF_EQ, 3, Instruction::GOTO | 0xFE00, // inner loop Instruction::GOTO | 0xFB00, Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header of outer loop const int blocks2[] = {2, 3, 4, 5, 8}; TestBlock(graph, 2, true, 2, blocks2, 5); // outer loop header const int blocks3[] = {3, 4}; TestBlock(graph, 3, true, 3, blocks3, 2); // inner loop header TestBlock(graph, 4, false, 3); // back edge on inner loop TestBlock(graph, 5, false, 2); // back edge on outer loop TestBlock(graph, 6, false, kInvalidBlockId); // return block TestBlock(graph, 7, false, kInvalidBlockId); // exit block TestBlock(graph, 8, false, 2); // synthesized block as pre header of inner loop ASSERT_TRUE(graph->GetBlocks()[3]->GetLoopInformation()->IsIn( *graph->GetBlocks()[2]->GetLoopInformation())); ASSERT_FALSE(graph->GetBlocks()[2]->GetLoopInformation()->IsIn( *graph->GetBlocks()[3]->GetLoopInformation())); } TEST_F(FindLoopsTest, TwoLoops) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0xFE00, // first loop Instruction::IF_EQ, 3, Instruction::GOTO | 0xFE00, // second loop Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop const int blocks2[] = {2, 3}; TestBlock(graph, 2, true, 2, blocks2, 2); // first loop header TestBlock(graph, 3, false, 2); // back edge of first loop const int blocks4[] = {4, 5}; TestBlock(graph, 4, true, 4, blocks4, 2); // second loop header TestBlock(graph, 5, false, 4); // back edge of second loop TestBlock(graph, 6, false, kInvalidBlockId); // return block TestBlock(graph, 7, false, kInvalidBlockId); // exit block ASSERT_FALSE(graph->GetBlocks()[4]->GetLoopInformation()->IsIn( *graph->GetBlocks()[2]->GetLoopInformation())); ASSERT_FALSE(graph->GetBlocks()[2]->GetLoopInformation()->IsIn( *graph->GetBlocks()[4]->GetLoopInformation())); } TEST_F(FindLoopsTest, NonNaturalLoop) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x0100, Instruction::IF_EQ, 3, Instruction::GOTO | 0xFD00, Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); ASSERT_TRUE(graph->GetBlocks()[3]->IsLoopHeader()); HLoopInformation* info = graph->GetBlocks()[3]->GetLoopInformation(); ASSERT_EQ(1u, info->NumberOfBackEdges()); ASSERT_FALSE(info->GetHeader()->Dominates(info->GetBackEdges()[0])); } TEST_F(FindLoopsTest, DoWhileLoop) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::GOTO | 0x0100, Instruction::IF_EQ, 0xFFFF, Instruction::RETURN | 0 << 8); ArenaPool arena; ArenaAllocator allocator(&arena); HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop const int blocks2[] = {2, 3, 6}; TestBlock(graph, 2, true, 2, blocks2, 3); // loop header TestBlock(graph, 3, false, 2); // back edge of first loop TestBlock(graph, 4, false, kInvalidBlockId); // return block TestBlock(graph, 5, false, kInvalidBlockId); // exit block TestBlock(graph, 6, false, 2); // synthesized block to avoid a critical edge } } // namespace art