ART: Register allocation and runtime support for try/catch
This patch completes a series of CLs that add support for try/catch
in the Optimizing compiler. With it, Optimizing can compile all
methods containing try/catch, provided they don't contain catch loops.
Future work will focus on improving performance of the generated code.
SsaLivenessAnalysis was updated to propagate liveness information of
instructions live at catch blocks, and to keep location information on
instructions which may be caught by catch phis.
RegisterAllocator was extended to spill values used after catch, and
to allocate spill slots for catch phis. Catch phis generated for the
same vreg share a spill slot as the raw value must be the same.
Location builders and slow paths were updated to reflect the fact that
throwing an exception may not lead to escaping the method.
Instruction code generators are forbidden from using of implicit null
checks in try blocks as live registers need to be saved before handing
over to the runtime.
CodeGenerator emits a stack map for each catch block, storing locations
of catch phis. CodeInfo and StackMapStream recognize this new type of
stack map and store them separate from other stack maps to avoid dex_pc
conflicts.
After having found the target catch block to deliver an exception to,
QuickExceptionHandler looks up the dex register maps at the throwing
instruction and the catch block and copies the values over to their
respective locations.
The runtime-support approach was selected because it allows for the
best performance in the normal control-flow path, since no propagation
of catch phi values is necessary until the exception is thrown. In
addition, it also greatly simplifies the register allocation phase.
ConstantHoisting was removed from LICMTest because it instantiated
(now abstract) HConstant and was bogus anyway (constants are always in
the entry block).
Change-Id: Ie31038ad8e3ee0c13a5bbbbaf5f0b3e532310e4e
diff --git a/test/510-checker-try-catch/smali/RegisterAllocator.smali b/test/510-checker-try-catch/smali/RegisterAllocator.smali
new file mode 100644
index 0000000..fd3c84c
--- /dev/null
+++ b/test/510-checker-try-catch/smali/RegisterAllocator.smali
@@ -0,0 +1,94 @@
+# 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 LRegisterAllocator;
+
+.super Ljava/lang/Object;
+
+# Test that catch phis are allocated to a stack slot, and that equivalent catch
+# phis are allocated to the same stack slot.
+
+## CHECK-START: int RegisterAllocator.testEquivalentCatchPhiSlot_Single(int, int, int) register (after)
+## CHECK-DAG: Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}-><<SlotA1:\d+>>(sp)
+## CHECK-DAG: Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}-><<SlotA2:\d+>>(sp)
+## CHECK-DAG: Phi reg:1 is_catch_phi:true locations:{{\[.*\]}}-><<SlotB:\d+>>(sp)
+## CHECK-EVAL: <<SlotA1>> == <<SlotA2>>
+## CHECK-EVAL: <<SlotB>> != <<SlotA1>>
+
+.method public static testEquivalentCatchPhiSlot_Single(III)I
+ .registers 8
+
+ :try_start
+ const/high16 v0, 0x40000000 # float 2
+ move v1, p0
+ div-int/2addr p0, p1
+
+ const/high16 v0, 0x41000000 # float 8
+ move v1, p1
+ div-int/2addr p0, p2
+ goto :return
+ :try_end
+ .catchall {:try_start .. :try_end} :catch_all
+
+ :catch_all
+ # 2x CatchPhi for v0, 1x for v1
+ if-eqz v1, :use_as_float
+
+ :use_as_int
+ goto :return
+
+ :use_as_float
+ float-to-int v0, v0
+
+ :return
+ return v0
+.end method
+
+# Test that wide catch phis are allocated to two stack slots.
+
+## CHECK-START: long RegisterAllocator.testEquivalentCatchPhiSlot_Wide(int, int, int) register (after)
+## CHECK-DAG: Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}->2x<<SlotB1:\d+>>(sp)
+## CHECK-DAG: Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}->2x<<SlotB2:\d+>>(sp)
+## CHECK-DAG: Phi reg:2 is_catch_phi:true locations:{{\[.*\]}}-><<SlotA:\d+>>(sp)
+## CHECK-EVAL: <<SlotB1>> == <<SlotB2>>
+## CHECK-EVAL: abs(<<SlotA>> - <<SlotB1>>) >= 8
+
+.method public static testEquivalentCatchPhiSlot_Wide(III)J
+ .registers 8
+
+ :try_start
+ const-wide/high16 v0, 0x4000000000000000L # double 2
+ move v2, p0
+ div-int/2addr p0, p1
+
+ const-wide/high16 v0, 0x4100000000000000L # double 8
+ move v2, p1
+ div-int/2addr p0, p2
+ goto :return
+ :try_end
+ .catchall {:try_start .. :try_end} :catch_all
+
+ :catch_all
+ # 2x CatchPhi for v0, 1x for v2
+ if-eqz v2, :use_as_double
+
+ :use_as_long
+ goto :return
+
+ :use_as_double
+ double-to-long v0, v0
+
+ :return
+ return-wide v0
+.end method
diff --git a/test/510-checker-try-catch/smali/Runtime.smali b/test/510-checker-try-catch/smali/Runtime.smali
new file mode 100644
index 0000000..19b43a3
--- /dev/null
+++ b/test/510-checker-try-catch/smali/Runtime.smali
@@ -0,0 +1,555 @@
+# 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
+
+.field public static intArray:[I
+.field public static longArray:[J
+.field public static floatArray:[F
+.field public static doubleArray:[D
diff --git a/test/510-checker-try-catch/src/Main.java b/test/510-checker-try-catch/src/Main.java
index ae78ba0..25cdc0e 100644
--- a/test/510-checker-try-catch/src/Main.java
+++ b/test/510-checker-try-catch/src/Main.java
@@ -14,10 +14,55 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
// Workaround for b/18051191.
class InnerClass {}
- public static void main(String[] args) {}
+ public enum TestPath {
+ ExceptionalFlow1(true, false, 3),
+ ExceptionalFlow2(false, true, 8),
+ NormalFlow(false, false, 42);
+
+ TestPath(boolean arg1, boolean arg2, int expected) {
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ this.expected = expected;
+ }
+
+ public boolean arg1;
+ public boolean arg2;
+ public int expected;
+ }
+
+ public static void testMethod(String method) throws Exception {
+ Class<?> c = Class.forName("Runtime");
+ Method m = c.getMethod(method, new Class[] { boolean.class, boolean.class });
+
+ for (TestPath path : TestPath.values()) {
+ Object[] arguments = new Object[] { path.arg1, path.arg2 };
+ int actual = (Integer) m.invoke(null, arguments);
+
+ if (actual != path.expected) {
+ throw new Error("Method: \"" + method + "\", path: " + path + ", " +
+ "expected: " + path.expected + ", actual: " + actual);
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ testMethod("testUseAfterCatch_int");
+ testMethod("testUseAfterCatch_long");
+ testMethod("testUseAfterCatch_float");
+ testMethod("testUseAfterCatch_double");
+ testMethod("testCatchPhi_const");
+ testMethod("testCatchPhi_int");
+ testMethod("testCatchPhi_long");
+ testMethod("testCatchPhi_float");
+ testMethod("testCatchPhi_double");
+ testMethod("testCatchPhi_singleSlot");
+ testMethod("testCatchPhi_doubleSlot");
+ }
}