| /* | 
 |  * 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 "mir_graph.h" | 
 | #include "gtest/gtest.h" | 
 |  | 
 | namespace art { | 
 |  | 
 | class TopologicalSortOrderTest : public testing::Test { | 
 |  protected: | 
 |   struct BBDef { | 
 |     static constexpr size_t kMaxSuccessors = 4; | 
 |     static constexpr size_t kMaxPredecessors = 4; | 
 |  | 
 |     BBType type; | 
 |     size_t num_successors; | 
 |     BasicBlockId successors[kMaxPredecessors]; | 
 |     size_t num_predecessors; | 
 |     BasicBlockId predecessors[kMaxPredecessors]; | 
 |   }; | 
 |  | 
 | #define DEF_SUCC0() \ | 
 |     0u, { } | 
 | #define DEF_SUCC1(s1) \ | 
 |     1u, { s1 } | 
 | #define DEF_SUCC2(s1, s2) \ | 
 |     2u, { s1, s2 } | 
 | #define DEF_SUCC3(s1, s2, s3) \ | 
 |     3u, { s1, s2, s3 } | 
 | #define DEF_SUCC4(s1, s2, s3, s4) \ | 
 |     4u, { s1, s2, s3, s4 } | 
 | #define DEF_PRED0() \ | 
 |     0u, { } | 
 | #define DEF_PRED1(p1) \ | 
 |     1u, { p1 } | 
 | #define DEF_PRED2(p1, p2) \ | 
 |     2u, { p1, p2 } | 
 | #define DEF_PRED3(p1, p2, p3) \ | 
 |     3u, { p1, p2, p3 } | 
 | #define DEF_PRED4(p1, p2, p3, p4) \ | 
 |     4u, { p1, p2, p3, p4 } | 
 | #define DEF_BB(type, succ, pred) \ | 
 |     { type, succ, pred } | 
 |  | 
 |   void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { | 
 |     cu_.mir_graph->block_id_map_.clear(); | 
 |     cu_.mir_graph->block_list_.clear(); | 
 |     ASSERT_LT(3u, count);  // null, entry, exit and at least one bytecode block. | 
 |     ASSERT_EQ(kNullBlock, defs[0].type); | 
 |     ASSERT_EQ(kEntryBlock, defs[1].type); | 
 |     ASSERT_EQ(kExitBlock, defs[2].type); | 
 |     for (size_t i = 0u; i != count; ++i) { | 
 |       const BBDef* def = &defs[i]; | 
 |       BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); | 
 |       if (def->num_successors <= 2) { | 
 |         bb->successor_block_list_type = kNotUsed; | 
 |         bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; | 
 |         bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; | 
 |       } else { | 
 |         bb->successor_block_list_type = kPackedSwitch; | 
 |         bb->fall_through = 0u; | 
 |         bb->taken = 0u; | 
 |         bb->successor_blocks.reserve(def->num_successors); | 
 |         for (size_t j = 0u; j != def->num_successors; ++j) { | 
 |           SuccessorBlockInfo* successor_block_info = | 
 |               static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), | 
 |                                                                kArenaAllocSuccessor)); | 
 |           successor_block_info->block = j; | 
 |           successor_block_info->key = 0u;  // Not used by class init check elimination. | 
 |           bb->successor_blocks.push_back(successor_block_info); | 
 |         } | 
 |       } | 
 |       bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); | 
 |       if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { | 
 |         bb->data_flow_info = static_cast<BasicBlockDataFlow*>( | 
 |             cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); | 
 |       } | 
 |     } | 
 |     cu_.mir_graph->num_blocks_ = count; | 
 |     ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); | 
 |     cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; | 
 |     ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); | 
 |     cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; | 
 |     ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); | 
 |  | 
 |     DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(cu_.arena.Alloc(sizeof(DexFile::CodeItem), | 
 |                                                                                    kArenaAllocMisc)); | 
 |     cu_.mir_graph->current_code_item_ = code_item; | 
 |   } | 
 |  | 
 |   template <size_t count> | 
 |   void PrepareBasicBlocks(const BBDef (&defs)[count]) { | 
 |     DoPrepareBasicBlocks(defs, count); | 
 |   } | 
 |  | 
 |   void ComputeTopologicalSortOrder() { | 
 |     cu_.mir_graph->SSATransformationStart(); | 
 |     cu_.mir_graph->ComputeDFSOrders(); | 
 |     cu_.mir_graph->ComputeDominators(); | 
 |     cu_.mir_graph->ComputeTopologicalSortOrder(); | 
 |     cu_.mir_graph->SSATransformationEnd(); | 
 |     ASSERT_FALSE(cu_.mir_graph->topological_order_.empty()); | 
 |     ASSERT_FALSE(cu_.mir_graph->topological_order_loop_ends_.empty()); | 
 |     ASSERT_FALSE(cu_.mir_graph->topological_order_indexes_.empty()); | 
 |     ASSERT_EQ(cu_.mir_graph->GetNumBlocks(), cu_.mir_graph->topological_order_indexes_.size()); | 
 |     for (size_t i = 0, size = cu_.mir_graph->GetTopologicalSortOrder().size(); i != size; ++i) { | 
 |       ASSERT_LT(cu_.mir_graph->topological_order_[i], cu_.mir_graph->GetNumBlocks()); | 
 |       BasicBlockId id = cu_.mir_graph->topological_order_[i]; | 
 |       EXPECT_EQ(i, cu_.mir_graph->topological_order_indexes_[id]); | 
 |     } | 
 |   } | 
 |  | 
 |   void DoCheckOrder(const BasicBlockId* ids, size_t count) { | 
 |     ASSERT_EQ(count, cu_.mir_graph->GetTopologicalSortOrder().size()); | 
 |     for (size_t i = 0; i != count; ++i) { | 
 |       EXPECT_EQ(ids[i], cu_.mir_graph->GetTopologicalSortOrder()[i]) << i; | 
 |     } | 
 |   } | 
 |  | 
 |   template <size_t count> | 
 |   void CheckOrder(const BasicBlockId (&ids)[count]) { | 
 |     DoCheckOrder(ids, count); | 
 |   } | 
 |  | 
 |   void DoCheckLoopEnds(const uint16_t* ends, size_t count) { | 
 |     for (size_t i = 0; i != count; ++i) { | 
 |       ASSERT_LT(i, cu_.mir_graph->GetTopologicalSortOrderLoopEnds().size()); | 
 |       EXPECT_EQ(ends[i], cu_.mir_graph->GetTopologicalSortOrderLoopEnds()[i]) << i; | 
 |     } | 
 |   } | 
 |  | 
 |   template <size_t count> | 
 |   void CheckLoopEnds(const uint16_t (&ends)[count]) { | 
 |     DoCheckLoopEnds(ends, count); | 
 |   } | 
 |  | 
 |   TopologicalSortOrderTest() | 
 |       : pool_(), | 
 |         cu_(&pool_) { | 
 |     cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); | 
 |   } | 
 |  | 
 |   ArenaPool pool_; | 
 |   CompilationUnit cu_; | 
 | }; | 
 |  | 
 | TEST_F(TopologicalSortOrderTest, DoWhile) { | 
 |   const BBDef bbs[] = { | 
 |       DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), | 
 |       DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), | 
 |       DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)),  // "taken" loops to self. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), | 
 |   }; | 
 |   const BasicBlockId expected_order[] = { | 
 |       1, 3, 4, 5, 2 | 
 |   }; | 
 |   const uint16_t loop_ends[] = { | 
 |       0, 0, 3, 0, 0 | 
 |   }; | 
 |  | 
 |   PrepareBasicBlocks(bbs); | 
 |   ComputeTopologicalSortOrder(); | 
 |   CheckOrder(expected_order); | 
 |   CheckLoopEnds(loop_ends); | 
 | } | 
 |  | 
 | TEST_F(TopologicalSortOrderTest, While) { | 
 |   const BBDef bbs[] = { | 
 |       DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), | 
 |       DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), | 
 |       DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED2(1, 4)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(3)),     // Loops to 3. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), | 
 |   }; | 
 |   const BasicBlockId expected_order[] = { | 
 |       1, 3, 4, 5, 2 | 
 |   }; | 
 |   const uint16_t loop_ends[] = { | 
 |       0, 3, 0, 0, 0 | 
 |   }; | 
 |  | 
 |   PrepareBasicBlocks(bbs); | 
 |   ComputeTopologicalSortOrder(); | 
 |   CheckOrder(expected_order); | 
 |   CheckLoopEnds(loop_ends); | 
 | } | 
 |  | 
 | TEST_F(TopologicalSortOrderTest, WhileWithTwoBackEdges) { | 
 |   const BBDef bbs[] = { | 
 |       DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), | 
 |       DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), | 
 |       DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED3(1, 4, 5)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 3), DEF_PRED1(3)),     // Loops to 3. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(4)),        // Loops to 3. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), | 
 |   }; | 
 |   const BasicBlockId expected_order[] = { | 
 |       1, 3, 4, 5, 6, 2 | 
 |   }; | 
 |   const uint16_t loop_ends[] = { | 
 |       0, 4, 0, 0, 0, 0 | 
 |   }; | 
 |  | 
 |   PrepareBasicBlocks(bbs); | 
 |   ComputeTopologicalSortOrder(); | 
 |   CheckOrder(expected_order); | 
 |   CheckLoopEnds(loop_ends); | 
 | } | 
 |  | 
 | TEST_F(TopologicalSortOrderTest, NestedLoop) { | 
 |   const BBDef bbs[] = { | 
 |       DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), | 
 |       DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), | 
 |       DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 7), DEF_PRED2(1, 6)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED2(3, 5)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)),            // Loops to 4. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(4)),            // Loops to 3. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), | 
 |   }; | 
 |   const BasicBlockId expected_order[] = { | 
 |       1, 3, 4, 5, 6, 7, 2 | 
 |   }; | 
 |   const uint16_t loop_ends[] = { | 
 |       0, 5, 4, 0, 0, 0, 0 | 
 |   }; | 
 |  | 
 |   PrepareBasicBlocks(bbs); | 
 |   ComputeTopologicalSortOrder(); | 
 |   CheckOrder(expected_order); | 
 |   CheckLoopEnds(loop_ends); | 
 | } | 
 |  | 
 | TEST_F(TopologicalSortOrderTest, NestedLoopHeadLoops) { | 
 |   const BBDef bbs[] = { | 
 |       DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), | 
 |       DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), | 
 |       DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED2(1, 4)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 3), DEF_PRED2(3, 5)),      // Nested head, loops to 3. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)),            // Loops to 4. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), | 
 |   }; | 
 |   const BasicBlockId expected_order[] = { | 
 |       1, 3, 4, 5, 6, 2 | 
 |   }; | 
 |   const uint16_t loop_ends[] = { | 
 |       0, 4, 4, 0, 0, 0 | 
 |   }; | 
 |  | 
 |   PrepareBasicBlocks(bbs); | 
 |   ComputeTopologicalSortOrder(); | 
 |   CheckOrder(expected_order); | 
 |   CheckLoopEnds(loop_ends); | 
 | } | 
 |  | 
 | TEST_F(TopologicalSortOrderTest, NestedLoopSameBackBranchBlock) { | 
 |   const BBDef bbs[] = { | 
 |       DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), | 
 |       DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), | 
 |       DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED2(1, 5)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(3, 5)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 3), DEF_PRED1(4)),         // Loops to 4 and 3. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), | 
 |   }; | 
 |   const BasicBlockId expected_order[] = { | 
 |       1, 3, 4, 5, 6, 2 | 
 |   }; | 
 |   const uint16_t loop_ends[] = { | 
 |       0, 4, 4, 0, 0, 0 | 
 |   }; | 
 |  | 
 |   PrepareBasicBlocks(bbs); | 
 |   ComputeTopologicalSortOrder(); | 
 |   CheckOrder(expected_order); | 
 |   CheckLoopEnds(loop_ends); | 
 | } | 
 |  | 
 | TEST_F(TopologicalSortOrderTest, TwoReorderedInnerLoops) { | 
 |   // This is a simplified version of real code graph where the branch from 8 to 5 must prevent | 
 |   // the block 5 from being considered a loop head before processing the loop 7-8. | 
 |   const BBDef bbs[] = { | 
 |       DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), | 
 |       DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), | 
 |       DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(9)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 9), DEF_PRED2(1, 5)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 7), DEF_PRED1(3)),         // Branch over loop in 5. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 3), DEF_PRED3(4, 6, 8)),   // Loops to 4; inner loop. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)),            // Loops to 5. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(8), DEF_PRED2(4, 8)),         // Loop head. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(7, 5), DEF_PRED1(7)),         // Loops to 7; branches to 5. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), | 
 |   }; | 
 |   const BasicBlockId expected_order[] = { | 
 |       1, 3, 4, 7, 8, 5, 6, 9, 2 | 
 |   }; | 
 |   const uint16_t loop_ends[] = { | 
 |       0, 7, 0, 5, 0, 7, 0, 0, 0 | 
 |   }; | 
 |  | 
 |   PrepareBasicBlocks(bbs); | 
 |   ComputeTopologicalSortOrder(); | 
 |   CheckOrder(expected_order); | 
 |   CheckLoopEnds(loop_ends); | 
 | } | 
 |  | 
 | TEST_F(TopologicalSortOrderTest, NestedLoopWithBackEdgeAfterOuterLoopBackEdge) { | 
 |   // This is a simplified version of real code graph. The back-edge from 7 to the inner | 
 |   // loop head 4 comes after the back-edge from 6 to the outer loop head 3. To make this | 
 |   // appear a bit more complex, there's also a back-edge from 5 to 4. | 
 |   const BBDef bbs[] = { | 
 |       DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), | 
 |       DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), | 
 |       DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED2(1, 6)),         // Outer loop head. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED3(3, 5, 7)),   // Inner loop head. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)),            // Loops to inner loop head 4. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(7, 3), DEF_PRED1(4)),         // Loops to outer loop head 3. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(2, 4), DEF_PRED1(6)),         // Loops to inner loop head 4. | 
 |   }; | 
 |   const BasicBlockId expected_order[] = { | 
 |       // NOTE: The 5 goes before 6 only because 5 is a "fall-through" from 4 while 6 is "taken". | 
 |       1, 3, 4, 5, 6, 7, 2 | 
 |   }; | 
 |   const uint16_t loop_ends[] = { | 
 |       0, 6, 6, 0, 0, 0, 0 | 
 |   }; | 
 |  | 
 |   PrepareBasicBlocks(bbs); | 
 |   ComputeTopologicalSortOrder(); | 
 |   CheckOrder(expected_order); | 
 |   CheckLoopEnds(loop_ends); | 
 | } | 
 |  | 
 | TEST_F(TopologicalSortOrderTest, LoopWithTwoEntryPoints) { | 
 |   const BBDef bbs[] = { | 
 |       DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), | 
 |       DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), | 
 |       DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED1(1)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(3, 6)),  // Fall-back block is chosen as | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED2(3, 4)),  // the earlier from these two. | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 7), DEF_PRED1(5)), | 
 |       DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(6)), | 
 |   }; | 
 |   const BasicBlockId expected_order[] = { | 
 |       1, 3, 4, 5, 6, 7, 2 | 
 |   }; | 
 |   const uint16_t loop_ends[] = { | 
 |       0, 0, 5, 0, 0, 0, 0 | 
 |   }; | 
 |  | 
 |   PrepareBasicBlocks(bbs); | 
 |   ComputeTopologicalSortOrder(); | 
 |   CheckOrder(expected_order); | 
 |   CheckLoopEnds(loop_ends); | 
 | } | 
 |  | 
 | }  // namespace art |