ART: Enable inlining under try/catch
This patch updates the inliner to set try/catch information
when inlining into a method with try/catch. It does not yet
allow inlining of methods with try/catch because that will
require generating catch stack maps with inline info.
Change-Id: I7d57e1454e7da537d75c5c7eda60b22f3a30fa60
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 8b28ff9..68fb0ac 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1652,7 +1652,8 @@
// Update the meta information surrounding blocks:
// (1) the graph they are now in,
// (2) the reverse post order of that graph,
- // (3) the potential loop information they are now in.
+ // (3) the potential loop information they are now in,
+ // (4) try block membership.
// We don't add the entry block, the exit block, and the first block, which
// has been merged with `at`.
@@ -1668,41 +1669,47 @@
size_t index_of_at = IndexOfElement(outer_graph->reverse_post_order_, at);
MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at);
- // Do a reverse post order of the blocks in the callee and do (1), (2),
- // and (3) to the blocks that apply.
- HLoopInformation* info = at->GetLoopInformation();
+ HLoopInformation* loop_info = at->GetLoopInformation();
+ // Copy TryCatchInformation if `at` is a try block, not if it is a catch block.
+ TryCatchInformation* try_catch_info = at->IsTryBlock() ? at->GetTryCatchInformation() : nullptr;
+
+ // Do a reverse post order of the blocks in the callee and do (1), (2), (3)
+ // and (4) to the blocks that apply.
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
HBasicBlock* current = it.Current();
if (current != exit_block_ && current != entry_block_ && current != first) {
DCHECK(!current->IsInLoop());
+ DCHECK(current->GetTryCatchInformation() == nullptr);
DCHECK(current->GetGraph() == this);
current->SetGraph(outer_graph);
outer_graph->AddBlock(current);
outer_graph->reverse_post_order_[++index_of_at] = current;
- if (info != nullptr) {
- current->SetLoopInformation(info);
+ if (loop_info != nullptr) {
+ current->SetLoopInformation(loop_info);
for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) {
loop_it.Current()->Add(current);
}
}
+ current->SetTryCatchInformation(try_catch_info);
}
}
- // Do (1), (2), and (3) to `to`.
+ // Do (1), (2), (3) and (4) to `to`.
to->SetGraph(outer_graph);
outer_graph->AddBlock(to);
outer_graph->reverse_post_order_[++index_of_at] = to;
- if (info != nullptr) {
- to->SetLoopInformation(info);
+ if (loop_info != nullptr) {
+ to->SetLoopInformation(loop_info);
for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) {
loop_it.Current()->Add(to);
}
- if (info->IsBackEdge(*at)) {
+ if (loop_info->IsBackEdge(*at)) {
// Only `to` can become a back edge, as the inlined blocks
// are predecessors of `to`.
- info->ReplaceBackEdge(at, to);
+ loop_info->ReplaceBackEdge(at, to);
}
}
+ to->SetTryCatchInformation(try_catch_info);
}
// Update the next instruction id of the outer graph, so that instructions
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 6632f95..8cb2cfc 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -492,6 +492,8 @@
RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer);
+ MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles);
+
// TODO: Update passes incompatible with try/catch so we have the same
// pipeline for all methods.
if (graph->HasTryCatch()) {
@@ -507,8 +509,6 @@
RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer);
} else {
- MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles);
-
HOptimization* optimizations2[] = {
// BooleanSimplifier depends on the InstructionSimplifier removing
// redundant suspend checks to recognize empty blocks.
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 34fb3f8..2dbd7e8 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -49,7 +49,9 @@
if (m_name.compare("f") == 0) {
CHECK_REGS_CONTAIN_REFS(0x03U, true, 8); // v8: this
CHECK_REGS_CONTAIN_REFS(0x06U, true, 8, 1); // v8: this, v1: x
- CHECK_REGS_CONTAIN_REFS(0x08U, true, 8, 3, 1); // v8: this, v3: y, v1: x
+ if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) {
+ CHECK_REGS_CONTAIN_REFS(0x08U, true, 8, 3, 1); // v8: this, v3: y, v1: x
+ }
CHECK_REGS_CONTAIN_REFS(0x0cU, true, 8, 3, 1); // v8: this, v3: y, v1: x
if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) {
CHECK_REGS_CONTAIN_REFS(0x0eU, true, 8, 3, 1); // v8: this, v3: y, v1: x
@@ -66,9 +68,10 @@
CHECK_REGS_CONTAIN_REFS(0x13U, false, 3); // v3: y
// Note that v0: ex can be eliminated because it's a dead merge of two different exceptions.
CHECK_REGS_CONTAIN_REFS(0x18U, true, 8, 2, 1); // v8: this, v2: y, v1: x (dead v0: ex)
- CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1); // v8: this, v5: x[1], v2: y, v1: x (dead v0: ex)
if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) {
// v8: this, v5: x[1], v2: y, v1: x (dead v0: ex)
+ CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1);
+ // v8: this, v5: x[1], v2: y, v1: x (dead v0: ex)
CHECK_REGS_CONTAIN_REFS(0x1dU, true, 8, 5, 2, 1);
// v5 is removed from the root set because there is a "merge" operation.
// See 0015: if-nez v2, 001f.
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 22829cd..ffeae7d 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -624,12 +624,13 @@
constantIndexing2(new int[3]);
} catch (ArrayIndexOutOfBoundsException e) {
assertIsManaged(); // This is to ensure that single-frame deoptimization works.
- // Will need to be updated if constantIndexing2 is inlined.
+ // Will need to be updated if constantIndexing2 is inlined.
try {
// This will cause AIOOBE.
constantIndexingForward6(new int[3]);
} catch (ArrayIndexOutOfBoundsException e2) {
- assertIsManaged();
+ // Having deopted, we expect to be running interpreted at this point.
+ // Does not apply to debuggable, however, since we do not inline.
return 99;
}
}
diff --git a/test/542-inline-trycatch/expected.txt b/test/542-inline-trycatch/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/542-inline-trycatch/expected.txt
diff --git a/test/542-inline-trycatch/info.txt b/test/542-inline-trycatch/info.txt
new file mode 100644
index 0000000..b3e50d3
--- /dev/null
+++ b/test/542-inline-trycatch/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler under try/catch.
\ No newline at end of file
diff --git a/test/542-inline-trycatch/src/Main.java b/test/542-inline-trycatch/src/Main.java
new file mode 100644
index 0000000..5a6e06f
--- /dev/null
+++ b/test/542-inline-trycatch/src/Main.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+public class Main {
+
+ // The following tests make sure that we inline methods used inside try and catch
+ // blocks, provided they meet other inlining criteria. To do that, we rely on
+ // the compiler recognizing and enforcing the $inline$ and $noinline$ markers.
+
+ // We expect a single block to always be inlined.
+
+ private static int $inline$SingleBlock(String str) throws NumberFormatException {
+ return Integer.parseInt(str);
+ }
+
+ // We expect a "simple" method with multiple blocks to always be inlined.
+
+ private static int $inline$MultipleBlocks(String str, boolean is_hex)
+ throws NumberFormatException {
+ return is_hex ? Integer.parseInt(str, 16) : Integer.parseInt(str);
+ }
+
+ // We expect methods with try/catch to not be inlined. Inlined try/catch
+ // blocks are not supported at the moment.
+
+ private static int $noinline$TryCatch(String str) {
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
+
+ public static void testSingleBlockFromTry() {
+ int val = 0;
+
+ try {
+ val = $inline$SingleBlock("42");
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(42, val);
+
+ try {
+ $inline$SingleBlock("xyz");
+ unreachable();
+ } catch (NumberFormatException ex) {}
+ }
+
+ public static void testSingleBlockFromCatch() {
+ int val = 0;
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $inline$SingleBlock("42");
+ }
+ assertEquals(42, val);
+ }
+
+ public static void testMultipleBlocksFromTry() {
+ int val = 0;
+
+ try {
+ val = $inline$MultipleBlocks("42", false);
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(42, val);
+
+ try {
+ val = $inline$MultipleBlocks("20", true);
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(32, val);
+
+ try {
+ $inline$MultipleBlocks("xyz", false);
+ unreachable();
+ } catch (NumberFormatException ex) {}
+
+ try {
+ $inline$MultipleBlocks("xyz", true);
+ unreachable();
+ } catch (NumberFormatException ex) {}
+ }
+
+ public static void testMultipleBlocksFromCatch() {
+ int val = 0;
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $inline$MultipleBlocks("42", false);
+ }
+ assertEquals(42, val);
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $inline$MultipleBlocks("20", true);
+ }
+ assertEquals(32, val);
+ }
+
+ public static void testTryCatchFromTry() {
+ int val = 0;
+
+ try {
+ val = $noinline$TryCatch("42");
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(42, val);
+
+ try {
+ val = $noinline$TryCatch("xyz");
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(-1, val);
+ }
+
+ public static void testTryCatchFromCatch() {
+ int val = 0;
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $noinline$TryCatch("42");
+ }
+ assertEquals(42, val);
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $noinline$TryCatch("xyz");
+ }
+ assertEquals(-1, val);
+ }
+
+ public static void main(String[] args) {
+ testSingleBlockFromTry();
+ testSingleBlockFromCatch();
+ testMultipleBlocksFromTry();
+ testMultipleBlocksFromCatch();
+ testTryCatchFromTry();
+ testTryCatchFromCatch();
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+
+ private static void unreachable() {
+ throw new Error("Unreachable");
+ }
+
+ private static void throwException() throws Exception {
+ throw new Exception();
+ }
+}