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 "gvn.h" 18 19 #include "base/arena_allocator.h" 20 #include "builder.h" 21 #include "nodes.h" 22 #include "optimizing_unit_test.h" 23 #include "side_effects_analysis.h" 24 25 namespace art { 26 27 class GVNTest : public OptimizingUnitTest {}; 28 29 TEST_F(GVNTest, LocalFieldElimination) { 30 HGraph* graph = CreateGraph(); 31 HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph); 32 graph->AddBlock(entry); 33 graph->SetEntryBlock(entry); 34 HInstruction* parameter = new (GetAllocator()) HParameterValue(graph->GetDexFile(), 35 dex::TypeIndex(0), 36 0, 37 DataType::Type::kReference); 38 entry->AddInstruction(parameter); 39 40 HBasicBlock* block = new (GetAllocator()) HBasicBlock(graph); 41 graph->AddBlock(block); 42 entry->AddSuccessor(block); 43 44 block->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 45 nullptr, 46 DataType::Type::kReference, 47 MemberOffset(42), 48 false, 49 kUnknownFieldIndex, 50 kUnknownClassDefIndex, 51 graph->GetDexFile(), 52 0)); 53 block->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 54 nullptr, 55 DataType::Type::kReference, 56 MemberOffset(42), 57 false, 58 kUnknownFieldIndex, 59 kUnknownClassDefIndex, 60 graph->GetDexFile(), 61 0)); 62 HInstruction* to_remove = block->GetLastInstruction(); 63 block->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 64 nullptr, 65 DataType::Type::kReference, 66 MemberOffset(43), 67 false, 68 kUnknownFieldIndex, 69 kUnknownClassDefIndex, 70 graph->GetDexFile(), 71 0)); 72 HInstruction* different_offset = block->GetLastInstruction(); 73 // Kill the value. 74 block->AddInstruction(new (GetAllocator()) HInstanceFieldSet(parameter, 75 parameter, 76 nullptr, 77 DataType::Type::kReference, 78 MemberOffset(42), 79 false, 80 kUnknownFieldIndex, 81 kUnknownClassDefIndex, 82 graph->GetDexFile(), 83 0)); 84 block->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 85 nullptr, 86 DataType::Type::kReference, 87 MemberOffset(42), 88 false, 89 kUnknownFieldIndex, 90 kUnknownClassDefIndex, 91 graph->GetDexFile(), 92 0)); 93 HInstruction* use_after_kill = block->GetLastInstruction(); 94 block->AddInstruction(new (GetAllocator()) HExit()); 95 96 ASSERT_EQ(to_remove->GetBlock(), block); 97 ASSERT_EQ(different_offset->GetBlock(), block); 98 ASSERT_EQ(use_after_kill->GetBlock(), block); 99 100 graph->BuildDominatorTree(); 101 SideEffectsAnalysis side_effects(graph); 102 side_effects.Run(); 103 GVNOptimization(graph, side_effects).Run(); 104 105 ASSERT_TRUE(to_remove->GetBlock() == nullptr); 106 ASSERT_EQ(different_offset->GetBlock(), block); 107 ASSERT_EQ(use_after_kill->GetBlock(), block); 108 } 109 110 TEST_F(GVNTest, GlobalFieldElimination) { 111 HGraph* graph = CreateGraph(); 112 HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph); 113 graph->AddBlock(entry); 114 graph->SetEntryBlock(entry); 115 HInstruction* parameter = new (GetAllocator()) HParameterValue(graph->GetDexFile(), 116 dex::TypeIndex(0), 117 0, 118 DataType::Type::kReference); 119 entry->AddInstruction(parameter); 120 121 HBasicBlock* block = new (GetAllocator()) HBasicBlock(graph); 122 graph->AddBlock(block); 123 entry->AddSuccessor(block); 124 block->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 125 nullptr, 126 DataType::Type::kBool, 127 MemberOffset(42), 128 false, 129 kUnknownFieldIndex, 130 kUnknownClassDefIndex, 131 graph->GetDexFile(), 132 0)); 133 134 block->AddInstruction(new (GetAllocator()) HIf(block->GetLastInstruction())); 135 HBasicBlock* then = new (GetAllocator()) HBasicBlock(graph); 136 HBasicBlock* else_ = new (GetAllocator()) HBasicBlock(graph); 137 HBasicBlock* join = new (GetAllocator()) HBasicBlock(graph); 138 graph->AddBlock(then); 139 graph->AddBlock(else_); 140 graph->AddBlock(join); 141 142 block->AddSuccessor(then); 143 block->AddSuccessor(else_); 144 then->AddSuccessor(join); 145 else_->AddSuccessor(join); 146 147 then->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 148 nullptr, 149 DataType::Type::kBool, 150 MemberOffset(42), 151 false, 152 kUnknownFieldIndex, 153 kUnknownClassDefIndex, 154 graph->GetDexFile(), 155 0)); 156 then->AddInstruction(new (GetAllocator()) HGoto()); 157 else_->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 158 nullptr, 159 DataType::Type::kBool, 160 MemberOffset(42), 161 false, 162 kUnknownFieldIndex, 163 kUnknownClassDefIndex, 164 graph->GetDexFile(), 165 0)); 166 else_->AddInstruction(new (GetAllocator()) HGoto()); 167 join->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 168 nullptr, 169 DataType::Type::kBool, 170 MemberOffset(42), 171 false, 172 kUnknownFieldIndex, 173 kUnknownClassDefIndex, 174 graph->GetDexFile(), 175 0)); 176 join->AddInstruction(new (GetAllocator()) HExit()); 177 178 graph->BuildDominatorTree(); 179 SideEffectsAnalysis side_effects(graph); 180 side_effects.Run(); 181 GVNOptimization(graph, side_effects).Run(); 182 183 // Check that all field get instructions have been GVN'ed. 184 ASSERT_TRUE(then->GetFirstInstruction()->IsGoto()); 185 ASSERT_TRUE(else_->GetFirstInstruction()->IsGoto()); 186 ASSERT_TRUE(join->GetFirstInstruction()->IsExit()); 187 } 188 189 TEST_F(GVNTest, LoopFieldElimination) { 190 HGraph* graph = CreateGraph(); 191 HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph); 192 graph->AddBlock(entry); 193 graph->SetEntryBlock(entry); 194 195 HInstruction* parameter = new (GetAllocator()) HParameterValue(graph->GetDexFile(), 196 dex::TypeIndex(0), 197 0, 198 DataType::Type::kReference); 199 entry->AddInstruction(parameter); 200 201 HBasicBlock* block = new (GetAllocator()) HBasicBlock(graph); 202 graph->AddBlock(block); 203 entry->AddSuccessor(block); 204 block->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 205 nullptr, 206 DataType::Type::kBool, 207 MemberOffset(42), 208 false, 209 kUnknownFieldIndex, 210 kUnknownClassDefIndex, 211 graph->GetDexFile(), 212 0)); 213 block->AddInstruction(new (GetAllocator()) HGoto()); 214 215 HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph); 216 HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph); 217 HBasicBlock* exit = new (GetAllocator()) HBasicBlock(graph); 218 219 graph->AddBlock(loop_header); 220 graph->AddBlock(loop_body); 221 graph->AddBlock(exit); 222 block->AddSuccessor(loop_header); 223 loop_header->AddSuccessor(loop_body); 224 loop_header->AddSuccessor(exit); 225 loop_body->AddSuccessor(loop_header); 226 227 loop_header->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 228 nullptr, 229 DataType::Type::kBool, 230 MemberOffset(42), 231 false, 232 kUnknownFieldIndex, 233 kUnknownClassDefIndex, 234 graph->GetDexFile(), 235 0)); 236 HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction(); 237 loop_header->AddInstruction(new (GetAllocator()) HIf(block->GetLastInstruction())); 238 239 // Kill inside the loop body to prevent field gets inside the loop header 240 // and the body to be GVN'ed. 241 loop_body->AddInstruction(new (GetAllocator()) HInstanceFieldSet(parameter, 242 parameter, 243 nullptr, 244 DataType::Type::kBool, 245 MemberOffset(42), 246 false, 247 kUnknownFieldIndex, 248 kUnknownClassDefIndex, 249 graph->GetDexFile(), 250 0)); 251 HInstruction* field_set = loop_body->GetLastInstruction(); 252 loop_body->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 253 nullptr, 254 DataType::Type::kBool, 255 MemberOffset(42), 256 false, 257 kUnknownFieldIndex, 258 kUnknownClassDefIndex, 259 graph->GetDexFile(), 260 0)); 261 HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction(); 262 loop_body->AddInstruction(new (GetAllocator()) HGoto()); 263 264 exit->AddInstruction(new (GetAllocator()) HInstanceFieldGet(parameter, 265 nullptr, 266 DataType::Type::kBool, 267 MemberOffset(42), 268 false, 269 kUnknownFieldIndex, 270 kUnknownClassDefIndex, 271 graph->GetDexFile(), 272 0)); 273 HInstruction* field_get_in_exit = exit->GetLastInstruction(); 274 exit->AddInstruction(new (GetAllocator()) HExit()); 275 276 ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header); 277 ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body); 278 ASSERT_EQ(field_get_in_exit->GetBlock(), exit); 279 280 graph->BuildDominatorTree(); 281 { 282 SideEffectsAnalysis side_effects(graph); 283 side_effects.Run(); 284 GVNOptimization(graph, side_effects).Run(); 285 } 286 287 // Check that all field get instructions are still there. 288 ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header); 289 ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body); 290 // The exit block is dominated by the loop header, whose field get 291 // does not get killed by the loop flags. 292 ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr); 293 294 // Now remove the field set, and check that all field get instructions have been GVN'ed. 295 loop_body->RemoveInstruction(field_set); 296 { 297 SideEffectsAnalysis side_effects(graph); 298 side_effects.Run(); 299 GVNOptimization(graph, side_effects).Run(); 300 } 301 302 ASSERT_TRUE(field_get_in_loop_header->GetBlock() == nullptr); 303 ASSERT_TRUE(field_get_in_loop_body->GetBlock() == nullptr); 304 ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr); 305 } 306 307 // Test that inner loops affect the side effects of the outer loop. 308 TEST_F(GVNTest, LoopSideEffects) { 309 static const SideEffects kCanTriggerGC = SideEffects::CanTriggerGC(); 310 311 HGraph* graph = CreateGraph(); 312 HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph); 313 graph->AddBlock(entry); 314 graph->SetEntryBlock(entry); 315 316 HBasicBlock* outer_loop_header = new (GetAllocator()) HBasicBlock(graph); 317 HBasicBlock* outer_loop_body = new (GetAllocator()) HBasicBlock(graph); 318 HBasicBlock* outer_loop_exit = new (GetAllocator()) HBasicBlock(graph); 319 HBasicBlock* inner_loop_header = new (GetAllocator()) HBasicBlock(graph); 320 HBasicBlock* inner_loop_body = new (GetAllocator()) HBasicBlock(graph); 321 HBasicBlock* inner_loop_exit = new (GetAllocator()) HBasicBlock(graph); 322 323 graph->AddBlock(outer_loop_header); 324 graph->AddBlock(outer_loop_body); 325 graph->AddBlock(outer_loop_exit); 326 graph->AddBlock(inner_loop_header); 327 graph->AddBlock(inner_loop_body); 328 graph->AddBlock(inner_loop_exit); 329 330 entry->AddSuccessor(outer_loop_header); 331 outer_loop_header->AddSuccessor(outer_loop_body); 332 outer_loop_header->AddSuccessor(outer_loop_exit); 333 outer_loop_body->AddSuccessor(inner_loop_header); 334 inner_loop_header->AddSuccessor(inner_loop_body); 335 inner_loop_header->AddSuccessor(inner_loop_exit); 336 inner_loop_body->AddSuccessor(inner_loop_header); 337 inner_loop_exit->AddSuccessor(outer_loop_header); 338 339 HInstruction* parameter = new (GetAllocator()) HParameterValue(graph->GetDexFile(), 340 dex::TypeIndex(0), 341 0, 342 DataType::Type::kBool); 343 entry->AddInstruction(parameter); 344 entry->AddInstruction(new (GetAllocator()) HGoto()); 345 outer_loop_header->AddInstruction(new (GetAllocator()) HSuspendCheck()); 346 outer_loop_header->AddInstruction(new (GetAllocator()) HIf(parameter)); 347 outer_loop_body->AddInstruction(new (GetAllocator()) HGoto()); 348 inner_loop_header->AddInstruction(new (GetAllocator()) HSuspendCheck()); 349 inner_loop_header->AddInstruction(new (GetAllocator()) HIf(parameter)); 350 inner_loop_body->AddInstruction(new (GetAllocator()) HGoto()); 351 inner_loop_exit->AddInstruction(new (GetAllocator()) HGoto()); 352 outer_loop_exit->AddInstruction(new (GetAllocator()) HExit()); 353 354 graph->BuildDominatorTree(); 355 356 ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn( 357 *outer_loop_header->GetLoopInformation())); 358 359 // Check that the only side effect of loops is to potentially trigger GC. 360 { 361 // Make one block with a side effect. 362 entry->AddInstruction(new (GetAllocator()) HInstanceFieldSet(parameter, 363 parameter, 364 nullptr, 365 DataType::Type::kReference, 366 MemberOffset(42), 367 false, 368 kUnknownFieldIndex, 369 kUnknownClassDefIndex, 370 graph->GetDexFile(), 371 0)); 372 373 SideEffectsAnalysis side_effects(graph); 374 side_effects.Run(); 375 376 ASSERT_TRUE(side_effects.GetBlockEffects(entry).DoesAnyWrite()); 377 ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite()); 378 ASSERT_FALSE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite()); 379 ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite()); 380 ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).Equals(kCanTriggerGC)); 381 ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC)); 382 } 383 384 // Check that the side effects of the outer loop does not affect the inner loop. 385 { 386 outer_loop_body->InsertInstructionBefore( 387 new (GetAllocator()) HInstanceFieldSet(parameter, 388 parameter, 389 nullptr, 390 DataType::Type::kReference, 391 MemberOffset(42), 392 false, 393 kUnknownFieldIndex, 394 kUnknownClassDefIndex, 395 graph->GetDexFile(), 396 0), 397 outer_loop_body->GetLastInstruction()); 398 399 SideEffectsAnalysis side_effects(graph); 400 side_effects.Run(); 401 402 ASSERT_TRUE(side_effects.GetBlockEffects(entry).DoesAnyWrite()); 403 ASSERT_TRUE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite()); 404 ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite()); 405 ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite()); 406 ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC)); 407 } 408 409 // Check that the side effects of the inner loop affects the outer loop. 410 { 411 outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction()); 412 inner_loop_body->InsertInstructionBefore( 413 new (GetAllocator()) HInstanceFieldSet(parameter, 414 parameter, 415 nullptr, 416 DataType::Type::kReference, 417 MemberOffset(42), 418 false, 419 kUnknownFieldIndex, 420 kUnknownClassDefIndex, 421 graph->GetDexFile(), 422 0), 423 inner_loop_body->GetLastInstruction()); 424 425 SideEffectsAnalysis side_effects(graph); 426 side_effects.Run(); 427 428 ASSERT_TRUE(side_effects.GetBlockEffects(entry).DoesAnyWrite()); 429 ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite()); 430 ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite()); 431 ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite()); 432 } 433 } 434 } // namespace art 435