1 /* 2 * Copyright (C) 2015 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 public class Main { 18 19 static boolean doThrow = false; 20 21 /* 22 * Ensure an inlined static invoke explicitly triggers the 23 * initialization check of the called method's declaring class, and 24 * that the corresponding load class instruction does not get 25 * removed before register allocation & code generation. 26 */ 27 28 /// CHECK-START: void Main.invokeStaticInlined() builder (after) 29 /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false 30 /// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>] 31 /// CHECK-DAG: InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>] 32 33 /// CHECK-START: void Main.invokeStaticInlined() inliner (after) 34 /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false 35 /// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>] 36 37 /// CHECK-START: void Main.invokeStaticInlined() inliner (after) 38 /// CHECK-NOT: InvokeStaticOrDirect 39 40 // The following checks ensure the clinit check instruction added by 41 // the builder is pruned by the PrepareForRegisterAllocation, while 42 // the load class instruction is preserved. As the control flow 43 // graph is not dumped after (nor before) this step, we check the 44 // CFG as it is before the next pass (liveness analysis) instead. 45 46 /// CHECK-START: void Main.invokeStaticInlined() liveness (before) 47 /// CHECK-DAG: LoadClass gen_clinit_check:true 48 49 /// CHECK-START: void Main.invokeStaticInlined() liveness (before) 50 /// CHECK-NOT: ClinitCheck 51 /// CHECK-NOT: InvokeStaticOrDirect 52 invokeStaticInlined()53 static void invokeStaticInlined() { 54 ClassWithClinit1.$opt$inline$StaticMethod(); 55 } 56 57 static class ClassWithClinit1 { 58 static { 59 System.out.println("Main$ClassWithClinit1's static initializer"); 60 } 61 $opt$inline$StaticMethod()62 static void $opt$inline$StaticMethod() { 63 } 64 } 65 66 /* 67 * Ensure a non-inlined static invoke eventually has an implicit 68 * initialization check of the called method's declaring class. 69 */ 70 71 /// CHECK-START: void Main.invokeStaticNotInlined() builder (after) 72 /// CHECK: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false 73 /// CHECK: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>] 74 /// CHECK: InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>] 75 76 /// CHECK-START: void Main.invokeStaticNotInlined() inliner (after) 77 /// CHECK: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false 78 /// CHECK: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>] 79 /// CHECK: InvokeStaticOrDirect [{{([ij]\d+,)?}}<<ClinitCheck>>] 80 81 // The following checks ensure the clinit check and load class 82 // instructions added by the builder are pruned by the 83 // PrepareForRegisterAllocation. As the control flow graph is not 84 // dumped after (nor before) this step, we check the CFG as it is 85 // before the next pass (liveness analysis) instead. 86 87 /// CHECK-START: void Main.invokeStaticNotInlined() liveness (before) 88 /// CHECK: InvokeStaticOrDirect clinit_check:implicit 89 90 /// CHECK-START: void Main.invokeStaticNotInlined() liveness (before) 91 /// CHECK-NOT: LoadClass 92 /// CHECK-NOT: ClinitCheck 93 invokeStaticNotInlined()94 static void invokeStaticNotInlined() { 95 ClassWithClinit2.$noinline$staticMethod(); 96 } 97 98 static class ClassWithClinit2 { 99 static { 100 System.out.println("Main$ClassWithClinit2's static initializer"); 101 } 102 103 static boolean doThrow = false; 104 $noinline$staticMethod()105 static void $noinline$staticMethod() { 106 if (doThrow) { 107 // Try defeating inlining. 108 throw new Error(); 109 } 110 } 111 } 112 113 /* 114 * Ensure an inlined call to a static method whose declaring class 115 * is statically known to have been initialized does not require an 116 * explicit clinit check. 117 */ 118 119 /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after) 120 /// CHECK-DAG: InvokeStaticOrDirect 121 122 /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after) 123 /// CHECK-NOT: LoadClass 124 /// CHECK-NOT: ClinitCheck 125 126 /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() inliner (after) 127 /// CHECK-NOT: LoadClass 128 /// CHECK-NOT: ClinitCheck 129 /// CHECK-NOT: InvokeStaticOrDirect 130 131 static class ClassWithClinit3 { invokeStaticInlined()132 static void invokeStaticInlined() { 133 // The invocation of invokeStaticInlined triggers the 134 // initialization of ClassWithClinit3, meaning that the 135 // hereinbelow call to $opt$inline$StaticMethod does not need a 136 // clinit check. 137 $opt$inline$StaticMethod(); 138 } 139 140 static { 141 System.out.println("Main$ClassWithClinit3's static initializer"); 142 } 143 $opt$inline$StaticMethod()144 static void $opt$inline$StaticMethod() { 145 } 146 } 147 148 /* 149 * Ensure an non-inlined call to a static method whose declaring 150 * class is statically known to have been initialized does not 151 * require an explicit clinit check. 152 */ 153 154 /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after) 155 /// CHECK-DAG: InvokeStaticOrDirect 156 157 /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after) 158 /// CHECK-NOT: LoadClass 159 /// CHECK-NOT: ClinitCheck 160 161 /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after) 162 /// CHECK-DAG: InvokeStaticOrDirect 163 164 /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after) 165 /// CHECK-NOT: LoadClass 166 /// CHECK-NOT: ClinitCheck 167 168 static class ClassWithClinit4 { invokeStaticNotInlined()169 static void invokeStaticNotInlined() { 170 // The invocation of invokeStaticNotInlined triggers the 171 // initialization of ClassWithClinit4, meaning that the 172 // call to staticMethod below does not need a clinit 173 // check. 174 $noinline$staticMethod(); 175 } 176 177 static { 178 System.out.println("Main$ClassWithClinit4's static initializer"); 179 } 180 181 static boolean doThrow = false; 182 $noinline$staticMethod()183 static void $noinline$staticMethod() { 184 if (doThrow) { 185 // Try defeating inlining. 186 throw new Error(); 187 } 188 } 189 } 190 191 /* 192 * Ensure an inlined call to a static method whose declaring class 193 * is a super class of the caller's class does not require an 194 * explicit clinit check. 195 */ 196 197 /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after) 198 /// CHECK-DAG: InvokeStaticOrDirect 199 200 /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after) 201 /// CHECK-NOT: LoadClass 202 /// CHECK-NOT: ClinitCheck 203 204 /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() inliner (after) 205 /// CHECK-NOT: LoadClass 206 /// CHECK-NOT: ClinitCheck 207 /// CHECK-NOT: InvokeStaticOrDirect 208 209 static class ClassWithClinit5 { $opt$inline$StaticMethod()210 static void $opt$inline$StaticMethod() { 211 } 212 213 static { 214 System.out.println("Main$ClassWithClinit5's static initializer"); 215 } 216 } 217 218 static class SubClassOfClassWithClinit5 extends ClassWithClinit5 { invokeStaticInlined()219 static void invokeStaticInlined() { 220 ClassWithClinit5.$opt$inline$StaticMethod(); 221 } 222 } 223 224 /* 225 * Ensure an non-inlined call to a static method whose declaring 226 * class is a super class of the caller's class does not require an 227 * explicit clinit check. 228 */ 229 230 /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after) 231 /// CHECK-DAG: InvokeStaticOrDirect 232 233 /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after) 234 /// CHECK-NOT: LoadClass 235 /// CHECK-NOT: ClinitCheck 236 237 /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after) 238 /// CHECK-DAG: InvokeStaticOrDirect 239 240 /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after) 241 /// CHECK-NOT: LoadClass 242 /// CHECK-NOT: ClinitCheck 243 244 static class ClassWithClinit6 { 245 static boolean doThrow = false; 246 $noinline$staticMethod()247 static void $noinline$staticMethod() { 248 if (doThrow) { 249 // Try defeating inlining. 250 throw new Error(); 251 } 252 } 253 254 static { 255 System.out.println("Main$ClassWithClinit6's static initializer"); 256 } 257 } 258 259 static class SubClassOfClassWithClinit6 extends ClassWithClinit6 { invokeStaticNotInlined()260 static void invokeStaticNotInlined() { 261 ClassWithClinit6.$noinline$staticMethod(); 262 } 263 } 264 265 266 /* 267 * Verify that if we have a static call immediately after the load class 268 * we don't do generate a clinit check. 269 */ 270 271 /// CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before) 272 /// CHECK-DAG: <<IntConstant:i\d+>> IntConstant 0 273 /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false 274 /// CHECK-DAG: InvokeStaticOrDirect clinit_check:implicit 275 /// CHECK-DAG: StaticFieldSet [<<LoadClass>>,<<IntConstant>>] 276 277 /// CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before) 278 /// CHECK-NOT: ClinitCheck 279 noClinitBecauseOfInvokeStatic()280 static void noClinitBecauseOfInvokeStatic() { 281 ClassWithClinit2.$noinline$staticMethod(); 282 ClassWithClinit2.doThrow = false; 283 } 284 285 /* 286 * Verify that if the static call is after a field access, the load class 287 * will generate a clinit check. 288 */ 289 290 /// CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before) 291 /// CHECK-DAG: <<IntConstant:i\d+>> IntConstant 0 292 /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:true 293 /// CHECK-DAG: StaticFieldSet [<<LoadClass>>,<<IntConstant>>] 294 /// CHECK-DAG: InvokeStaticOrDirect clinit_check:none 295 296 /// CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before) 297 /// CHECK-NOT: ClinitCheck clinitBecauseOfFieldAccess()298 static void clinitBecauseOfFieldAccess() { 299 ClassWithClinit2.doThrow = false; 300 ClassWithClinit2.$noinline$staticMethod(); 301 } 302 303 /* 304 * Verify that LoadClass from const-class is not merged with 305 * later invoke-static (or it's ClinitCheck). 306 */ 307 308 /// CHECK-START: void Main.constClassAndInvokeStatic(java.lang.Iterable) liveness (before) 309 /// CHECK: LoadClass gen_clinit_check:false 310 /// CHECK: InvokeStaticOrDirect clinit_check:implicit 311 312 /// CHECK-START: void Main.constClassAndInvokeStatic(java.lang.Iterable) liveness (before) 313 /// CHECK-NOT: ClinitCheck 314 constClassAndInvokeStatic(Iterable<?> it)315 static void constClassAndInvokeStatic(Iterable<?> it) { 316 $opt$inline$ignoreClass(ClassWithClinit7.class); 317 ClassWithClinit7.someStaticMethod(it); 318 } 319 $opt$inline$ignoreClass(Class<?> c)320 static void $opt$inline$ignoreClass(Class<?> c) { 321 } 322 323 static class ClassWithClinit7 { 324 static { 325 System.out.println("Main$ClassWithClinit7's static initializer"); 326 } 327 328 // Note: not inlined from constClassAndInvokeStatic() but fully inlined from main(). someStaticMethod(Iterable<?> it)329 static void someStaticMethod(Iterable<?> it) { 330 // We're not inlining invoke-interface at the moment. 331 it.iterator(); 332 } 333 } 334 335 /* 336 * Verify that LoadClass from sget is not merged with later invoke-static. 337 */ 338 339 /// CHECK-START: void Main.sgetAndInvokeStatic(java.lang.Iterable) liveness (before) 340 /// CHECK: LoadClass gen_clinit_check:true 341 /// CHECK: InvokeStaticOrDirect clinit_check:none 342 343 /// CHECK-START: void Main.sgetAndInvokeStatic(java.lang.Iterable) liveness (before) 344 /// CHECK-NOT: ClinitCheck 345 sgetAndInvokeStatic(Iterable<?> it)346 static void sgetAndInvokeStatic(Iterable<?> it) { 347 $opt$inline$ignoreInt(ClassWithClinit8.value); 348 ClassWithClinit8.someStaticMethod(it); 349 } 350 $opt$inline$ignoreInt(int i)351 static void $opt$inline$ignoreInt(int i) { 352 } 353 354 static class ClassWithClinit8 { 355 public static int value = 0; 356 static { 357 System.out.println("Main$ClassWithClinit8's static initializer"); 358 } 359 360 // Note: not inlined from sgetAndInvokeStatic() but fully inlined from main(). someStaticMethod(Iterable<?> it)361 static void someStaticMethod(Iterable<?> it) { 362 // We're not inlining invoke-interface at the moment. 363 it.iterator(); 364 } 365 } 366 367 /* 368 * Verify that LoadClass from const-class, ClinitCheck from sget and 369 * InvokeStaticOrDirect from invoke-static are not merged. 370 */ 371 372 /// CHECK-START: void Main.constClassSgetAndInvokeStatic(java.lang.Iterable) liveness (before) 373 /// CHECK: LoadClass gen_clinit_check:false 374 /// CHECK: ClinitCheck 375 /// CHECK: InvokeStaticOrDirect clinit_check:none 376 constClassSgetAndInvokeStatic(Iterable<?> it)377 static void constClassSgetAndInvokeStatic(Iterable<?> it) { 378 $opt$inline$ignoreClass(ClassWithClinit9.class); 379 $opt$inline$ignoreInt(ClassWithClinit9.value); 380 ClassWithClinit9.someStaticMethod(it); 381 } 382 383 static class ClassWithClinit9 { 384 public static int value = 0; 385 static { 386 System.out.println("Main$ClassWithClinit9's static initializer"); 387 } 388 389 // Note: not inlined from constClassSgetAndInvokeStatic() but fully inlined from main(). someStaticMethod(Iterable<?> it)390 static void someStaticMethod(Iterable<?> it) { 391 // We're not inlining invoke-interface at the moment. 392 it.iterator(); 393 } 394 } 395 396 /* 397 * Verify that LoadClass from a fully-inlined invoke-static is not merged 398 * with InvokeStaticOrDirect from a later invoke-static to the same method. 399 */ 400 401 /// CHECK-START: void Main.inlinedInvokeStaticViaNonStatic(java.lang.Iterable) liveness (before) 402 /// CHECK: LoadClass gen_clinit_check:true 403 /// CHECK: InvokeStaticOrDirect clinit_check:none 404 405 /// CHECK-START: void Main.inlinedInvokeStaticViaNonStatic(java.lang.Iterable) liveness (before) 406 /// CHECK-NOT: ClinitCheck 407 inlinedInvokeStaticViaNonStatic(Iterable<?> it)408 static void inlinedInvokeStaticViaNonStatic(Iterable<?> it) { 409 inlinedInvokeStaticViaNonStaticHelper(null); 410 inlinedInvokeStaticViaNonStaticHelper(it); 411 } 412 inlinedInvokeStaticViaNonStaticHelper(Iterable<?> it)413 static void inlinedInvokeStaticViaNonStaticHelper(Iterable<?> it) { 414 ClassWithClinit10.inlinedForNull(it); 415 } 416 417 static class ClassWithClinit10 { 418 public static int value = 0; 419 static { 420 System.out.println("Main$ClassWithClinit10's static initializer"); 421 } 422 inlinedForNull(Iterable<?> it)423 static void inlinedForNull(Iterable<?> it) { 424 if (it != null) { 425 // We're not inlining invoke-interface at the moment. 426 it.iterator(); 427 } 428 } 429 } 430 431 /* 432 * Check that the LoadClass from an invoke-static C.foo() doesn't get merged with 433 * an invoke-static inside C.foo(). This would mess up the stack walk in the 434 * resolution trampoline where we would have to load C (if C isn't loaded yet) 435 * which is not permitted there. 436 * 437 * Note: In case of failure, we would get an failed assertion during compilation, 438 * so we wouldn't really get to the checker tests below. 439 */ 440 441 /// CHECK-START: void Main.inlinedInvokeStaticViaStatic(java.lang.Iterable) liveness (before) 442 /// CHECK: LoadClass gen_clinit_check:true 443 /// CHECK: InvokeStaticOrDirect clinit_check:none 444 445 /// CHECK-START: void Main.inlinedInvokeStaticViaStatic(java.lang.Iterable) liveness (before) 446 /// CHECK-NOT: ClinitCheck 447 inlinedInvokeStaticViaStatic(Iterable<?> it)448 static void inlinedInvokeStaticViaStatic(Iterable<?> it) { 449 ClassWithClinit11.callInlinedForNull(it); 450 } 451 452 static class ClassWithClinit11 { 453 public static int value = 0; 454 static { 455 System.out.println("Main$ClassWithClinit11's static initializer"); 456 } 457 callInlinedForNull(Iterable<?> it)458 static void callInlinedForNull(Iterable<?> it) { 459 inlinedForNull(it); 460 } 461 inlinedForNull(Iterable<?> it)462 static void inlinedForNull(Iterable<?> it) { 463 // We're not inlining invoke-interface at the moment. 464 it.iterator(); 465 } 466 } 467 468 /* 469 * A test similar to inlinedInvokeStaticViaStatic() but doing the indirect invoke 470 * twice with the first one to be fully inlined. 471 */ 472 473 /// CHECK-START: void Main.inlinedInvokeStaticViaStaticTwice(java.lang.Iterable) liveness (before) 474 /// CHECK: LoadClass gen_clinit_check:true 475 /// CHECK: InvokeStaticOrDirect clinit_check:none 476 477 /// CHECK-START: void Main.inlinedInvokeStaticViaStaticTwice(java.lang.Iterable) liveness (before) 478 /// CHECK-NOT: ClinitCheck 479 inlinedInvokeStaticViaStaticTwice(Iterable<?> it)480 static void inlinedInvokeStaticViaStaticTwice(Iterable<?> it) { 481 ClassWithClinit12.callInlinedForNull(null); 482 ClassWithClinit12.callInlinedForNull(it); 483 } 484 485 static class ClassWithClinit12 { 486 public static int value = 0; 487 static { 488 System.out.println("Main$ClassWithClinit12's static initializer"); 489 } 490 callInlinedForNull(Iterable<?> it)491 static void callInlinedForNull(Iterable<?> it) { 492 inlinedForNull(it); 493 } 494 inlinedForNull(Iterable<?> it)495 static void inlinedForNull(Iterable<?> it) { 496 if (it != null) { 497 // We're not inlining invoke-interface at the moment. 498 it.iterator(); 499 } 500 } 501 } 502 503 static class ClassWithClinit13 { 504 static { 505 System.out.println("Main$ClassWithClinit13's static initializer"); 506 } 507 $inline$forwardToGetIterator(Iterable<?> it)508 public static void $inline$forwardToGetIterator(Iterable<?> it) { 509 $noinline$getIterator(it); 510 } 511 $noinline$getIterator(Iterable<?> it)512 public static void $noinline$getIterator(Iterable<?> it) { 513 // We're not inlining invoke-interface at the moment. 514 it.iterator(); 515 } 516 } 517 518 // TODO: Write checker statements. $noinline$testInliningAndNewInstance(Iterable<?> it)519 static Object $noinline$testInliningAndNewInstance(Iterable<?> it) { 520 if (doThrow) { throw new Error(); } 521 ClassWithClinit13.$inline$forwardToGetIterator(it); 522 return new ClassWithClinit13(); 523 } 524 525 // TODO: Add a test for the case of a static method whose declaring 526 // class type index is not available (i.e. when `storage_index` 527 // equals `DexFile::kDexNoIndex` in 528 // art::HGraphBuilder::BuildInvoke). 529 main(String[] args)530 public static void main(String[] args) { 531 invokeStaticInlined(); 532 invokeStaticNotInlined(); 533 ClassWithClinit3.invokeStaticInlined(); 534 ClassWithClinit4.invokeStaticNotInlined(); 535 SubClassOfClassWithClinit5.invokeStaticInlined(); 536 SubClassOfClassWithClinit6.invokeStaticNotInlined(); 537 Iterable it = new Iterable() { public java.util.Iterator iterator() { return null; } }; 538 constClassAndInvokeStatic(it); 539 sgetAndInvokeStatic(it); 540 constClassSgetAndInvokeStatic(it); 541 inlinedInvokeStaticViaNonStatic(it); 542 inlinedInvokeStaticViaStatic(it); 543 inlinedInvokeStaticViaStaticTwice(it); 544 $noinline$testInliningAndNewInstance(it); 545 } 546 } 547