| # Copyright (C) 2015 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 LRuntime; |
| .super Ljava/lang/Object; |
| |
| # The following tests all share the same structure, signature and return values: |
| # - foo(false, false): normal path, returns 42 |
| # - foo(true, false): exceptional path #1, returns 3 |
| # - foo(false, true): exceptional path #2, returns 8 |
| # - foo(true, true): undefined |
| |
| |
| # Test register allocation of 32-bit core intervals crossing catch block positions. |
| |
| ## CHECK-START: int Runtime.testUseAfterCatch_int(boolean, boolean) register (after) |
| ## CHECK-NOT: Phi is_catch_phi:true |
| |
| .method public static testUseAfterCatch_int(ZZ)I |
| .registers 6 |
| |
| sget-object v0, LRuntime;->intArray:[I |
| const/4 v1, 0 |
| aget v1, v0, v1 |
| const/4 v2, 1 |
| aget v2, v0, v2 |
| const/4 v3, 2 |
| aget v3, v0, v3 |
| |
| :try_start |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| return v3 # Normal path return. |
| |
| :catch_all |
| if-eqz p0, :second_throw |
| return v1 # Exceptional path #1 return. |
| |
| :second_throw |
| return v2 # Exceptional path #2 return. |
| .end method |
| |
| |
| # Test register allocation of 64-bit core intervals crossing catch block positions. |
| |
| # The sum of the low and high 32 bits treated as integers is returned to prove |
| # that both vregs allocated correctly. |
| |
| ## CHECK-START: int Runtime.testUseAfterCatch_long(boolean, boolean) register (after) |
| ## CHECK-NOT: Phi is_catch_phi:true |
| |
| .method public static testUseAfterCatch_long(ZZ)I |
| .registers 10 |
| |
| sget-object v0, LRuntime;->longArray:[J |
| const/4 v1, 0 |
| aget-wide v1, v0, v1 |
| const/4 v3, 1 |
| aget-wide v3, v0, v3 |
| const/4 v5, 2 |
| aget-wide v5, v0, v5 |
| |
| :try_start |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| const v0, 32 |
| ushr-long v7, v5, v0 |
| long-to-int v5, v5 |
| long-to-int v7, v7 |
| add-int/2addr v5, v7 |
| return v5 # Normal path return. |
| |
| :catch_all |
| const v0, 32 |
| if-eqz p0, :second_throw |
| |
| ushr-long v7, v1, v0 |
| long-to-int v1, v1 |
| long-to-int v7, v7 |
| add-int/2addr v1, v7 |
| return v1 # Exceptional path #1 return. |
| |
| :second_throw |
| ushr-long v7, v3, v0 |
| long-to-int v3, v3 |
| long-to-int v7, v7 |
| add-int/2addr v3, v7 |
| return v3 # Exceptional path #2 return. |
| .end method |
| |
| |
| # Test register allocation of 32-bit floating-point intervals crossing catch block positions. |
| |
| ## CHECK-START: int Runtime.testUseAfterCatch_float(boolean, boolean) register (after) |
| ## CHECK-NOT: Phi is_catch_phi:true |
| |
| .method public static testUseAfterCatch_float(ZZ)I |
| .registers 6 |
| |
| sget-object v0, LRuntime;->floatArray:[F |
| const/4 v1, 0 |
| aget v1, v0, v1 |
| const/4 v2, 1 |
| aget v2, v0, v2 |
| const/4 v3, 2 |
| aget v3, v0, v3 |
| |
| :try_start |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| float-to-int v3, v3 |
| return v3 # Normal path return. |
| |
| :catch_all |
| if-eqz p0, :second_throw |
| float-to-int v1, v1 |
| return v1 # Exceptional path #1 return. |
| |
| :second_throw |
| float-to-int v2, v2 |
| return v2 # Exceptional path #2 return. |
| .end method |
| |
| |
| # Test register allocation of 64-bit floating-point intervals crossing catch block positions. |
| |
| ## CHECK-START: int Runtime.testUseAfterCatch_double(boolean, boolean) register (after) |
| ## CHECK-NOT: Phi is_catch_phi:true |
| |
| .method public static testUseAfterCatch_double(ZZ)I |
| .registers 10 |
| |
| sget-object v0, LRuntime;->doubleArray:[D |
| const/4 v1, 0 |
| aget-wide v1, v0, v1 |
| const/4 v3, 1 |
| aget-wide v3, v0, v3 |
| const/4 v5, 2 |
| aget-wide v5, v0, v5 |
| |
| :try_start |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| double-to-int v5, v5 |
| return v5 # Normal path return. |
| |
| :catch_all |
| if-eqz p0, :second_throw |
| double-to-int v1, v1 |
| return v1 # Exceptional path #1 return. |
| |
| :second_throw |
| double-to-int v3, v3 |
| return v3 # Exceptional path #2 return. |
| .end method |
| |
| |
| # Test catch-phi runtime support for constant values. |
| |
| # Register v0 holds different constants at two throwing instructions. Runtime is |
| # expected to load them from stack map and copy to the catch phi's location. |
| |
| ## CHECK-START: int Runtime.testCatchPhi_const(boolean, boolean) register (after) |
| ## CHECK-DAG: <<Const3:i\d+>> IntConstant 3 |
| ## CHECK-DAG: <<Const8:i\d+>> IntConstant 8 |
| ## CHECK-DAG: Phi [<<Const3>>,<<Const8>>] is_catch_phi:true |
| |
| .method public static testCatchPhi_const(ZZ)I |
| .registers 3 |
| |
| :try_start |
| const v0, 3 |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| |
| const v0, 8 |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| const v0, 42 |
| return v0 # Normal path return. |
| |
| :catch_all |
| return v0 # Exceptional path #1/#2 return. |
| .end method |
| |
| |
| # Test catch-phi runtime support for 32-bit values stored in core registers. |
| |
| # Register v0 holds different integer values at two throwing instructions. |
| # Runtime is expected to find their location in the stack map and copy the value |
| # to the location of the catch phi. |
| |
| ## CHECK-START: int Runtime.testCatchPhi_int(boolean, boolean) register (after) |
| ## CHECK-DAG: <<Val1:i\d+>> ArrayGet |
| ## CHECK-DAG: <<Val2:i\d+>> ArrayGet |
| ## CHECK-DAG: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true |
| |
| .method public static testCatchPhi_int(ZZ)I |
| .registers 6 |
| |
| sget-object v0, LRuntime;->intArray:[I |
| const/4 v1, 0 |
| aget v1, v0, v1 |
| const/4 v2, 1 |
| aget v2, v0, v2 |
| const/4 v3, 2 |
| aget v3, v0, v3 |
| |
| :try_start |
| move v0, v1 # Set catch phi value |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| |
| move v0, v2 # Set catch phi value |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| return v3 # Normal path return. |
| |
| :catch_all |
| return v0 # Exceptional path #1/#2 return. |
| .end method |
| |
| |
| # Test catch-phi runtime support for 64-bit values stored in core registers. |
| |
| # Register pair (v0, v1) holds different long values at two throwing instructions. |
| # Runtime is expected to find their location in the stack map and copy the value |
| # to the location of the catch phi. The sum of the low and high 32 bits treated |
| # as integers is returned to prove that both vregs were copied. |
| |
| # Note: values will be spilled on x86 because of too few callee-save core registers. |
| |
| ## CHECK-START: int Runtime.testCatchPhi_long(boolean, boolean) register (after) |
| ## CHECK-DAG: <<Val1:j\d+>> ArrayGet |
| ## CHECK-DAG: <<Val2:j\d+>> ArrayGet |
| ## CHECK-DAG: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true |
| |
| .method public static testCatchPhi_long(ZZ)I |
| .registers 10 |
| |
| sget-object v0, LRuntime;->longArray:[J |
| const/4 v2, 0 |
| aget-wide v2, v0, v2 |
| const/4 v4, 1 |
| aget-wide v4, v0, v4 |
| const/4 v6, 2 |
| aget-wide v6, v0, v6 |
| |
| :try_start |
| move-wide v0, v2 # Set catch phi value |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| |
| move-wide v0, v4 # Set catch phi value |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| const v2, 32 |
| ushr-long v2, v6, v2 |
| long-to-int v2, v2 |
| long-to-int v6, v6 |
| add-int/2addr v6, v2 |
| return v6 # Normal path return. |
| |
| :catch_all |
| const v2, 32 |
| ushr-long v2, v0, v2 |
| long-to-int v2, v2 |
| long-to-int v0, v0 |
| add-int/2addr v0, v2 |
| return v0 # Exceptional path #1/#2 return. |
| .end method |
| |
| |
| # Test catch-phi runtime support for 32-bit values stored in FPU registers. |
| |
| # Register v0 holds different float values at two throwing instructions. Runtime |
| # is expected to find their location in the stack map and copy the value to the |
| # location of the catch phi. The value is converted to int and returned. |
| |
| # Note: values will be spilled on x86 as there are no callee-save FPU registers. |
| |
| ## CHECK-START: int Runtime.testCatchPhi_float(boolean, boolean) register (after) |
| ## CHECK-DAG: <<Val1:f\d+>> ArrayGet |
| ## CHECK-DAG: <<Val2:f\d+>> ArrayGet |
| ## CHECK-DAG: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true |
| |
| .method public static testCatchPhi_float(ZZ)I |
| .registers 6 |
| |
| sget-object v0, LRuntime;->floatArray:[F |
| const/4 v1, 0 |
| aget v1, v0, v1 |
| const/4 v2, 1 |
| aget v2, v0, v2 |
| const/4 v3, 2 |
| aget v3, v0, v3 |
| |
| :try_start |
| move v0, v1 # Set catch phi value |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| |
| move v0, v2 # Set catch phi value |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| float-to-int v3, v3 |
| return v3 # Normal path return. |
| |
| :catch_all |
| float-to-int v0, v0 |
| return v0 # Exceptional path #1/#2 return. |
| .end method |
| |
| |
| # Test catch-phi runtime support for 64-bit values stored in FPU registers. |
| |
| # Register pair (v0, v1) holds different double values at two throwing instructions. |
| # Runtime is expected to find their location in the stack map and copy the value |
| # to the location of the catch phi. The value is converted to int and returned. |
| # Values were chosen so that all 64 bits are used. |
| |
| # Note: values will be spilled on x86 as there are no callee-save FPU registers. |
| |
| ## CHECK-START: int Runtime.testCatchPhi_double(boolean, boolean) register (after) |
| ## CHECK-DAG: <<Val1:d\d+>> ArrayGet |
| ## CHECK-DAG: <<Val2:d\d+>> ArrayGet |
| ## CHECK-DAG: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true |
| |
| .method public static testCatchPhi_double(ZZ)I |
| .registers 10 |
| |
| sget-object v0, LRuntime;->doubleArray:[D |
| const/4 v2, 0 |
| aget-wide v2, v0, v2 |
| const/4 v4, 1 |
| aget-wide v4, v0, v4 |
| const/4 v6, 2 |
| aget-wide v6, v0, v6 |
| |
| :try_start |
| move-wide v0, v2 # Set catch phi value |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| |
| move-wide v0, v4 # Set catch phi value |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| double-to-int v6, v6 |
| return v6 |
| |
| :catch_all |
| double-to-int v0, v0 |
| return v0 |
| .end method |
| |
| # Test catch-phi runtime support for 32-bit values stored on the stack. |
| |
| # Register v0 holds different integer values at two throwing instructions. |
| # These values were forced to spill by an always-throwing try/catch after their |
| # definition. Runtime is expected to find their location in the stack map and |
| # copy the value to the location of the catch phi. The value is then returned. |
| |
| ## CHECK-START: int Runtime.testCatchPhi_singleSlot(boolean, boolean) register (after) |
| ## CHECK: <<Val1:i\d+>> ArrayGet |
| ## CHECK-NEXT: ParallelMove moves:[{{.*->}}{{\d+}}(sp)] |
| ## CHECK: <<Val2:i\d+>> ArrayGet |
| ## CHECK-NEXT: ParallelMove moves:[{{.*->}}{{\d+}}(sp)] |
| ## CHECK: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true |
| |
| .method public static testCatchPhi_singleSlot(ZZ)I |
| .registers 6 |
| |
| sget-object v0, LRuntime;->intArray:[I |
| const/4 v1, 0 |
| aget v1, v0, v1 |
| const/4 v2, 1 |
| aget v2, v0, v2 |
| const/4 v3, 2 |
| aget v3, v0, v3 |
| |
| # Insert a try/catch to force v1,v2,v3 to spill. |
| :try_start_spill |
| const/4 v0, 1 |
| invoke-static {v0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end_spill |
| .catchall {:try_start_spill .. :try_end_spill} :catch_all_spill |
| return v0 # Unreachable |
| :catch_all_spill # Catch and continue |
| |
| :try_start |
| move v0, v1 # Set catch phi value |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| |
| move v0, v2 # Set catch phi value |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| return v3 # Normal path return. |
| |
| :catch_all |
| return v0 # Exceptional path #1/#2 return. |
| .end method |
| |
| # Test catch-phi runtime support for 64-bit values stored on the stack. |
| |
| # Register pair (v0, v1) holds different double values at two throwing instructions. |
| # These values were forced to spill by an always-throwing try/catch after their |
| # definition. Runtime is expected to find their location in the stack map and |
| # copy the value to the location of the catch phi. The value is converted to int |
| # and returned. Values were chosen so that all 64 bits are used. |
| |
| ## CHECK-START: int Runtime.testCatchPhi_doubleSlot(boolean, boolean) register (after) |
| ## CHECK: <<Val1:d\d+>> ArrayGet |
| ## CHECK-NEXT: ParallelMove moves:[{{.*->}}2x{{\d+}}(sp)] |
| ## CHECK: <<Val2:d\d+>> ArrayGet |
| ## CHECK-NEXT: ParallelMove moves:[{{.*->}}2x{{\d+}}(sp)] |
| ## CHECK: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true |
| |
| .method public static testCatchPhi_doubleSlot(ZZ)I |
| .registers 10 |
| |
| sget-object v0, LRuntime;->doubleArray:[D |
| const/4 v2, 0 |
| aget-wide v2, v0, v2 |
| const/4 v4, 1 |
| aget-wide v4, v0, v4 |
| const/4 v6, 2 |
| aget-wide v6, v0, v6 |
| |
| # Insert a try/catch to force (v2, v3), (v4, v5), (v6, v7) to spill. |
| :try_start_spill |
| const/4 v0, 1 |
| invoke-static {v0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end_spill |
| .catchall {:try_start_spill .. :try_end_spill} :catch_all_spill |
| return v0 # Unreachable |
| :catch_all_spill # Catch and continue |
| |
| :try_start |
| move-wide v0, v2 # Set catch phi value |
| invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| |
| move-wide v0, v4 # Set catch phi value |
| invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V |
| :try_end |
| .catchall {:try_start .. :try_end} :catch_all |
| |
| double-to-int v6, v6 |
| return v6 # Normal path return. |
| |
| :catch_all |
| double-to-int v0, v0 |
| return v0 # Exceptional path #1/#2 return. |
| .end method |
| |
| |
| |
| # Helper methods and initialization. |
| |
| .method public static $noinline$ThrowIfTrue(Z)V |
| .registers 2 |
| if-nez p0, :throw |
| return-void |
| |
| :throw |
| new-instance v0, Ljava/lang/Exception; |
| invoke-direct {v0}, Ljava/lang/Exception;-><init>()V |
| throw v0 |
| .end method |
| |
| .method public static constructor <clinit>()V |
| .registers 2 |
| |
| const/4 v1, 4 |
| |
| new-array v0, v1, [I |
| fill-array-data v0, :array_int |
| sput-object v0, LRuntime;->intArray:[I |
| |
| new-array v0, v1, [J |
| fill-array-data v0, :array_long |
| sput-object v0, LRuntime;->longArray:[J |
| |
| new-array v0, v1, [F |
| fill-array-data v0, :array_float |
| sput-object v0, LRuntime;->floatArray:[F |
| |
| new-array v0, v1, [D |
| fill-array-data v0, :array_double |
| sput-object v0, LRuntime;->doubleArray:[D |
| |
| return-void |
| |
| :array_int |
| .array-data 4 |
| 0x03 # int 3 |
| 0x08 # int 8 |
| 0x2a # int 42 |
| .end array-data |
| |
| :array_long |
| .array-data 8 |
| 0x0000000100000002L # long (1 << 32) + 2 |
| 0x0000000500000003L # long (5 << 32) + 3 |
| 0x0000001e0000000cL # long (30 << 32) + 12 |
| .end array-data |
| |
| :array_float |
| .array-data 4 |
| 0x40400000 # float 3 |
| 0x41000000 # float 8 |
| 0x42280000 # float 42 |
| .end array-data |
| |
| :array_double |
| .array-data 8 |
| 0x400b333333333333L # double 3.4 |
| 0x4020cccccccccccdL # double 8.4 |
| 0x4045333333333333L # double 42.4 |
| .end array-data |
| .end method |
| |
| |
| ## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after) |
| ## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 |
| ## CHECK-DAG: <<Offset:i\d+>> IntConstant 12 |
| ## CHECK-DAG: <<IndexParam:i\d+>> ParameterValue |
| ## CHECK-DAG: <<Array:l\d+>> ParameterValue |
| |
| ## CHECK-DAG: <<NullCh1:l\d+>> NullCheck [<<Array>>] |
| ## CHECK-DAG: <<Length:i\d+>> ArrayLength |
| ## CHECK-DAG: <<BoundsCh1:i\d+>> BoundsCheck [<<IndexParam>>,<<Length>>] |
| ## CHECK-DAG: <<IntAddr1:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>] |
| ## CHECK-DAG: ArrayGet [<<IntAddr1>>,<<BoundsCh1>>] |
| ## CHECK-DAG: TryBoundary |
| |
| ## CHECK-DAG: <<Xplus1:i\d+>> Add [<<IndexParam>>,<<Const1>>] |
| ## CHECK-DAG: <<BoundsCh2:i\d+>> BoundsCheck [<<Xplus1>>,<<Length>>] |
| ## CHECK-DAG: ArrayGet [<<IntAddr1>>,<<BoundsCh2>>] |
| ## CHECK-DAG: TryBoundary |
| |
| ## CHECK-DAG: <<Phi:i\d+>> Phi [<<Xplus1>>] |
| ## CHECK-DAG: <<Phiplus1:i\d+>> Add [<<Phi>>,<<Const1>>] |
| ## CHECK-DAG: <<BoundsCh3:i\d+>> BoundsCheck [<<Phiplus1>>,<<Length>>] |
| ## CHECK-DAG: <<IntAddr3:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>] |
| ## CHECK-DAG: ArrayGet [<<IntAddr3>>,<<BoundsCh3>>] |
| |
| ## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after) |
| ## CHECK: NullCheck |
| ## CHECK-NOT: NullCheck |
| |
| ## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after) |
| ## CHECK: IntermediateAddress |
| ## CHECK: IntermediateAddress |
| ## CHECK-NOT: IntermediateAddress |
| |
| ## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after) |
| ## CHECK: BoundsCheck |
| ## CHECK: BoundsCheck |
| ## CHECK: BoundsCheck |
| ## CHECK-NOT: BoundsCheck |
| |
| ## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after) |
| ## CHECK: ArrayGet |
| ## CHECK: ArrayGet |
| ## CHECK: ArrayGet |
| ## CHECK-NOT: ArrayGet |
| .method public static testIntAddressCatch(I[I)I |
| .registers 4 |
| aget v0, p1, p0 |
| add-int v1, v0, v0 |
| |
| :try_start |
| const/4 v0, 0x1 |
| add-int p0, p0, v0 |
| aget v0, p1, p0 |
| |
| :try_end |
| .catch Ljava/lang/ArithmException; {:try_start .. :try_end} :catch_block |
| |
| :return |
| add-int v1, v1, v0 |
| return v1 |
| |
| :catch_block |
| const/4 v0, 0x1 |
| add-int p0, p0, v0 |
| aget v0, p1, p0 |
| |
| goto :return |
| .end method |
| |
| .field public static intArray:[I |
| .field public static longArray:[J |
| .field public static floatArray:[F |
| .field public static doubleArray:[D |