| /* |
| * 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 |