1 # Copyright (C) 2016 The Android Open Source Project 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 .class public LTestCase; 16 .super Ljava/lang/Object; 17 18 # Test that all vregs holding the new-instance are updated after the 19 # StringFactory call. 20 21 ## CHECK-START: java.lang.String TestCase.vregAliasing(byte[]) register (after) 22 ## CHECK-DAG: Return [<<String:l\d+>>] 23 ## CHECK-DAG: <<String>> InvokeStaticOrDirect method_name:java.lang.String.<init> 24 25 .method public static vregAliasing([B)Ljava/lang/String; 26 .registers 5 27 28 # Create new instance of String and store it to v0, v1, v2. 29 new-instance v0, Ljava/lang/String; 30 move-object v1, v0 31 move-object v2, v0 32 33 # Call String.<init> on v1. 34 const-string v3, "UTF8" 35 invoke-direct {v1, p0, v3}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 36 37 # Return the object from v2. 38 return-object v2 39 40 .end method 41 42 # Test usage of String new-instance before it is initialized. 43 44 ## CHECK-START: void TestCase.compareNewInstance() register (after) 45 ## CHECK-DAG: <<Null:l\d+>> InvokeStaticOrDirect method_name:Main.$noinline$HiddenNull 46 ## CHECK-DAG: <<String:l\d+>> NewInstance 47 ## CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Null>>,<<String>>] 48 ## CHECK-DAG: If [<<Cond>>] 49 50 .method public static compareNewInstance()V 51 .registers 3 52 53 invoke-static {}, LMain;->$noinline$HiddenNull()Ljava/lang/Object; 54 move-result-object v1 55 56 new-instance v0, Ljava/lang/String; 57 if-ne v0, v1, :return 58 59 # Will throw NullPointerException if this branch is taken. 60 const v1, 0x0 61 const-string v2, "UTF8" 62 invoke-direct {v0, v1, v2}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 63 return-void 64 65 :return 66 return-void 67 68 .end method 69 70 # Test deoptimization between String's allocation and initialization. When not 71 # compiling --debuggable, the NewInstance will be optimized out. 72 73 ## CHECK-START: int TestCase.deoptimizeNewInstance(int[], byte[]) register (after) 74 ## CHECK: <<Null:l\d+>> NullConstant 75 ## CHECK: Deoptimize env:[[<<Null>>,{{.*]]}} 76 ## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init> 77 78 ## CHECK-START-DEBUGGABLE: int TestCase.deoptimizeNewInstance(int[], byte[]) register (after) 79 ## CHECK: <<String:l\d+>> NewInstance 80 ## CHECK: Deoptimize env:[[<<String>>,{{.*]]}} 81 ## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init> 82 83 .method public static deoptimizeNewInstance([I[B)I 84 .registers 6 85 86 const v2, 0x0 87 const v1, 0x1 88 89 new-instance v0, Ljava/lang/String; # HNewInstance(String) 90 91 # Deoptimize here if the array is too short. 92 aget v1, p0, v1 # v1 = int_array[0x1] 93 add-int/2addr v2, v1 # v2 = 0x0 + v1 94 95 # Check that we're being executed by the interpreter. 96 invoke-static {}, LMain;->assertIsInterpreted()V 97 98 # String allocation should succeed. 99 const-string v3, "UTF8" 100 invoke-direct {v0, p1, v3}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 101 # Transformed into invoke StringFactory(p1,v3). 102 # The use of v0 is dropped (so HNewInstance(String) ends up having 0 uses and is removed). 103 104 # This ArrayGet will throw ArrayIndexOutOfBoundsException. 105 const v1, 0x4 106 aget v1, p0, v1 107 add-int/2addr v2, v1 108 109 return v2 110 111 .end method 112 113 # Test that a redundant NewInstance is removed if not used and not compiling 114 # --debuggable. 115 116 ## CHECK-START: java.lang.String TestCase.removeNewInstance(byte[]) register (after) 117 ## CHECK-NOT: NewInstance 118 ## CHECK-NOT: LoadClass 119 120 ## CHECK-START-DEBUGGABLE: java.lang.String TestCase.removeNewInstance(byte[]) register (after) 121 ## CHECK: NewInstance 122 123 .method public static removeNewInstance([B)Ljava/lang/String; 124 .registers 5 125 126 new-instance v0, Ljava/lang/String; 127 const-string v1, "UTF8" 128 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 129 return-object v0 130 # Although it looks like we "use" the new-instance v0 here, the optimizing compiler 131 # transforms all uses of the new-instance into uses of the StringFactory invoke. 132 # therefore the HNewInstance for v0 becomes dead and is removed. 133 134 .end method 135 136 # Test #1 for irreducible loops and String.<init>. 137 .method public static irreducibleLoopAndStringInit1([BZ)Ljava/lang/String; 138 .registers 5 139 140 new-instance v0, Ljava/lang/String; 141 142 # Irreducible loop 143 if-eqz p1, :loop_entry 144 :loop_header 145 xor-int/lit8 p1, p1, 0x1 146 :loop_entry 147 if-eqz p1, :string_init 148 goto :loop_header 149 150 :string_init 151 const-string v1, "UTF8" 152 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 153 return-object v0 154 155 .end method 156 157 # Test #2 for irreducible loops and String.<init>. 158 .method public static irreducibleLoopAndStringInit2([BZ)Ljava/lang/String; 159 .registers 5 160 161 new-instance v0, Ljava/lang/String; 162 163 # Irreducible loop 164 if-eqz p1, :loop_entry 165 :loop_header 166 if-eqz p1, :string_init 167 :loop_entry 168 xor-int/lit8 p1, p1, 0x1 169 goto :loop_header 170 171 :string_init 172 const-string v1, "UTF8" 173 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 174 return-object v0 175 176 .end method 177 178 # Test #3 for irreducible loops and String.<init> alias. 179 .method public static irreducibleLoopAndStringInit3([BZ)Ljava/lang/String; 180 .registers 5 181 182 new-instance v0, Ljava/lang/String; 183 move-object v2, v0 184 185 # Irreducible loop 186 if-eqz p1, :loop_entry 187 :loop_header 188 xor-int/lit8 p1, p1, 0x1 189 :loop_entry 190 if-eqz p1, :string_init 191 goto :loop_header 192 193 :string_init 194 const-string v1, "UTF8" 195 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 196 return-object v2 197 198 .end method 199 200 # Test with a loop between allocation and String.<init>. 201 .method public static loopAndStringInit([BZ)Ljava/lang/String; 202 .registers 5 203 204 new-instance v0, Ljava/lang/String; 205 206 # Loop 207 :loop_header 208 if-eqz p1, :loop_exit 209 xor-int/lit8 p1, p1, 0x1 210 goto :loop_header 211 212 :loop_exit 213 const-string v1, "UTF8" 214 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 215 return-object v0 216 217 .end method 218 219 # Test with a loop and aliases between allocation and String.<init>. 220 .method public static loopAndStringInitAlias([BZ)Ljava/lang/String; 221 .registers 5 222 223 new-instance v0, Ljava/lang/String; 224 move-object v2, v0 225 226 # Loop 227 :loop_header 228 if-eqz p1, :loop_exit 229 xor-int/lit8 p1, p1, 0x1 230 goto :loop_header 231 232 :loop_exit 233 const-string v1, "UTF8" 234 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 235 return-object v2 236 237 .end method 238 239 # Test deoptimization after String initialization of a phi. 240 ## CHECK-START: int TestCase.deoptimizeNewInstanceAfterLoop(int[], byte[], int) register (after) 241 ## CHECK: <<Invoke:l\d+>> InvokeStaticOrDirect method_name:java.lang.String.<init> 242 ## CHECK: Deoptimize env:[[<<Invoke>>,{{.*]]}} 243 244 .method public static deoptimizeNewInstanceAfterLoop([I[BI)I 245 .registers 8 246 247 const v2, 0x0 248 const v1, 0x1 249 250 new-instance v0, Ljava/lang/String; # HNewInstance(String) 251 move-object v4, v0 252 # Loop 253 :loop_header 254 if-eqz p2, :loop_exit 255 xor-int/lit8 p2, p2, 0x1 256 goto :loop_header 257 258 :loop_exit 259 const-string v3, "UTF8" 260 invoke-direct {v0, p1, v3}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 261 262 # Deoptimize here if the array is too short. 263 aget v1, p0, v1 # v1 = int_array[0x1] 264 add-int/2addr v2, v1 # v2 = 0x0 + v1 265 266 # Check that we're being executed by the interpreter. 267 invoke-static {}, LMain;->assertIsInterpreted()V 268 269 # Check that the environments contain the right string. 270 invoke-static {p1, v0}, LMain;->assertEqual([BLjava/lang/String;)V 271 invoke-static {p1, v4}, LMain;->assertEqual([BLjava/lang/String;)V 272 273 # This ArrayGet will throw ArrayIndexOutOfBoundsException. 274 const v1, 0x4 275 aget v1, p0, v1 276 add-int/2addr v2, v1 277 278 return v2 279 280 .end method 281 282 # Test with a loop between allocation and String.<init> and a null check. 283 ## CHECK-START: java.lang.String TestCase.loopAndStringInitAndTest(byte[], boolean) builder (after) 284 ## CHECK-DAG: <<Null:l\d+>> NullConstant 285 ## CHECK-DAG: <<String:l\d+>> NewInstance 286 ## CHECK-DAG: <<Cond:z\d+>> NotEqual [<<String>>,<<Null>>] 287 288 ## CHECK-START: java.lang.String TestCase.loopAndStringInitAndTest(byte[], boolean) register (after) 289 ## CHECK-DAG: <<String:l\d+>> NewInstance 290 .method public static loopAndStringInitAndTest([BZ)Ljava/lang/String; 291 .registers 5 292 293 new-instance v0, Ljava/lang/String; 294 295 # Loop 296 :loop_header 297 # Use the new-instance in the only way it can be used. 298 if-nez v0, :loop_exit 299 xor-int/lit8 p1, p1, 0x1 300 goto :loop_header 301 302 :loop_exit 303 const-string v1, "UTF8" 304 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 305 return-object v0 306 307 .end method 308 309 ## CHECK-START: java.lang.String TestCase.loopAndStringInitAndPhi(byte[], boolean) register (after) 310 ## CHECK-NOT: NewInstance 311 ## CHECK-DAG: <<Invoke1:l\d+>> InvokeStaticOrDirect method_name:java.lang.String.<init> 312 ## CHECK-DAG: <<Invoke2:l\d+>> InvokeStaticOrDirect method_name:java.lang.String.<init> 313 ## CHECK-DAG: <<Phi:l\d+>> Phi [<<Invoke2>>,<<Invoke1>>] 314 ## CHECK-DAG: Return [<<Phi>>] 315 .method public static loopAndStringInitAndPhi([BZ)Ljava/lang/String; 316 .registers 4 317 318 if-nez p1, :allocate_other 319 new-instance v0, Ljava/lang/String; 320 321 # Loop 322 :loop_header 323 if-eqz p1, :loop_exit 324 goto :loop_header 325 326 :loop_exit 327 const-string v1, "UTF8" 328 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 329 goto : exit 330 331 :allocate_other 332 const-string v1, "UTF8" 333 new-instance v0, Ljava/lang/String; 334 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 335 :exit 336 return-object v0 337 338 .end method 339 340 .method public static loopAndTwoStringInitAndPhi([BZZ)Ljava/lang/String; 341 .registers 6 342 343 new-instance v0, Ljava/lang/String; 344 new-instance v2, Ljava/lang/String; 345 346 if-nez p2, :allocate_other 347 348 # Loop 349 :loop_header 350 if-eqz p1, :loop_exit 351 goto :loop_header 352 353 :loop_exit 354 const-string v1, "UTF8" 355 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 356 goto :exit 357 358 :allocate_other 359 360 # Loop 361 :loop_header2 362 if-eqz p1, :loop_exit2 363 goto :loop_header2 364 365 :loop_exit2 366 const-string v1, "UTF8" 367 invoke-direct {v2, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 368 move-object v0, v2 369 370 :exit 371 return-object v0 372 373 .end method 374 375 # Regression test for a new string flowing into a catch phi. 376 .method public static stringAndCatch([BZ)Ljava/lang/Object; 377 .registers 4 378 379 const v0, 0x0 380 381 :try_start_a 382 new-instance v0, Ljava/lang/String; 383 384 # Loop 385 :loop_header 386 if-eqz p1, :loop_exit 387 goto :loop_header 388 389 :loop_exit 390 const-string v1, "UTF8" 391 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 392 goto :exit 393 :try_end_a 394 .catch Ljava/lang/Exception; {:try_start_a .. :try_end_a} :catch_a 395 396 :catch_a 397 # Initially, we create a catch phi with the potential uninitalized string, which used to 398 # trip the compiler. However, using that catch phi is an error caught by the verifier, so 399 # having the phi is benign. 400 const v0, 0x0 401 402 :exit 403 return-object v0 404 405 .end method 406 407 # Same test as above, but with a catch phi being used by the string constructor. 408 .method public static stringAndCatch2([BZ)Ljava/lang/Object; 409 .registers 4 410 411 const v0, 0x0 412 new-instance v0, Ljava/lang/String; 413 414 :try_start_a 415 const-string v1, "UTF8" 416 :try_end_a 417 .catch Ljava/lang/Exception; {:try_start_a .. :try_end_a} :catch_a 418 419 :catch_a 420 const-string v1, "UTF8" 421 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 422 return-object v0 423 424 .end method 425 426 # Same test as above, but with a catch phi being used by the string constructor and 427 # a null test. 428 .method public static stringAndCatch3([BZ)Ljava/lang/Object; 429 .registers 4 430 431 const v0, 0x0 432 new-instance v0, Ljava/lang/String; 433 434 :try_start_a 435 const-string v1, "UTF8" 436 :try_end_a 437 .catch Ljava/lang/Exception; {:try_start_a .. :try_end_a} :catch_a 438 439 :catch_a 440 if-eqz v0, :unexpected 441 const-string v1, "UTF8" 442 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 443 goto :exit 444 :unexpected 445 const-string v0, "UTF8" 446 :exit 447 return-object v0 448 449 .end method 450 451 # Regression test that tripped the compiler. 452 .method public static stringAndPhi([BZ)Ljava/lang/Object; 453 .registers 4 454 455 new-instance v0, Ljava/lang/String; 456 const-string v1, "UTF8" 457 458 :loop_header 459 if-nez p1, :unused 460 if-eqz p1, :invoke 461 goto :loop_header 462 463 :invoke 464 invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 465 goto :exit 466 467 :unused 468 const-string v0, "UTF8" 469 if-nez p1, :exit 470 goto :unused 471 472 :exit 473 return-object v0 474 475 .end method 476