OSDN Git Service

ART: Enable inlining under try/catch
authorDavid Brazdil <dbrazdil@google.com>
Fri, 30 Oct 2015 17:56:58 +0000 (12:56 -0500)
committerDavid Brazdil <dbrazdil@google.com>
Fri, 30 Oct 2015 20:02:37 +0000 (15:02 -0500)
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

compiler/optimizing/nodes.cc
compiler/optimizing/optimizing_compiler.cc
test/004-ReferenceMap/stack_walk_refmap_jni.cc
test/449-checker-bce/src/Main.java
test/542-inline-trycatch/expected.txt [new file with mode: 0644]
test/542-inline-trycatch/info.txt [new file with mode: 0644]
test/542-inline-trycatch/src/Main.java [new file with mode: 0644]

index 8b28ff9..68fb0ac 100644 (file)
@@ -1652,7 +1652,8 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
     // 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 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
     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
index 6632f95..8cb2cfc 100644 (file)
@@ -492,6 +492,8 @@ static void RunOptimizations(HGraph* graph,
 
   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 @@ static void RunOptimizations(HGraph* graph,
 
     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.
index 34fb3f8..2dbd7e8 100644 (file)
@@ -49,7 +49,9 @@ struct ReferenceMap2Visitor : public CheckReferenceMapVisitor {
     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 @@ struct ReferenceMap2Visitor : public CheckReferenceMapVisitor {
       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.
index 22829cd..ffeae7d 100644 (file)
@@ -624,12 +624,13 @@ public class Main {
       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 (file)
index 0000000..e69de29
diff --git a/test/542-inline-trycatch/info.txt b/test/542-inline-trycatch/info.txt
new file mode 100644 (file)
index 0000000..b3e50d3
--- /dev/null
@@ -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 (file)
index 0000000..5a6e06f
--- /dev/null
@@ -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();
+  }
+}