Support for inlining methods that call/throw.

Mostly fixes here and there to make it working.

Change-Id: I1b535e895105d78b65634636d675b818551f783e
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 997f980..15f3deb 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -36,8 +36,8 @@
 
 namespace art {
 
-static constexpr int kMaxInlineCodeUnits = 100;
-static constexpr int kDepthLimit = 5;
+static constexpr int kMaxInlineCodeUnits = 18;
+static constexpr int kDepthLimit = 3;
 
 void HInliner::Run() {
   if (graph_->IsDebuggable()) {
@@ -46,8 +46,15 @@
     return;
   }
   const GrowableArray<HBasicBlock*>& blocks = graph_->GetReversePostOrder();
+  HBasicBlock* next_block = blocks.Get(0);
   for (size_t i = 0; i < blocks.Size(); ++i) {
-    HBasicBlock* block = blocks.Get(i);
+    // Because we are changing the graph when inlining, we need to remember the next block.
+    // This avoids doing the inlining work again on the inlined blocks.
+    if (blocks.Get(i) != next_block) {
+      continue;
+    }
+    HBasicBlock* block = next_block;
+    next_block = (i == blocks.Size() - 1) ? nullptr : blocks.Get(i + 1);
     for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) {
       HInstruction* next = instruction->GetNext();
       HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect();
@@ -90,10 +97,10 @@
     return false;
   }
 
-  bool can_use_dex_cache = true;
+  bool same_dex_file = true;
   const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
   if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) {
-    can_use_dex_cache = false;
+    same_dex_file = false;
   }
 
   const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
@@ -140,7 +147,7 @@
     return false;
   }
 
-  if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index, can_use_dex_cache)) {
+  if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index, same_dex_file)) {
     return false;
   }
 
@@ -152,7 +159,7 @@
 bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method,
                                  HInvoke* invoke_instruction,
                                  uint32_t method_index,
-                                 bool can_use_dex_cache) const {
+                                 bool same_dex_file) const {
   ScopedObjectAccess soa(Thread::Current());
   const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
   const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
@@ -254,6 +261,31 @@
     inliner.Run();
   }
 
+  // TODO: We should abort only if all predecessors throw. However,
+  // HGraph::InlineInto currently does not handle an exit block with
+  // a throw predecessor.
+  HBasicBlock* exit_block = callee_graph->GetExitBlock();
+  if (exit_block == nullptr) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
+                   << " could not be inlined because it has an infinite loop";
+    resolved_method->SetShouldNotInline();
+    return false;
+  }
+
+  bool has_throw_predecessor = false;
+  for (size_t i = 0, e = exit_block->GetPredecessors().Size(); i < e; ++i) {
+    if (exit_block->GetPredecessors().Get(i)->GetLastInstruction()->IsThrow()) {
+      has_throw_predecessor = true;
+      break;
+    }
+  }
+  if (has_throw_predecessor) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
+                   << " could not be inlined because one branch always throws";
+    resolved_method->SetShouldNotInline();
+    return false;
+  }
+
   HReversePostOrderIterator it(*callee_graph);
   it.Advance();  // Past the entry block, it does not contain instructions that prevent inlining.
   for (; !it.Done(); it.Advance()) {
@@ -269,27 +301,24 @@
          !instr_it.Done();
          instr_it.Advance()) {
       HInstruction* current = instr_it.Current();
-      if (current->IsSuspendCheck()) {
-        continue;
-      }
 
-      if (current->CanThrow()) {
+      if (current->IsInvokeInterface()) {
+        // Disable inlining of interface calls. The cost in case of entering the
+        // resolution conflict is currently too high.
         VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
-                       << " could not be inlined because " << current->DebugName()
-                       << " can throw";
+                       << " could not be inlined because it has an interface call.";
         resolved_method->SetShouldNotInline();
         return false;
       }
 
-      if (current->NeedsEnvironment()) {
+      if (!same_dex_file && current->NeedsEnvironment()) {
         VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                        << " could not be inlined because " << current->DebugName()
-                       << " needs an environment";
-        resolved_method->SetShouldNotInline();
+                       << " needs an environment and is in a different dex file";
         return false;
       }
 
-      if (!can_use_dex_cache && current->NeedsDexCache()) {
+      if (!same_dex_file && current->NeedsDexCache()) {
         VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                        << " could not be inlined because " << current->DebugName()
                        << " it is in a different dex file and requires access to the dex cache";
@@ -302,10 +331,6 @@
 
   callee_graph->InlineInto(graph_, invoke_instruction);
 
-  if (callee_graph->HasBoundsChecks()) {
-    graph_->SetHasBoundsChecks(true);
-  }
-
   return true;
 }