summaryrefslogtreecommitdiff
path: root/compiler/optimizing/inliner.cc
diff options
context:
space:
mode:
author Aart Bik <ajcbik@google.com> 2018-01-09 11:01:02 -0800
committer Aart Bik <ajcbik@google.com> 2018-01-16 09:44:28 -0800
commita8b8e9b12a9740d71cff2fa65d47825b74f72c37 (patch)
tree301275759cf145711175992a503fcc7d710c2d2f /compiler/optimizing/inliner.cc
parent6d4c343ee5db18f039aeb3e07ff8d3c1fd37c3a0 (diff)
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
Diffstat (limited to 'compiler/optimizing/inliner.cc')
-rw-r--r--compiler/optimizing/inliner.cc33
1 files changed, 33 insertions, 0 deletions
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 81a75584a4..41e4bbe4ff 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -392,6 +392,34 @@ ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) {
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 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) {
} 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;
}