# 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 LBuilder; .super Ljava/lang/Object; # Basic test case with two try blocks and three catch handlers, one of which # is shared by the two tries. ## CHECK-START: int Builder.testMultipleTryCatch(int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK-DAG: <> IntConstant -1 ## CHECK-DAG: <> IntConstant -2 ## CHECK-DAG: <> IntConstant -3 ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: DivZeroCheck ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Add ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: DivZeroCheck ## CHECK: <> Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" "<>" ## CHECK: Phi [<
>,<>,<>,<>] ## CHECK: Return ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" ## CHECK: TryBoundary kind:exit .method public static testMultipleTryCatch(III)I .registers 3 :try_start_1 div-int/2addr p0, p1 :try_end_1 .catch Ljava/lang/ArithmeticException; {:try_start_1 .. :try_end_1} :catch_arith .catchall {:try_start_1 .. :try_end_1} :catch_other add-int/2addr p0, p0 :try_start_2 div-int/2addr p0, p2 :try_end_2 .catch Ljava/lang/OutOfMemoryError; {:try_start_2 .. :try_end_2} :catch_mem .catchall {:try_start_2 .. :try_end_2} :catch_other nop :return return p0 :catch_arith const/4 p0, -0x1 goto :return :catch_mem const/4 p0, -0x2 goto :return :catch_other const/4 p0, -0x3 goto :return .end method # Tests try-entry block when there are multiple entry points into the try block. ## CHECK-START: int Builder.testMultipleEntries(int, int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK: <> IntConstant -1 ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" "<>" ## CHECK: If ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: Return ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testMultipleEntries(IIII)I .registers 4 if-eqz p2, :else div-int/2addr p0, p1 :try_start div-int/2addr p0, p2 :else div-int/2addr p0, p3 :try_end .catchall {:try_start .. :try_end} :catch_all :return return p0 :catch_all const/4 p0, -0x1 goto :return .end method # Test that multiple try-exit blocks are generated if (normal) control flow can # jump out of the try block at multiple points. ## CHECK-START: int Builder.testMultipleExits(int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK-DAG: <> IntConstant -1 ## CHECK-DAG: <> IntConstant -2 ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" "<>" ## CHECK: <> Div ## CHECK: If ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" ## CHECK: Phi [<
>,<>,<>] ## CHECK: Return ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testMultipleExits(II)I .registers 2 :try_start div-int/2addr p0, p1 if-eqz p0, :then :try_end .catchall {:try_start .. :try_end} :catch_all :return return p0 :then const/4 p0, -0x1 goto :return :catch_all const/4 p0, -0x2 goto :return .end method # Test that only one TryBoundary is inserted when an edge connects two different # try ranges. ## CHECK-START: int Builder.testSharedBoundary(int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK-DAG: <> IntConstant -1 ## CHECK-DAG: <> IntConstant -2 ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: <> Div ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" ## CHECK: Phi [<
>,<>,<>] ## CHECK: Return ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testSharedBoundary(III)I .registers 3 :try_start_1 div-int/2addr p0, p1 :try_end_1 .catchall {:try_start_1 .. :try_end_1} :catch_all_1 :try_start_2 div-int/2addr p0, p2 :try_end_2 .catchall {:try_start_2 .. :try_end_2} :catch_all_2 :return return p0 :catch_all_1 const/4 p0, -0x1 goto :return :catch_all_2 const/4 p0, -0x2 goto :return .end method # Same as previous test, only the blocks are processed in the opposite order. ## CHECK-START: int Builder.testSharedBoundary_Reverse(int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK-DAG: <> IntConstant -1 ## CHECK-DAG: <> IntConstant -2 ## CHECK: name "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: <> Div ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" ## CHECK: Phi [<
>,<>,<>] ## CHECK: Return ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testSharedBoundary_Reverse(III)I .registers 3 goto :try_start_2 :try_start_1 div-int/2addr p0, p1 goto :return :try_end_1 .catchall {:try_start_1 .. :try_end_1} :catch_all_1 :try_start_2 div-int/2addr p0, p2 goto :try_start_1 :try_end_2 .catchall {:try_start_2 .. :try_end_2} :catch_all_2 :return return p0 :catch_all_1 const/4 p0, -0x1 goto :return :catch_all_2 const/4 p0, -0x2 goto :return .end method # Test that nested tries are split into non-overlapping blocks and TryBoundary # blocks are correctly created between them. ## CHECK-START: int Builder.testNestedTry(int, int, int, int) builder (after) ## CHECK: name "B0" ## CHECK-DAG: <> IntConstant -1 ## CHECK-DAG: <> IntConstant -2 ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: <> Div ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" ## CHECK: Phi [<
>,<>,<>] ## CHECK: Return ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" "<>" "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testNestedTry(IIII)I .registers 4 :try_start_1 div-int/2addr p0, p1 :try_start_2 div-int/2addr p0, p2 :try_end_2 .catch Ljava/lang/ArithmeticException; {:try_start_2 .. :try_end_2} :catch_arith div-int/2addr p0, p3 :try_end_1 .catchall {:try_start_1 .. :try_end_1} :catch_all :return return p0 :catch_arith const/4 p0, -0x1 goto :return :catch_all const/4 p0, -0x2 goto :return .end method # Test control flow that enters a try block, leaves it and returns again. ## CHECK-START: int Builder.testIncontinuousTry(int, int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: <> IntConstant -1 ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: <> Div ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: Phi [<
>,<>] ## CHECK: Return ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testIncontinuousTry(IIII)I .registers 4 :try_start div-int/2addr p0, p1 goto :outside :inside div-int/2addr p0, p3 :try_end .catchall {:try_start .. :try_end} :catch_all :return return p0 :outside div-int/2addr p0, p2 goto :inside :catch_all const/4 p0, -0x1 goto :return .end method ## CHECK-START: int Builder.testSwitchTryEnter(int, int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" "<>" ## CHECK: If ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" "<>" ## CHECK: If ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: Return ## CHECK: name "<>" ## CHECK: Exit ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testSwitchTryEnter(IIII)I .registers 4 packed-switch p0, :pswitch_data :try_start div-int/2addr p0, p1 :pswitch1 div-int/2addr p0, p2 goto :pswitch2 :pswitch_data .packed-switch 0x0 :pswitch1 :pswitch2 .end packed-switch :try_end .catchall {:try_start .. :try_end} :catch_all :pswitch2 div-int/2addr p0, p3 :catch_all return p0 .end method ## CHECK-START: int Builder.testSwitchTryExit(int, int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" "<>" ## CHECK: If ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" "<>" ## CHECK: If ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: Return ## CHECK: name "<>" ## CHECK: Exit ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testSwitchTryExit(IIII)I .registers 4 :try_start div-int/2addr p0, p1 packed-switch p0, :pswitch_data div-int/2addr p0, p1 :pswitch1 div-int/2addr p0, p2 :try_end .catchall {:try_start .. :try_end} :catch_all :pswitch2 div-int/2addr p0, p3 :catch_all return p0 :pswitch_data .packed-switch 0x0 :pswitch1 :pswitch2 .end packed-switch .end method # Test that a TryBoundary is inserted between a Throw instruction and the exit # block when covered by a try range. ## CHECK-START: int Builder.testThrow(java.lang.Exception) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK: <> IntConstant -1 ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Throw ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Return [<>] ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: Exit ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit .method public static testThrow(Ljava/lang/Exception;)I .registers 2 :try_start throw p0 :try_end .catchall {:try_start .. :try_end} :catch_all :catch_all const/4 v0, -0x1 return v0 .end method # Test graph with a throw/catch loop. ## CHECK-START: int Builder.testCatchLoop(int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Return ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: Exit ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: Goto .method public static testCatchLoop(III)I .registers 4 :try_start :catch_all div-int/2addr p0, p2 :try_end .catchall {:try_start .. :try_end} :catch_all :return return p0 .end method # Test that handler edges are not split. In this scenario, the catch block is # only the handler of the try block. ## CHECK-START: int Builder.testHandlerEdge1(int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Return ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: Exit ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testHandlerEdge1(III)I .registers 4 :try_start div-int/2addr p0, p1 :catch_all div-int/2addr p0, p2 :try_end .catchall {:try_start .. :try_end} :catch_all return p0 .end method # Test that handler edges are not split. In this scenario, the catch block is # the handler and also the successor of the try block. ## CHECK-START: int Builder.testHandlerEdge2(int, int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Return ## CHECK: name "<>" ## CHECK: Exit ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testHandlerEdge2(III)I .registers 4 :try_start_1 :catch_all_1 div-int/2addr p0, p1 :try_end_1 .catchall {:try_start_1 .. :try_end_1} :catch_all_2 :try_start_2 :catch_all_2 div-int/2addr p0, p2 :try_end_2 .catchall {:try_start_2 .. :try_end_2} :catch_all_1 return p0 .end method # Test graph with try/catch inside a loop. ## CHECK-START: int Builder.testTryInLoop(int, int) builder (after) ## CHECK: name "B0" ## CHECK: successors "<>" ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" ## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: Goto ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Goto .method public static testTryInLoop(II)I .registers 3 :try_start div-int/2addr p0, p1 goto :try_start :try_end .catchall {:try_start .. :try_end} :catch_all :catch_all goto :try_start .end method # Test that a MOVE_RESULT instruction is placed into the same block as the # INVOKE it follows, even if there is a try boundary between them. ## CHECK-START: int Builder.testMoveResult_Invoke(int, int, int) builder (after) ## CHECK-DAG: <> IntConstant -1 ## CHECK-DAG: <> InvokeStaticOrDirect ## CHECK-DAG: <> Phi [<>,<>] ## CHECK-DAG: Return [<>] .method public static testMoveResult_Invoke(III)I .registers 3 :try_start invoke-static {p0, p1, p2}, LBuilder;->testCatchLoop(III)I :try_end .catchall {:try_start .. :try_end} :catch_all move-result p0 :return return p0 :catch_all const/4 p0, -0x1 goto :return .end method # Test that a MOVE_RESULT instruction is placed into the same block as the # FILLED_NEW_ARRAY it follows, even if there is a try boundary between them. ## CHECK-START: int[] Builder.testMoveResult_FilledNewArray(int, int, int) builder (after) ## CHECK-DAG: <> ParameterValue ## CHECK-DAG: <> ParameterValue ## CHECK-DAG: <> ParameterValue ## CHECK-DAG: <> NullConstant ## CHECK-DAG: <> NewArray ## CHECK-DAG: ArraySet [<>,{{i\d+}},<>] ## CHECK-DAG: ArraySet [<>,{{i\d+}},<>] ## CHECK-DAG: ArraySet [<>,{{i\d+}},<>] ## CHECK-DAG: <> Phi [<>,<>] ## CHECK-DAG: Return [<>] .method public static testMoveResult_FilledNewArray(III)[I .registers 3 :try_start filled-new-array {p0, p1, p2}, [I :try_end .catchall {:try_start .. :try_end} :catch_all move-result-object p0 :return return-object p0 :catch_all const/4 p0, 0x0 goto :return .end method # Test case for ReturnVoid inside a try block. Builder needs to move it outside # the try block so as to not split the ReturnVoid-Exit edge. # This invariant is enforced by GraphChecker. .method public static testReturnVoidInTry(II)V .registers 2 :catch_all :try_start return-void :try_end .catchall {:try_start .. :try_end} :catch_all .end method # Test case for Return inside a try block. Builder needs to move it outside the # try block so as to not split the Return-Exit edge. # This invariant is enforced by GraphChecker. .method public static testReturnInTry(II)I .registers 2 :try_start div-int/2addr p0, p1 return p0 :try_end .catchall {:try_start .. :try_end} :catch_all :catch_all const/4 v0, 0x0 return v0 .end method # Test a (dead) try block which flows out of the method. The block will be # removed by DCE but needs to pass post-builder GraphChecker. ## CHECK-START: int Builder.testDeadEndTry(int) builder (after) ## CHECK-NOT: TryBoundary is_exit:true .method public static testDeadEndTry(I)I .registers 1 return p0 :catch_all nop :try_start nop :try_end .catchall {:try_start .. :try_end} :catch_all .end method # Test that a throw-catch loop on monitor-exit is eliminated. # Note that we do not test this until after DCE which merges trivially split blocks. ## CHECK-START: int Builder.testSynchronized(java.lang.Object) dead_code_elimination$initial (after) ## CHECK: flags "catch_block" ## CHECK-NOT: end_block ## CHECK: MonitorOperation kind:exit .method public static testSynchronized(Ljava/lang/Object;)I .registers 2 monitor-enter p0 :try_start_9 invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I move-result v0 monitor-exit p0 return v0 :catchall_11 move-exception v0 monitor-exit p0 :try_end_15 .catchall {:try_start_9 .. :try_end_15} :catchall_11 throw v0 .end method