diff options
author | Vladimir Marko <vmarko@google.com> | 2014-01-23 15:51:58 +0000 |
---|---|---|
committer | Vladimir Marko <vmarko@google.com> | 2014-03-10 10:59:57 +0000 |
commit | f096aad9203d7c50b2f9cbe1c1215a50c265a059 (patch) | |
tree | 44be6a56bbc5e8e697bbc2cd8cabc51e602eeefc /compiler | |
parent | 0307b5c91c287e08cd414ecc5de32befceb7e371 (diff) | |
download | android_art-f096aad9203d7c50b2f9cbe1c1215a50c265a059.tar.gz android_art-f096aad9203d7c50b2f9cbe1c1215a50c265a059.tar.bz2 android_art-f096aad9203d7c50b2f9cbe1c1215a50c265a059.zip |
Cache method lowering info in mir graph.
This should enable easy inlining checks. It should also
improve compilation time of methods that call the same
methods over and over - it is exactly such methods that
tend to exceed our 100ms time limit.
Change-Id: If01cd18e039071a74a1444570283c153429c9cd4
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/Android.mk | 1 | ||||
-rw-r--r-- | compiler/dex/bb_optimizations.h | 14 | ||||
-rw-r--r-- | compiler/dex/mir_analysis.cc | 116 | ||||
-rw-r--r-- | compiler/dex/mir_graph.cc | 4 | ||||
-rw-r--r-- | compiler/dex/mir_graph.h | 14 | ||||
-rw-r--r-- | compiler/dex/mir_method_info.cc | 88 | ||||
-rw-r--r-- | compiler/dex/mir_method_info.h | 185 | ||||
-rw-r--r-- | compiler/dex/pass_driver.cc | 1 | ||||
-rw-r--r-- | compiler/dex/quick/gen_invoke.cc | 58 | ||||
-rw-r--r-- | compiler/driver/compiler_driver-inl.h | 128 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.cc | 209 | ||||
-rw-r--r-- | compiler/driver/compiler_driver.h | 56 |
12 files changed, 706 insertions, 168 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index fdc854016f..48e2bcd732 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -51,6 +51,7 @@ LIBART_COMPILER_SRC_FILES := \ dex/dex_to_dex_compiler.cc \ dex/mir_dataflow.cc \ dex/mir_field_info.cc \ + dex/mir_method_info.cc \ dex/mir_optimization.cc \ dex/pass_driver.cc \ dex/bb_optimizations.cc \ diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h index bd7c40ba5b..1a90ca849f 100644 --- a/compiler/dex/bb_optimizations.h +++ b/compiler/dex/bb_optimizations.h @@ -37,6 +37,20 @@ class CacheFieldLoweringInfo : public Pass { }; /** + * @class CacheMethodLoweringInfo + * @brief Cache the lowering info for methods called by INVOKEs. + */ +class CacheMethodLoweringInfo : public Pass { + public: + CacheMethodLoweringInfo() : Pass("CacheMethodLoweringInfo", kNoNodes) { + } + + void Start(CompilationUnit* cUnit) const { + cUnit->mir_graph->DoCacheMethodLoweringInfo(); + } +}; + +/** * @class CodeLayout * @brief Perform the code layout pass. */ diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 5314bb7025..b96c40de7f 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -19,6 +19,7 @@ #include "dataflow_iterator-inl.h" #include "dex_instruction.h" #include "dex_instruction-inl.h" +#include "dex/verified_method.h" #include "dex/quick/dex_file_method_inliner.h" #include "dex/quick/dex_file_to_method_inliner_map.h" #include "driver/compiler_options.h" @@ -1168,6 +1169,121 @@ void MIRGraph::DoCacheFieldLoweringInfo() { } } +void MIRGraph::DoCacheMethodLoweringInfo() { + static constexpr uint16_t invoke_types[] = { kVirtual, kSuper, kDirect, kStatic, kInterface }; + + // Embed the map value in the entry to avoid extra padding in 64-bit builds. + struct MapEntry { + // Map key: target_method_idx, invoke_type, devirt_target. Ordered to avoid padding. + const MethodReference* devirt_target; + uint16_t target_method_idx; + uint16_t invoke_type; + // Map value. + uint32_t lowering_info_index; + }; + + // Sort INVOKEs by method index, then by opcode, then by devirtualization target. + struct MapEntryComparator { + bool operator()(const MapEntry& lhs, const MapEntry& rhs) const { + if (lhs.target_method_idx != rhs.target_method_idx) { + return lhs.target_method_idx < rhs.target_method_idx; + } + if (lhs.invoke_type != rhs.invoke_type) { + return lhs.invoke_type < rhs.invoke_type; + } + if (lhs.devirt_target != rhs.devirt_target) { + if (lhs.devirt_target == nullptr) { + return true; + } + if (rhs.devirt_target == nullptr) { + return false; + } + return devirt_cmp(*lhs.devirt_target, *rhs.devirt_target); + } + return false; + } + MethodReferenceComparator devirt_cmp; + }; + + // Map invoke key (see MapEntry) to lowering info index. + typedef std::set<MapEntry, MapEntryComparator, ScopedArenaAllocatorAdapter<MapEntry> > InvokeMap; + + ScopedArenaAllocator allocator(&cu_->arena_stack); + + // All INVOKE instructions take 3 code units and there must also be a RETURN. + uint32_t max_refs = (current_code_item_->insns_size_in_code_units_ - 1u) / 3u; + + // The invoke_map and sequential entries are essentially equivalent to Boost.MultiIndex's + // multi_index_container with one ordered index and one sequential index. + InvokeMap invoke_map(MapEntryComparator(), allocator.Adapter()); + const MapEntry** sequential_entries = reinterpret_cast<const MapEntry**>( + allocator.Alloc(max_refs * sizeof(sequential_entries[0]), kArenaAllocMisc)); + + // Find INVOKE insns and their devirtualization targets. + AllNodesIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { + if (bb->block_type != kDalvikByteCode) { + continue; + } + for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { + if (mir->dalvikInsn.opcode >= Instruction::INVOKE_VIRTUAL && + mir->dalvikInsn.opcode <= Instruction::INVOKE_INTERFACE_RANGE && + mir->dalvikInsn.opcode != Instruction::RETURN_VOID_BARRIER) { + // Decode target method index and invoke type. + const Instruction* insn = Instruction::At(current_code_item_->insns_ + mir->offset); + uint16_t target_method_idx; + uint16_t invoke_type_idx; + if (mir->dalvikInsn.opcode <= Instruction::INVOKE_INTERFACE) { + target_method_idx = insn->VRegB_35c(); + invoke_type_idx = mir->dalvikInsn.opcode - Instruction::INVOKE_VIRTUAL; + } else { + target_method_idx = insn->VRegB_3rc(); + invoke_type_idx = mir->dalvikInsn.opcode - Instruction::INVOKE_VIRTUAL_RANGE; + } + + // Find devirtualization target. + // TODO: The devirt map is ordered by the dex pc here. Is there a way to get INVOKEs + // ordered by dex pc as well? That would allow us to keep an iterator to devirt targets + // and increment it as needed instead of making O(log n) lookups. + const VerifiedMethod* verified_method = GetCurrentDexCompilationUnit()->GetVerifiedMethod(); + const MethodReference* devirt_target = verified_method->GetDevirtTarget(mir->offset); + + // Try to insert a new entry. If the insertion fails, we will have found an old one. + MapEntry entry = { + devirt_target, + target_method_idx, + invoke_types[invoke_type_idx], + static_cast<uint32_t>(invoke_map.size()) + }; + auto it = invoke_map.insert(entry).first; // Iterator to either the old or the new entry. + mir->meta.method_lowering_info = it->lowering_info_index; + // If we didn't actually insert, this will just overwrite an existing value with the same. + sequential_entries[it->lowering_info_index] = &*it; + } + } + } + + if (invoke_map.empty()) { + return; + } + + // Prepare unique method infos, set method info indexes for their MIRs. + DCHECK_EQ(method_lowering_infos_.Size(), 0u); + const size_t count = invoke_map.size(); + method_lowering_infos_.Resize(count); + for (size_t pos = 0u; pos != count; ++pos) { + const MapEntry* entry = sequential_entries[pos]; + MirMethodLoweringInfo method_info(entry->target_method_idx, + static_cast<InvokeType>(entry->invoke_type)); + if (entry->devirt_target != nullptr) { + method_info.SetDevirtualizationTarget(*entry->devirt_target); + } + method_lowering_infos_.Insert(method_info); + } + MirMethodLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(), + method_lowering_infos_.GetRawStorage(), count); +} + bool MIRGraph::SkipCompilation(const std::string& methodname) { return cu_->compiler_driver->SkipCompilation(methodname); } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 868730fc37..0b50e2f358 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -89,7 +89,8 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) max_available_non_special_compiler_temps_(0), punt_to_interpreter_(false), ifield_lowering_infos_(arena, 0u), - sfield_lowering_infos_(arena, 0u) { + sfield_lowering_infos_(arena, 0u), + method_lowering_infos_(arena, 0u) { try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */); max_available_special_compiler_temps_ = std::abs(static_cast<int>(kVRegNonSpecialTempBaseReg)) - std::abs(static_cast<int>(kVRegTempBaseReg)); @@ -1176,6 +1177,7 @@ CallInfo* MIRGraph::NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, info->is_range = is_range; info->index = mir->dalvikInsn.vB; info->offset = mir->offset; + info->mir = mir; return info; } diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 94b3816586..8a3341464f 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -20,8 +20,9 @@ #include "dex_file.h" #include "dex_instruction.h" #include "compiler_ir.h" -#include "mir_field_info.h" #include "invoke_type.h" +#include "mir_field_info.h" +#include "mir_method_info.h" #include "utils/arena_bit_vector.h" #include "utils/growable_array.h" #include "reg_storage.h" @@ -267,6 +268,8 @@ struct MIR { // SGET/SPUT lowering info index, points to MIRGraph::sfield_lowering_infos_. Due to limit on // the number of code points (64K) and size of SGET/SPUT insn (2), this will never exceed 32K. uint32_t sfield_lowering_info; + // INVOKE data index, points to MIRGraph::method_lowering_infos_. + uint32_t method_lowering_info; } meta; }; @@ -365,6 +368,7 @@ struct CallInfo { bool skip_this; bool is_range; DexOffset offset; // Offset in code units. + MIR* mir; }; @@ -491,6 +495,13 @@ class MIRGraph { return sfield_lowering_infos_.GetRawStorage()[mir->meta.sfield_lowering_info]; } + void DoCacheMethodLoweringInfo(); + + const MirMethodLoweringInfo& GetMethodLoweringInfo(MIR* mir) { + DCHECK_LT(mir->meta.method_lowering_info, method_lowering_infos_.Size()); + return method_lowering_infos_.GetRawStorage()[mir->meta.method_lowering_info]; + } + void InitRegLocations(); void RemapRegLocations(); @@ -950,6 +961,7 @@ class MIRGraph { bool punt_to_interpreter_; // Difficult or not worthwhile - just interpret. GrowableArray<MirIFieldLoweringInfo> ifield_lowering_infos_; GrowableArray<MirSFieldLoweringInfo> sfield_lowering_infos_; + GrowableArray<MirMethodLoweringInfo> method_lowering_infos_; friend class LocalValueNumberingTest; }; diff --git a/compiler/dex/mir_method_info.cc b/compiler/dex/mir_method_info.cc new file mode 100644 index 0000000000..4580e76e0b --- /dev/null +++ b/compiler/dex/mir_method_info.cc @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 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. + */ + +# include "mir_method_info.h" + +#include "driver/compiler_driver.h" +#include "driver/dex_compilation_unit.h" +#include "driver/compiler_driver-inl.h" +#include "mirror/class_loader.h" // Only to allow casts in SirtRef<ClassLoader>. +#include "mirror/dex_cache.h" // Only to allow casts in SirtRef<DexCache>. +#include "scoped_thread_state_change.h" +#include "sirt_ref.h" + +namespace art { + +void MirMethodLoweringInfo::Resolve(CompilerDriver* compiler_driver, + const DexCompilationUnit* mUnit, + MirMethodLoweringInfo* method_infos, size_t count) { + if (kIsDebugBuild) { + DCHECK(method_infos != nullptr); + DCHECK_NE(count, 0u); + for (auto it = method_infos, end = method_infos + count; it != end; ++it) { + MirMethodLoweringInfo unresolved(it->MethodIndex(), it->GetInvokeType()); + if (it->target_dex_file_ != nullptr) { + unresolved.target_dex_file_ = it->target_dex_file_; + unresolved.target_method_idx_ = it->target_method_idx_; + } + DCHECK_EQ(memcmp(&unresolved, &*it, sizeof(*it)), 0); + } + } + + // We're going to resolve methods and check access in a tight loop. It's better to hold + // the lock and needed references once than re-acquiring them again and again. + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), compiler_driver->GetDexCache(mUnit)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), + compiler_driver->GetClassLoader(soa, mUnit)); + SirtRef<mirror::Class> referrer_class(soa.Self(), + compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit)); + // Even if the referrer class is unresolved (i.e. we're compiling a method without class + // definition) we still want to resolve methods and record all available info. + + for (auto it = method_infos, end = method_infos + count; it != end; ++it) { + // Remember devirtualized invoke target and set the called method to the default. + MethodReference devirt_ref(it->target_dex_file_, it->target_method_idx_); + MethodReference* devirt_target = (it->target_dex_file_ != nullptr) ? &devirt_ref : nullptr; + it->target_dex_file_ = mUnit->GetDexFile(); + it->target_method_idx_ = it->MethodIndex(); + + InvokeType invoke_type = it->GetInvokeType(); + mirror::ArtMethod* resolved_method = + compiler_driver->ResolveMethod(soa, dex_cache, class_loader, mUnit, it->MethodIndex(), + invoke_type); + if (UNLIKELY(resolved_method == nullptr)) { + continue; + } + compiler_driver->GetResolvedMethodDexFileLocation(resolved_method, + &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_method_idx_); + it->vtable_idx_ = compiler_driver->GetResolvedMethodVTableIndex(resolved_method, invoke_type); + + MethodReference target_method(mUnit->GetDexFile(), it->MethodIndex()); + int fast_path_flags = compiler_driver->IsFastInvoke( + soa, dex_cache, class_loader, mUnit, referrer_class.get(), resolved_method, &invoke_type, + &target_method, devirt_target, &it->direct_code_, &it->direct_method_); + uint16_t other_flags = it->flags_ & ~kFlagFastPath & ~(kInvokeTypeMask << kBitSharpTypeBegin); + it->flags_ = other_flags | + (fast_path_flags != 0 ? kFlagFastPath : 0u) | + (static_cast<uint16_t>(invoke_type) << kBitSharpTypeBegin); + it->target_dex_file_ = target_method.dex_file; + it->target_method_idx_ = target_method.dex_method_index; + it->stats_flags_ = fast_path_flags; + } +} + +} // namespace art diff --git a/compiler/dex/mir_method_info.h b/compiler/dex/mir_method_info.h new file mode 100644 index 0000000000..a43238c24e --- /dev/null +++ b/compiler/dex/mir_method_info.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ART_COMPILER_DEX_MIR_METHOD_INFO_H_ +#define ART_COMPILER_DEX_MIR_METHOD_INFO_H_ + +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "invoke_type.h" +#include "method_reference.h" + +namespace art { + +class CompilerDriver; +class DexCompilationUnit; +class DexFile; + +class MirMethodInfo { + public: + uint16_t MethodIndex() const { + return method_idx_; + } + + bool IsStatic() const { + return (flags_ & kFlagIsStatic) != 0u; + } + + bool IsResolved() const { + return declaring_dex_file_ != nullptr; + } + + const DexFile* DeclaringDexFile() const { + return declaring_dex_file_; + } + + uint16_t DeclaringClassIndex() const { + return declaring_class_idx_; + } + + uint16_t DeclaringMethodIndex() const { + return declaring_method_idx_; + } + + protected: + enum { + kBitIsStatic = 0, + kMethodInfoBitEnd + }; + COMPILE_ASSERT(kMethodInfoBitEnd <= 16, too_many_flags); + static constexpr uint16_t kFlagIsStatic = 1u << kBitIsStatic; + + MirMethodInfo(uint16_t method_idx, uint16_t flags) + : method_idx_(method_idx), + flags_(flags), + declaring_method_idx_(0u), + declaring_class_idx_(0u), + declaring_dex_file_(nullptr) { + } + + // Make copy-ctor/assign/dtor protected to avoid slicing. + MirMethodInfo(const MirMethodInfo& other) = default; + MirMethodInfo& operator=(const MirMethodInfo& other) = default; + ~MirMethodInfo() = default; + + // The method index in the compiling method's dex file. + uint16_t method_idx_; + // Flags, for volatility and derived class data. + uint16_t flags_; + // The method index in the dex file that defines the method, 0 if unresolved. + uint16_t declaring_method_idx_; + // The type index of the class declaring the method, 0 if unresolved. + uint16_t declaring_class_idx_; + // The dex file that defines the class containing the method and the method, + // nullptr if unresolved. + const DexFile* declaring_dex_file_; +}; + +class MirMethodLoweringInfo : public MirMethodInfo { + public: + // For each requested method retrieve the method's declaring location (dex file, class + // index and method index) and compute whether we can fast path the method call. For fast + // path methods, retrieve the method's vtable index and direct code and method when applicable. + static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit, + MirMethodLoweringInfo* method_infos, size_t count) + LOCKS_EXCLUDED(Locks::mutator_lock_); + + MirMethodLoweringInfo(uint16_t method_idx, InvokeType type) + : MirMethodInfo(method_idx, + ((type == kStatic) ? kFlagIsStatic : 0u) | + (static_cast<uint16_t>(type) << kBitInvokeTypeBegin) | + (static_cast<uint16_t>(type) << kBitSharpTypeBegin)), + direct_code_(0u), + direct_method_(0u), + target_dex_file_(nullptr), + target_method_idx_(0u), + vtable_idx_(0u), + stats_flags_(0) { + } + + void SetDevirtualizationTarget(const MethodReference& ref) { + DCHECK(target_dex_file_ == nullptr); + DCHECK_EQ(target_method_idx_, 0u); + DCHECK_LE(ref.dex_method_index, 0xffffu); + target_dex_file_ = ref.dex_file; + target_method_idx_ = ref.dex_method_index; + } + + bool FastPath() const { + return (flags_ & kFlagFastPath) != 0u; + } + + InvokeType GetInvokeType() const { + return static_cast<InvokeType>((flags_ >> kBitInvokeTypeBegin) & kInvokeTypeMask); + } + + art::InvokeType GetSharpType() const { + return static_cast<InvokeType>((flags_ >> kBitSharpTypeBegin) & kInvokeTypeMask); + } + + MethodReference GetTargetMethod() const { + return MethodReference(target_dex_file_, target_method_idx_); + } + + uint16_t VTableIndex() const { + return vtable_idx_; + } + + uintptr_t DirectCode() const { + return direct_code_; + } + + uintptr_t DirectMethod() const { + return direct_method_; + } + + int StatsFlags() const { + return stats_flags_; + } + + private: + enum { + kBitFastPath = kMethodInfoBitEnd, + kBitInvokeTypeBegin, + kBitInvokeTypeEnd = kBitInvokeTypeBegin + 3, // 3 bits for invoke type. + kBitSharpTypeBegin, + kBitSharpTypeEnd = kBitSharpTypeBegin + 3, // 3 bits for sharp type. + kMethodLoweringInfoEnd = kBitSharpTypeEnd + }; + COMPILE_ASSERT(kMethodLoweringInfoEnd <= 16, too_many_flags); + static constexpr uint16_t kFlagFastPath = 1u << kBitFastPath; + static constexpr uint16_t kInvokeTypeMask = 7u; + COMPILE_ASSERT((1u << (kBitInvokeTypeEnd - kBitInvokeTypeBegin)) - 1u == kInvokeTypeMask, + assert_invoke_type_bits_ok); + COMPILE_ASSERT((1u << (kBitSharpTypeEnd - kBitSharpTypeBegin)) - 1u == kInvokeTypeMask, + assert_sharp_type_bits_ok); + + uintptr_t direct_code_; + uintptr_t direct_method_; + // Before Resolve(), target_dex_file_ and target_method_idx_ hold the verification-based + // devirtualized invoke target if available, nullptr and 0u otherwise. + // After Resolve() they hold the actual target method that will be called; it will be either + // a devirtualized target method or the compilation's unit's dex file and MethodIndex(). + const DexFile* target_dex_file_; + uint16_t target_method_idx_; + uint16_t vtable_idx_; + int stats_flags_; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_MIR_METHOD_INFO_H_ diff --git a/compiler/dex/pass_driver.cc b/compiler/dex/pass_driver.cc index 256bcb1473..291012f0d9 100644 --- a/compiler/dex/pass_driver.cc +++ b/compiler/dex/pass_driver.cc @@ -92,6 +92,7 @@ void PassDriver::CreatePasses() { */ static const Pass* const passes[] = { GetPassInstance<CacheFieldLoweringInfo>(), + GetPassInstance<CacheMethodLoweringInfo>(), GetPassInstance<CodeLayout>(), GetPassInstance<SSATransformation>(), GetPassInstance<ConstantPropagation>(), diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 2f017c8352..424cdd69bd 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -573,33 +573,32 @@ static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, ThreadOffset tr static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, const MethodReference& target_method, - uint32_t method_idx, - uintptr_t unused, uintptr_t unused2, - InvokeType unused3) { + uint32_t unused, uintptr_t unused2, + uintptr_t unused3, InvokeType unused4) { ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck); return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, const MethodReference& target_method, - uint32_t method_idx, uintptr_t unused, - uintptr_t unused2, InvokeType unused3) { + uint32_t unused, uintptr_t unused2, + uintptr_t unused3, InvokeType unused4) { ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck); return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, const MethodReference& target_method, - uint32_t method_idx, uintptr_t unused, - uintptr_t unused2, InvokeType unused3) { + uint32_t unused, uintptr_t unused2, + uintptr_t unused3, InvokeType unused4) { ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck); return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, const MethodReference& target_method, - uint32_t method_idx, uintptr_t unused, - uintptr_t unused2, InvokeType unused3) { + uint32_t unused, uintptr_t unused2, + uintptr_t unused3, InvokeType unused4) { ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck); return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } @@ -607,9 +606,8 @@ static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state, static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu, CallInfo* info, int state, const MethodReference& target_method, - uint32_t unused, - uintptr_t unused2, uintptr_t unused3, - InvokeType unused4) { + uint32_t unused, uintptr_t unused2, + uintptr_t unused3, InvokeType unused4) { ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck); return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0); } @@ -1400,7 +1398,6 @@ void Mir2Lir::GenInvoke(CallInfo* info) { return; } } - InvokeType original_type = info->type; // avoiding mutation by ComputeInvokeInfo int call_state = 0; LIR* null_ck; LIR** p_null_ck = NULL; @@ -1409,19 +1406,12 @@ void Mir2Lir::GenInvoke(CallInfo* info) { // Explicit register usage LockCallTemps(); - DexCompilationUnit* cUnit = mir_graph_->GetCurrentDexCompilationUnit(); - MethodReference target_method(cUnit->GetDexFile(), info->index); - int vtable_idx; - uintptr_t direct_code; - uintptr_t direct_method; + const MirMethodLoweringInfo& method_info = mir_graph_->GetMethodLoweringInfo(info->mir); + cu_->compiler_driver->ProcessedInvoke(method_info.GetInvokeType(), method_info.StatsFlags()); + InvokeType original_type = static_cast<InvokeType>(method_info.GetInvokeType()); + info->type = static_cast<InvokeType>(method_info.GetSharpType()); + bool fast_path = method_info.FastPath(); bool skip_this; - bool fast_path = - cu_->compiler_driver->ComputeInvokeInfo(mir_graph_->GetCurrentDexCompilationUnit(), - current_dalvik_offset_, - true, true, - &info->type, &target_method, - &vtable_idx, - &direct_code, &direct_method) && !SLOW_INVOKE_PATH; if (info->type == kInterface) { next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck; skip_this = fast_path; @@ -1443,29 +1433,29 @@ void Mir2Lir::GenInvoke(CallInfo* info) { next_call_insn = fast_path ? NextVCallInsn : NextVCallInsnSP; skip_this = fast_path; } + MethodReference target_method = method_info.GetTargetMethod(); if (!info->is_range) { call_state = GenDalvikArgsNoRange(info, call_state, p_null_ck, - next_call_insn, target_method, - vtable_idx, direct_code, direct_method, + next_call_insn, target_method, method_info.VTableIndex(), + method_info.DirectCode(), method_info.DirectMethod(), original_type, skip_this); } else { call_state = GenDalvikArgsRange(info, call_state, p_null_ck, - next_call_insn, target_method, vtable_idx, - direct_code, direct_method, original_type, - skip_this); + next_call_insn, target_method, method_info.VTableIndex(), + method_info.DirectCode(), method_info.DirectMethod(), + original_type, skip_this); } // Finish up any of the call sequence not interleaved in arg loading while (call_state >= 0) { - call_state = next_call_insn(cu_, info, call_state, target_method, - vtable_idx, direct_code, direct_method, - original_type); + call_state = next_call_insn(cu_, info, call_state, target_method, method_info.VTableIndex(), + method_info.DirectCode(), method_info.DirectMethod(), original_type); } LIR* call_inst; if (cu_->instruction_set != kX86) { call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt)); } else { if (fast_path) { - if (direct_code == static_cast<unsigned int>(-1)) { + if (method_info.DirectCode() == static_cast<uintptr_t>(-1)) { // We can have the linker fixup a call relative. call_inst = reinterpret_cast<X86Mir2Lir*>(this)->CallWithLinkerFixup( diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 1499ae4872..664f809810 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -21,8 +21,11 @@ #include "dex/compiler_ir.h" #include "mirror/art_field.h" #include "mirror/art_field-inl.h" +#include "mirror/art_method.h" +#include "mirror/art_method-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" +#include "mirror/dex_cache-inl.h" #include "mirror/art_field-inl.h" #include "scoped_thread_state_change.h" #include "sirt_ref-inl.h" @@ -161,6 +164,131 @@ inline std::pair<bool, bool> CompilerDriver::IsFastStaticField( return std::make_pair(false, false); } +inline mirror::ArtMethod* CompilerDriver::ResolveMethod( + ScopedObjectAccess& soa, const SirtRef<mirror::DexCache>& dex_cache, + const SirtRef<mirror::ClassLoader>& class_loader, const DexCompilationUnit* mUnit, + uint32_t method_idx, InvokeType invoke_type) { + DCHECK(dex_cache->GetDexFile() == mUnit->GetDexFile()); + DCHECK(class_loader.get() == soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); + mirror::ArtMethod* resolved_method = mUnit->GetClassLinker()->ResolveMethod( + *mUnit->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type); + DCHECK_EQ(resolved_method == nullptr, soa.Self()->IsExceptionPending()); + if (UNLIKELY(resolved_method == nullptr)) { + // Clean up any exception left by type resolution. + soa.Self()->ClearException(); + return nullptr; + } + if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(invoke_type))) { + // Silently return nullptr on incompatible class change. + return nullptr; + } + return resolved_method; +} + +inline void CompilerDriver::GetResolvedMethodDexFileLocation( + mirror::ArtMethod* resolved_method, const DexFile** declaring_dex_file, + uint16_t* declaring_class_idx, uint16_t* declaring_method_idx) { + mirror::Class* declaring_class = resolved_method->GetDeclaringClass(); + *declaring_dex_file = declaring_class->GetDexCache()->GetDexFile(); + *declaring_class_idx = declaring_class->GetDexTypeIndex(); + *declaring_method_idx = resolved_method->GetDexMethodIndex(); +} + +inline uint16_t CompilerDriver::GetResolvedMethodVTableIndex( + mirror::ArtMethod* resolved_method, InvokeType type) { + if (type == kVirtual || type == kSuper) { + return resolved_method->GetMethodIndex(); + } else if (type == kInterface) { + return resolved_method->GetDexMethodIndex(); + } else { + return DexFile::kDexNoIndex16; + } +} + +inline int CompilerDriver::IsFastInvoke( + ScopedObjectAccess& soa, const SirtRef<mirror::DexCache>& dex_cache, + const SirtRef<mirror::ClassLoader>& class_loader, const DexCompilationUnit* mUnit, + mirror::Class* referrer_class, mirror::ArtMethod* resolved_method, InvokeType* invoke_type, + MethodReference* target_method, const MethodReference* devirt_target, + uintptr_t* direct_code, uintptr_t* direct_method) { + // Don't try to fast-path if we don't understand the caller's class. + if (UNLIKELY(referrer_class == nullptr)) { + return 0; + } + mirror::Class* methods_class = resolved_method->GetDeclaringClass(); + if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_class, resolved_method, + dex_cache.get(), + target_method->dex_method_index))) { + return 0; + } + + // Sharpen a virtual call into a direct call when the target is known not to have been + // overridden (ie is final). + bool can_sharpen_virtual_based_on_type = + (*invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal()); + // For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of + // the super class. + bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) && + (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) && + resolved_method->GetMethodIndex() < methods_class->GetVTable()->GetLength() && + (methods_class->GetVTable()->Get(resolved_method->GetMethodIndex()) == resolved_method); + + if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) { + // Sharpen a virtual call into a direct call. The method_idx is into referrer's + // dex cache, check that this resolved method is where we expect it. + CHECK(target_method->dex_file == mUnit->GetDexFile()); + DCHECK(dex_cache.get() == mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); + CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method->dex_method_index) == + resolved_method) << PrettyMethod(resolved_method); + int stats_flags = kFlagMethodResolved; + GetCodeAndMethodForDirectCall(invoke_type, kDirect, false, referrer_class, resolved_method, + &stats_flags, target_method, direct_code, direct_method); + DCHECK_NE(*invoke_type, kSuper) << PrettyMethod(resolved_method); + if (*invoke_type == kDirect) { + stats_flags |= kFlagsMethodResolvedVirtualMadeDirect; + } + return stats_flags; + } + + if ((*invoke_type == kVirtual || *invoke_type == kInterface) && devirt_target != nullptr) { + // Post-verification callback recorded a more precise invoke target based on its type info. + mirror::ArtMethod* called_method; + ClassLinker* class_linker = mUnit->GetClassLinker(); + if (LIKELY(devirt_target->dex_file == mUnit->GetDexFile())) { + called_method = class_linker->ResolveMethod(*devirt_target->dex_file, + devirt_target->dex_method_index, + dex_cache, class_loader, NULL, kVirtual); + } else { + SirtRef<mirror::DexCache> target_dex_cache(soa.Self(), + class_linker->FindDexCache(*devirt_target->dex_file)); + called_method = class_linker->ResolveMethod(*devirt_target->dex_file, + devirt_target->dex_method_index, + target_dex_cache, class_loader, NULL, kVirtual); + } + CHECK(called_method != NULL); + CHECK(!called_method->IsAbstract()); + int stats_flags = kFlagMethodResolved; + GetCodeAndMethodForDirectCall(invoke_type, kDirect, true, referrer_class, called_method, + &stats_flags, target_method, direct_code, direct_method); + DCHECK_NE(*invoke_type, kSuper); + if (*invoke_type == kDirect) { + stats_flags |= kFlagsMethodResolvedPreciseTypeDevirtualization; + } + return stats_flags; + } + + if (UNLIKELY(*invoke_type == kSuper)) { + // Unsharpened super calls are suspicious so go slow-path. + return 0; + } + + // Sharpening failed so generate a regular resolved method dispatch. + int stats_flags = kFlagMethodResolved; + GetCodeAndMethodForDirectCall(invoke_type, *invoke_type, false, referrer_class, resolved_method, + &stats_flags, target_method, direct_code, direct_method); + return stats_flags; +} + } // namespace art #endif // ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_ diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index a46015d0f9..7c4a6f7c19 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -71,7 +71,7 @@ static void DumpStat(size_t x, size_t y, const char* str) { LOG(INFO) << Percentage(x, y) << "% of " << str << " for " << (x + y) << " cases"; } -class AOTCompilationStats { +class CompilerDriver::AOTCompilationStats { public: AOTCompilationStats() : stats_lock_("AOT compilation statistics lock"), @@ -242,6 +242,30 @@ class AOTCompilationStats { direct_methods_to_boot_[type]++; } + void ProcessedInvoke(InvokeType type, int flags) { + STATS_LOCK(); + if (flags == 0) { + unresolved_methods_[type]++; + } else { + DCHECK_NE((flags & kFlagMethodResolved), 0); + resolved_methods_[type]++; + if ((flags & kFlagVirtualMadeDirect) != 0) { + virtual_made_direct_[type]++; + if ((flags & kFlagPreciseTypeDevirtualization) != 0) { + type_based_devirtualization_++; + } + } else { + DCHECK_EQ((flags & kFlagPreciseTypeDevirtualization), 0); + } + if ((flags & kFlagDirectCallToBoot) != 0) { + direct_calls_to_boot_[type]++; + } + if ((flags & kFlagDirectMethodToBoot) != 0) { + direct_methods_to_boot_[type]++; + } + } + } + // A check-cast could be eliminated due to verifier type analysis. void SafeCast() { STATS_LOCK(); @@ -933,32 +957,8 @@ void CompilerDriver::ProcessedStaticField(bool resolved, bool local) { } } -static mirror::Class* ComputeCompilingMethodsClass(ScopedObjectAccess& soa, - SirtRef<mirror::DexCache>& dex_cache, - const DexCompilationUnit* mUnit) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // The passed dex_cache is a hint, sanity check before asking the class linker that will take a - // lock. - if (dex_cache->GetDexFile() != mUnit->GetDexFile()) { - dex_cache.reset(mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); - } - SirtRef<mirror::ClassLoader> - class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); - const DexFile::MethodId& referrer_method_id = - mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); - return mUnit->GetClassLinker()->ResolveType(*mUnit->GetDexFile(), referrer_method_id.class_idx_, - dex_cache, class_loader); -} - -static mirror::ArtMethod* ComputeMethodReferencedFromCompilingMethod(ScopedObjectAccess& soa, - const DexCompilationUnit* mUnit, - uint32_t method_idx, - InvokeType type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SirtRef<mirror::DexCache> dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); - SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); - return mUnit->GetClassLinker()->ResolveMethod(*mUnit->GetDexFile(), method_idx, dex_cache, - class_loader, NULL, type); +void CompilerDriver::ProcessedInvoke(InvokeType invoke_type, int flags) { + stats_->ProcessedInvoke(invoke_type, flags); } bool CompilerDriver::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put, @@ -1065,7 +1065,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType bool no_guarantee_of_dex_cache_entry, mirror::Class* referrer_class, mirror::ArtMethod* method, - bool update_stats, + int* stats_flags, MethodReference* target_method, uintptr_t* direct_code, uintptr_t* direct_method) { @@ -1103,9 +1103,8 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType } } } - if (update_stats && method_code_in_boot) { - stats_->DirectCallsToBoot(*type); - stats_->DirectMethodsToBoot(*type); + if (method_code_in_boot) { + *stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot; } if (!use_dex_cache && compiling_boot) { MethodHelper mh(method); @@ -1174,110 +1173,63 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui InvokeType* invoke_type, MethodReference* target_method, int* vtable_idx, uintptr_t* direct_code, uintptr_t* direct_method) { + InvokeType orig_invoke_type = *invoke_type; + int stats_flags = 0; ScopedObjectAccess soa(Thread::Current()); - *vtable_idx = -1; - *direct_code = 0; - *direct_method = 0; - mirror::ArtMethod* resolved_method = - ComputeMethodReferencedFromCompilingMethod(soa, mUnit, target_method->dex_method_index, - *invoke_type); - if (resolved_method != NULL) { - if (*invoke_type == kVirtual || *invoke_type == kSuper) { - *vtable_idx = resolved_method->GetMethodIndex(); - } else if (*invoke_type == kInterface) { - *vtable_idx = resolved_method->GetDexMethodIndex(); - } - // Don't try to fast-path if we don't understand the caller's class or this appears to be an - // Incompatible Class Change Error. - SirtRef<mirror::DexCache> dex_cache(soa.Self(), resolved_method->GetDeclaringClass()->GetDexCache()); - mirror::Class* referrer_class = - ComputeCompilingMethodsClass(soa, dex_cache, mUnit); - bool icce = resolved_method->CheckIncompatibleClassChange(*invoke_type); - if (referrer_class != NULL && !icce) { - mirror::Class* methods_class = resolved_method->GetDeclaringClass(); - if (referrer_class->CanAccessResolvedMethod(methods_class, resolved_method, dex_cache.get(), - target_method->dex_method_index)) { - const bool enableFinalBasedSharpening = enable_devirtualization; - // Sharpen a virtual call into a direct call when the target is known not to have been - // overridden (ie is final). - bool can_sharpen_virtual_based_on_type = - (*invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal()); - // For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of - // the super class. - bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) && - (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) && - resolved_method->GetMethodIndex() < methods_class->GetVTable()->GetLength() && - (methods_class->GetVTable()->Get(resolved_method->GetMethodIndex()) == resolved_method); - - if (enableFinalBasedSharpening && (can_sharpen_virtual_based_on_type || - can_sharpen_super_based_on_type)) { - // Sharpen a virtual call into a direct call. The method_idx is into the DexCache - // associated with target_method->dex_file. - CHECK(target_method->dex_file == mUnit->GetDexFile()); - DCHECK(dex_cache.get() == mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); - CHECK(dex_cache->GetResolvedMethod(target_method->dex_method_index) == - resolved_method) << PrettyMethod(resolved_method); - InvokeType orig_invoke_type = *invoke_type; - GetCodeAndMethodForDirectCall(invoke_type, kDirect, false, referrer_class, resolved_method, - update_stats, target_method, direct_code, direct_method); - if (update_stats && (*invoke_type == kDirect)) { - stats_->ResolvedMethod(orig_invoke_type); - stats_->VirtualMadeDirect(orig_invoke_type); - } - DCHECK_NE(*invoke_type, kSuper) << PrettyMethod(resolved_method); - return true; - } - const bool enableVerifierBasedSharpening = enable_devirtualization; - if (enableVerifierBasedSharpening && (*invoke_type == kVirtual || - *invoke_type == kInterface)) { - // Did the verifier record a more precise invoke target based on its type information? - DCHECK(mUnit->GetVerifiedMethod() != nullptr); - const MethodReference* devirt_map_target = - mUnit->GetVerifiedMethod()->GetDevirtTarget(dex_pc); - if (devirt_map_target != NULL) { - SirtRef<mirror::DexCache> target_dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file)); - SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); - mirror::ArtMethod* called_method = - mUnit->GetClassLinker()->ResolveMethod(*devirt_map_target->dex_file, - devirt_map_target->dex_method_index, - target_dex_cache, class_loader, NULL, - kVirtual); - CHECK(called_method != NULL); - CHECK(!called_method->IsAbstract()); - InvokeType orig_invoke_type = *invoke_type; - GetCodeAndMethodForDirectCall(invoke_type, kDirect, true, referrer_class, called_method, - update_stats, target_method, direct_code, direct_method); - if (update_stats && (*invoke_type == kDirect)) { - stats_->ResolvedMethod(orig_invoke_type); - stats_->VirtualMadeDirect(orig_invoke_type); - stats_->PreciseTypeDevirtualization(); - } - DCHECK_NE(*invoke_type, kSuper); - return true; - } - } - if (*invoke_type == kSuper) { - // Unsharpened super calls are suspicious so go slow-path. - } else { - // Sharpening failed so generate a regular resolved method dispatch. - if (update_stats) { - stats_->ResolvedMethod(*invoke_type); - } - GetCodeAndMethodForDirectCall(invoke_type, *invoke_type, false, referrer_class, resolved_method, - update_stats, target_method, direct_code, direct_method); - return true; - } + // Try to resolve the method and compiling method's class. + mirror::ArtMethod* resolved_method; + mirror::Class* referrer_class; + SirtRef<mirror::DexCache> dex_cache(soa.Self(), + mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), + soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); + { + uint32_t method_idx = target_method->dex_method_index; + SirtRef<mirror::ArtMethod> resolved_method_sirt(soa.Self(), + ResolveMethod(soa, dex_cache, class_loader, mUnit, method_idx, orig_invoke_type)); + referrer_class = (resolved_method_sirt.get() != nullptr) + ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit) : nullptr; + resolved_method = resolved_method_sirt.get(); + } + bool result = false; + if (resolved_method != nullptr) { + *vtable_idx = GetResolvedMethodVTableIndex(resolved_method, orig_invoke_type); + + if (enable_devirtualization) { + DCHECK(mUnit->GetVerifiedMethod() != nullptr); + const MethodReference* devirt_target = mUnit->GetVerifiedMethod()->GetDevirtTarget(dex_pc); + + stats_flags = IsFastInvoke( + soa, dex_cache, class_loader, mUnit, referrer_class, resolved_method, + invoke_type, target_method, devirt_target, direct_code, direct_method); + result = stats_flags != 0; + } else { + // Devirtualization not enabled. Inline IsFastInvoke(), dropping the devirtualization parts. + if (UNLIKELY(referrer_class == nullptr) || + UNLIKELY(!referrer_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(), + resolved_method, dex_cache.get(), + target_method->dex_method_index)) || + *invoke_type == kSuper) { + // Slow path. (Without devirtualization, all super calls go slow path as well.) + } else { + // Sharpening failed so generate a regular resolved method dispatch. + stats_flags = kFlagMethodResolved; + GetCodeAndMethodForDirectCall(invoke_type, *invoke_type, false, referrer_class, resolved_method, + &stats_flags, target_method, direct_code, direct_method); + result = true; } } } - // Clean up any exception left by method/invoke_type resolution - if (soa.Self()->IsExceptionPending()) { - soa.Self()->ClearException(); + if (!result) { + // Conservative defaults. + *vtable_idx = -1; + *direct_code = 0u; + *direct_method = 0u; } if (update_stats) { - stats_->UnresolvedMethod(*invoke_type); + ProcessedInvoke(orig_invoke_type, stats_flags); } - return false; // Incomplete knowledge needs slow path. + return result; } const VerifiedMethod* CompilerDriver::GetVerifiedMethod(const DexFile* dex_file, @@ -1297,7 +1249,6 @@ bool CompilerDriver::IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc return result; } - void CompilerDriver::AddCodePatch(const DexFile* dex_file, uint16_t referrer_class_def_idx, uint32_t referrer_method_idx, diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 817da17dc3..26210c944c 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -44,7 +44,6 @@ namespace verifier { class MethodVerifier; } // namespace verifier -class AOTCompilationStats; class CompilerOptions; class DexCompilationUnit; class DexFileToMethodInlinerMap; @@ -256,8 +255,37 @@ class CompilerDriver { uint32_t* storage_index, bool* is_referrers_class, bool* is_initialized) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Resolve a method. Returns nullptr on failure, including incompatible class change. + mirror::ArtMethod* ResolveMethod( + ScopedObjectAccess& soa, const SirtRef<mirror::DexCache>& dex_cache, + const SirtRef<mirror::ClassLoader>& class_loader, const DexCompilationUnit* mUnit, + uint32_t method_idx, InvokeType invoke_type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Get declaration location of a resolved field. + void GetResolvedMethodDexFileLocation( + mirror::ArtMethod* resolved_method, const DexFile** declaring_dex_file, + uint16_t* declaring_class_idx, uint16_t* declaring_method_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Get declaration location of a resolved field. + uint16_t GetResolvedMethodVTableIndex( + mirror::ArtMethod* resolved_method, InvokeType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Can we fast-path an INVOKE? If no, returns 0. If yes, returns a non-zero opaque flags value + // for ProcessedInvoke() and computes the necessary lowering info. + int IsFastInvoke( + ScopedObjectAccess& soa, const SirtRef<mirror::DexCache>& dex_cache, + const SirtRef<mirror::ClassLoader>& class_loader, const DexCompilationUnit* mUnit, + mirror::Class* referrer_class, mirror::ArtMethod* resolved_method, InvokeType* invoke_type, + MethodReference* target_method, const MethodReference* devirt_target, + uintptr_t* direct_code, uintptr_t* direct_method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void ProcessedInstanceField(bool resolved); void ProcessedStaticField(bool resolved, bool local); + void ProcessedInvoke(InvokeType invoke_type, int flags); // 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. @@ -594,16 +622,37 @@ class CompilerDriver { bool SkipCompilation(const std::string& method_name); private: - // Compute constant code and method pointers when possible + // These flags are internal to CompilerDriver for collecting INVOKE resolution statistics. + // The only external contract is that unresolved method has flags 0 and resolved non-0. + enum { + kBitMethodResolved = 0, + kBitVirtualMadeDirect, + kBitPreciseTypeDevirtualization, + kBitDirectCallToBoot, + kBitDirectMethodToBoot + }; + static constexpr int kFlagMethodResolved = 1 << kBitMethodResolved; + static constexpr int kFlagVirtualMadeDirect = 1 << kBitVirtualMadeDirect; + static constexpr int kFlagPreciseTypeDevirtualization = 1 << kBitPreciseTypeDevirtualization; + static constexpr int kFlagDirectCallToBoot = 1 << kBitDirectCallToBoot; + static constexpr int kFlagDirectMethodToBoot = 1 << kBitDirectMethodToBoot; + static constexpr int kFlagsMethodResolvedVirtualMadeDirect = + kFlagMethodResolved | kFlagVirtualMadeDirect; + static constexpr int kFlagsMethodResolvedPreciseTypeDevirtualization = + kFlagsMethodResolvedVirtualMadeDirect | kFlagPreciseTypeDevirtualization; + + public: // TODO make private or eliminate. + // Compute constant code and method pointers when possible. void GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type, bool no_guarantee_of_dex_cache_entry, mirror::Class* referrer_class, mirror::ArtMethod* method, - bool update_stats, + int* stats_flags, MethodReference* target_method, uintptr_t* direct_code, uintptr_t* direct_method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, ThreadPool* thread_pool, TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_); @@ -688,6 +737,7 @@ class CompilerDriver { size_t thread_count_; uint64_t start_ns_; + class AOTCompilationStats; UniquePtr<AOTCompilationStats> stats_; bool dump_stats_; |