| # 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 "<<BEnterTry1:B\d+>>" |
| ## CHECK: <<Minus1:i\d+>> IntConstant -1 |
| ## CHECK: <<Minus2:i\d+>> IntConstant -2 |
| ## CHECK: <<Minus3:i\d+>> IntConstant -3 |
| |
| ## CHECK: name "<<BTry1:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry1>>" |
| ## CHECK: successors "<<BExitTry1:B\d+>>" |
| ## CHECK: DivZeroCheck |
| |
| ## CHECK: name "<<BAdd:B\d+>>" |
| ## CHECK: predecessors "<<BExitTry1>>" |
| ## CHECK: successors "<<BEnterTry2:B\d+>>" |
| ## CHECK: Add |
| |
| ## CHECK: name "<<BTry2:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry2>>" |
| ## CHECK: successors "<<BExitTry2:B\d+>>" |
| ## CHECK: DivZeroCheck |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExitTry2>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>" "<<BCatch3:B\d+>>" |
| ## CHECK: Return |
| |
| ## CHECK: name "<<BCatch1>>" |
| ## CHECK: predecessors "<<BEnterTry1>>" "<<BExitTry1>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus1>>] |
| |
| ## CHECK: name "<<BCatch2>>" |
| ## CHECK: predecessors "<<BEnterTry2>>" "<<BExitTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus2>>] |
| |
| ## CHECK: name "<<BCatch3>>" |
| ## CHECK: predecessors "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2>>" "<<BExitTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus3>>] |
| |
| ## CHECK: name "<<BEnterTry1>>" |
| ## CHECK: predecessors "B0" |
| ## CHECK: successors "<<BTry1>>" |
| ## CHECK: xhandlers "<<BCatch1>>" "<<BCatch3>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry1>>" |
| ## CHECK: predecessors "<<BTry1>>" |
| ## CHECK: successors "<<BAdd>>" |
| ## CHECK: xhandlers "<<BCatch1>>" "<<BCatch3>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BEnterTry2>>" |
| ## CHECK: predecessors "<<BAdd>>" |
| ## CHECK: successors "<<BTry2>>" |
| ## CHECK: xhandlers "<<BCatch2>>" "<<BCatch3>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry2>>" |
| ## CHECK: predecessors "<<BTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatch2>>" "<<BCatch3>>" |
| ## 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 |
| |
| :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 |
| |
| # Test that multiple try-entry blocks are generated if 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 "<<BIf:B\d+>>" |
| ## CHECK: <<Minus1:i\d+>> IntConstant -1 |
| |
| ## CHECK: name "<<BIf>>" |
| ## CHECK: predecessors "B0" |
| ## CHECK: successors "<<BEnterTry2:B\d+>>" "<<BThen:B\d+>>" |
| ## CHECK: If |
| |
| ## CHECK: name "<<BThen>>" |
| ## CHECK: predecessors "<<BIf>>" |
| ## CHECK: successors "<<BEnterTry1:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BTry1:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry1>>" |
| ## CHECK: successors "<<BTry2:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BTry2>>" |
| ## CHECK: predecessors "<<BEnterTry2>>" "<<BTry1>>" |
| ## CHECK: successors "<<BExitTry:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExitTry>>" "<<BCatch:B\d+>>" |
| ## CHECK: Return |
| |
| ## CHECK: name "<<BCatch>>" |
| ## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus1>>] |
| |
| ## CHECK: name "<<BEnterTry1>>" |
| ## CHECK: predecessors "<<BThen>>" |
| ## CHECK: successors "<<BTry1>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BEnterTry2>>" |
| ## CHECK: predecessors "<<BIf>>" |
| ## CHECK: successors "<<BTry2>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry>>" |
| ## CHECK: predecessors "<<BTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| .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 "<<BEnterTry:B\d+>>" |
| ## CHECK: <<Minus1:i\d+>> IntConstant -1 |
| ## CHECK: <<Minus2:i\d+>> IntConstant -2 |
| |
| ## CHECK: name "<<BTry:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry>>" |
| ## CHECK: successors "<<BExitTry1:B\d+>>" "<<BExitTry2:B\d+>>" |
| ## CHECK: Div |
| ## CHECK: If |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExitTry2>>" "<<BThen:B\d+>>" "<<BCatch:B\d+>>" |
| ## CHECK: Return |
| |
| ## CHECK: name "<<BThen>>" |
| ## CHECK: predecessors "<<BExitTry1>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: StoreLocal [v0,<<Minus1>>] |
| |
| ## CHECK: name "<<BCatch>>" |
| ## CHECK: predecessors "<<BEnterTry>>" "<<BExitTry1>>" "<<BExitTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus2>>] |
| |
| ## CHECK: name "<<BEnterTry>>" |
| ## CHECK: predecessors "B0" |
| ## CHECK: successors "<<BTry>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry1>>" |
| ## CHECK: predecessors "<<BTry>>" |
| ## CHECK: successors "<<BThen>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BExitTry2>>" |
| ## CHECK: predecessors "<<BTry>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| .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 "<<BEnter1:B\d+>>" |
| ## CHECK: <<Minus1:i\d+>> IntConstant -1 |
| ## CHECK: <<Minus2:i\d+>> IntConstant -2 |
| |
| ## CHECK: name "<<BTry1:B\d+>>" |
| ## CHECK: predecessors "<<BEnter1>>" |
| ## CHECK: successors "<<BExit1:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BTry2:B\d+>>" |
| ## CHECK: predecessors "<<BEnter2:B\d+>>" |
| ## CHECK: successors "<<BExit2:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExit2>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>" |
| ## CHECK: Return |
| |
| ## CHECK: name "<<BCatch1>>" |
| ## CHECK: predecessors "<<BEnter1>>" "<<BExit1>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus1>>] |
| |
| ## CHECK: name "<<BCatch2>>" |
| ## CHECK: predecessors "<<BEnter2>>" "<<BExit2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus2>>] |
| |
| ## CHECK: name "<<BEnter1>>" |
| ## CHECK: predecessors "B0" |
| ## CHECK: successors "<<BTry1>>" |
| ## CHECK: xhandlers "<<BCatch1>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExit1>>" |
| ## CHECK: predecessors "<<BTry1>>" |
| ## CHECK: successors "<<BEnter2>>" |
| ## CHECK: xhandlers "<<BCatch1>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BEnter2>>" |
| ## CHECK: predecessors "<<BExit1>>" |
| ## CHECK: successors "<<BTry2>>" |
| ## CHECK: xhandlers "<<BCatch2>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExit2>>" |
| ## CHECK: predecessors "<<BTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatch2>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| .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 "<<BGoto:B\d+>>" |
| ## CHECK: <<Minus1:i\d+>> IntConstant -1 |
| ## CHECK: <<Minus2:i\d+>> IntConstant -2 |
| |
| ## CHECK: name "<<BGoto>>" |
| ## CHECK: successors "<<BEnter2:B\d+>>" |
| ## CHECK: Goto |
| |
| ## CHECK: name "<<BTry1:B\d+>>" |
| ## CHECK: predecessors "<<BEnter1:B\d+>>" |
| ## CHECK: successors "<<BExit1:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BTry2:B\d+>>" |
| ## CHECK: predecessors "<<BEnter2>>" |
| ## CHECK: successors "<<BExit2:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExit1>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>" |
| ## CHECK: Return |
| |
| ## CHECK: name "<<BCatch1>>" |
| ## CHECK: predecessors "<<BEnter1>>" "<<BExit1>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus1>>] |
| |
| ## CHECK: name "<<BCatch2>>" |
| ## CHECK: predecessors "<<BEnter2>>" "<<BExit2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus2>>] |
| |
| ## CHECK: name "<<BEnter1>>" |
| ## CHECK: predecessors "<<BExit2>>" |
| ## CHECK: successors "<<BTry1>>" |
| ## CHECK: xhandlers "<<BCatch1>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExit1>>" |
| ## CHECK: predecessors "<<BTry1>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatch1>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BEnter2>>" |
| ## CHECK: predecessors "<<BGoto>>" |
| ## CHECK: successors "<<BTry2>>" |
| ## CHECK: xhandlers "<<BCatch2>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExit2>>" |
| ## CHECK: predecessors "<<BTry2>>" |
| ## CHECK: successors "<<BEnter1>>" |
| ## CHECK: xhandlers "<<BCatch2>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| .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: <<Minus1:i\d+>> IntConstant -1 |
| ## CHECK: <<Minus2:i\d+>> IntConstant -2 |
| |
| ## CHECK: name "<<BTry1:B\d+>>" |
| ## CHECK: predecessors "<<BEnter1:B\d+>>" |
| ## CHECK: successors "<<BExit1:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BTry2:B\d+>>" |
| ## CHECK: predecessors "<<BEnter2:B\d+>>" |
| ## CHECK: successors "<<BExit2:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BTry3:B\d+>>" |
| ## CHECK: predecessors "<<BEnter3:B\d+>>" |
| ## CHECK: successors "<<BExit3:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExit3>>" "<<BCatchArith:B\d+>>" "<<BCatchAll:B\d+>>" |
| |
| ## CHECK: name "<<BCatchArith>>" |
| ## CHECK: predecessors "<<BEnter2>>" "<<BExit2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus1>>] |
| |
| ## CHECK: name "<<BCatchAll>>" |
| ## CHECK: predecessors "<<BEnter1>>" "<<BExit1>>" "<<BEnter2>>" "<<BExit2>>" "<<BEnter3>>" "<<BExit3>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus2>>] |
| |
| ## CHECK: name "<<BEnter1>>" |
| ## CHECK: predecessors "B0" |
| ## CHECK: successors "<<BTry1>>" |
| ## CHECK: xhandlers "<<BCatchAll>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExit1>>" |
| ## CHECK: predecessors "<<BTry1>>" |
| ## CHECK: successors "<<BEnter2>>" |
| ## CHECK: xhandlers "<<BCatchAll>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BEnter2>>" |
| ## CHECK: predecessors "<<BExit1>>" |
| ## CHECK: successors "<<BTry2>>" |
| ## CHECK: xhandlers "<<BCatchArith>>" "<<BCatchAll>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExit2>>" |
| ## CHECK: predecessors "<<BTry2>>" |
| ## CHECK: successors "<<BEnter3>>" |
| ## CHECK: xhandlers "<<BCatchArith>>" "<<BCatchAll>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BEnter3>>" |
| ## CHECK: predecessors "<<BExit2>>" |
| ## CHECK: successors "<<BTry3>>" |
| ## CHECK: xhandlers "<<BCatchAll>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExit3>>" |
| ## CHECK: predecessors "<<BTry3>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatchAll>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| .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: <<Minus1:i\d+>> IntConstant -1 |
| |
| ## CHECK: name "<<BTry1:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry1:B\d+>>" |
| ## CHECK: successors "<<BExitTry1:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BTry2:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry2:B\d+>>" |
| ## CHECK: successors "<<BExitTry2:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExitTry2>>" "<<BCatch:B\d+>>" |
| |
| ## CHECK: name "<<BOutside:B\d+>>" |
| ## CHECK: predecessors "<<BExitTry1>>" |
| ## CHECK: successors "<<BEnterTry2>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BCatch>>" |
| ## CHECK: predecessors "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2>>" "<<BExitTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus1>>] |
| |
| ## CHECK: name "<<BEnterTry1>>" |
| ## CHECK: predecessors "B0" |
| ## CHECK: successors "<<BTry1>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry1>>" |
| ## CHECK: predecessors "<<BTry1>>" |
| ## CHECK: successors "<<BOutside>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BEnterTry2>>" |
| ## CHECK: predecessors "<<BOutside>>" |
| ## CHECK: successors "<<BTry2>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry2>>" |
| ## CHECK: predecessors "<<BTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| .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 |
| |
| # 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 "<<BEnterTry:B\d+>>" |
| ## CHECK: <<Minus1:i\d+>> IntConstant -1 |
| |
| ## CHECK: name "<<BTry:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry>>" |
| ## CHECK: successors "<<BExitTry:B\d+>>" |
| ## CHECK: Throw |
| |
| ## CHECK: name "<<BCatch:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry>>" "<<BExitTry>>" |
| ## CHECK: successors "<<BExit:B\d+>>" |
| ## CHECK: flags "catch_block" |
| ## CHECK: StoreLocal [v0,<<Minus1>>] |
| |
| ## CHECK: name "<<BEnterTry>>" |
| ## CHECK: predecessors "B0" |
| ## CHECK: successors "<<BTry>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry>>" |
| ## CHECK: predecessors "<<BTry>>" |
| ## CHECK: successors "<<BExit>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BExit>>" |
| ## CHECK: predecessors "<<BExitTry>>" "<<BCatch>>" |
| ## CHECK: 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 "<<BCatch:B\d+>>" |
| |
| ## CHECK: name "<<BCatch>>" |
| ## CHECK: predecessors "B0" "<<BEnterTry:B\d+>>" "<<BExitTry:B\d+>>" |
| ## CHECK: successors "<<BEnterTry>>" |
| ## CHECK: flags "catch_block" |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExitTry>>" |
| |
| ## CHECK: name "<<BTry:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry>>" |
| ## CHECK: successors "<<BExitTry>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BEnterTry>>" |
| ## CHECK: predecessors "<<BCatch>>" |
| ## CHECK: successors "<<BTry>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry>>" |
| ## CHECK: predecessors "<<BTry>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| .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 "<<BEnterTry1:B\d+>>" |
| |
| ## CHECK: name "<<BTry1:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry1>>" |
| ## CHECK: successors "<<BExitTry1:B\d+>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BCatch:B\d+>>" |
| ## CHECK: predecessors "<<BExitTry1>>" "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2:B\d+>>" "<<BExitTry2:B\d+>>" |
| ## CHECK: successors "<<BEnterTry2>>" |
| ## CHECK: flags "catch_block" |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExitTry2>>" |
| |
| ## CHECK: name "<<BEnterTry1>>" |
| ## CHECK: predecessors "B0" |
| ## CHECK: successors "<<BTry1>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry1>>" |
| ## CHECK: predecessors "<<BTry1>>" |
| ## CHECK: successors "<<BCatch>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BTry2:B\d+>>" |
| ## CHECK: predecessors "<<BEnterTry2>>" |
| ## CHECK: successors "<<BExitTry2>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BEnterTry2>>" |
| ## CHECK: predecessors "<<BCatch>>" |
| ## CHECK: successors "<<BTry2>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExitTry2>>" |
| ## CHECK: predecessors "<<BTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatch>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| .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 "<<BCatch1:B\d+>>" |
| |
| ## CHECK: name "<<BCatch1>>" |
| ## CHECK: predecessors "B0" "<<BEnter2:B\d+>>" "<<BExit2:B\d+>>" |
| ## CHECK: successors "<<BEnter1:B\d+>>" |
| ## CHECK: flags "catch_block" |
| |
| ## CHECK: name "<<BCatch2:B\d+>>" |
| ## CHECK: predecessors "<<BExit1:B\d+>>" "<<BEnter1>>" "<<BExit1>>" |
| ## CHECK: successors "<<BEnter2>>" |
| ## CHECK: flags "catch_block" |
| |
| ## CHECK: name "<<BReturn:B\d+>>" |
| ## CHECK: predecessors "<<BExit2>>" |
| ## CHECK: Return |
| |
| ## CHECK: name "<<BTry1:B\d+>>" |
| ## CHECK: predecessors "<<BEnter1>>" |
| ## CHECK: successors "<<BExit1>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BEnter1>>" |
| ## CHECK: predecessors "<<BCatch1>>" |
| ## CHECK: successors "<<BTry1>>" |
| ## CHECK: xhandlers "<<BCatch2>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExit1>>" |
| ## CHECK: predecessors "<<BTry1>>" |
| ## CHECK: successors "<<BCatch2>>" |
| ## CHECK: xhandlers "<<BCatch2>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| ## CHECK: name "<<BTry2:B\d+>>" |
| ## CHECK: predecessors "<<BEnter2>>" |
| ## CHECK: successors "<<BExit2>>" |
| ## CHECK: Div |
| |
| ## CHECK: name "<<BEnter2>>" |
| ## CHECK: predecessors "<<BCatch2>>" |
| ## CHECK: successors "<<BTry2>>" |
| ## CHECK: xhandlers "<<BCatch1>>" |
| ## CHECK: TryBoundary kind:entry |
| |
| ## CHECK: name "<<BExit2>>" |
| ## CHECK: predecessors "<<BTry2>>" |
| ## CHECK: successors "<<BReturn>>" |
| ## CHECK: xhandlers "<<BCatch1>>" |
| ## CHECK: TryBoundary kind:exit |
| |
| .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 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: <<Res:i\d+>> InvokeStaticOrDirect |
| ## CHECK-NEXT: StoreLocal [v0,<<Res>>] |
| |
| .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: <<Res:l\d+>> NewArray |
| ## CHECK-NEXT: Temporary |
| ## CHECK-NEXT: <<Local1:i\d+>> LoadLocal [v0] |
| ## CHECK-NEXT: ArraySet [<<Res>>,{{i\d+}},<<Local1>>] |
| ## CHECK-NEXT: <<Local2:i\d+>> LoadLocal [v1] |
| ## CHECK-NEXT: ArraySet [<<Res>>,{{i\d+}},<<Local2>>] |
| ## CHECK-NEXT: <<Local3:i\d+>> LoadLocal [v2] |
| ## CHECK-NEXT: ArraySet [<<Res>>,{{i\d+}},<<Local3>>] |
| ## CHECK-NEXT: StoreLocal [v0,<<Res>>] |
| |
| .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 |