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