Improve code sinking near "always throwing" method calls

Rationale:
With simple dex bytecode analysis, the inliner marks methods
that always throw to help subsequent code sinking. This reduces
overhead of non-nullable enforcing calls found in e.g the Kotlin
runtime library (1%-2% improvement on tree microbenchmark, about
5% on Denis' benchmark).

Test: test-art-host test-art-target

Change-Id: I45348f049721476828eb5443738021720d2857c0
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 81a7558..41e4bbe 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -392,6 +392,34 @@
   return single_impl;
 }
 
+static bool AlwaysThrows(ArtMethod* method) {
+  CodeItemDataAccessor accessor(method);
+  // Skip native methods, methods with try blocks, and methods that are too large.
+  if (!accessor.HasCodeItem() ||
+      accessor.TriesSize() != 0 ||
+      accessor.InsnsSizeInCodeUnits() > kMaximumNumberOfTotalInstructions) {
+    return false;
+  }
+  // Scan for exits.
+  bool throw_seen = false;
+  for (const DexInstructionPcPair& pair : accessor) {
+    switch (pair.Inst().Opcode()) {
+      case Instruction::RETURN:
+      case Instruction::RETURN_VOID:
+      case Instruction::RETURN_WIDE:
+      case Instruction::RETURN_OBJECT:
+      case Instruction::RETURN_VOID_NO_BARRIER:
+        return false;  // found regular control flow back
+      case Instruction::THROW:
+        throw_seen = true;
+        break;
+      default:
+        break;
+    }
+  }
+  return throw_seen;
+}
+
 bool HInliner::TryInline(HInvoke* invoke_instruction) {
   if (invoke_instruction->IsInvokeUnresolved() ||
       invoke_instruction->IsInvokePolymorphic()) {
@@ -445,6 +473,11 @@
       } else {
         MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
       }
+    } else if (!result && invoke_instruction->IsInvokeStaticOrDirect()) {
+      // Analyze always throws property for static/direct method call with single target.
+      if (AlwaysThrows(actual_method)) {
+        invoke_instruction->SetAlwaysThrows(true);
+      }
     }
     return result;
   }