From: Vladimir Marko Date: Fri, 22 Jan 2016 12:09:44 +0000 (+0000) Subject: Optimizing: Try pattern substitution when we cannot inline. X-Git-Tag: android-x86-7.1-r1~424^2~106^2^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=be10e8e99a78caae01fb65769218800d465144ae;p=android-x86%2Fart.git Optimizing: Try pattern substitution when we cannot inline. Change-Id: I7c01f4494bac8498accc0f087044ec509fee4c98 --- diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 35109fa53..51fef7c9c 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 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do // 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 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do 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(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 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 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 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, 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 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, } } } - - return true; } } // namespace art diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 3c01751a7..0127d5519 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -65,6 +65,20 @@ class HInliner : public HOptimization { bool TryInline(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp = true) SHARED_REQUIRES(Locks::mutator_lock_); + // Try to recognize known simple patterns and replace invoke call with appropriate instructions. + bool TryPatternSubstitution(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp) + SHARED_REQUIRES(Locks::mutator_lock_); + + // Create a new HInstanceFieldGet. + HInstanceFieldGet* CreateInstanceFieldGet(ArtMethod* resolved_method, + uint32_t field_index, + HInstruction* obj); + // Create a new HInstanceFieldSet. + HInstanceFieldSet* CreateInstanceFieldSet(ArtMethod* resolved_method, + uint32_t field_index, + HInstruction* obj, + HInstruction* value); + // Try to inline the target of a monomorphic call. If successful, the code // in the graph will look like: // if (receiver.getClass() != ic.GetMonomorphicType()) deopt @@ -90,6 +104,12 @@ class HInliner : public HOptimization { uint32_t dex_pc) const SHARED_REQUIRES(Locks::mutator_lock_); + void FixUpReturnReferenceType(ArtMethod* resolved_method, + HInvoke* invoke_instruction, + HInstruction* return_replacement, + bool do_rtp) + SHARED_REQUIRES(Locks::mutator_lock_); + HGraph* const outermost_graph_; const DexCompilationUnit& outer_compilation_unit_; const DexCompilationUnit& caller_compilation_unit_; diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 881beb49a..52a7b10ca 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -29,6 +29,7 @@ enum MethodCompilationStat { kAttemptCompilation = 0, kCompiled, kInlinedInvoke, + kReplacedInvokeWithSimplePattern, kInstructionSimplifications, kInstructionSimplificationsArch, kUnresolvedMethod, @@ -97,6 +98,7 @@ class OptimizingCompilerStats { case kAttemptCompilation : name = "AttemptCompilation"; break; case kCompiled : name = "Compiled"; break; case kInlinedInvoke : name = "InlinedInvoke"; break; + case kReplacedInvokeWithSimplePattern: name = "ReplacedInvokeWithSimplePattern"; break; case kInstructionSimplifications: name = "InstructionSimplifications"; break; case kInstructionSimplificationsArch: name = "InstructionSimplificationsArch"; break; case kUnresolvedMethod : name = "UnresolvedMethod"; break; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 779f319e6..1224a48fa 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -94,7 +94,6 @@ ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph, : HOptimization(graph, name), handle_cache_(handles), worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)) { - worklist_.reserve(kDefaultWorklistSize); } void ReferenceTypePropagation::ValidateTypes() { @@ -125,7 +124,14 @@ void ReferenceTypePropagation::ValidateTypes() { } } +void ReferenceTypePropagation::Visit(HInstruction* instruction) { + RTPVisitor visitor(graph_, &handle_cache_, &worklist_); + instruction->Accept(&visitor); +} + void ReferenceTypePropagation::Run() { + worklist_.reserve(kDefaultWorklistSize); + // To properly propagate type info we need to visit in the dominator-based order. // Reverse post order guarantees a node's dominators are visited first. // We take advantage of this order in `VisitBasicBlock`. diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 47ba0277a..a7f10a65a 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -35,6 +35,9 @@ class ReferenceTypePropagation : public HOptimization { StackHandleScopeCollection* handles, const char* name = kReferenceTypePropagationPassName); + // Visit a single instruction. + void Visit(HInstruction* instruction); + void Run() OVERRIDE; static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation"; diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc index 65543942c..17306c984 100644 --- a/runtime/quick/inline_method_analyser.cc +++ b/runtime/quick/inline_method_analyser.cc @@ -71,14 +71,37 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) == // we need to be able to detect possibly inlined method, we pass a null inline method to indicate // we don't want to take unresolved methods and fields into account during analysis. bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier, - InlineMethod* method) { + InlineMethod* result) { DCHECK(verifier != nullptr); if (!Runtime::Current()->UseJit()) { - DCHECK_EQ(verifier->CanLoadClasses(), method != nullptr); + DCHECK_EQ(verifier->CanLoadClasses(), result != nullptr); + } + + // Note: verifier->GetMethod() may be null. + return AnalyseMethodCode(verifier->CodeItem(), + verifier->GetMethodReference(), + (verifier->GetAccessFlags() & kAccStatic) != 0u, + verifier->GetMethod(), + result); +} + +bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) { + const DexFile::CodeItem* code_item = method->GetCodeItem(); + if (code_item == nullptr) { + // Native or abstract. + return false; } + return AnalyseMethodCode( + code_item, method->ToMethodReference(), method->IsStatic(), method, result); +} + +bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item, + const MethodReference& method_ref, + bool is_static, + ArtMethod* method, + InlineMethod* result) { // We currently support only plain return or 2-instruction methods. - const DexFile::CodeItem* code_item = verifier->CodeItem(); DCHECK_NE(code_item->insns_size_in_code_units_, 0u); const Instruction* instruction = Instruction::At(code_item->insns_); Instruction::Code opcode = instruction->Opcode(); @@ -86,21 +109,21 @@ bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier, switch (opcode) { case Instruction::RETURN_VOID: if (method != nullptr) { - method->opcode = kInlineOpNop; - method->flags = kInlineSpecial; - method->d.data = 0u; + result->opcode = kInlineOpNop; + result->flags = kInlineSpecial; + result->d.data = 0u; } return true; case Instruction::RETURN: case Instruction::RETURN_OBJECT: case Instruction::RETURN_WIDE: - return AnalyseReturnMethod(code_item, method); + return AnalyseReturnMethod(code_item, result); case Instruction::CONST: case Instruction::CONST_4: case Instruction::CONST_16: case Instruction::CONST_HIGH16: // TODO: Support wide constants (RETURN_WIDE). - return AnalyseConstMethod(code_item, method); + return AnalyseConstMethod(code_item, result); case Instruction::IGET: case Instruction::IGET_OBJECT: case Instruction::IGET_BOOLEAN: @@ -112,7 +135,7 @@ bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier, // case Instruction::IGET_QUICK: // case Instruction::IGET_WIDE_QUICK: // case Instruction::IGET_OBJECT_QUICK: - return AnalyseIGetMethod(verifier, method); + return AnalyseIGetMethod(code_item, method_ref, is_static, method, result); case Instruction::IPUT: case Instruction::IPUT_OBJECT: case Instruction::IPUT_BOOLEAN: @@ -124,7 +147,7 @@ bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier, // case Instruction::IPUT_QUICK: // case Instruction::IPUT_WIDE_QUICK: // case Instruction::IPUT_OBJECT_QUICK: - return AnalyseIPutMethod(verifier, method); + return AnalyseIPutMethod(code_item, method_ref, is_static, method, result); default: return false; } @@ -194,9 +217,11 @@ bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item return true; } -bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier, +bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item, + const MethodReference& method_ref, + bool is_static, + ArtMethod* method, InlineMethod* result) { - const DexFile::CodeItem* code_item = verifier->CodeItem(); const Instruction* instruction = Instruction::At(code_item->insns_); Instruction::Code opcode = instruction->Opcode(); DCHECK(IsInstructionIGet(opcode)); @@ -227,10 +252,10 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier, return false; // Not returning the value retrieved by IGET? } - if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) { + if (is_static || object_arg != 0u) { // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE). // Allow synthetic accessors. We don't care about losing their stack frame in NPE. - if (!IsSyntheticAccessor(verifier->GetMethodReference())) { + if (!IsSyntheticAccessor(method_ref)) { return false; } } @@ -243,13 +268,13 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier, if (result != nullptr) { InlineIGetIPutData* data = &result->d.ifield_data; - if (!ComputeSpecialAccessorInfo(field_idx, false, verifier, data)) { + if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) { return false; } result->opcode = kInlineOpIGet; result->flags = kInlineSpecial; data->op_variant = IGetVariant(opcode); - data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u; + data->method_is_static = is_static ? 1u : 0u; data->object_arg = object_arg; // Allow IGET on any register, not just "this". data->src_arg = 0u; data->return_arg_plus1 = 0u; @@ -257,9 +282,11 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier, return true; } -bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier, +bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item, + const MethodReference& method_ref, + bool is_static, + ArtMethod* method, InlineMethod* result) { - const DexFile::CodeItem* code_item = verifier->CodeItem(); const Instruction* instruction = Instruction::At(code_item->insns_); Instruction::Code opcode = instruction->Opcode(); DCHECK(IsInstructionIPut(opcode)); @@ -292,10 +319,10 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier, uint32_t object_arg = object_reg - arg_start; uint32_t src_arg = src_reg - arg_start; - if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) { + if (is_static || object_arg != 0u) { // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE). // Allow synthetic accessors. We don't care about losing their stack frame in NPE. - if (!IsSyntheticAccessor(verifier->GetMethodReference())) { + if (!IsSyntheticAccessor(method_ref)) { return false; } } @@ -310,13 +337,13 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier, if (result != nullptr) { InlineIGetIPutData* data = &result->d.ifield_data; - if (!ComputeSpecialAccessorInfo(field_idx, true, verifier, data)) { + if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) { return false; } result->opcode = kInlineOpIPut; result->flags = kInlineSpecial; data->op_variant = IPutVariant(opcode); - data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u; + data->method_is_static = is_static ? 1u : 0u; data->object_arg = object_arg; // Allow IPUT on any register, not just "this". data->src_arg = src_arg; data->return_arg_plus1 = return_arg_plus1; @@ -324,15 +351,17 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier, return true; } -bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put, - verifier::MethodVerifier* verifier, +bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, + uint32_t field_idx, + bool is_put, InlineIGetIPutData* result) { - mirror::DexCache* dex_cache = verifier->GetDexCache(); - uint32_t method_idx = verifier->GetMethodReference().dex_method_index; - auto* cl = Runtime::Current()->GetClassLinker(); - ArtMethod* method = dex_cache->GetResolvedMethod(method_idx, cl->GetImagePointerSize()); - ArtField* field = cl->GetResolvedField(field_idx, dex_cache); - if (method == nullptr || field == nullptr || field->IsStatic()) { + if (method == nullptr) { + return false; + } + mirror::DexCache* dex_cache = method->GetDexCache(); + size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + ArtField* field = dex_cache->GetResolvedField(field_idx, pointer_size); + if (field == nullptr || field->IsStatic()) { return false; } mirror::Class* method_class = method->GetDeclaringClass(); diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 1bb816bb2..046d2257f 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -188,7 +188,9 @@ class InlineMethodAnalyser { * @param method placeholder for the inline method data. * @return true if the method is a candidate for inlining, false otherwise. */ - static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* method) + static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* result) + SHARED_REQUIRES(Locks::mutator_lock_); + static bool AnalyseMethodCode(ArtMethod* method, InlineMethod* result) SHARED_REQUIRES(Locks::mutator_lock_); static constexpr bool IsInstructionIGet(Instruction::Code opcode) { @@ -211,17 +213,32 @@ class InlineMethodAnalyser { static bool IsSyntheticAccessor(MethodReference ref); private: + static bool AnalyseMethodCode(const DexFile::CodeItem* code_item, + const MethodReference& method_ref, + bool is_static, + ArtMethod* method, + InlineMethod* result) + SHARED_REQUIRES(Locks::mutator_lock_); static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result); static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result); - static bool AnalyseIGetMethod(verifier::MethodVerifier* verifier, InlineMethod* result) + static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item, + const MethodReference& method_ref, + bool is_static, + ArtMethod* method, + InlineMethod* result) SHARED_REQUIRES(Locks::mutator_lock_); - static bool AnalyseIPutMethod(verifier::MethodVerifier* verifier, InlineMethod* result) + static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item, + const MethodReference& method_ref, + bool is_static, + ArtMethod* method, + InlineMethod* result) SHARED_REQUIRES(Locks::mutator_lock_); // Can we fast path instance field access in a verified accessor? // If yes, computes field's offset and volatility and whether the method is static or not. - static bool ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put, - verifier::MethodVerifier* verifier, + static bool ComputeSpecialAccessorInfo(ArtMethod* method, + uint32_t field_idx, + bool is_put, InlineIGetIPutData* result) SHARED_REQUIRES(Locks::mutator_lock_); }; diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index f52d0110a..def61db81 100644 --- a/runtime/verifier/method_verifier-inl.h +++ b/runtime/verifier/method_verifier-inl.h @@ -50,6 +50,10 @@ inline mirror::DexCache* MethodVerifier::GetDexCache() { return dex_cache_.Get(); } +inline ArtMethod* MethodVerifier::GetMethod() const { + return mirror_method_; +} + inline MethodReference MethodVerifier::GetMethodReference() const { return MethodReference(dex_file_, dex_method_idx_); } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index a26e0fba1..613d5af21 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -270,6 +270,7 @@ class MethodVerifier { ALWAYS_INLINE InstructionFlags& GetInstructionFlags(size_t index); mirror::ClassLoader* GetClassLoader() SHARED_REQUIRES(Locks::mutator_lock_); mirror::DexCache* GetDexCache() SHARED_REQUIRES(Locks::mutator_lock_); + ArtMethod* GetMethod() const SHARED_REQUIRES(Locks::mutator_lock_); MethodReference GetMethodReference() const; uint32_t GetAccessFlags() const; bool HasCheckCasts() const; diff --git a/test/569-checker-pattern-replacement/expected.txt b/test/569-checker-pattern-replacement/expected.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/569-checker-pattern-replacement/info.txt b/test/569-checker-pattern-replacement/info.txt new file mode 100644 index 000000000..4dfa93200 --- /dev/null +++ b/test/569-checker-pattern-replacement/info.txt @@ -0,0 +1 @@ +Test pattern substitution used when we cannot inline. diff --git a/test/569-checker-pattern-replacement/run b/test/569-checker-pattern-replacement/run new file mode 100755 index 000000000..f7e9df211 --- /dev/null +++ b/test/569-checker-pattern-replacement/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2016 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. + +exec ${RUN} "$@" \ + -Xcompiler-option --no-inline-from=core-oj,569-checker-pattern-replacement.jar:classes2.dex diff --git a/test/569-checker-pattern-replacement/src-multidex/Second.java b/test/569-checker-pattern-replacement/src-multidex/Second.java new file mode 100644 index 000000000..cba1dc8dc --- /dev/null +++ b/test/569-checker-pattern-replacement/src-multidex/Second.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 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 final class Second { + public static void staticNop(int unused) { } + + public void nop() { } + + public static Object staticReturnArg2(int unused1, String arg2) { + return arg2; + } + + public long returnArg1(long arg1) { + return arg1; + } + + public static int staticReturn9() { + return 9; + } + + public int return7(Object unused) { + return 7; + } + + public static String staticReturnNull() { + return null; + } + + public Object returnNull() { + return null; + } + + public int getInstanceIntField() { + return instanceIntField; + } + + public double getInstanceDoubleField(int unused1) { + return instanceDoubleField; + } + + public Object getInstanceObjectField(long unused1) { + return instanceObjectField; + } + + public String getInstanceStringField(Object unused1, String unused2, long unused3) { + return instanceStringField; + } + + public static int staticGetInstanceIntField(Second s) { + return s.instanceIntField; + } + + public double getInstanceDoubleFieldFromParam(Second s) { + return s.instanceDoubleField; + } + + public int getStaticIntField() { + return staticIntField; + } + + public void setInstanceLongField(int ignored, long value) { + instanceLongField = value; + } + + public int setInstanceLongFieldReturnArg2(long value, int arg2) { + instanceLongField = value; + return arg2; + } + + public static void staticSetInstanceLongField(Second s, long value) { + s.instanceLongField = value; + } + + public void setInstanceLongFieldThroughParam(Second s, long value) { + s.instanceLongField = value; + } + + public void setStaticFloatField(float value) { + staticFloatField = value; + } + + public int instanceIntField = 42; + public double instanceDoubleField = -42.0; + public Object instanceObjectField = null; + public String instanceStringField = "dummy"; + public long instanceLongField = 0; // Overwritten by setters. + + public static int staticIntField = 4242; + public static float staticFloatField = 0.0f; // Overwritten by setters. +} diff --git a/test/569-checker-pattern-replacement/src/Main.java b/test/569-checker-pattern-replacement/src/Main.java new file mode 100644 index 000000000..9a85c8124 --- /dev/null +++ b/test/569-checker-pattern-replacement/src/Main.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2016 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 { + /// CHECK-START: void Main.staticNop() inliner (before) + /// CHECK: InvokeStaticOrDirect + + /// CHECK-START: void Main.staticNop() inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + public static void staticNop() { + Second.staticNop(11); + } + + /// CHECK-START: void Main.nop(Second) inliner (before) + /// CHECK: InvokeVirtual + + /// CHECK-START: void Main.nop(Second) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static void nop(Second s) { + s.nop(); + } + + /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 77 + /// CHECK-DAG: <> ClinitCheck + /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: Return [<>] + + /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + public static Object staticReturnArg2(String value) { + return Second.staticReturnArg2(77, value); + } + + /// CHECK-START: long Main.returnArg1(Second, long) inliner (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> NullCheck [<>] + /// CHECK-DAG: <> InvokeVirtual [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: long Main.returnArg1(Second, long) inliner (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: Return [<>] + + /// CHECK-START: long Main.returnArg1(Second, long) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static long returnArg1(Second s, long value) { + return s.returnArg1(value); + } + + /// CHECK-START: int Main.staticReturn9() inliner (before) + /// CHECK: {{i\d+}} InvokeStaticOrDirect + + /// CHECK-START: int Main.staticReturn9() inliner (before) + /// CHECK-NOT: IntConstant 9 + + /// CHECK-START: int Main.staticReturn9() inliner (after) + /// CHECK-DAG: <> IntConstant 9 + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.staticReturn9() inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + public static int staticReturn9() { + return Second.staticReturn9(); + } + + /// CHECK-START: int Main.return7(Second) inliner (before) + /// CHECK: {{i\d+}} InvokeVirtual + + /// CHECK-START: int Main.return7(Second) inliner (before) + /// CHECK-NOT: IntConstant 7 + + /// CHECK-START: int Main.return7(Second) inliner (after) + /// CHECK-DAG: <> IntConstant 7 + /// CHECK-DAG: Return [<>] + + /// CHECK-START: int Main.return7(Second) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static int return7(Second s) { + return s.return7(null); + } + + /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before) + /// CHECK: {{l\d+}} InvokeStaticOrDirect + + /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before) + /// CHECK-NOT: NullConstant + + /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after) + /// CHECK-DAG: <> NullConstant + /// CHECK-DAG: Return [<>] + + /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + public static String staticReturnNull() { + return Second.staticReturnNull(); + } + + /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before) + /// CHECK: {{l\d+}} InvokeVirtual + + /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before) + /// CHECK-NOT: NullConstant + + /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after) + /// CHECK-DAG: <> NullConstant + /// CHECK-DAG: Return [<>] + + /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static Object returnNull(Second s) { + return s.returnNull(); + } + + /// CHECK-START: int Main.getInt(Second) inliner (before) + /// CHECK: {{i\d+}} InvokeVirtual + + /// CHECK-START: int Main.getInt(Second) inliner (after) + /// CHECK: {{i\d+}} InstanceFieldGet + + /// CHECK-START: int Main.getInt(Second) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static int getInt(Second s) { + return s.getInstanceIntField(); + } + + /// CHECK-START: double Main.getDouble(Second) inliner (before) + /// CHECK: {{d\d+}} InvokeVirtual + + /// CHECK-START: double Main.getDouble(Second) inliner (after) + /// CHECK: {{d\d+}} InstanceFieldGet + + /// CHECK-START: double Main.getDouble(Second) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static double getDouble(Second s) { + return s.getInstanceDoubleField(22); + } + + /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (before) + /// CHECK: {{l\d+}} InvokeVirtual + + /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after) + /// CHECK: {{l\d+}} InstanceFieldGet + + /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static Object getObject(Second s) { + return s.getInstanceObjectField(-1L); + } + + /// CHECK-START: java.lang.String Main.getString(Second) inliner (before) + /// CHECK: {{l\d+}} InvokeVirtual + + /// CHECK-START: java.lang.String Main.getString(Second) inliner (after) + /// CHECK: {{l\d+}} InstanceFieldGet + + /// CHECK-START: java.lang.String Main.getString(Second) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static String getString(Second s) { + return s.getInstanceStringField(null, "whatever", 1234L); + } + + /// CHECK-START: int Main.staticGetInt(Second) inliner (before) + /// CHECK: {{i\d+}} InvokeStaticOrDirect + + /// CHECK-START: int Main.staticGetInt(Second) inliner (after) + /// CHECK: {{i\d+}} InvokeStaticOrDirect + + /// CHECK-START: int Main.staticGetInt(Second) inliner (after) + /// CHECK-NOT: InstanceFieldGet + + public static int staticGetInt(Second s) { + return Second.staticGetInstanceIntField(s); + } + + /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (before) + /// CHECK: {{d\d+}} InvokeVirtual + + /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after) + /// CHECK: {{d\d+}} InvokeVirtual + + /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after) + /// CHECK-NOT: InstanceFieldGet + + public static double getDoubleFromParam(Second s) { + return s.getInstanceDoubleFieldFromParam(s); + } + + /// CHECK-START: int Main.getStaticInt(Second) inliner (before) + /// CHECK: {{i\d+}} InvokeVirtual + + /// CHECK-START: int Main.getStaticInt(Second) inliner (after) + /// CHECK: {{i\d+}} InvokeVirtual + + /// CHECK-START: int Main.getStaticInt(Second) inliner (after) + /// CHECK-NOT: InstanceFieldGet + /// CHECK-NOT: StaticFieldGet + + public static int getStaticInt(Second s) { + return s.getStaticIntField(); + } + + /// CHECK-START: long Main.setLong(Second, long) inliner (before) + /// CHECK: InvokeVirtual + + /// CHECK-START: long Main.setLong(Second, long) inliner (after) + /// CHECK: InstanceFieldSet + + /// CHECK-START: long Main.setLong(Second, long) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static long setLong(Second s, long value) { + s.setInstanceLongField(-1, value); + return s.instanceLongField; + } + + /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (before) + /// CHECK: InvokeVirtual + + /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> NullCheck [<>] + /// CHECK-DAG: InstanceFieldSet [<>,<>] + /// CHECK-DAG: <> NullCheck [<>] + /// CHECK-DAG: <> InstanceFieldGet [<>] + /// CHECK-DAG: <> TypeConversion [<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: Return [<>] + + /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after) + /// CHECK-NOT: InvokeVirtual + + public static long setLongReturnArg2(Second s, long value, int arg2) { + int result = s.setInstanceLongFieldReturnArg2(value, arg2); + return s.instanceLongField + result; + } + + /// CHECK-START: long Main.staticSetLong(Second, long) inliner (before) + /// CHECK: InvokeStaticOrDirect + + /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after) + /// CHECK: InvokeStaticOrDirect + + /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after) + /// CHECK-NOT: InstanceFieldSet + + public static long staticSetLong(Second s, long value) { + Second.staticSetInstanceLongField(s, value); + return s.instanceLongField; + } + + /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (before) + /// CHECK: InvokeVirtual + + /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after) + /// CHECK: InvokeVirtual + + /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after) + /// CHECK-NOT: InstanceFieldSet + + public static long setLongThroughParam(Second s, long value) { + s.setInstanceLongFieldThroughParam(s, value); + return s.instanceLongField; + } + + /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (before) + /// CHECK: InvokeVirtual + + /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after) + /// CHECK: InvokeVirtual + + /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after) + /// CHECK-NOT: InstanceFieldSet + /// CHECK-NOT: StaticFieldSet + + public static float setStaticFloat(Second s, float value) { + s.setStaticFloatField(value); + return s.staticFloatField; + } + + /// CHECK-START: java.lang.Object Main.newObject() inliner (before) + /// CHECK-DAG: <> NewInstance + /// CHECK-DAG: InvokeStaticOrDirect [<>] method_name:java.lang.Object. + + /// CHECK-START: java.lang.Object Main.newObject() inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + public static Object newObject() { + return new Object(); + } + + public static void main(String[] args) throws Exception { + Second s = new Second(); + + // Replaced NOP pattern. + staticNop(); + nop(s); + // Replaced "return arg" pattern. + assertEquals("arbitrary string", staticReturnArg2("arbitrary string")); + assertEquals(4321L, returnArg1(s, 4321L)); + // Replaced "return const" pattern. + assertEquals(9, staticReturn9()); + assertEquals(7, return7(s)); + assertEquals(null, staticReturnNull()); + assertEquals(null, returnNull(s)); + // Replaced IGET pattern. + assertEquals(42, getInt(s)); + assertEquals(-42.0, getDouble(s)); + assertEquals(null, getObject(s)); + assertEquals("dummy", getString(s)); + // Not replaced IGET pattern. + assertEquals(42, staticGetInt(s)); + assertEquals(-42.0, getDoubleFromParam(s)); + // SGET. + assertEquals(4242, getStaticInt(s)); + // Replaced IPUT pattern. + assertEquals(111L, setLong(s, 111L)); + assertEquals(345L, setLongReturnArg2(s, 222L, 123)); + // Not replaced IPUT pattern. + assertEquals(222L, staticSetLong(s, 222L)); + assertEquals(333L, setLongThroughParam(s, 333L)); + // SPUT. + assertEquals(-11.5f, setStaticFloat(s, -11.5f)); + + if (newObject() == null) { + throw new AssertionError("new Object() cannot be null."); + } + } + + private static void assertEquals(int expected, int actual) { + if (expected != actual) { + throw new AssertionError("Wrong result: " + expected + " != " + actual); + } + } + + private static void assertEquals(double expected, double actual) { + if (expected != actual) { + throw new AssertionError("Wrong result: " + expected + " != " + actual); + } + } + + private static void assertEquals(Object expected, Object actual) { + if (expected != actual && (expected == null || !expected.equals(actual))) { + throw new AssertionError("Wrong result: " + expected + " != " + actual); + } + } +}