1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "base/arena_allocator.h" 18 #include "builder.h" 19 #include "dex/dex_file.h" 20 #include "dex/dex_instruction.h" 21 #include "nodes.h" 22 #include "optimizing_unit_test.h" 23 #include "pretty_printer.h" 24 #include "ssa_liveness_analysis.h" 25 26 #include "gtest/gtest.h" 27 28 namespace art { 29 30 class FindLoopsTest : public OptimizingUnitTest {}; 31 32 TEST_F(FindLoopsTest, CFG1) { 33 // Constant is not used. 34 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 35 Instruction::CONST_4 | 0 | 0, 36 Instruction::RETURN_VOID); 37 38 HGraph* graph = CreateCFG(data); 39 for (HBasicBlock* block : graph->GetBlocks()) { 40 ASSERT_EQ(block->GetLoopInformation(), nullptr); 41 } 42 } 43 44 TEST_F(FindLoopsTest, CFG2) { 45 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 46 Instruction::CONST_4 | 0 | 0, 47 Instruction::RETURN); 48 49 HGraph* graph = CreateCFG(data); 50 for (HBasicBlock* block : graph->GetBlocks()) { 51 ASSERT_EQ(block->GetLoopInformation(), nullptr); 52 } 53 } 54 55 TEST_F(FindLoopsTest, CFG3) { 56 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( 57 Instruction::CONST_4 | 3 << 12 | 0, 58 Instruction::CONST_4 | 4 << 12 | 1 << 8, 59 Instruction::ADD_INT_2ADDR | 1 << 12, 60 Instruction::GOTO | 0x100, 61 Instruction::RETURN); 62 63 HGraph* graph = CreateCFG(data); 64 for (HBasicBlock* block : graph->GetBlocks()) { 65 ASSERT_EQ(block->GetLoopInformation(), nullptr); 66 } 67 } 68 69 TEST_F(FindLoopsTest, CFG4) { 70 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 71 Instruction::CONST_4 | 0 | 0, 72 Instruction::IF_EQ, 4, 73 Instruction::CONST_4 | 4 << 12 | 0, 74 Instruction::GOTO | 0x200, 75 Instruction::CONST_4 | 5 << 12 | 0, 76 Instruction::RETURN | 0 << 8); 77 78 HGraph* graph = CreateCFG(data); 79 for (HBasicBlock* block : graph->GetBlocks()) { 80 ASSERT_EQ(block->GetLoopInformation(), nullptr); 81 } 82 } 83 84 TEST_F(FindLoopsTest, CFG5) { 85 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 86 Instruction::CONST_4 | 0 | 0, 87 Instruction::IF_EQ, 3, 88 Instruction::CONST_4 | 4 << 12 | 0, 89 Instruction::RETURN | 0 << 8); 90 91 HGraph* graph = CreateCFG(data); 92 for (HBasicBlock* block : graph->GetBlocks()) { 93 ASSERT_EQ(block->GetLoopInformation(), nullptr); 94 } 95 } 96 97 static void TestBlock(HGraph* graph, 98 uint32_t block_id, 99 bool is_loop_header, 100 uint32_t parent_loop_header_id, 101 const int* blocks_in_loop = nullptr, 102 size_t number_of_blocks = 0) { 103 HBasicBlock* block = graph->GetBlocks()[block_id]; 104 ASSERT_EQ(block->IsLoopHeader(), is_loop_header); 105 if (parent_loop_header_id == kInvalidBlockId) { 106 ASSERT_EQ(block->GetLoopInformation(), nullptr); 107 } else { 108 ASSERT_EQ(block->GetLoopInformation()->GetHeader()->GetBlockId(), parent_loop_header_id); 109 } 110 111 if (blocks_in_loop != nullptr) { 112 HLoopInformation* info = block->GetLoopInformation(); 113 const BitVector& blocks = info->GetBlocks(); 114 ASSERT_EQ(blocks.NumSetBits(), number_of_blocks); 115 for (size_t i = 0; i < number_of_blocks; ++i) { 116 ASSERT_TRUE(blocks.IsBitSet(blocks_in_loop[i])); 117 } 118 } else { 119 ASSERT_FALSE(block->IsLoopHeader()); 120 } 121 } 122 123 TEST_F(FindLoopsTest, Loop1) { 124 // Simple loop with one preheader and one back edge. 125 // var a = 0; 126 // while (a == a) { 127 // } 128 // return; 129 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 130 Instruction::CONST_4 | 0 | 0, 131 Instruction::IF_EQ, 3, 132 Instruction::GOTO | 0xFE00, 133 Instruction::RETURN_VOID); 134 135 HGraph* graph = CreateCFG(data); 136 137 TestBlock(graph, 0, false, kInvalidBlockId); // entry block 138 TestBlock(graph, 1, false, kInvalidBlockId); // pre header 139 const int blocks2[] = {2, 3}; 140 TestBlock(graph, 2, true, 2, blocks2, 2); // loop header 141 TestBlock(graph, 3, false, 2); // block in loop 142 TestBlock(graph, 4, false, kInvalidBlockId); // return block 143 TestBlock(graph, 5, false, kInvalidBlockId); // exit block 144 } 145 146 TEST_F(FindLoopsTest, Loop2) { 147 // Make sure we support a preheader of a loop not being the first predecessor 148 // in the predecessor list of the header. 149 // var a = 0; 150 // while (a == a) { 151 // } 152 // return a; 153 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 154 Instruction::CONST_4 | 0 | 0, 155 Instruction::GOTO | 0x400, 156 Instruction::IF_EQ, 4, 157 Instruction::GOTO | 0xFE00, 158 Instruction::GOTO | 0xFD00, 159 Instruction::RETURN | 0 << 8); 160 161 HGraph* graph = CreateCFG(data); 162 163 TestBlock(graph, 0, false, kInvalidBlockId); // entry block 164 TestBlock(graph, 1, false, kInvalidBlockId); // goto block 165 const int blocks2[] = {2, 3}; 166 TestBlock(graph, 2, true, 2, blocks2, 2); // loop header 167 TestBlock(graph, 3, false, 2); // block in loop 168 TestBlock(graph, 4, false, kInvalidBlockId); // pre header 169 TestBlock(graph, 5, false, kInvalidBlockId); // return block 170 TestBlock(graph, 6, false, kInvalidBlockId); // exit block 171 } 172 173 TEST_F(FindLoopsTest, Loop3) { 174 // Make sure we create a preheader of a loop when a header originally has two 175 // incoming blocks and one back edge. 176 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 177 Instruction::CONST_4 | 0 | 0, 178 Instruction::IF_EQ, 3, 179 Instruction::GOTO | 0x100, 180 Instruction::IF_EQ, 3, 181 Instruction::GOTO | 0xFE00, 182 Instruction::RETURN | 0 << 8); 183 184 HGraph* graph = CreateCFG(data); 185 186 TestBlock(graph, 0, false, kInvalidBlockId); // entry block 187 TestBlock(graph, 1, false, kInvalidBlockId); // goto block 188 TestBlock(graph, 2, false, kInvalidBlockId); 189 const int blocks2[] = {3, 4}; 190 TestBlock(graph, 3, true, 3, blocks2, 2); // loop header 191 TestBlock(graph, 4, false, 3); // block in loop 192 TestBlock(graph, 5, false, kInvalidBlockId); // pre header 193 TestBlock(graph, 6, false, kInvalidBlockId); // return block 194 TestBlock(graph, 7, false, kInvalidBlockId); // exit block 195 TestBlock(graph, 8, false, kInvalidBlockId); // synthesized pre header 196 } 197 198 TEST_F(FindLoopsTest, Loop4) { 199 // Test loop with originally two back edges. 200 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 201 Instruction::CONST_4 | 0 | 0, 202 Instruction::IF_EQ, 6, 203 Instruction::IF_EQ, 3, 204 Instruction::GOTO | 0xFC00, 205 Instruction::GOTO | 0xFB00, 206 Instruction::RETURN | 0 << 8); 207 208 HGraph* graph = CreateCFG(data); 209 210 TestBlock(graph, 0, false, kInvalidBlockId); // entry block 211 TestBlock(graph, 1, false, kInvalidBlockId); // pre header 212 const int blocks2[] = {2, 3, 4, 5}; 213 TestBlock(graph, 2, true, 2, blocks2, arraysize(blocks2)); // loop header 214 TestBlock(graph, 3, false, 2); // block in loop 215 TestBlock(graph, 4, false, 2); // back edge 216 TestBlock(graph, 5, false, 2); // back edge 217 TestBlock(graph, 6, false, kInvalidBlockId); // return block 218 TestBlock(graph, 7, false, kInvalidBlockId); // exit block 219 } 220 221 222 TEST_F(FindLoopsTest, Loop5) { 223 // Test loop with two exit edges. 224 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 225 Instruction::CONST_4 | 0 | 0, 226 Instruction::IF_EQ, 6, 227 Instruction::IF_EQ, 3, 228 Instruction::GOTO | 0x0200, 229 Instruction::GOTO | 0xFB00, 230 Instruction::RETURN | 0 << 8); 231 232 HGraph* graph = CreateCFG(data); 233 234 TestBlock(graph, 0, false, kInvalidBlockId); // entry block 235 TestBlock(graph, 1, false, kInvalidBlockId); // pre header 236 const int blocks2[] = {2, 3, 5}; 237 TestBlock(graph, 2, true, 2, blocks2, 3); // loop header 238 TestBlock(graph, 3, false, 2); // block in loop 239 TestBlock(graph, 4, false, kInvalidBlockId); // loop exit 240 TestBlock(graph, 5, false, 2); // back edge 241 TestBlock(graph, 6, false, kInvalidBlockId); // return block 242 TestBlock(graph, 7, false, kInvalidBlockId); // exit block 243 TestBlock(graph, 8, false, kInvalidBlockId); // synthesized block at the loop exit 244 } 245 246 TEST_F(FindLoopsTest, InnerLoop) { 247 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 248 Instruction::CONST_4 | 0 | 0, 249 Instruction::IF_EQ, 6, 250 Instruction::IF_EQ, 3, 251 Instruction::GOTO | 0xFE00, // inner loop 252 Instruction::GOTO | 0xFB00, 253 Instruction::RETURN | 0 << 8); 254 255 HGraph* graph = CreateCFG(data); 256 257 TestBlock(graph, 0, false, kInvalidBlockId); // entry block 258 TestBlock(graph, 1, false, kInvalidBlockId); // pre header of outer loop 259 const int blocks2[] = {2, 3, 4, 5, 8}; 260 TestBlock(graph, 2, true, 2, blocks2, 5); // outer loop header 261 const int blocks3[] = {3, 4}; 262 TestBlock(graph, 3, true, 3, blocks3, 2); // inner loop header 263 TestBlock(graph, 4, false, 3); // back edge on inner loop 264 TestBlock(graph, 5, false, 2); // back edge on outer loop 265 TestBlock(graph, 6, false, kInvalidBlockId); // return block 266 TestBlock(graph, 7, false, kInvalidBlockId); // exit block 267 TestBlock(graph, 8, false, 2); // synthesized block as pre header of inner loop 268 269 ASSERT_TRUE(graph->GetBlocks()[3]->GetLoopInformation()->IsIn( 270 *graph->GetBlocks()[2]->GetLoopInformation())); 271 ASSERT_FALSE(graph->GetBlocks()[2]->GetLoopInformation()->IsIn( 272 *graph->GetBlocks()[3]->GetLoopInformation())); 273 } 274 275 TEST_F(FindLoopsTest, TwoLoops) { 276 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 277 Instruction::CONST_4 | 0 | 0, 278 Instruction::IF_EQ, 3, 279 Instruction::GOTO | 0xFE00, // first loop 280 Instruction::IF_EQ, 3, 281 Instruction::GOTO | 0xFE00, // second loop 282 Instruction::RETURN | 0 << 8); 283 284 HGraph* graph = CreateCFG(data); 285 286 TestBlock(graph, 0, false, kInvalidBlockId); // entry block 287 TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop 288 const int blocks2[] = {2, 3}; 289 TestBlock(graph, 2, true, 2, blocks2, 2); // first loop header 290 TestBlock(graph, 3, false, 2); // back edge of first loop 291 const int blocks4[] = {4, 5}; 292 TestBlock(graph, 4, true, 4, blocks4, 2); // second loop header 293 TestBlock(graph, 5, false, 4); // back edge of second loop 294 TestBlock(graph, 6, false, kInvalidBlockId); // return block 295 TestBlock(graph, 7, false, kInvalidBlockId); // exit block 296 297 ASSERT_FALSE(graph->GetBlocks()[4]->GetLoopInformation()->IsIn( 298 *graph->GetBlocks()[2]->GetLoopInformation())); 299 ASSERT_FALSE(graph->GetBlocks()[2]->GetLoopInformation()->IsIn( 300 *graph->GetBlocks()[4]->GetLoopInformation())); 301 } 302 303 TEST_F(FindLoopsTest, NonNaturalLoop) { 304 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 305 Instruction::CONST_4 | 0 | 0, 306 Instruction::IF_EQ, 3, 307 Instruction::GOTO | 0x0100, 308 Instruction::IF_EQ, 3, 309 Instruction::GOTO | 0xFD00, 310 Instruction::RETURN | 0 << 8); 311 312 HGraph* graph = CreateCFG(data); 313 ASSERT_TRUE(graph->GetBlocks()[3]->IsLoopHeader()); 314 HLoopInformation* info = graph->GetBlocks()[3]->GetLoopInformation(); 315 ASSERT_EQ(1u, info->NumberOfBackEdges()); 316 ASSERT_FALSE(info->GetHeader()->Dominates(info->GetBackEdges()[0])); 317 } 318 319 TEST_F(FindLoopsTest, DoWhileLoop) { 320 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( 321 Instruction::CONST_4 | 0 | 0, 322 Instruction::GOTO | 0x0100, 323 Instruction::IF_EQ, 0xFFFF, 324 Instruction::RETURN | 0 << 8); 325 326 HGraph* graph = CreateCFG(data); 327 328 TestBlock(graph, 0, false, kInvalidBlockId); // entry block 329 TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop 330 const int blocks2[] = {2, 3, 6}; 331 TestBlock(graph, 2, true, 2, blocks2, 3); // loop header 332 TestBlock(graph, 3, false, 2); // back edge of first loop 333 TestBlock(graph, 4, false, kInvalidBlockId); // return block 334 TestBlock(graph, 5, false, kInvalidBlockId); // exit block 335 TestBlock(graph, 6, false, 2); // synthesized block to avoid a critical edge 336 } 337 338 } // namespace art 339