Allow to sink code to catch blocks
We now allow to sink code to catch blocks, while blocking moving it
to blocks that would throw into catch blocks.
Bug: 75971227
Test: art/test/testrunner/testrunner.py --host --64 --optimizing -b
Change-Id: Iaf5ea647e96170cbfb95c4286c5c37c991cb2113
diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc
index 2b56c88..0f336a2 100644
--- a/compiler/optimizing/code_sinking.cc
+++ b/compiler/optimizing/code_sinking.cc
@@ -217,8 +217,14 @@
DCHECK(target_block != nullptr);
}
- // Bail if the instruction can throw and we are about to move into a catch block.
- if (instruction->CanThrow() && target_block->GetTryCatchInformation() != nullptr) {
+ // Bail if the instruction would throw into a catch block.
+ if (instruction->CanThrow() && target_block->IsTryBlock()) {
+ // TODO(solanes): Here we could do something similar to the loop above and move to the first
+ // dominator, which is not a try block, instead of just returning nullptr. If we do so, we have
+ // to also make sure we are not in a loop.
+ // TODO(solanes): Alternatively, we could split the try block at the try boundary having two
+ // blocks: A and B. Block B would have the try boundary (and xhandler) and therefore block A
+ // would be the target block available to move the instruction.
return nullptr;
}
diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java
index 91c3ec4..361806a 100644
--- a/test/639-checker-code-sinking/src/Main.java
+++ b/test/639-checker-code-sinking/src/Main.java
@@ -46,6 +46,7 @@
// expected
System.out.println(e.getMessage());
}
+ testCatchBlock();
}
/// CHECK-START: void Main.testSimpleUse() code_sinking (before)
@@ -390,12 +391,116 @@
return "" + intField;
}
+ private static void testCatchBlock() {
+ assertEquals(456, testSinkToCatchBlock());
+ assertEquals(456, testDoNotSinkToTry());
+ assertEquals(456, testDoNotSinkToCatchInsideTry());
+ }
+
+ /// CHECK-START: int Main.testSinkToCatchBlock() code_sinking (before)
+ /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
+ /// CHECK: NewInstance [<<ObjLoadClass>>]
+ /// CHECK: TryBoundary kind:entry
+
+ /// CHECK-START: int Main.testSinkToCatchBlock() code_sinking (after)
+ /// CHECK: TryBoundary kind:entry
+ /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
+ /// CHECK: NewInstance [<<ObjLoadClass>>]
+
+ // Consistency check to make sure there's only one entry TryBoundary.
+ /// CHECK-START: int Main.testSinkToCatchBlock() code_sinking (after)
+ /// CHECK: TryBoundary kind:entry
+ /// CHECK-NOT: TryBoundary kind:entry
+
+ // Tests that we can sink the Object creation to the catch block.
+ private static int testSinkToCatchBlock() {
+ Object o = new Object();
+ try {
+ if (doEarlyReturn) {
+ return 123;
+ }
+ } catch (Error e) {
+ throw new Error(o.toString());
+ }
+ return 456;
+ }
+
+ /// CHECK-START: int Main.testDoNotSinkToTry() code_sinking (before)
+ /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
+ /// CHECK: NewInstance [<<ObjLoadClass>>]
+ /// CHECK: TryBoundary kind:entry
+
+ /// CHECK-START: int Main.testDoNotSinkToTry() code_sinking (after)
+ /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
+ /// CHECK: NewInstance [<<ObjLoadClass>>]
+ /// CHECK: TryBoundary kind:entry
+
+ // Consistency check to make sure there's only one entry TryBoundary.
+ /// CHECK-START: int Main.testDoNotSinkToTry() code_sinking (after)
+ /// CHECK: TryBoundary kind:entry
+ /// CHECK-NOT: TryBoundary kind:entry
+
+ // Tests that we don't sink the Object creation into the try.
+ private static int testDoNotSinkToTry() {
+ Object o = new Object();
+ try {
+ if (doEarlyReturn) {
+ throw new Error(o.toString());
+ }
+ } catch (Error e) {
+ throw new Error();
+ }
+ return 456;
+ }
+
+ /// CHECK-START: int Main.testDoNotSinkToCatchInsideTry() code_sinking (before)
+ /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
+ /// CHECK: NewInstance [<<ObjLoadClass>>]
+ /// CHECK: TryBoundary kind:entry
+ /// CHECK: TryBoundary kind:entry
+
+ /// CHECK-START: int Main.testDoNotSinkToCatchInsideTry() code_sinking (after)
+ /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
+ /// CHECK: NewInstance [<<ObjLoadClass>>]
+ /// CHECK: TryBoundary kind:entry
+ /// CHECK: TryBoundary kind:entry
+
+ // Consistency check to make sure there's exactly two entry TryBoundary.
+ /// CHECK-START: int Main.testDoNotSinkToCatchInsideTry() code_sinking (after)
+ /// CHECK: TryBoundary kind:entry
+ /// CHECK: TryBoundary kind:entry
+ /// CHECK-NOT: TryBoundary kind:entry
+
+ // Tests that we don't sink the Object creation into a catch handler surrounded by try/catch.
+ private static int testDoNotSinkToCatchInsideTry() {
+ Object o = new Object();
+ try {
+ try {
+ if (doEarlyReturn) {
+ return 123;
+ }
+ } catch (Error e) {
+ throw new Error(o.toString());
+ }
+ } catch (Error e) {
+ throw new Error();
+ }
+ return 456;
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected: " + expected + ", Actual: " + actual);
+ }
+ }
+
volatile int volatileField;
int intField;
int intField2;
Object objectField;
static boolean doThrow;
static boolean doLoop;
+ static boolean doEarlyReturn;
static Main mainField = new Main();
static Object obj = new Object();
}