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