Optimizing: Try pattern substitution when we cannot inline.
Change-Id: I7c01f4494bac8498accc0f087044ec509fee4c98
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 35109fa..51fef7c 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -21,6 +21,8 @@
#include "class_linker.h"
#include "constant_folding.h"
#include "dead_code_elimination.h"
+#include "dex/verified_method.h"
+#include "dex/verification_results.h"
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
@@ -32,13 +34,12 @@
#include "optimizing_compiler.h"
#include "reference_type_propagation.h"
#include "register_allocator.h"
+#include "quick/inline_method_analyser.h"
#include "sharpening.h"
#include "ssa_builder.h"
#include "ssa_phi_elimination.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
-#include "dex/verified_method.h"
-#include "dex/verification_results.h"
namespace art {
@@ -488,6 +489,11 @@
// dex file here (though the transitivity of an inline chain would allow checking the calller).
if (!compiler_driver_->MayInline(method->GetDexFile(),
outer_compilation_unit_.GetDexFile())) {
+ if (TryPatternSubstitution(invoke_instruction, method, do_rtp)) {
+ VLOG(compiler) << "Successfully replaced pattern of invoke " << PrettyMethod(method);
+ MaybeRecordStat(kReplacedInvokeWithSimplePattern);
+ return true;
+ }
VLOG(compiler) << "Won't inline " << PrettyMethod(method) << " in "
<< outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
<< caller_compilation_unit_.GetDexFile()->GetLocation() << ") from "
@@ -559,6 +565,140 @@
return true;
}
+static HInstruction* GetInvokeInputForArgVRegIndex(HInvoke* invoke_instruction,
+ size_t arg_vreg_index)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ size_t input_index = 0;
+ for (size_t i = 0; i < arg_vreg_index; ++i, ++input_index) {
+ DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+ if (Primitive::Is64BitType(invoke_instruction->InputAt(input_index)->GetType())) {
+ ++i;
+ DCHECK_NE(i, arg_vreg_index);
+ }
+ }
+ DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+ return invoke_instruction->InputAt(input_index);
+}
+
+// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
+bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ bool do_rtp) {
+ InlineMethod inline_method;
+ if (!InlineMethodAnalyser::AnalyseMethodCode(resolved_method, &inline_method)) {
+ return false;
+ }
+
+ HInstruction* return_replacement = nullptr;
+ switch (inline_method.opcode) {
+ case kInlineOpNop:
+ DCHECK_EQ(invoke_instruction->GetType(), Primitive::kPrimVoid);
+ break;
+ case kInlineOpReturnArg:
+ return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
+ inline_method.d.return_data.arg);
+ break;
+ case kInlineOpNonWideConst:
+ if (resolved_method->GetShorty()[0] == 'L') {
+ DCHECK_EQ(inline_method.d.data, 0u);
+ return_replacement = graph_->GetNullConstant();
+ } else {
+ return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
+ }
+ break;
+ case kInlineOpIGet: {
+ const InlineIGetIPutData& data = inline_method.d.ifield_data;
+ if (data.method_is_static || data.object_arg != 0u) {
+ // TODO: Needs null check.
+ return false;
+ }
+ HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+ HInstanceFieldGet* iget = CreateInstanceFieldGet(resolved_method, data.field_idx, obj);
+ DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset);
+ DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction);
+ return_replacement = iget;
+ break;
+ }
+ case kInlineOpIPut: {
+ const InlineIGetIPutData& data = inline_method.d.ifield_data;
+ if (data.method_is_static || data.object_arg != 0u) {
+ // TODO: Needs null check.
+ return false;
+ }
+ HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+ HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, data.src_arg);
+ HInstanceFieldSet* iput = CreateInstanceFieldSet(resolved_method, data.field_idx, obj, value);
+ DCHECK_EQ(iput->GetFieldOffset().Uint32Value(), data.field_offset);
+ DCHECK_EQ(iput->IsVolatile() ? 1u : 0u, data.is_volatile);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
+ if (data.return_arg_plus1 != 0u) {
+ size_t return_arg = data.return_arg_plus1 - 1u;
+ return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ UNREACHABLE();
+ }
+
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+
+ FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ return true;
+}
+
+HInstanceFieldGet* HInliner::CreateInstanceFieldGet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ DCHECK(resolved_field != nullptr);
+ HInstanceFieldGet* iget = new (graph_->GetArena()) HInstanceFieldGet(
+ obj,
+ resolved_field->GetTypeAsPrimitiveType(),
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *resolved_method->GetDexFile(),
+ dex_cache,
+ kNoDexPc);
+ if (iget->GetType() == Primitive::kPrimNot) {
+ ReferenceTypePropagation rtp(graph_, handles_);
+ rtp.Visit(iget);
+ }
+ return iget;
+}
+
+HInstanceFieldSet* HInliner::CreateInstanceFieldSet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj,
+ HInstruction* value)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ DCHECK(resolved_field != nullptr);
+ HInstanceFieldSet* iput = new (graph_->GetArena()) HInstanceFieldSet(
+ obj,
+ value,
+ resolved_field->GetTypeAsPrimitiveType(),
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *resolved_method->GetDexFile(),
+ dex_cache,
+ kNoDexPc);
+ return iput;
+}
bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
HInvoke* invoke_instruction,
bool same_dex_file,
@@ -815,7 +955,14 @@
if (return_replacement != nullptr) {
DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph());
}
+ FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ return true;
+}
+void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
+ HInvoke* invoke_instruction,
+ HInstruction* return_replacement,
+ bool do_rtp) {
// Check the integrity of reference types and run another type propagation if needed.
if (return_replacement != nullptr) {
if (return_replacement->GetType() == Primitive::kPrimNot) {
@@ -849,8 +996,6 @@
}
}
}
-
- return true;
}
} // namespace art