# Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .class public LTestCase; .super Ljava/lang/Object; # Test that all vregs holding the new-instance are updated after the # StringFactory call. ## CHECK-START: java.lang.String TestCase.vregAliasing(byte[]) register (after) ## CHECK-DAG: Return [<>] ## CHECK-DAG: <> InvokeStaticOrDirect method_name:java.lang.String. .method public static vregAliasing([B)Ljava/lang/String; .registers 5 # Create new instance of String and store it to v0, v1, v2. new-instance v0, Ljava/lang/String; move-object v1, v0 move-object v2, v0 # Call String. on v1. const-string v3, "UTF8" invoke-direct {v1, p0, v3}, Ljava/lang/String;->([BLjava/lang/String;)V # Return the object from v2. return-object v2 .end method # Test usage of String new-instance before it is initialized. ## CHECK-START: void TestCase.compareNewInstance() register (after) ## CHECK-DAG: <> InvokeStaticOrDirect method_name:Main.$noinline$HiddenNull ## CHECK-DAG: <> NewInstance ## CHECK-DAG: <> NotEqual [<>,<>] ## CHECK-DAG: If [<>] .method public static compareNewInstance()V .registers 3 invoke-static {}, LMain;->$noinline$HiddenNull()Ljava/lang/Object; move-result-object v1 new-instance v0, Ljava/lang/String; if-ne v0, v1, :return # Will throw NullPointerException if this branch is taken. const v1, 0x0 const-string v2, "UTF8" invoke-direct {v0, v1, v2}, Ljava/lang/String;->([BLjava/lang/String;)V return-void :return return-void .end method # Test deoptimization between String's allocation and initialization. When not # compiling --debuggable, the NewInstance will be optimized out. ## CHECK-START: int TestCase.deoptimizeNewInstance(int[], byte[]) register (after) ## CHECK: <> NullConstant ## CHECK: Deoptimize env:[[<>,{{.*]]}} ## CHECK: InvokeStaticOrDirect method_name:java.lang.String. ## CHECK-START-DEBUGGABLE: int TestCase.deoptimizeNewInstance(int[], byte[]) register (after) ## CHECK: <> NewInstance ## CHECK: Deoptimize env:[[<>,{{.*]]}} ## CHECK: InvokeStaticOrDirect method_name:java.lang.String. .method public static deoptimizeNewInstance([I[B)I .registers 6 const v2, 0x0 const v1, 0x1 new-instance v0, Ljava/lang/String; # HNewInstance(String) # Deoptimize here if the array is too short. aget v1, p0, v1 # v1 = int_array[0x1] add-int/2addr v2, v1 # v2 = 0x0 + v1 # Check that we're being executed by the interpreter. invoke-static {}, LMain;->assertIsInterpreted()V # String allocation should succeed. const-string v3, "UTF8" invoke-direct {v0, p1, v3}, Ljava/lang/String;->([BLjava/lang/String;)V # Transformed into invoke StringFactory(p1,v3). # The use of v0 is dropped (so HNewInstance(String) ends up having 0 uses and is removed). # This ArrayGet will throw ArrayIndexOutOfBoundsException. const v1, 0x4 aget v1, p0, v1 add-int/2addr v2, v1 return v2 .end method # Test that a redundant NewInstance is removed if not used and not compiling # --debuggable. ## CHECK-START: java.lang.String TestCase.removeNewInstance(byte[]) register (after) ## CHECK-NOT: NewInstance ## CHECK-NOT: LoadClass ## CHECK-START-DEBUGGABLE: java.lang.String TestCase.removeNewInstance(byte[]) register (after) ## CHECK: NewInstance .method public static removeNewInstance([B)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V return-object v0 # Although it looks like we "use" the new-instance v0 here, the optimizing compiler # transforms all uses of the new-instance into uses of the StringFactory invoke. # therefore the HNewInstance for v0 becomes dead and is removed. .end method # Test #1 for irreducible loops and String.. .method public static irreducibleLoopAndStringInit1([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; # Irreducible loop if-eqz p1, :loop_entry :loop_header xor-int/lit8 p1, p1, 0x1 :loop_entry if-eqz p1, :string_init goto :loop_header :string_init const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V return-object v0 .end method # Test #2 for irreducible loops and String.. .method public static irreducibleLoopAndStringInit2([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; # Irreducible loop if-eqz p1, :loop_entry :loop_header if-eqz p1, :string_init :loop_entry xor-int/lit8 p1, p1, 0x1 goto :loop_header :string_init const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V return-object v0 .end method # Test #3 for irreducible loops and String. alias. .method public static irreducibleLoopAndStringInit3([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; move-object v2, v0 # Irreducible loop if-eqz p1, :loop_entry :loop_header xor-int/lit8 p1, p1, 0x1 :loop_entry if-eqz p1, :string_init goto :loop_header :string_init const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V return-object v2 .end method # Test with a loop between allocation and String.. .method public static loopAndStringInit([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; # Loop :loop_header if-eqz p1, :loop_exit xor-int/lit8 p1, p1, 0x1 goto :loop_header :loop_exit const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V return-object v0 .end method # Test with a loop and aliases between allocation and String.. .method public static loopAndStringInitAlias([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; move-object v2, v0 # Loop :loop_header if-eqz p1, :loop_exit xor-int/lit8 p1, p1, 0x1 goto :loop_header :loop_exit const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V return-object v2 .end method # Test deoptimization after String initialization of a phi. ## CHECK-START: int TestCase.deoptimizeNewInstanceAfterLoop(int[], byte[], int) register (after) ## CHECK: <> InvokeStaticOrDirect method_name:java.lang.String. ## CHECK: Deoptimize env:[[<>,{{.*]]}} .method public static deoptimizeNewInstanceAfterLoop([I[BI)I .registers 8 const v2, 0x0 const v1, 0x1 new-instance v0, Ljava/lang/String; # HNewInstance(String) move-object v4, v0 # Loop :loop_header if-eqz p2, :loop_exit xor-int/lit8 p2, p2, 0x1 goto :loop_header :loop_exit const-string v3, "UTF8" invoke-direct {v0, p1, v3}, Ljava/lang/String;->([BLjava/lang/String;)V # Deoptimize here if the array is too short. aget v1, p0, v1 # v1 = int_array[0x1] add-int/2addr v2, v1 # v2 = 0x0 + v1 # Check that we're being executed by the interpreter. invoke-static {}, LMain;->assertIsInterpreted()V # Check that the environments contain the right string. invoke-static {p1, v0}, LMain;->assertEqual([BLjava/lang/String;)V invoke-static {p1, v4}, LMain;->assertEqual([BLjava/lang/String;)V # This ArrayGet will throw ArrayIndexOutOfBoundsException. const v1, 0x4 aget v1, p0, v1 add-int/2addr v2, v1 return v2 .end method # Test with a loop between allocation and String. and a null check. ## CHECK-START: java.lang.String TestCase.loopAndStringInitAndTest(byte[], boolean) builder (after) ## CHECK-DAG: <> NullConstant ## CHECK-DAG: <> NewInstance ## CHECK-DAG: <> NotEqual [<>,<>] ## CHECK-START: java.lang.String TestCase.loopAndStringInitAndTest(byte[], boolean) register (after) ## CHECK-DAG: <> NewInstance .method public static loopAndStringInitAndTest([BZ)Ljava/lang/String; .registers 5 new-instance v0, Ljava/lang/String; # Loop :loop_header # Use the new-instance in the only way it can be used. if-nez v0, :loop_exit xor-int/lit8 p1, p1, 0x1 goto :loop_header :loop_exit const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V return-object v0 .end method ## CHECK-START: java.lang.String TestCase.loopAndStringInitAndPhi(byte[], boolean) register (after) ## CHECK-NOT: NewInstance ## CHECK-DAG: <> InvokeStaticOrDirect method_name:java.lang.String. ## CHECK-DAG: <> InvokeStaticOrDirect method_name:java.lang.String. ## CHECK-DAG: <> Phi [<>,<>] ## CHECK-DAG: Return [<>] .method public static loopAndStringInitAndPhi([BZ)Ljava/lang/String; .registers 4 if-nez p1, :allocate_other new-instance v0, Ljava/lang/String; # Loop :loop_header if-eqz p1, :loop_exit goto :loop_header :loop_exit const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V goto : exit :allocate_other const-string v1, "UTF8" new-instance v0, Ljava/lang/String; invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V :exit return-object v0 .end method .method public static loopAndTwoStringInitAndPhi([BZZ)Ljava/lang/String; .registers 6 new-instance v0, Ljava/lang/String; new-instance v2, Ljava/lang/String; if-nez p2, :allocate_other # Loop :loop_header if-eqz p1, :loop_exit goto :loop_header :loop_exit const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V goto :exit :allocate_other # Loop :loop_header2 if-eqz p1, :loop_exit2 goto :loop_header2 :loop_exit2 const-string v1, "UTF8" invoke-direct {v2, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V move-object v0, v2 :exit return-object v0 .end method # Regression test for a new string flowing into a catch phi. .method public static stringAndCatch([BZ)Ljava/lang/Object; .registers 4 const v0, 0x0 :try_start_a new-instance v0, Ljava/lang/String; # Loop :loop_header if-eqz p1, :loop_exit goto :loop_header :loop_exit const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V goto :exit :try_end_a .catch Ljava/lang/Exception; {:try_start_a .. :try_end_a} :catch_a :catch_a # Initially, we create a catch phi with the potential uninitalized string, which used to # trip the compiler. However, using that catch phi is an error caught by the verifier, so # having the phi is benign. const v0, 0x0 :exit return-object v0 .end method # Same test as above, but with a catch phi being used by the string constructor. .method public static stringAndCatch2([BZ)Ljava/lang/Object; .registers 4 const v0, 0x0 new-instance v0, Ljava/lang/String; :try_start_a const-string v1, "UTF8" :try_end_a .catch Ljava/lang/Exception; {:try_start_a .. :try_end_a} :catch_a :catch_a const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V return-object v0 .end method # Same test as above, but with a catch phi being used by the string constructor and # a null test. .method public static stringAndCatch3([BZ)Ljava/lang/Object; .registers 4 const v0, 0x0 new-instance v0, Ljava/lang/String; :try_start_a const-string v1, "UTF8" :try_end_a .catch Ljava/lang/Exception; {:try_start_a .. :try_end_a} :catch_a :catch_a if-eqz v0, :unexpected const-string v1, "UTF8" invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V goto :exit :unexpected const-string v0, "UTF8" :exit return-object v0 .end method # Regression test that tripped the compiler. .method public static stringAndPhi([BZ)Ljava/lang/Object; .registers 4 new-instance v0, Ljava/lang/String; const-string v1, "UTF8" :loop_header if-nez p1, :unused if-eqz p1, :invoke goto :loop_header :invoke invoke-direct {v0, p0, v1}, Ljava/lang/String;->([BLjava/lang/String;)V goto :exit :unused const-string v0, "UTF8" if-nez p1, :exit goto :unused :exit return-object v0 .end method