diff options
-rw-r--r-- | compiler/optimizing/code_sinking.cc | 17 | ||||
-rw-r--r-- | test/639-checker-code-sinking/src/Main.java | 61 |
2 files changed, 78 insertions, 0 deletions
diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc index e7a4389ed2..76885ec0a6 100644 --- a/compiler/optimizing/code_sinking.cc +++ b/compiler/optimizing/code_sinking.cc @@ -37,6 +37,23 @@ bool CodeSinking::Run() { // as an indicator of an uncommon branch. for (HBasicBlock* exit_predecessor : exit->GetPredecessors()) { HInstruction* last = exit_predecessor->GetLastInstruction(); + + // TryBoundary instructions are sometimes inserted between the last instruction (e.g. Throw, + // Return) and Exit. We don't want to use that instruction for our "uncommon branch" heuristic + // because they are not as good an indicator as throwing branches, so we skip them and fetch the + // actual last instruction. + if (last->IsTryBoundary()) { + // We have an exit try boundary. Fetch the previous instruction. + DCHECK(!last->AsTryBoundary()->IsEntry()); + if (last->GetPrevious() == nullptr) { + DCHECK(exit_predecessor->IsSingleTryBoundary()); + exit_predecessor = exit_predecessor->GetSinglePredecessor(); + last = exit_predecessor->GetLastInstruction(); + } else { + last = last->GetPrevious(); + } + } + // Any predecessor of the exit that does not return, throws an exception. if (!last->IsReturn() && !last->IsReturnVoid()) { SinkCodeToUncommonBranch(exit_predecessor); diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java index 17dceb5d1b..f8c1d9d4e7 100644 --- a/test/639-checker-code-sinking/src/Main.java +++ b/test/639-checker-code-sinking/src/Main.java @@ -39,6 +39,7 @@ public class Main { } catch (Error e) { // expected } + $noinline$testMethodEndsWithTryBoundary(); doThrow = true; try { testInstanceSideEffects(); @@ -301,6 +302,66 @@ public class Main { } } + private static void $noinline$testMethodEndsWithTryBoundary() throws Exception { + assertEquals(0, $noinline$testDontSinkToReturnBranch(0, 0, false, new Object())); + assertEquals(1, $noinline$testSinkToThrowBranch(0, 0, true, new Object())); + try { + $noinline$testSinkToThrowBranch(0, 0, false, new Object()); + throw new Exception("Unreachable"); + } catch (Error expected) { + } + } + + // Consistency check: only one add + /// CHECK-START: int Main.$noinline$testDontSinkToReturnBranch(int, int, boolean, java.lang.Object) code_sinking (before) + /// CHECK: Add + /// CHECK-NOT: Add + + /// CHECK-START: int Main.$noinline$testDontSinkToReturnBranch(int, int, boolean, java.lang.Object) code_sinking (before) + /// CHECK: Add + /// CHECK-NEXT: If + + /// CHECK-START: int Main.$noinline$testDontSinkToReturnBranch(int, int, boolean, java.lang.Object) code_sinking (after) + /// CHECK: Add + /// CHECK-NEXT: If + private static int $noinline$testDontSinkToReturnBranch(int a, int b, boolean flag, Object obj) { + int c = a + b; + if (flag) { + return 1; + } + + synchronized (obj) { + return $noinline$returnSameValue(c); + } + } + + private static int $noinline$returnSameValue(int value) { + return value; + } + + // Consistency check: only one add + /// CHECK-START: int Main.$noinline$testSinkToThrowBranch(int, int, boolean, java.lang.Object) code_sinking (before) + /// CHECK: Add + /// CHECK-NOT: Add + + /// CHECK-START: int Main.$noinline$testSinkToThrowBranch(int, int, boolean, java.lang.Object) code_sinking (before) + /// CHECK: Add + /// CHECK: If + + /// CHECK-START: int Main.$noinline$testSinkToThrowBranch(int, int, boolean, java.lang.Object) code_sinking (after) + /// CHECK: If + /// CHECK: Add + private static int $noinline$testSinkToThrowBranch(int a, int b, boolean flag, Object obj) { + int c = a + b; + if (flag) { + return 1; + } + + synchronized (obj) { + throw new Error(Integer.toString(c)); + } + } + public static void testInstanceSideEffects() { int a = mainField.intField; $noinline$changeIntField(); |