diff options
-rw-r--r-- | src/compiler.cc | 183 | ||||
-rw-r--r-- | src/compiler.h | 79 | ||||
-rw-r--r-- | src/compiler/Compiler.h | 2 | ||||
-rw-r--r-- | src/compiler/CompilerIR.h | 2 | ||||
-rw-r--r-- | src/compiler/Frontend.cc | 2 |
5 files changed, 241 insertions, 27 deletions
diff --git a/src/compiler.cc b/src/compiler.cc index 8210c9b..b1a7066 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -36,7 +36,7 @@ namespace art { -CompiledMethod* oatCompileMethod(const Compiler& compiler, const DexFile::CodeItem* code_item, +CompiledMethod* oatCompileMethod(Compiler& compiler, const DexFile::CodeItem* code_item, uint32_t access_flags, uint32_t method_idx, const ClassLoader* class_loader, const DexFile& dex_file, InstructionSet); @@ -54,6 +54,115 @@ namespace x86 { ByteArray* CreateJniDlsymLookupStub(); } +static double Percentage(size_t x, size_t y) { + return 100.0 * ((double)x) / ((double)(x + y)); +} + +static void DumpStat(size_t x, size_t y, const char* str) { + if (x == 0 && y == 0) { + return; + } + LOG(INFO) << Percentage(x, y) << "% of " << str << " for " << (x + y) << " cases"; +} + +void AOTCompilationStats::Dump() { + DumpStat(types_in_dex_cache_, types_not_in_dex_cache_, "types known to be in dex cache"); + DumpStat(strings_in_dex_cache_, strings_not_in_dex_cache_, "strings known to be in dex cache"); + DumpStat(resolved_types_, unresolved_types_, "types resolved"); + DumpStat(resolved_instance_fields_, unresolved_instance_fields_, "instance fields resolved"); + DumpStat(resolved_local_static_fields_ + resolved_static_fields_, unresolved_static_fields_, + "static fields resolved"); + DumpStat(resolved_local_static_fields_, resolved_static_fields_ + unresolved_static_fields_, + "static fields local to a class"); + DumpStat(resolved_virtual_methods_, unresolved_virtual_methods_, "resolved virtual methods"); + DumpStat(resolved_super_methods_, unresolved_super_methods_, "resolved super-class methods"); + DumpStat(resolved_interface_methods_, unresolved_interface_methods_, "resolved interface methods"); +} + +// Allow lossy statistics in non-debug builds +#ifndef NDEBUG +#define STATS_LOCK() MutexLock mu(stats_lock_) +#else +#define STATS_LOCK() +#endif + +void AOTCompilationStats::TypeInDexCache() { + STATS_LOCK(); + types_in_dex_cache_++; +} + +void AOTCompilationStats::TypeNotInDexCache() { + STATS_LOCK(); + types_not_in_dex_cache_++; +} + +void AOTCompilationStats::StringInDexCache() { + STATS_LOCK(); + strings_in_dex_cache_++; +} + +void AOTCompilationStats::StringNotInDexCache() { + STATS_LOCK(); + strings_not_in_dex_cache_++; +} + +void AOTCompilationStats::TypeDoesntNeedAccessCheck() { + STATS_LOCK(); + resolved_types_++; +} + +void AOTCompilationStats::TypeNeedsAccessCheck() { + STATS_LOCK(); + unresolved_types_++; +} + +void AOTCompilationStats::ResolvedInstanceField() { + STATS_LOCK(); + resolved_instance_fields_++; +} + +void AOTCompilationStats::UnresolvedInstanceField(){ + STATS_LOCK(); + unresolved_instance_fields_++; +} + +void AOTCompilationStats::ResolvedLocalStaticField() { + STATS_LOCK(); + resolved_local_static_fields_++; +} + +void AOTCompilationStats::ResolvedStaticField() { + STATS_LOCK(); + resolved_static_fields_++; +} + +void AOTCompilationStats::UnresolvedStaticField() { + STATS_LOCK(); + unresolved_static_fields_++; +} + +void AOTCompilationStats::ResolvedMethod(bool is_interface, bool is_super) { + STATS_LOCK(); + if (is_interface) { + resolved_interface_methods_++; + } else if (is_super) { + resolved_super_methods_++; + } else { + resolved_virtual_methods_++; + } +} + +void AOTCompilationStats::UnresolvedMethod(bool is_interface, bool is_super) { + STATS_LOCK(); + if (is_interface) { + unresolved_interface_methods_++; + } else if (is_super) { + unresolved_super_methods_++; + } else { + unresolved_virtual_methods_++; + } +} + Compiler::Compiler(InstructionSet instruction_set, bool image, size_t thread_count, const std::set<std::string>* image_classes) : instruction_set_(instruction_set), @@ -136,6 +245,8 @@ void Compiler::CompileAll(const ClassLoader* class_loader, if (timings.GetTotalNs() > MsToNs(1000)) { timings.Dump(); } + + stats_.Dump(); } void Compiler::CompileOne(const Method* method) { @@ -193,61 +304,91 @@ bool Compiler::IsImageClass(const std::string& descriptor) const { } bool Compiler::CanAssumeTypeIsPresentInDexCache(const DexCache* dex_cache, - uint32_t type_idx) const { + uint32_t type_idx) { if (!IsImage()) { + stats_.TypeNotInDexCache(); return false; } Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == NULL) { + stats_.TypeNotInDexCache(); return false; } - return IsImageClass(ClassHelper(resolved_class).GetDescriptor()); + bool result = IsImageClass(ClassHelper(resolved_class).GetDescriptor()); + if (result) { + stats_.TypeInDexCache(); + } else { + stats_.TypeNotInDexCache(); + } + return result; } bool Compiler::CanAssumeStringIsPresentInDexCache(const DexCache* dex_cache, - uint32_t string_idx) const { + uint32_t string_idx) { // TODO: Add support for loading strings referenced by image_classes_ // See also Compiler::ResolveDexFile // The following is a test saying that if we're building the image without a restricted set of // image classes then we can assume the string is present in the dex cache if it is there now - return IsImage() && image_classes_ == NULL && dex_cache->GetResolvedString(string_idx) != NULL; + bool result = IsImage() && image_classes_ == NULL && dex_cache->GetResolvedString(string_idx) != NULL; + if (result) { + stats_.StringInDexCache(); + } else { + stats_.StringNotInDexCache(); + } + return result; } bool Compiler::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache, - const DexFile& dex_file, uint32_t type_idx) const { + const DexFile& dex_file, uint32_t type_idx) { // Get type from dex cache assuming it was populated by the verifier Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == NULL) { + stats_.TypeNeedsAccessCheck(); return false; // Unknown class needs access checks. } const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx); Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); if (referrer_class == NULL) { + stats_.TypeNeedsAccessCheck(); return false; // Incomplete referrer knowledge needs access check. } // Perform access check, will return true if access is ok or false if we're going to have to // check this at runtime (for example for class loaders). - return referrer_class->CanAccess(resolved_class); + bool result = referrer_class->CanAccess(resolved_class); + if (result) { + stats_.TypeDoesntNeedAccessCheck(); + } else { + stats_.TypeNeedsAccessCheck(); + } + return result; } bool Compiler::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache, const DexFile& dex_file, - uint32_t type_idx) const { + uint32_t type_idx) { // Get type from dex cache assuming it was populated by the verifier. Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == NULL) { + stats_.TypeNeedsAccessCheck(); return false; // Unknown class needs access checks. } const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx); Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); if (referrer_class == NULL) { + stats_.TypeNeedsAccessCheck(); return false; // Incomplete referrer knowledge needs access check. } // Perform access and instantiable checks, will return true if access is ok or false if we're // going to have to check this at runtime (for example for class loaders). - return referrer_class->CanAccess(resolved_class) && resolved_class->IsInstantiable(); + bool result = referrer_class->CanAccess(resolved_class) && resolved_class->IsInstantiable(); + if (result) { + stats_.TypeDoesntNeedAccessCheck(); + } else { + stats_.TypeNeedsAccessCheck(); + } + return result; } static Class* ComputeReferrerClass(CompilationUnit* cUnit) { @@ -268,7 +409,7 @@ static Method* ComputeReferrerMethod(CompilationUnit* cUnit, uint32_t method_idx } bool Compiler::ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUnit, - int& field_offset, bool& is_volatile) const { + int& field_offset, bool& is_volatile) { // Conservative defaults field_offset = -1; is_volatile = true; @@ -284,6 +425,7 @@ bool Compiler::ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUn resolved_field->GetAccessFlags())) { field_offset = resolved_field->GetOffset().Int32Value(); is_volatile = resolved_field->IsVolatile(); + stats_.ResolvedInstanceField(); return true; // Fast path. } } @@ -292,12 +434,13 @@ bool Compiler::ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUn if (thread->IsExceptionPending()) { thread->ClearException(); } + stats_.UnresolvedInstanceField(); return false; // Incomplete knowledge needs slow path. } bool Compiler::ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit, int& field_offset, int& ssb_index, - bool& is_referrers_class, bool& is_volatile) const { + bool& is_referrers_class, bool& is_volatile) { // Conservative defaults field_offset = -1; ssb_index = -1; @@ -313,6 +456,7 @@ bool Compiler::ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit is_referrers_class = true; // implies no worrying about class initialization field_offset = resolved_field->GetOffset().Int32Value(); is_volatile = resolved_field->IsVolatile(); + stats_.ResolvedLocalStaticField(); return true; // fast path } else { Class* fields_class = resolved_field->GetDeclaringClass(); @@ -329,6 +473,7 @@ bool Compiler::ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit ssb_index = fields_class->GetDexTypeIndex(); field_offset = resolved_field->GetOffset().Int32Value(); is_volatile = resolved_field->IsVolatile(); + stats_.ResolvedStaticField(); return true; } // Search dex file for localized ssb index @@ -343,6 +488,7 @@ bool Compiler::ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit ssb_index = cUnit->dex_file->GetIndexForTypeId(*type_id); field_offset = resolved_field->GetOffset().Int32Value(); is_volatile = resolved_field->IsVolatile(); + stats_.ResolvedStaticField(); return true; } } @@ -355,12 +501,13 @@ bool Compiler::ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit if (thread->IsExceptionPending()) { thread->ClearException(); } + stats_.UnresolvedStaticField(); return false; // Incomplete knowledge needs slow path. } bool Compiler::ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit, bool is_interface, bool is_super, - int& vtable_idx) const { + int& vtable_idx) { vtable_idx = -1; Method* resolved_method = ComputeReferrerMethod(cUnit, method_idx); if (resolved_method != NULL) { @@ -369,7 +516,7 @@ bool Compiler::ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit, Class* methods_class = resolved_method->GetDeclaringClass(); if (!referrer_class->CanAccess(methods_class) || !referrer_class->CanAccessMember(methods_class, - resolved_method->GetAccessFlags())) { + resolved_method->GetAccessFlags())) { // The referring class can't access the resolved method, this may occur as a result of a // protected method being made public by implementing an interface that re-declares the // method public. Resort to the dex file to determine the correct class for the access check @@ -384,14 +531,15 @@ bool Compiler::ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit, referrer_class->CanAccessMember(methods_class, resolved_method->GetAccessFlags())) { vtable_idx = resolved_method->GetMethodIndex(); - if (is_interface || is_super) { + if (is_interface || !is_super) { // nothing left to do for virtual/interface dispatch + stats_.ResolvedMethod(is_interface, is_super); return true; } else { // ensure the vtable index will be correct to dispatch in the vtable of the super class - Class* super_class = methods_class->GetSuperClass(); - if (super_class != NULL && vtable_idx <= super_class->GetVTable()->GetLength()) { - vtable_idx = resolved_method->GetMethodIndex(); + if (referrer_class->IsSubClass(methods_class) && + vtable_idx < methods_class->GetVTable()->GetLength()) { + stats_.ResolvedMethod(is_interface, is_super); return true; } } @@ -403,6 +551,7 @@ bool Compiler::ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit, if (thread->IsExceptionPending()) { thread->ClearException(); } + stats_.UnresolvedMethod(is_interface, is_super); return false; // Incomplete knowledge needs slow path. } diff --git a/src/compiler.h b/src/compiler.h index b55960b..1c71330 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -36,6 +36,69 @@ namespace art { class Context; class TimingLogger; typedef struct CompilationUnit CompilationUnit; +class AOTCompilationStats { + public: + AOTCompilationStats() : stats_lock_("AOT compilation statistics lock"), + types_in_dex_cache_(0), types_not_in_dex_cache_(0), + strings_in_dex_cache_(0), strings_not_in_dex_cache_(0), + resolved_types_(0), unresolved_types_(0), + resolved_instance_fields_(0), unresolved_instance_fields_(0), + resolved_local_static_fields_(0), resolved_static_fields_(0), unresolved_static_fields_(0), + resolved_virtual_methods_(0), unresolved_virtual_methods_(0), + resolved_super_methods_(0), unresolved_super_methods_(0), + resolved_interface_methods_(0), unresolved_interface_methods_(0) {} + + void Dump(); + + void TypeInDexCache(); + void TypeNotInDexCache(); + + void StringInDexCache(); + void StringNotInDexCache(); + + void TypeDoesntNeedAccessCheck(); + void TypeNeedsAccessCheck(); + + void ResolvedInstanceField(); + void UnresolvedInstanceField(); + + void ResolvedLocalStaticField(); + void ResolvedStaticField(); + void UnresolvedStaticField(); + + void ResolvedMethod(bool is_interface, bool is_super); + void UnresolvedMethod(bool is_interface, bool is_super); + + private: + Mutex stats_lock_; + + size_t types_in_dex_cache_; + size_t types_not_in_dex_cache_; + + size_t strings_in_dex_cache_; + size_t strings_not_in_dex_cache_; + + size_t resolved_types_; + size_t unresolved_types_; + + size_t resolved_instance_fields_; + size_t unresolved_instance_fields_; + + size_t resolved_local_static_fields_; + size_t resolved_static_fields_; + size_t unresolved_static_fields_; + + size_t resolved_virtual_methods_; + size_t unresolved_virtual_methods_; + + size_t resolved_super_methods_; + size_t unresolved_super_methods_; + + size_t resolved_interface_methods_; + size_t unresolved_interface_methods_; + + DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats);; +}; class Compiler { public: @@ -87,31 +150,31 @@ class Compiler { // Callbacks from OAT/ART compiler to see what runtime checks must be generated - bool CanAssumeTypeIsPresentInDexCache(const DexCache* dex_cache, uint32_t type_idx) const; + bool CanAssumeTypeIsPresentInDexCache(const DexCache* dex_cache, uint32_t type_idx); - bool CanAssumeStringIsPresentInDexCache(const DexCache* dex_cache, uint32_t string_idx) const; + bool CanAssumeStringIsPresentInDexCache(const DexCache* dex_cache, uint32_t string_idx); // Are runtime access checks necessary in the compiled code? bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache, - const DexFile& dex_file, uint32_t type_idx) const; + const DexFile& dex_file, uint32_t type_idx); // Are runtime access and instantiable checks necessary in the code? bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache, - const DexFile& dex_file, uint32_t type_idx) const; + const DexFile& dex_file, uint32_t type_idx); // Can we fast path instance field access? Computes field's offset and volatility bool ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUnit, - int& field_offset, bool& is_volatile) const; + int& field_offset, bool& is_volatile); // Can we fastpath static field access? Computes field's offset, volatility and whether the // field is within the referrer (which can avoid checking class initialization) bool ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit, int& field_offset, int& ssb_index, - bool& is_referrers_class, bool& is_volatile) const; + bool& is_referrers_class, bool& is_volatile); // Can we fastpath a interface, super class or virtual method call? Computes method's vtable index bool ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit, bool is_interface, - bool is_super, int& vtable_idx) const; + bool is_super, int& vtable_idx); private: @@ -177,6 +240,8 @@ class Compiler { size_t thread_count_; uint64_t start_ns_; + AOTCompilationStats stats_; + const std::set<std::string>* image_classes_; DISALLOW_COPY_AND_ASSIGN(Compiler); diff --git a/src/compiler/Compiler.h b/src/compiler/Compiler.h index 451fae5..16a48df 100644 --- a/src/compiler/Compiler.h +++ b/src/compiler/Compiler.h @@ -173,7 +173,7 @@ bool oatArchInit(void); void oatArchDump(void); bool oatStartup(void); void oatShutdown(void); -CompiledMethod* oatCompileMethod(const Compiler& compiler, bool is_direct, +CompiledMethod* oatCompileMethod(Compiler& compiler, bool is_direct, uint32_t method_idx, const ClassLoader* class_loader, const DexFile& dex_file, OatInstructionSetType); void oatDumpStats(void); diff --git a/src/compiler/CompilerIR.h b/src/compiler/CompilerIR.h index ade478f..9a992a1 100644 --- a/src/compiler/CompilerIR.h +++ b/src/compiler/CompilerIR.h @@ -212,7 +212,7 @@ typedef struct CompilationUnit { int numInsts; int numBlocks; GrowableList blockList; - const Compiler* compiler; // Compiler driving this compiler + Compiler* compiler; // Compiler driving this compiler ClassLinker* class_linker; // Linker to resolve fields and methods const DexFile* dex_file; // DexFile containing the method being compiled DexCache* dex_cache; // DexFile's corresponding cache diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc index 873567c..3b03a35 100644 --- a/src/compiler/Frontend.cc +++ b/src/compiler/Frontend.cc @@ -736,7 +736,7 @@ STATIC void processCanThrow(CompilationUnit* cUnit, BasicBlock* curBlock, /* * Compile a method. */ -CompiledMethod* oatCompileMethod(const Compiler& compiler, const DexFile::CodeItem* code_item, +CompiledMethod* oatCompileMethod(Compiler& compiler, const DexFile::CodeItem* code_item, uint32_t access_flags, uint32_t method_idx, const ClassLoader* class_loader, const DexFile& dex_file, InstructionSet insnSet) |