summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/code_sinking.cc17
-rw-r--r--test/639-checker-code-sinking/src/Main.java61
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();