From e3cd2f0e3c3d976ae9c65c8a731003a5aaf71986 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 24 May 2013 15:32:56 -0700 Subject: Enable devirtualization for abstract and sub-class methods. If we know the type of a receiver in the verifier we record devirtualization data. Currently we only use this data to avoid virtual method dispatch when we know the receiver of a method isn't a sub-class. This change allows devirtualization of virtual and interface methods when we know the receiver's type and the method the we'd find via dispatch is either known within boot or has a reference from the current dex file. Pass the receiver through to the method resolution trampoline as devirtualization may mean the dex method index needs to be made more accurate for the receiver. Tidy up method devirtualization and related statistics. Push the devirtualization map lookup into a less common case to avoid taking its lock. Make MethodReference a struct rather than a typedef of a pair, so the members can have more meaningful names than first and second. Rough statistics show that we devirtualize using this change around 2.5% of the time, whilst some apps like GMS core devirtualize over 3.4% of the time. Change-Id: Ieed3471dbedfc4cc881d652631b67176bb37d394 --- src/compiler/driver/compiler_driver.h | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'src/compiler/driver/compiler_driver.h') diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index 75d276d5b1..250532b920 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -39,7 +39,6 @@ class ParallelCompilationManager; class DexCompilationUnit; class TimingLogger; -const uint32_t kDexPCNotReady = 0xFFFFFF; enum CompilerBackend { kQuick, kPortable, @@ -106,7 +105,22 @@ class CompilerDriver { LOCKS_EXCLUDED(compiled_classes_lock_); // A method is uniquely located by its DexFile and the method_ids_ table index into that DexFile - typedef std::pair MethodReference; + struct MethodReference { + MethodReference(const DexFile* file, uint32_t index) : dex_file(file), dex_method_index(index) { + } + const DexFile* dex_file; + uint32_t dex_method_index; + }; + + struct MethodReferenceComparator { + bool operator()(MethodReference mr1, MethodReference mr2) const { + if (mr1.dex_file == mr2.dex_file) { + return mr1.dex_method_index < mr2.dex_method_index; + } else { + return mr1.dex_file < mr2.dex_file; + } + } + }; CompiledMethod* GetCompiledMethod(MethodReference ref) const LOCKS_EXCLUDED(compiled_methods_lock_); @@ -146,9 +160,9 @@ class CompilerDriver { // Can we fastpath a interface, super class or virtual method call? Computes method's vtable // index. - bool ComputeInvokeInfo(uint32_t method_idx, uint32_t dex_pc, - const DexCompilationUnit* mUnit, InvokeType& type, int& vtable_idx, - uintptr_t& direct_code, uintptr_t& direct_method) + bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc, + InvokeType& type, MethodReference& target_method, int& vtable_idx, + uintptr_t& direct_code, uintptr_t& direct_method, bool update_stats) LOCKS_EXCLUDED(Locks::mutator_lock_); // Record patch information for later fix up. @@ -262,7 +276,8 @@ class CompilerDriver { void GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type, mirror::Class* referrer_class, mirror::AbstractMethod* method, - uintptr_t& direct_code, uintptr_t& direct_method) + uintptr_t& direct_code, uintptr_t& direct_method, + bool update_stats) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void PreCompile(jobject class_loader, const std::vector& dex_files, @@ -321,7 +336,7 @@ class CompilerDriver { mutable Mutex compiled_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; ClassTable compiled_classes_ GUARDED_BY(compiled_classes_lock_); - typedef SafeMap MethodTable; + typedef SafeMap MethodTable; // All method references that this compiler has compiled. mutable Mutex compiled_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; MethodTable compiled_methods_ GUARDED_BY(compiled_methods_lock_); -- cgit v1.2.3-59-g8ed1b From 1bf8d4dbe5cb9891e8a1125ff1928b544efc243a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 30 May 2013 00:18:49 -0700 Subject: Profiler directed clean-up of dex2oat. Fix bad usage of std::string in: the verifier and compiler driver method arguments, causing unnecessary boxing and allocations; in creating a symbol for the dex compilation unit, that is only used in portable builds; in pattern matching for intrinsics by name. Make class linker dex and classes locks reader/writer to allow concurrent dex cache or class querying. Refactor ComputeCompilingMethodsClass to pass in a dex cache hint, to avoid taking any locks when the dex file of the compiling method matches that of the field or method being resolved. Make the RegType's HasClass method virtual to avoid frequent virtual method dispatch. Make RegTypeCache GetFromId inlinable. Various other bits of whitespace and formatting clean-up. Change-Id: Id152e1e5a6fed2961dad0b612b7aa0c48001ef94 --- src/class_linker.cc | 62 +++++++------- src/class_linker.h | 14 ++-- src/compiler/dex/quick/gen_invoke.cc | 14 +++- src/compiler/driver/compiler_driver.cc | 21 +++-- src/compiler/driver/compiler_driver.h | 2 +- src/compiler/driver/dex_compilation_unit.cc | 30 ++++--- src/compiler/driver/dex_compilation_unit.h | 6 +- src/compiler/llvm/llvm_compilation_unit.h | 6 +- src/locks.cc | 5 +- src/locks.h | 2 +- src/verifier/method_verifier.cc | 14 ++-- src/verifier/method_verifier.h | 9 ++- src/verifier/reg_type.cc | 121 +++++++++++++++++----------- src/verifier/reg_type.h | 94 +++++++++++---------- src/verifier/reg_type_cache-inl.h | 8 +- src/verifier/reg_type_cache.cc | 102 +++++++++++------------ src/verifier/reg_type_cache.h | 8 +- 17 files changed, 295 insertions(+), 223 deletions(-) (limited to 'src/compiler/driver/compiler_driver.h') diff --git a/src/class_linker.cc b/src/class_linker.cc index 46c2ade314..cbdcbe0b31 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -196,7 +196,7 @@ ClassLinker* ClassLinker::CreateFromImage(InternTable* intern_table) { ClassLinker::ClassLinker(InternTable* intern_table) // dex_lock_ is recursive as it may be used in stack dumping. - : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel, true), + : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel), class_roots_(NULL), array_iftable_(NULL), init_done_(false), @@ -663,22 +663,22 @@ bool ClassLinker::GenerateOatFile(const std::string& dex_filename, } void ClassLinker::RegisterOatFile(const OatFile& oat_file) { - MutexLock mu(Thread::Current(), dex_lock_); + WriterMutexLock mu(Thread::Current(), dex_lock_); RegisterOatFileLocked(oat_file); } void ClassLinker::RegisterOatFileLocked(const OatFile& oat_file) { - dex_lock_.AssertHeld(Thread::Current()); -#ifndef NDEBUG - for (size_t i = 0; i < oat_files_.size(); ++i) { - CHECK_NE(&oat_file, oat_files_[i]) << oat_file.GetLocation(); + dex_lock_.AssertExclusiveHeld(Thread::Current()); + if (kIsDebugBuild) { + for (size_t i = 0; i < oat_files_.size(); ++i) { + CHECK_NE(&oat_file, oat_files_[i]) << oat_file.GetLocation(); + } } -#endif oat_files_.push_back(&oat_file); } OatFile* ClassLinker::OpenOat(const ImageSpace* space) { - MutexLock mu(Thread::Current(), dex_lock_); + WriterMutexLock mu(Thread::Current(), dex_lock_); const Runtime* runtime = Runtime::Current(); const ImageHeader& image_header = space->GetImageHeader(); // Grab location but don't use Object::AsString as we haven't yet initialized the roots to @@ -709,7 +709,7 @@ OatFile* ClassLinker::OpenOat(const ImageSpace* space) { } const OatFile* ClassLinker::FindOpenedOatFileForDexFile(const DexFile& dex_file) { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); return FindOpenedOatFileFromDexLocation(dex_file.GetLocation()); } @@ -755,7 +755,7 @@ static const DexFile* FindDexFileInOatLocation(const std::string& dex_location, const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation(const std::string& dex_location, const std::string& oat_location) { - MutexLock mu(Thread::Current(), dex_lock_); + WriterMutexLock mu(Thread::Current(), dex_lock_); return FindOrCreateOatFileForDexLocationLocked(dex_location, oat_location); } @@ -857,7 +857,7 @@ const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const OatFile* oat_f } const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const std::string& dex_location) { - MutexLock mu(Thread::Current(), dex_lock_); + WriterMutexLock mu(Thread::Current(), dex_lock_); const OatFile* open_oat_file = FindOpenedOatFileFromDexLocation(dex_location); if (open_oat_file != NULL) { @@ -924,7 +924,7 @@ const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& } const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location) { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); return FindOatFileFromOatLocationLocked(oat_location); } @@ -1062,14 +1062,14 @@ void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg) { visitor(class_roots_, arg); Thread* self = Thread::Current(); { - MutexLock mu(self, dex_lock_); + ReaderMutexLock mu(self, dex_lock_); for (size_t i = 0; i < dex_caches_.size(); i++) { visitor(dex_caches_[i], arg); } } { - MutexLock mu(self, *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); typedef Table::const_iterator It; // TODO: C++0x auto for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) { visitor(it->second, arg); @@ -1084,7 +1084,7 @@ void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg) { } void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) const { - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); typedef Table::const_iterator It; // TODO: C++0x auto for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) { if (!visitor(it->second, arg)) { @@ -1813,7 +1813,7 @@ void ClassLinker::AppendToBootClassPath(const DexFile& dex_file, SirtRefGetDexFile() == &dex_file) { return true; @@ -1823,12 +1823,12 @@ bool ClassLinker::IsDexFileRegisteredLocked(const DexFile& dex_file) const { } bool ClassLinker::IsDexFileRegistered(const DexFile& dex_file) const { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); return IsDexFileRegisteredLocked(dex_file); } void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, SirtRef& dex_cache) { - dex_lock_.AssertHeld(Thread::Current()); + dex_lock_.AssertExclusiveHeld(Thread::Current()); CHECK(dex_cache.get() != NULL) << dex_file.GetLocation(); CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation())); dex_caches_.push_back(dex_cache.get()); @@ -1839,7 +1839,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, SirtRef dex_cache(self, AllocDexCache(self, dex_file)); { - MutexLock mu(self, dex_lock_); + WriterMutexLock mu(self, dex_lock_); if (IsDexFileRegisteredLocked(dex_file)) { return; } @@ -1858,12 +1858,12 @@ void ClassLinker::RegisterDexFile(const DexFile& dex_file) { } void ClassLinker::RegisterDexFile(const DexFile& dex_file, SirtRef& dex_cache) { - MutexLock mu(Thread::Current(), dex_lock_); + WriterMutexLock mu(Thread::Current(), dex_lock_); RegisterDexFileLocked(dex_file, dex_cache); } mirror::DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); // Search assuming unique-ness of dex file. for (size_t i = 0; i != dex_caches_.size(); ++i) { mirror::DexCache* dex_cache = dex_caches_[i]; @@ -1889,7 +1889,7 @@ mirror::DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const { } void ClassLinker::FixupDexCaches(mirror::AbstractMethod* resolution_method) const { - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i != dex_caches_.size(); ++i) { dex_caches_[i]->Fixup(resolution_method); } @@ -2085,7 +2085,7 @@ mirror::Class* ClassLinker::InsertClass(const StringPiece& descriptor, mirror::C LOG(INFO) << "Loaded class " << descriptor << source; } size_t hash = StringPieceHash()(descriptor); - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); Table& classes = image_class ? image_classes_ : classes_; mirror::Class* existing = LookupClassLocked(descriptor.data(), klass->GetClassLoader(), hash, classes); #ifndef NDEBUG @@ -2103,7 +2103,7 @@ mirror::Class* ClassLinker::InsertClass(const StringPiece& descriptor, mirror::C bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) { size_t hash = Hash(descriptor); - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); typedef Table::iterator It; // TODO: C++0x auto // TODO: determine if its better to search classes_ or image_classes_ first ClassHelper kh; @@ -2131,7 +2131,7 @@ bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* mirror::Class* ClassLinker::LookupClass(const char* descriptor, const mirror::ClassLoader* class_loader) { size_t hash = Hash(descriptor); - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); // TODO: determine if its better to search classes_ or image_classes_ first mirror::Class* klass = NULL; // Use image class only if the class_loader is null. @@ -2171,7 +2171,7 @@ mirror::Class* ClassLinker::LookupClassLocked(const char* descriptor, void ClassLinker::LookupClasses(const char* descriptor, std::vector& classes) { classes.clear(); size_t hash = Hash(descriptor); - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); typedef Table::const_iterator It; // TODO: C++0x auto // TODO: determine if its better to search classes_ or image_classes_ first ClassHelper kh(NULL, this); @@ -2505,7 +2505,7 @@ mirror::AbstractMethod* ClassLinker::FindMethodForProxy(const mirror::Class* pro mirror::DexCache* dex_cache = NULL; { mirror::ObjectArray* resolved_types = proxy_method->GetDexCacheResolvedTypes(); - MutexLock mu(Thread::Current(), dex_lock_); + ReaderMutexLock mu(Thread::Current(), dex_lock_); for (size_t i = 0; i != dex_caches_.size(); ++i) { if (dex_caches_[i]->GetResolvedTypes() == resolved_types) { dex_cache = dex_caches_[i]; @@ -3885,7 +3885,7 @@ void ClassLinker::DumpAllClasses(int flags) const { // lock held, because it might need to resolve a field's type, which would try to take the lock. std::vector all_classes; { - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); typedef Table::const_iterator It; // TODO: C++0x auto for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) { all_classes.push_back(it->second); @@ -3901,13 +3901,13 @@ void ClassLinker::DumpAllClasses(int flags) const { } void ClassLinker::DumpForSigQuit(std::ostream& os) const { - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); os << "Loaded classes: " << image_classes_.size() << " image classes; " << classes_.size() << " allocated classes\n"; } size_t ClassLinker::NumLoadedClasses() const { - MutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); return classes_.size() + image_classes_.size(); } diff --git a/src/class_linker.h b/src/class_linker.h index d41373c7d7..79fa8bafbb 100644 --- a/src/class_linker.h +++ b/src/class_linker.h @@ -240,7 +240,7 @@ class ClassLinker { LOCKS_EXCLUDED(dex_lock_); const OatFile* FindOatFileFromOatLocationLocked(const std::string& location) - EXCLUSIVE_LOCKS_REQUIRED(dex_lock_); + SHARED_LOCKS_REQUIRED(dex_lock_); // Finds the oat file for a dex location, generating the oat file if // it is missing or out of date. Returns the DexFile from within the @@ -420,7 +420,7 @@ class ClassLinker { void RegisterDexFileLocked(const DexFile& dex_file, SirtRef& dex_cache) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsDexFileRegisteredLocked(const DexFile& dex_file) const EXCLUSIVE_LOCKS_REQUIRED(dex_lock_); + bool IsDexFileRegisteredLocked(const DexFile& dex_file) const SHARED_LOCKS_REQUIRED(dex_lock_); void RegisterOatFileLocked(const OatFile& oat_file) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_); @@ -489,10 +489,9 @@ class ClassLinker { LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const OatFile* FindOpenedOatFileFromDexLocation(const std::string& dex_location) - EXCLUSIVE_LOCKS_REQUIRED(dex_lock_) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, dex_lock_); const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) - EXCLUSIVE_LOCKS_REQUIRED(dex_lock_); + SHARED_LOCKS_REQUIRED(dex_lock_); const DexFile* VerifyAndOpenDexFileFromOatFile(const OatFile* oat_file, const std::string& dex_location, uint32_t dex_location_checksum) @@ -508,7 +507,7 @@ class ClassLinker { std::vector boot_class_path_; - mutable Mutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; std::vector dex_caches_ GUARDED_BY(dex_lock_); std::vector oat_files_ GUARDED_BY(dex_lock_); @@ -522,8 +521,7 @@ class ClassLinker { mirror::Class* LookupClassLocked(const char* descriptor, const mirror::ClassLoader* class_loader, size_t hash, const Table& classes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - EXCLUSIVE_LOCKS_REQUIRED(Locks::classlinker_classes_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::classlinker_classes_lock_); // indexes into class_roots_. // needs to be kept in sync with class_roots_descriptors_. diff --git a/src/compiler/dex/quick/gen_invoke.cc b/src/compiler/dex/quick/gen_invoke.cc index f44272a641..733fdc9848 100644 --- a/src/compiler/dex/quick/gen_invoke.cc +++ b/src/compiler/dex/quick/gen_invoke.cc @@ -15,6 +15,7 @@ */ #include "compiler/dex/compiler_ir.h" +#include "dex_file-inl.h" #include "invoke_type.h" #include "mirror/array.h" #include "mirror/string.h" @@ -1177,6 +1178,10 @@ bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long, // TODO - add Mips implementation return false; } + if (cu_->instruction_set == kX86 && is_object) { + // TODO: fix X86, it exhausts registers for card marking. + return false; + } // Unused - RegLocation rl_src_unsafe = info->args[0]; RegLocation rl_src_obj = info->args[1]; // Object RegLocation rl_src_offset = info->args[2]; // long low @@ -1220,8 +1225,10 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) * method. By doing this during basic block construction, we can also * take advantage of/generate new useful dataflow info. */ - std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); - if (tgt_method.find(" java.lang") != std::string::npos) { + const char* tgt_methods_declaring_class = + cu_->dex_file->GetMethodDeclaringClassDescriptor(cu_->dex_file->GetMethodId(info->index)); + if (strstr(tgt_methods_declaring_class, "Ljava/lang") != NULL) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") { return GenInlinedDoubleCvt(info); } @@ -1275,7 +1282,8 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") { return GenInlinedCurrentThread(info); } - } else if (tgt_method.find(" sun.misc.Unsafe") != std::string::npos) { + } else if (strstr(tgt_methods_declaring_class, "Lsun/misc/Unsafe;") != NULL) { + std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file)); if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") { return GenInlinedCas32(info, false); } diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 85dcdf671b..cc65cbee67 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -500,7 +500,7 @@ void CompilerDriver::PreCompile(jobject class_loader, const std::vectorGetClassLinker()->FindDexCache(*mUnit->GetDexFile()); + // 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 = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); + } mirror::ClassLoader* class_loader = soa.Decode(mUnit->GetClassLoader()); const DexFile::MethodId& referrer_method_id = mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); return mUnit->GetClassLinker()->ResolveType(*mUnit->GetDexFile(), referrer_method_id.class_idx_, @@ -649,7 +654,9 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi // Try to resolve field and ignore if an Incompatible Class Change Error (ie is static). mirror::Field* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && !resolved_field->IsStatic()) { - mirror::Class* referrer_class = ComputeCompilingMethodsClass(soa, mUnit); + mirror::Class* referrer_class = + ComputeCompilingMethodsClass(soa, resolved_field->GetDeclaringClass()->GetDexCache(), + mUnit); if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); bool access_ok = referrer_class->CanAccess(fields_class) && @@ -698,7 +705,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila // Try to resolve field and ignore if an Incompatible Class Change Error (ie isn't static). mirror::Field* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && resolved_field->IsStatic()) { - mirror::Class* referrer_class = ComputeCompilingMethodsClass(soa, mUnit); + mirror::Class* referrer_class = + ComputeCompilingMethodsClass(soa, resolved_field->GetDeclaringClass()->GetDexCache(), + mUnit); if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); if (fields_class == referrer_class) { @@ -842,7 +851,9 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui if (resolved_method != NULL) { // 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. - mirror::Class* referrer_class = ComputeCompilingMethodsClass(soa, mUnit); + mirror::Class* referrer_class = + ComputeCompilingMethodsClass(soa, resolved_method->GetDeclaringClass()->GetDexCache(), + mUnit); bool icce = resolved_method->CheckIncompatibleClassChange(invoke_type); if (referrer_class != NULL && !icce) { mirror::Class* methods_class = resolved_method->GetDeclaringClass(); diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index 250532b920..c1e449e6ed 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -267,7 +267,7 @@ class CompilerDriver { } // Checks if class specified by type_idx is one of the image_classes_ - bool IsImageClass(const std::string& descriptor) const; + bool IsImageClass(const char* descriptor) const; void RecordClassStatus(ClassReference ref, CompiledClass* compiled_class); diff --git a/src/compiler/driver/dex_compilation_unit.cc b/src/compiler/driver/dex_compilation_unit.cc index 962df42a21..c7a4df6ea4 100644 --- a/src/compiler/driver/dex_compilation_unit.cc +++ b/src/compiler/driver/dex_compilation_unit.cc @@ -31,18 +31,17 @@ DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu) code_item_(cu->code_item), class_def_idx_(cu->class_def_idx), dex_method_idx_(cu->method_idx), - access_flags_(cu->access_flags), - symbol_(StringPrintf("dex_%s", MangleForJni(PrettyMethod(dex_method_idx_, *dex_file_)).c_str())) { + access_flags_(cu->access_flags) { } -DexCompilationUnit:: DexCompilationUnit(CompilationUnit* cu, - jobject class_loader, - ClassLinker* class_linker, - const DexFile& dex_file, - const DexFile::CodeItem* code_item, - uint32_t class_def_idx, - uint32_t method_idx, - uint32_t access_flags) +DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu, + jobject class_loader, + ClassLinker* class_linker, + const DexFile& dex_file, + const DexFile::CodeItem* code_item, + uint32_t class_def_idx, + uint32_t method_idx, + uint32_t access_flags) : cu_(cu), class_loader_(class_loader), class_linker_(class_linker), @@ -50,8 +49,15 @@ DexCompilationUnit:: DexCompilationUnit(CompilationUnit* cu, code_item_(code_item), class_def_idx_(class_def_idx), dex_method_idx_(method_idx), - access_flags_(access_flags), - symbol_(StringPrintf("dex_%s", MangleForJni(PrettyMethod(dex_method_idx_, *dex_file_)).c_str())) { + access_flags_(access_flags) { +} + +const std::string& DexCompilationUnit::GetSymbol() { + if (symbol_.empty()) { + symbol_ = "dex_"; + symbol_ += MangleForJni(PrettyMethod(dex_method_idx_, *dex_file_)); + } + return symbol_; } } // namespace art diff --git a/src/compiler/driver/dex_compilation_unit.h b/src/compiler/driver/dex_compilation_unit.h index 0b90aaafdf..3c6129d642 100644 --- a/src/compiler/driver/dex_compilation_unit.h +++ b/src/compiler/driver/dex_compilation_unit.h @@ -92,9 +92,7 @@ class DexCompilationUnit { return ((access_flags_ & kAccSynchronized) != 0); } - const std::string& GetSymbol() const { - return symbol_; - } + const std::string& GetSymbol(); private: CompilationUnit* const cu_; @@ -110,7 +108,7 @@ class DexCompilationUnit { const uint32_t dex_method_idx_; const uint32_t access_flags_; - const std::string symbol_; + std::string symbol_; }; } // namespace art diff --git a/src/compiler/llvm/llvm_compilation_unit.h b/src/compiler/llvm/llvm_compilation_unit.h index d96e778912..857d924840 100644 --- a/src/compiler/llvm/llvm_compilation_unit.h +++ b/src/compiler/llvm/llvm_compilation_unit.h @@ -81,10 +81,10 @@ class LlvmCompilationUnit { void SetCompilerDriver(CompilerDriver* driver) { driver_ = driver; } - const DexCompilationUnit* GetDexCompilationUnit() { + DexCompilationUnit* GetDexCompilationUnit() { return dex_compilation_unit_; } - void SetDexCompilationUnit(const DexCompilationUnit* dex_compilation_unit) { + void SetDexCompilationUnit(DexCompilationUnit* dex_compilation_unit) { dex_compilation_unit_ = dex_compilation_unit; } @@ -113,7 +113,7 @@ class LlvmCompilationUnit { UniquePtr intrinsic_helper_; UniquePtr llvm_info_; CompilerDriver* driver_; - const DexCompilationUnit* dex_compilation_unit_; + DexCompilationUnit* dex_compilation_unit_; std::string bitcode_filename_; diff --git a/src/locks.cc b/src/locks.cc index eb0620c0c3..51a40c383a 100644 --- a/src/locks.cc +++ b/src/locks.cc @@ -22,7 +22,7 @@ namespace art { Mutex* Locks::abort_lock_ = NULL; Mutex* Locks::breakpoint_lock_ = NULL; -Mutex* Locks::classlinker_classes_lock_ = NULL; +ReaderWriterMutex* Locks::classlinker_classes_lock_ = NULL; ReaderWriterMutex* Locks::heap_bitmap_lock_ = NULL; Mutex* Locks::logging_lock_ = NULL; ReaderWriterMutex* Locks::mutator_lock_ = NULL; @@ -52,7 +52,8 @@ void Locks::Init() { DCHECK(breakpoint_lock_ == NULL); breakpoint_lock_ = new Mutex("breakpoint lock", kBreakpointLock); DCHECK(classlinker_classes_lock_ == NULL); - classlinker_classes_lock_ = new Mutex("ClassLinker classes lock", kClassLinkerClassesLock); + classlinker_classes_lock_ = new ReaderWriterMutex("ClassLinker classes lock", + kClassLinkerClassesLock); DCHECK(heap_bitmap_lock_ == NULL); heap_bitmap_lock_ = new ReaderWriterMutex("heap bitmap lock", kHeapBitmapLock); DCHECK(mutator_lock_ == NULL); diff --git a/src/locks.h b/src/locks.h index 431a14816a..ceb04b937a 100644 --- a/src/locks.h +++ b/src/locks.h @@ -143,7 +143,7 @@ class Locks { static Mutex* trace_lock_ ACQUIRED_AFTER(breakpoint_lock_); // Guards lists of classes within the class linker. - static Mutex* classlinker_classes_lock_ ACQUIRED_AFTER(trace_lock_); + static ReaderWriterMutex* classlinker_classes_lock_ ACQUIRED_AFTER(trace_lock_); // When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code // doesn't try to hold a higher level Mutex. diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc index 1b2d9f35bf..a7d26bb602 100644 --- a/src/verifier/method_verifier.cc +++ b/src/verifier/method_verifier.cc @@ -955,9 +955,9 @@ bool MethodVerifier::VerifyCodeFlow() { const std::vector* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get())); verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map); - MethodVerifier::PcToConreteMethod* pc_to_conrete_method = GenerateDevirtMap(); - if(pc_to_conrete_method != NULL ) { - SetDevirtMap(ref, pc_to_conrete_method); + MethodVerifier::PcToConcreteMethod* pc_to_concrete_method = GenerateDevirtMap(); + if(pc_to_concrete_method != NULL ) { + SetDevirtMap(ref, pc_to_concrete_method); } return true; } @@ -3160,7 +3160,7 @@ void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bit *log2_max_gc_pc = i; } -MethodVerifier::PcToConreteMethod* MethodVerifier::GenerateDevirtMap() { +MethodVerifier::PcToConcreteMethod* MethodVerifier::GenerateDevirtMap() { // It is risky to rely on reg_types for sharpening in cases of soft // verification, we might end up sharpening to a wrong implementation. Just abort. @@ -3168,7 +3168,7 @@ MethodVerifier::PcToConreteMethod* MethodVerifier::GenerateDevirtMap() { return NULL; } - UniquePtr pc_to_concrete_method(new PcToConreteMethod()); + UniquePtr pc_to_concrete_method(new PcToConcreteMethod()); uint32_t dex_pc = 0; const uint16_t* insns = code_item_->insns_ ; const Instruction* inst = Instruction::At(insns); @@ -3338,7 +3338,7 @@ void MethodVerifier::SetDexGcMap(CompilerDriver::MethodReference ref, const std: CHECK(GetDexGcMap(ref) != NULL); } -void MethodVerifier::SetDevirtMap(CompilerDriver::MethodReference ref, const PcToConreteMethod* devirt_map) { +void MethodVerifier::SetDevirtMap(CompilerDriver::MethodReference ref, const PcToConcreteMethod* devirt_map) { MutexLock mu(Thread::Current(), *devirt_maps_lock_); DevirtualizationMapTable::iterator it = devirt_maps_->find(ref); @@ -3371,7 +3371,7 @@ const CompilerDriver::MethodReference* MethodVerifier::GetDevirtMap(const Compil } // Look up the PC in the map, get the concrete method to execute and return its reference. - MethodVerifier::PcToConreteMethod::const_iterator pc_to_concrete_method = it->second->find(dex_pc); + MethodVerifier::PcToConcreteMethod::const_iterator pc_to_concrete_method = it->second->find(dex_pc); if(pc_to_concrete_method != it->second->end()) { return &(pc_to_concrete_method->second); } else { diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h index 755d089bd6..9b4b8e5503 100644 --- a/src/verifier/method_verifier.h +++ b/src/verifier/method_verifier.h @@ -584,15 +584,16 @@ class MethodVerifier { // Devirtualization map. - typedef SafeMap PcToConreteMethod; - typedef SafeMap PcToConcreteMethod; + typedef SafeMap DevirtualizationMapTable; - MethodVerifier::PcToConreteMethod* GenerateDevirtMap() + MethodVerifier::PcToConcreteMethod* GenerateDevirtMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static Mutex* devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; static DevirtualizationMapTable* devirt_maps_ GUARDED_BY(devirt_maps_lock_); - static void SetDevirtMap(CompilerDriver::MethodReference ref, const PcToConreteMethod* pc_method_map); + static void SetDevirtMap(CompilerDriver::MethodReference ref, + const PcToConcreteMethod* pc_method_map) LOCKS_EXCLUDED(devirt_maps_lock_); typedef std::set RejectedClassesTable; static Mutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc index 32679f6100..5db6affcae 100644 --- a/src/verifier/reg_type.cc +++ b/src/verifier/reg_type.cc @@ -32,7 +32,6 @@ namespace art { namespace verifier { -static const bool kIsDebugBuild = false; UndefinedType* UndefinedType::instance_ = NULL; ConflictType* ConflictType::instance_ = NULL; BooleanType* BooleanType::instance = NULL; @@ -70,36 +69,44 @@ std::string BooleanType::Dump() const { std::string ConflictType::Dump() const { return "Conflict"; } + std::string ByteType::Dump() const { return "Byte"; } + std::string ShortType::Dump() const { return "short"; } + std::string CharType::Dump() const { return "Char"; } + std::string FloatType::Dump() const { return "float"; } + std::string LongLoType::Dump() const { return "long (Low Half)"; } + std::string LongHiType::Dump() const { return "long (High Half)"; } + std::string DoubleLoType::Dump() const { return "Double (Low Half)"; } + std::string DoubleHiType::Dump() const { return "Double (High Half)"; } + std::string IntegerType::Dump() const { return "Integer"; } - -DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass, std::string& descriptor, +DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new DoubleHiType(klass, descriptor, cache_id); @@ -119,7 +126,7 @@ void DoubleHiType::Destroy() { } } -DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass, std::string& descriptor, +DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new DoubleLoType(klass, descriptor, cache_id); @@ -139,7 +146,7 @@ void DoubleLoType::Destroy() { } } -LongLoType* LongLoType::CreateInstance(mirror::Class* klass, std::string& descriptor, +LongLoType* LongLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new LongLoType(klass, descriptor, cache_id); @@ -147,7 +154,7 @@ LongLoType* LongLoType::CreateInstance(mirror::Class* klass, std::string& descri return instance_; } -LongHiType* LongHiType::CreateInstance(mirror::Class* klass, std::string& descriptor, +LongHiType* LongHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new LongHiType(klass, descriptor, cache_id); @@ -179,9 +186,8 @@ void LongLoType::Destroy() { } } -FloatType* FloatType::CreateInstance(mirror::Class* klass, std::string& descriptor, - uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +FloatType* FloatType::CreateInstance(mirror::Class* klass, const std::string& descriptor, + uint16_t cache_id) { if (instance_ == NULL) { instance_ = new FloatType(klass, descriptor, cache_id); } @@ -199,17 +205,19 @@ void FloatType::Destroy() { } } -CharType* CharType::CreateInstance(mirror::Class* klass, std::string& descriptor, +CharType* CharType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new CharType(klass, descriptor, cache_id); } return instance_; } + CharType* CharType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void CharType::Destroy() { if (instance_ != NULL) { delete instance_; @@ -217,81 +225,94 @@ void CharType::Destroy() { } } -ShortType* ShortType::CreateInstance(mirror::Class* klass, std::string& descriptor, +ShortType* ShortType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new ShortType(klass, descriptor, cache_id); } return instance_; } + ShortType* ShortType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void ShortType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } -ByteType* ByteType::CreateInstance(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + +ByteType* ByteType::CreateInstance(mirror::Class* klass, const std::string& descriptor, + uint16_t cache_id) { if (instance_ == NULL) { instance_ = new ByteType(klass, descriptor, cache_id); } return instance_; } + ByteType* ByteType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void ByteType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } -IntegerType* IntegerType::CreateInstance(mirror::Class* klass, std::string& descriptor, + +IntegerType* IntegerType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new IntegerType(klass, descriptor, cache_id); } return instance_; } + IntegerType* IntegerType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void IntegerType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } -ConflictType* ConflictType::CreateInstance(mirror::Class* klass, std::string& descriptor, + +ConflictType* ConflictType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new ConflictType(klass, descriptor, cache_id); } return instance_; } + ConflictType* ConflictType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void ConflictType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } -BooleanType* BooleanType::CreateInstance(mirror::Class* klass, std::string& descriptor, + +BooleanType* BooleanType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (BooleanType::instance == NULL) { instance = new BooleanType(klass, descriptor, cache_id); } return BooleanType::instance; } + BooleanType* BooleanType::GetInstance() { CHECK(BooleanType::instance != NULL); return BooleanType::instance; @@ -307,23 +328,27 @@ void BooleanType::Destroy() { std::string UndefinedType::Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return "Undefined"; } -UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass, std::string& descriptor, + +UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) { if (instance_ == NULL) { instance_ = new UndefinedType(klass, descriptor, cache_id); } return instance_; } + UndefinedType* UndefinedType::GetInstance() { CHECK(instance_ != NULL); return instance_; } + void UndefinedType::Destroy() { if (instance_ != NULL) { delete instance_; instance_ = NULL; } } + std::string UnresolvedMergedType::Dump() const { std::stringstream result; std::set types = GetMergedTypes(); @@ -338,6 +363,7 @@ std::string UnresolvedMergedType::Dump() const { result << ")"; return result.str(); } + std::string UnresolvedSuperClass::Dump() const { std::stringstream result; uint16_t super_type_id = GetUnresolvedSuperClassChildId(); @@ -358,7 +384,7 @@ std::string UnresolvedUninitializedRefType::Dump() const { return result.str(); } -std::string UnresolvedUninitialisedThisRefType::Dump() const { +std::string UnresolvedUninitializedThisRefType::Dump() const { std::stringstream result; result << "Unresolved And Uninitialized This Reference" << PrettyDescriptor(GetDescriptor()); return result.str(); @@ -376,13 +402,14 @@ std::string PreciseReferenceType::Dump() const { return result.str(); } -std::string UninitialisedReferenceType::Dump() const { +std::string UninitializedReferenceType::Dump() const { std::stringstream result; result << "Uninitialized Reference" << ": " << PrettyDescriptor(GetClass()); result << " Allocation PC: " << GetAllocationPc(); return result.str(); } -std::string UninitialisedThisReferenceType::Dump() const { + +std::string UninitializedThisReferenceType::Dump() const { std::stringstream result; result << "Uninitialized This Reference" << ": " << PrettyDescriptor(GetClass()); result << "Allocation PC: " << GetAllocationPc(); @@ -459,76 +486,77 @@ std::string ImpreciseConstHiType::Dump() const { return result.str(); } -BooleanType::BooleanType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) - : RegType(klass, descriptor, cache_id) { +BooleanType::BooleanType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) + : RegType(klass, descriptor, cache_id) { } -ConflictType::ConflictType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +ConflictType::ConflictType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -ByteType::ByteType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +ByteType::ByteType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -ShortType::ShortType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +ShortType::ShortType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -CharType::CharType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +CharType::CharType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -IntegerType::IntegerType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +IntegerType::IntegerType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -ConstantType::ConstantType(uint32_t constat, uint16_t cache_id) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_): RegType(NULL, "", cache_id), constant_(constat) { +ConstantType::ConstantType(uint32_t constant, uint16_t cache_id) + : RegType(NULL, "", cache_id), constant_(constant) { } -ReferenceType::ReferenceType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +ReferenceType::ReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, std::string& descriptor, +PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { DCHECK(klass->IsInstantiable()); } -UnresolvedUninitialisedThisRefType::UnresolvedUninitialisedThisRefType(std::string& descriptor, +UnresolvedUninitializedThisRefType::UnresolvedUninitializedThisRefType(const std::string& descriptor, uint16_t cache_id) : UninitializedType(NULL, descriptor, 0, cache_id) { } -UnresolvedUninitializedRefType::UnresolvedUninitializedRefType( std::string& descriptor, +UnresolvedUninitializedRefType::UnresolvedUninitializedRefType(const std::string& descriptor, uint32_t allocation_pc, uint16_t cache_id) : UninitializedType(NULL, descriptor, allocation_pc, cache_id) { } -UninitialisedReferenceType::UninitialisedReferenceType(mirror::Class* klass, - std::string& descriptor, uint32_t allocation_pc, uint16_t cache_id) +UninitializedReferenceType::UninitializedReferenceType(mirror::Class* klass, + const std::string& descriptor, + uint32_t allocation_pc, uint16_t cache_id) : UninitializedType(klass, descriptor, allocation_pc, cache_id) { } -LongHiType::LongHiType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +LongHiType::LongHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -FloatType::FloatType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +FloatType::FloatType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -DoubleLoType::DoubleLoType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +DoubleLoType::DoubleLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -DoubleHiType::DoubleHiType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +DoubleHiType::DoubleHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } -LongLoType::LongLoType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) +LongLoType::LongLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { } @@ -749,10 +777,12 @@ int32_t ConstantType::ConstantValue() const { DCHECK(IsConstantTypes()); return constant_; } + int32_t ConstantType::ConstantValueLo() const { DCHECK(IsConstantLo()); return constant_; } + int32_t ConstantType::ConstantValueHi() const { if (IsConstantHi() || IsPreciseConstantHi() || IsImpreciseConstantHi()) { return constant_; @@ -761,6 +791,7 @@ int32_t ConstantType::ConstantValueHi() const { return 0; } } + static const RegType& SelectNonConstant(const RegType& a, const RegType& b) { return a.IsConstant() ? b : a; } @@ -951,7 +982,7 @@ void RegType::CheckInvariants() const { } } -UninitializedType::UninitializedType(mirror::Class* klass, std::string& descriptor, +UninitializedType::UninitializedType(mirror::Class* klass, const std::string& descriptor, uint32_t allocation_pc, uint16_t cache_id) : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) { } @@ -960,15 +991,15 @@ void UninitializedType::CheckInvariants() const { CHECK_EQ(allocation_pc_, 0U) << *this; } -void UninitialisedThisReferenceType::CheckInvariants() const { +void UninitializedThisReferenceType::CheckInvariants() const { UninitializedType::CheckInvariants(); } -UninitialisedThisReferenceType::UninitialisedThisReferenceType(mirror::Class* klass, - std::string& descriptor, uint16_t cache_id) : UninitializedType(klass, descriptor, 0, cache_id) { +UninitializedThisReferenceType::UninitializedThisReferenceType(mirror::Class* klass, + const std::string& descriptor, uint16_t cache_id) : UninitializedType(klass, descriptor, 0, cache_id) { } -void UnresolvedUninitialisedThisRefType::CheckInvariants() const { +void UnresolvedUninitializedThisRefType::CheckInvariants() const { UninitializedType::CheckInvariants(); CHECK(!descriptor_.empty()) << *this; CHECK(klass_ == NULL) << *this; diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h index 7c4253604c..39c33c6c97 100644 --- a/src/verifier/reg_type.h +++ b/src/verifier/reg_type.h @@ -268,9 +268,8 @@ class RegType { inline virtual bool IsLong() const { return false; } - bool HasClass() const { - return IsReference() || IsPreciseReference() || IsUninitializedReference() || - IsUninitializedThisReference(); + virtual bool HasClass() const { + return false; } bool IsJavaLangObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -326,7 +325,7 @@ class RegType { static mirror::Class* ClassJoin(mirror::Class* s, mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - RegType(mirror::Class* klass, std::string descriptor, uint16_t cache_id) + RegType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : descriptor_(descriptor), klass_(klass), cache_id_(cache_id) { } @@ -349,12 +348,13 @@ class ConflictType : public RegType { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static ConflictType* CreateInstance(mirror::Class* klass, std::string& descriptor, - uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static ConflictType* CreateInstance(mirror::Class* klass, const std::string& descriptor, + uint16_t cache_id) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static ConflictType* GetInstance(); static void Destroy(); private: - ConflictType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + ConflictType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static ConflictType* instance_; }; @@ -365,13 +365,13 @@ class UndefinedType : public RegType { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static UndefinedType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static UndefinedType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static UndefinedType* GetInstance(); static void Destroy(); private: - UndefinedType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + UndefinedType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : RegType(klass, descriptor, cache_id) { } @@ -386,13 +386,13 @@ class IntegerType : public RegType { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static IntegerType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static IntegerType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static IntegerType* GetInstance(); static void Destroy(); private: - IntegerType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + IntegerType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static IntegerType* instance_; }; @@ -403,13 +403,13 @@ class BooleanType : public RegType { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static BooleanType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static BooleanType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static BooleanType* GetInstance(); static void Destroy(); private: - BooleanType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + BooleanType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static BooleanType* instance; }; @@ -420,13 +420,13 @@ class ByteType : public RegType { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static ByteType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static ByteType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static ByteType* GetInstance(); static void Destroy(); private: - ByteType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + ByteType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static ByteType* instance_; }; @@ -437,13 +437,13 @@ class ShortType : public RegType { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static ShortType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static ShortType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static ShortType* GetInstance(); static void Destroy(); private: - ShortType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + ShortType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static ShortType* instance_; }; @@ -454,13 +454,13 @@ class CharType : public RegType { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static CharType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static CharType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static CharType* GetInstance(); static void Destroy(); private: - CharType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + CharType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static CharType* instance_; }; @@ -471,13 +471,13 @@ class FloatType : public RegType { return true; } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static FloatType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static FloatType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static FloatType* GetInstance(); static void Destroy(); private: - FloatType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + FloatType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static FloatType* instance_; }; @@ -491,13 +491,13 @@ class LongLoType : public RegType { bool IsLong() const { return true; } - static LongLoType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static LongLoType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static LongLoType* GetInstance(); static void Destroy(); private: - LongLoType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + LongLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static LongLoType* instance_; }; @@ -508,13 +508,13 @@ class LongHiType : public RegType { bool IsLongHi() const { return true; } - static LongHiType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static LongHiType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static LongHiType* GetInstance(); static void Destroy(); private: - LongHiType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + LongHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static LongHiType* instance_; }; @@ -528,13 +528,13 @@ class DoubleLoType : public RegType { bool IsDouble() const { return true; } - static DoubleLoType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static DoubleLoType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static DoubleLoType* GetInstance(); static void Destroy(); private: - DoubleLoType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + DoubleLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static DoubleLoType* instance_; }; @@ -545,13 +545,13 @@ class DoubleHiType : public RegType { virtual bool IsDoubleHi() const { return true; } - static DoubleHiType* CreateInstance(mirror::Class* klass, std::string& descriptor, + static DoubleHiType* CreateInstance(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static DoubleHiType* GetInstance(); static void Destroy(); private: - DoubleHiType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + DoubleHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static DoubleHiType* instance_; }; @@ -664,7 +664,7 @@ class ImpreciseConstHiType : public ConstantType { class UninitializedType : public RegType { public: - UninitializedType(mirror::Class* klass, std::string& descriptor, uint32_t allocation_pc, + UninitializedType(mirror::Class* klass, const std::string& descriptor, uint32_t allocation_pc, uint16_t cache_id); inline virtual ~UninitializedType() { } @@ -679,20 +679,23 @@ class UninitializedType : public RegType { const uint32_t allocation_pc_; }; -class UninitialisedReferenceType : public UninitializedType { +class UninitializedReferenceType : public UninitializedType { public: - UninitialisedReferenceType(mirror::Class* klass, std::string& descriptor, uint32_t allocation_pc, + UninitializedReferenceType(mirror::Class* klass, const std::string& descriptor, uint32_t allocation_pc, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsUninitializedReference() const { return true; } + bool HasClass() const { + return true; + } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; class UnresolvedUninitializedRefType : public UninitializedType { public: - UnresolvedUninitializedRefType(std::string& descriptor, uint32_t allocation_pc, + UnresolvedUninitializedRefType(const std::string& descriptor, uint32_t allocation_pc, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsUnresolvedAndUninitializedReference() const { @@ -701,20 +704,23 @@ class UnresolvedUninitializedRefType : public UninitializedType { std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; -class UninitialisedThisReferenceType : public UninitializedType { +class UninitializedThisReferenceType : public UninitializedType { public: - UninitialisedThisReferenceType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + UninitializedThisReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - inline virtual bool IsUninitializedThisReference() const { + virtual bool IsUninitializedThisReference() const { + return true; + } + bool HasClass() const { return true; } }; -class UnresolvedUninitialisedThisRefType : public UninitializedType { +class UnresolvedUninitializedThisRefType : public UninitializedType { public: - UnresolvedUninitialisedThisRefType(std::string& descriptor, uint16_t cache_id) + UnresolvedUninitializedThisRefType(const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -725,27 +731,33 @@ class UnresolvedUninitialisedThisRefType : public UninitializedType { class ReferenceType : public RegType { public: - ReferenceType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + ReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsReference() const { return true; } + bool HasClass() const { + return true; + } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; class PreciseReferenceType : public RegType { public: - PreciseReferenceType(mirror::Class* klass, std::string& descriptor, uint16_t cache_id) + PreciseReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsPreciseReference() const { return true; } + bool HasClass() const { + return true; + } }; class UnresolvedReferenceType : public RegType { public: - UnresolvedReferenceType(std::string& descriptor, uint16_t cache_id) + UnresolvedReferenceType(const std::string& descriptor, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : RegType(NULL, descriptor, cache_id) { } std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/src/verifier/reg_type_cache-inl.h b/src/verifier/reg_type_cache-inl.h index f6b0056536..42474d1849 100644 --- a/src/verifier/reg_type_cache-inl.h +++ b/src/verifier/reg_type_cache-inl.h @@ -24,7 +24,7 @@ namespace art { namespace verifier { template -Type* RegTypeCache::CreatePrimitiveTypeInstance(std::string descriptor) { +Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) { mirror::Class* klass = NULL; // Try loading the class from linker. if (!descriptor.empty()) { @@ -35,6 +35,12 @@ Type* RegTypeCache::CreatePrimitiveTypeInstance(std::string descriptor) { return entry; } +inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const { + DCHECK_LT(id, entries_.size()); + RegType* result = entries_[id]; + DCHECK(result != NULL); + return *result; +} } // namespace verifier } // namespace art #endif // ART_SRC_VERIFIER_REG_TYPE_CACHE_INL_H_ diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc index e914d1e679..57a825b0bb 100644 --- a/src/verifier/reg_type_cache.cc +++ b/src/verifier/reg_type_cache.cc @@ -24,6 +24,7 @@ namespace art { namespace verifier { + bool RegTypeCache::primitive_initialized_ = false; uint16_t RegTypeCache::primitive_start_ = 0; uint16_t RegTypeCache::primitive_count_ = 0; @@ -49,9 +50,10 @@ void RegTypeCache::FillPrimitiveTypes() { DCHECK_EQ(entries_.size(), primitive_count_); } -const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise) { - CHECK(RegTypeCache::primitive_initialized_); - if (std::string(descriptor).length() == 1) { +const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, + bool precise) { + DCHECK(RegTypeCache::primitive_initialized_); + if (descriptor[1] == '\0') { switch (descriptor[0]) { case 'Z': return Boolean(); @@ -80,15 +82,7 @@ const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const c } }; -const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const { - DCHECK_LT(id, entries_.size()); - RegType* result = entries_[id]; - DCHECK(result != NULL); - return *result; -} - -const RegType& RegTypeCache::RegTypeFromPrimitiveType( - Primitive::Type prim_type) const { +const RegType& RegTypeCache::RegTypeFromPrimitiveType(Primitive::Type prim_type) const { CHECK(RegTypeCache::primitive_initialized_); switch (prim_type) { case Primitive::kPrimBoolean: @@ -113,21 +107,22 @@ const RegType& RegTypeCache::RegTypeFromPrimitiveType( } } -bool RegTypeCache::MatchDescriptor(size_t idx, std::string& descriptor, bool precise) { +bool RegTypeCache::MatchDescriptor(size_t idx, const char* descriptor, bool precise) { RegType* cur_entry = entries_[idx]; if (cur_entry->HasClass()) { // Check the descriptor in the reg_type if available. if(!cur_entry->descriptor_.empty()) { - if (descriptor == cur_entry->descriptor_ && MatchingPrecisionForClass(cur_entry, precise)) { + if (cur_entry->descriptor_ == descriptor && MatchingPrecisionForClass(cur_entry, precise)) { return true; } } else { - // Descriptor not found in reg_type , maybe available in Class object. + // TODO: maintain an invariant that when we have a Class the descriptor is computed from that. + // Descriptor not found in reg_type, maybe available in Class object. // So we might have cases where we have the class but not the descriptor // for that class we need the class helper to get the descriptor // and match it with the one we are given. ClassHelper kh(cur_entry->GetClass()); - if ((strcmp(descriptor.c_str(), kh.GetDescriptor()) == 0) && + if ((strcmp(descriptor, kh.GetDescriptor()) == 0) && MatchingPrecisionForClass(cur_entry, precise)) { return true; } @@ -138,16 +133,15 @@ bool RegTypeCache::MatchDescriptor(size_t idx, std::string& descriptor, bool pre return false; } - -mirror::Class* RegTypeCache::ResolveClass(std::string descriptor, mirror::ClassLoader* loader) { +mirror::Class* RegTypeCache::ResolveClass(const char* descriptor, mirror::ClassLoader* loader) { // Class was not found, must create new type. // Try resolving class ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::Class* klass = NULL; if (can_load_classes_) { - klass = class_linker->FindClass(descriptor.c_str(), loader); + klass = class_linker->FindClass(descriptor, loader); } else { - klass = class_linker->LookupClass(descriptor.c_str(), loader); + klass = class_linker->LookupClass(descriptor, loader); if (klass != NULL && !klass->IsLoaded()) { // We found the class but without it being loaded its not safe for use. klass = NULL; @@ -155,6 +149,7 @@ mirror::Class* RegTypeCache::ResolveClass(std::string descriptor, mirror::ClassL } return klass; } + void RegTypeCache::ClearException() { if (can_load_classes_) { DCHECK(Thread::Current()->IsExceptionPending()); @@ -163,8 +158,9 @@ void RegTypeCache::ClearException() { DCHECK(!Thread::Current()->IsExceptionPending()); } } -const RegType& RegTypeCache::From(mirror::ClassLoader* loader, std::string descriptor, bool precise) { +const RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descriptor, + bool precise) { // Try looking up the class in the cache first. for (size_t i = primitive_count_; i < entries_.size(); i++) { if (MatchDescriptor(i, descriptor, precise)) { @@ -198,7 +194,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, std::string descr // We tried loading the class and failed, this might get an exception raised // so we want to clear it before we go on. ClearException(); - if (IsValidDescriptor(descriptor.c_str())) { + if (IsValidDescriptor(descriptor)) { RegType* entry = new UnresolvedReferenceType(descriptor, entries_.size()); entries_.push_back(entry); return *entry; @@ -209,6 +205,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, std::string descr } } } + const RegType& RegTypeCache::FromClass(mirror::Class* klass, bool precise) { if (klass->IsPrimitive()) { return RegTypeFromPrimitiveType(klass->GetPrimitiveType()); @@ -223,11 +220,10 @@ const RegType& RegTypeCache::FromClass(mirror::Class* klass, bool precise) { } // No reference to the class was found, create new reference. RegType* entry; - std::string empty = ""; if (precise) { - entry = new PreciseReferenceType(klass, empty, entries_.size()); + entry = new PreciseReferenceType(klass, "", entries_.size()); } else { - entry = new ReferenceType(klass, empty, entries_.size()); + entry = new ReferenceType(klass, "", entries_.size()); } entries_.push_back(entry); return *entry; @@ -309,13 +305,14 @@ const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegT // Create entry. RegType* entry = new UnresolvedMergedType(left.GetId(), right.GetId(), this, entries_.size()); entries_.push_back(entry); -#ifndef NDEBUG - UnresolvedMergedType* tmp_entry = down_cast(entry); - std::set check_types = tmp_entry->GetMergedTypes(); - CHECK(check_types == types); -#endif + if (kIsDebugBuild) { + UnresolvedMergedType* tmp_entry = down_cast(entry); + std::set check_types = tmp_entry->GetMergedTypes(); + CHECK(check_types == types); + } return *entry; } + const RegType& RegTypeCache::FromUnresolvedSuperClass(const RegType& child) { // Check if entry already exists. for (size_t i = primitive_count_; i < entries_.size(); i++) { @@ -334,11 +331,12 @@ const RegType& RegTypeCache::FromUnresolvedSuperClass(const RegType& child) { entries_.push_back(entry); return *entry; } + const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) { RegType* entry = NULL; RegType* cur_entry = NULL; if (type.IsUnresolvedTypes()) { - std::string descriptor(type.GetDescriptor()); + const std::string& descriptor(type.GetDescriptor()); for (size_t i = primitive_count_; i < entries_.size(); i++) { cur_entry = entries_[i]; if (cur_entry->IsUnresolvedAndUninitializedReference() && @@ -347,29 +345,29 @@ const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocat return *cur_entry; } } - entry = new UnresolvedUninitializedRefType(descriptor, allocation_pc, entries_.size()); + entry = new UnresolvedUninitializedRefType(descriptor.c_str(), allocation_pc, entries_.size()); } else { mirror::Class* klass = type.GetClass(); for (size_t i = primitive_count_; i < entries_.size(); i++) { cur_entry = entries_[i]; if (cur_entry->IsUninitializedReference() && - down_cast(cur_entry) + down_cast(cur_entry) ->GetAllocationPc() == allocation_pc && cur_entry->GetClass() == klass) { return *cur_entry; } } - std::string descriptor(""); - entry = new UninitialisedReferenceType(klass, descriptor, allocation_pc, entries_.size()); + entry = new UninitializedReferenceType(klass, "", allocation_pc, entries_.size()); } entries_.push_back(entry); return *entry; } + const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { RegType* entry; if (uninit_type.IsUnresolvedTypes()) { - std::string descriptor(uninit_type.GetDescriptor()); + const std::string& descriptor(uninit_type.GetDescriptor()); for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->IsUnresolvedReference() && @@ -377,7 +375,7 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { return *cur_entry; } } - entry = new UnresolvedReferenceType(descriptor, entries_.size()); + entry = new UnresolvedReferenceType(descriptor.c_str(), entries_.size()); } else { mirror::Class* klass = uninit_type.GetClass(); if(uninit_type.IsUninitializedThisReference() && !klass->IsFinal()) { @@ -388,10 +386,8 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { return *cur_entry; } } - std::string descriptor(""); - entry = new ReferenceType(klass, descriptor, entries_.size()); + entry = new ReferenceType(klass, "", entries_.size()); } else { - std::string descriptor; if (klass->IsFinal()) { if (klass->IsInstantiable()) { for (size_t i = primitive_count_; i < entries_.size(); i++) { @@ -401,7 +397,7 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { } } // Precise type was not found , create one ! - entry = new PreciseReferenceType(klass, descriptor, entries_.size()); + entry = new PreciseReferenceType(klass, "", entries_.size()); } else { return Conflict(); } @@ -414,26 +410,30 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { return *cur_entry; } } - entry = new ReferenceType(klass, descriptor, entries_.size()); + entry = new ReferenceType(klass, "", entries_.size()); } } } entries_.push_back(entry); return *entry; } -const RegType& RegTypeCache::ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + +const RegType& RegTypeCache::ByteConstant() { return FromCat1Const(std::numeric_limits::min(), false); } -const RegType& RegTypeCache::ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + +const RegType& RegTypeCache::ShortConstant() { return FromCat1Const(std::numeric_limits::min(), false); } -const RegType& RegTypeCache::IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + +const RegType& RegTypeCache::IntConstant() { return FromCat1Const(std::numeric_limits::max(), false); } + const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { RegType* entry; if (type.IsUnresolvedTypes()) { - std::string descriptor(type.GetDescriptor()); + const std::string& descriptor(type.GetDescriptor()); for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; if (cur_entry->IsUnresolvedAndUninitializedThisReference() && @@ -441,7 +441,7 @@ const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { return *cur_entry; } } - entry = new UnresolvedUninitialisedThisRefType(descriptor, entries_.size()); + entry = new UnresolvedUninitializedThisRefType(descriptor, entries_.size()); } else { mirror::Class* klass = type.GetClass(); for (size_t i = primitive_count_; i < entries_.size(); i++) { @@ -451,12 +451,12 @@ const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { return *cur_entry; } } - std::string descriptor(""); - entry = new UninitialisedThisReferenceType(klass, descriptor, entries_.size()); + entry = new UninitializedThisReferenceType(klass, "", entries_.size()); } entries_.push_back(entry); return *entry; } + const RegType& RegTypeCache::FromCat1Const(int32_t value, bool precise) { for (size_t i = primitive_count_; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; @@ -514,8 +514,8 @@ const RegType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::ClassLoader* loader) { CHECK(array.IsArrayTypes()); if (array.IsUnresolvedTypes()) { - std::string descriptor(array.GetDescriptor()); - std::string component(descriptor.substr(1, descriptor.size() - 1)); + const std::string& descriptor(array.GetDescriptor()); + const std::string component(descriptor.substr(1, descriptor.size() - 1)); return FromDescriptor(loader, component.c_str(), false); } else { mirror::Class* klass = array.GetClass()->GetComponentType(); diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h index 602c95086b..a5304dbbb4 100644 --- a/src/verifier/reg_type_cache.h +++ b/src/verifier/reg_type_cache.h @@ -52,10 +52,10 @@ class RegTypeCache { } static void ShutDown(); const art::verifier::RegType& GetFromId(uint16_t id) const; - const RegType& From(mirror::ClassLoader* loader, std::string descriptor, bool precise) + const RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template - static Type* CreatePrimitiveTypeInstance(std::string descriptor) + static Type* CreatePrimitiveTypeInstance(const std::string& descriptor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void FillPrimitiveTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& FromClass(mirror::Class* klass, bool precise) @@ -152,10 +152,10 @@ class RegTypeCache { // Whether or not we're allowed to load classes. const bool can_load_classes_; DISALLOW_COPY_AND_ASSIGN(RegTypeCache); - mirror::Class* ResolveClass(std::string descriptor, mirror::ClassLoader* loader) + mirror::Class* ResolveClass(const char* descriptor, mirror::ClassLoader* loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ClearException(); - bool MatchDescriptor(size_t idx, std::string& descriptor, bool precise) + bool MatchDescriptor(size_t idx, const char* descriptor, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); }; -- cgit v1.2.3-59-g8ed1b From fae370a044f5817f69937cccfd2d12a16b374266 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 5 Jun 2013 08:33:27 -0700 Subject: Don't apply instance-of peephole when vDest == vSrc. Bug: 9284898. Also statistic to show check-cast ellision use. Fix bug where the check-cast ellision was using wrong dex pc. Avoid a use of DecodedInstruction. Other formatting clean-up. Change-Id: Ibf67941a24148b615896d0be6f2f29ce5034e53a --- src/compiler/dex/quick/gen_common.cc | 3 +- src/compiler/driver/compiler_driver.cc | 39 +++++++++++++++++++-- src/compiler/driver/compiler_driver.h | 2 ++ src/verifier/method_verifier.cc | 63 +++++++++++++++++----------------- src/verifier/reg_type.cc | 26 +++++++------- 5 files changed, 85 insertions(+), 48 deletions(-) (limited to 'src/compiler/driver/compiler_driver.h') diff --git a/src/compiler/dex/quick/gen_common.cc b/src/compiler/dex/quick/gen_common.cc index 69235273f8..12bdddd8d0 100644 --- a/src/compiler/dex/quick/gen_common.cc +++ b/src/compiler/dex/quick/gen_common.cc @@ -974,7 +974,8 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_ cu->GetDexFile(), cu->GetDexMethodIndex()); - if (verifier::MethodVerifier::IsSafeCast(mr, insn_idx)) { + if (cu_->compiler_driver->IsSafeCast(mr, insn_idx)) { + // Verifier type analysis proved this check cast would never cause an exception. return; } FlushAllRegs(); diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 2783e2e13e..3dddc751be 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -72,7 +72,8 @@ class AOTCompilationStats { 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), - type_based_devirtualization_(0) { + type_based_devirtualization_(0), + safe_casts_(0), not_safe_casts_(0) { for (size_t i = 0; i <= kMaxInvokeType; i++) { resolved_methods_[i] = 0; unresolved_methods_[i] = 0; @@ -91,8 +92,14 @@ class AOTCompilationStats { "static fields resolved"); DumpStat(resolved_local_static_fields_, resolved_static_fields_ + unresolved_static_fields_, "static fields local to a class"); - DumpStat(type_based_devirtualization_,virtual_made_direct_[kInterface] + virtual_made_direct_[kVirtual] - - type_based_devirtualization_, "sharpened calls based on type information"); + DumpStat(safe_casts_, not_safe_casts_, "check-casts removed based on type information"); + // Note, the code below subtracts the stat value so that when added to the stat value we have + // 100% of samples. TODO: clean this up. + DumpStat(type_based_devirtualization_, + resolved_methods_[kVirtual] + unresolved_methods_[kVirtual] + + resolved_methods_[kInterface] + unresolved_methods_[kInterface] - + type_based_devirtualization_, + "virtual/interface calls made direct based on type information"); for (size_t i = 0; i <= kMaxInvokeType; i++) { std::ostringstream oss; @@ -227,6 +234,18 @@ class AOTCompilationStats { direct_methods_to_boot_[type]++; } + // A check-cast could be eliminated due to verifier type analysis. + void SafeCast() { + STATS_LOCK(); + safe_casts_++; + } + + // A check-cast couldn't be eliminated due to verifier type analysis. + void NotASafeCast() { + STATS_LOCK(); + not_safe_casts_++; + } + private: Mutex stats_lock_; @@ -254,6 +273,9 @@ class AOTCompilationStats { size_t direct_calls_to_boot_[kMaxInvokeType + 1]; size_t direct_methods_to_boot_[kMaxInvokeType + 1]; + size_t safe_casts_; + size_t not_safe_casts_; + DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats); }; @@ -1014,6 +1036,17 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui return false; // Incomplete knowledge needs slow path. } +bool CompilerDriver::IsSafeCast(const MethodReference& mr, uint32_t dex_pc) { + bool result = verifier::MethodVerifier::IsSafeCast(mr, dex_pc); + if (result) { + stats_->SafeCast(); + } else { + stats_->NotASafeCast(); + } + return result; +} + + void CompilerDriver::AddCodePatch(const DexFile* dex_file, uint32_t referrer_method_idx, InvokeType referrer_invoke_type, diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index c1e449e6ed..1b5bd0d84c 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -165,6 +165,8 @@ class CompilerDriver { uintptr_t& direct_code, uintptr_t& direct_method, bool update_stats) LOCKS_EXCLUDED(Locks::mutator_lock_); + bool IsSafeCast(const MethodReference& mr, uint32_t dex_pc); + // Record patch information for later fix up. void AddCodePatch(const DexFile* dex_file, uint32_t referrer_method_idx, diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc index 519de80db8..9d4b902cf0 100644 --- a/src/verifier/method_verifier.cc +++ b/src/verifier/method_verifier.cc @@ -1777,31 +1777,33 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { /* Check for peep-hole pattern of: * ...; - * instance-of vX, vO, T; - * ifXXX vX, b ; + * instance-of vX, vY, T; + * ifXXX vX, label ; * ...; - * b: INST; + * label: * ...; - * and sharpen the type for either the fall-through or the branch case. + * and sharpen the type of vY to be type T. + * Note, this pattern can't be if: + * - if there are other branches to this branch, + * - when vX == vY. */ - if (!CurrentInsnFlags()->IsBranchTarget()) { - if ((Instruction::INSTANCE_OF == prev_inst->Opcode()) - && (inst->VRegA_21t() == prev_inst->VRegA_22c())) { - // Check that the we are not attempting conversion to interface types, - // which is not done because of the multiple inheritance implications. - const RegType& cast_type = - ResolveClassAndCheckAccess(prev_inst->VRegC_22c()); - - if(!cast_type.IsUnresolvedTypes() && !cast_type.GetClass()->IsInterface()) { - if (inst->Opcode() == Instruction::IF_EQZ) { - fallthrough_line.reset(new RegisterLine(code_item_->registers_size_, this)); - fallthrough_line->CopyFromLine(work_line_.get()); - fallthrough_line->SetRegisterType(prev_inst->VRegB_22c(), cast_type); - } else { - branch_line.reset(new RegisterLine(code_item_->registers_size_, this)); - branch_line->CopyFromLine(work_line_.get()); - branch_line->SetRegisterType(prev_inst->VRegB_22c(), cast_type); - } + if (!CurrentInsnFlags()->IsBranchTarget() && + (Instruction::INSTANCE_OF == prev_inst->Opcode()) && + (inst->VRegA_21t() == prev_inst->VRegA_22c()) && + (prev_inst->VRegA_22c() != prev_inst->VRegB_22c())) { + // Check that the we are not attempting conversion to interface types, + // which is not done because of the multiple inheritance implications. + const RegType& cast_type = ResolveClassAndCheckAccess(prev_inst->VRegC_22c()); + + if(!cast_type.IsUnresolvedTypes() && !cast_type.GetClass()->IsInterface()) { + if (inst->Opcode() == Instruction::IF_EQZ) { + fallthrough_line.reset(new RegisterLine(code_item_->registers_size_, this)); + fallthrough_line->CopyFromLine(work_line_.get()); + fallthrough_line->SetRegisterType(prev_inst->VRegB_22c(), cast_type); + } else { + branch_line.reset(new RegisterLine(code_item_->registers_size_, this)); + branch_line->CopyFromLine(work_line_.get()); + branch_line->SetRegisterType(prev_inst->VRegB_22c(), cast_type); } } } @@ -3304,24 +3306,22 @@ MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() { return NULL; } UniquePtr mscs; - uint32_t dex_pc = 0; const Instruction* inst = Instruction::At(code_item_->insns_); const Instruction* end = Instruction::At(code_item_->insns_ + - code_item_->insns_size_in_code_units_); + code_item_->insns_size_in_code_units_); for (; inst < end; inst = inst->Next()) { - if( Instruction::CHECK_CAST != inst->Opcode() ) + if (Instruction::CHECK_CAST != inst->Opcode()) { continue; - + } + uint32_t dex_pc = inst->GetDexPc(code_item_->insns_); RegisterLine* line = reg_table_.GetLine(dex_pc); - DecodedInstruction dec_insn(inst); - const RegType& reg_type(line->GetRegisterType(dec_insn.vA)); - const RegType& cast_type = ResolveClassAndCheckAccess(dec_insn.vB); + const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c())); + const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c()); if (cast_type.IsAssignableFrom(reg_type)) { if (mscs.get() == NULL) { mscs.reset(new MethodSafeCastSet()); } - uint32_t dex_pc = inst->GetDexPc(code_item_->insns_); mscs->insert(dex_pc); } } @@ -3511,7 +3511,8 @@ void MethodVerifier::SetDexGcMap(CompilerDriver::MethodReference ref, } -void MethodVerifier::SetSafeCastMap(CompilerDriver::MethodReference ref, const MethodSafeCastSet* cast_set) { +void MethodVerifier::SetSafeCastMap(CompilerDriver::MethodReference ref, + const MethodSafeCastSet* cast_set) { MutexLock mu(Thread::Current(), *safecast_map_lock_); SafeCastMap::iterator it = safecast_map_->find(ref); if (it != safecast_map_->end()) { diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc index e738e80514..55ebcdbff5 100644 --- a/src/verifier/reg_type.cc +++ b/src/verifier/reg_type.cc @@ -739,19 +739,19 @@ bool RegType::IsAssignableFrom(const RegType& src) const { } else if (IsJavaLangObject()) { return true; // all reference types can be assigned to Object } else if (!IsUnresolvedTypes() && GetClass()->IsInterface()) { - return true; // We allow assignment to any interface, see comment in ClassJoin - } else if (IsJavaLangObjectArray()) { - return src.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[] - } else if (!IsUnresolvedTypes() && !src.IsUnresolvedTypes() && - GetClass()->IsAssignableFrom(src.GetClass())) { - // We're assignable from the Class point-of-view - return true; - } else if (IsUnresolvedTypes()) { - // Unresolved types are only assignable for null, Object and equality. - return (src.IsZero() || src.IsJavaLangObject()); - } else { - return false; - } + return true; // We allow assignment to any interface, see comment in ClassJoin + } else if (IsJavaLangObjectArray()) { + return src.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[] + } else if (!IsUnresolvedTypes() && !src.IsUnresolvedTypes() && + GetClass()->IsAssignableFrom(src.GetClass())) { + // We're assignable from the Class point-of-view + return true; + } else if (IsUnresolvedTypes()) { + // Unresolved types are only assignable for null, Object and equality. + return (src.IsZero() || src.IsJavaLangObject()); + } else { + return false; + } } } } -- cgit v1.2.3-59-g8ed1b From c9e463c8aa083a5ed20293f42363ebff93de5f84 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 5 Jun 2013 16:52:26 -0700 Subject: Faster instance-of for final classes. If a class is final and not and array we can generate a 1/0 based on class equality. Use a method's declaring class if it matches the instance-of class (common in Java equals methods). Don't do a class pointer comparison in the case of an abstract or interface class, just go straight into the slow-path. Fix performance bug where peep-hole verifier pass didn't merge into the fall-through line if the next instruction wasn't interesting. Change-Id: Idb47ec6acebfd25a344ed74adaacba02fafc7df2 --- src/compiler/dex/quick/gen_common.cc | 148 +++++++++++++++++++++++++++------ src/compiler/dex/quick/mir_to_lir.h | 8 ++ src/compiler/driver/compiler_driver.cc | 22 ++++- src/compiler/driver/compiler_driver.h | 4 +- src/verifier/method_verifier.cc | 13 ++- 5 files changed, 164 insertions(+), 31 deletions(-) (limited to 'src/compiler/driver/compiler_driver.h') diff --git a/src/compiler/dex/quick/gen_common.cc b/src/compiler/dex/quick/gen_common.cc index 055b67c038..0091a80ab7 100644 --- a/src/compiler/dex/quick/gen_common.cc +++ b/src/compiler/dex/quick/gen_common.cc @@ -883,23 +883,81 @@ void Mir2Lir::GenThrow(RegLocation rl_src) CallRuntimeHelperRegLocation(ENTRYPOINT_OFFSET(pDeliverException), rl_src, true); } -void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, - RegLocation rl_src) -{ +// For final classes there are no sub-classes to check and so we can answer the instance-of +// question with simple comparisons. +void Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest, + RegLocation rl_src) { + RegLocation object = LoadValue(rl_src, kCoreReg); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + int result_reg = rl_result.low_reg; + if (result_reg == object.low_reg) { + result_reg = AllocTypedTemp(false, kCoreReg); + } + LoadConstant(result_reg, 0); // assume false + LIR* null_branchover = OpCmpImmBranch(kCondEq, object.low_reg, 0, NULL); + + int check_class = AllocTypedTemp(false, kCoreReg); + int object_class = AllocTypedTemp(false, kCoreReg); + + LoadCurrMethodDirect(check_class); + if (use_declaring_class) { + LoadWordDisp(check_class, mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), + check_class); + LoadWordDisp(object.low_reg, mirror::Object::ClassOffset().Int32Value(), object_class); + } else { + LoadWordDisp(check_class, mirror::AbstractMethod::DexCacheResolvedTypesOffset().Int32Value(), + check_class); + LoadWordDisp(object.low_reg, mirror::Object::ClassOffset().Int32Value(), object_class); + int32_t offset_of_type = + mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + + (sizeof(mirror::Class*) * type_idx); + LoadWordDisp(check_class, offset_of_type, check_class); + } + + LIR* ne_branchover = NULL; + if (cu_->instruction_set == kThumb2) { + OpRegReg(kOpCmp, check_class, object_class); // Same? + OpIT(kCondEq, ""); // if-convert the test + LoadConstant(result_reg, 1); // .eq case - load true + } else { + ne_branchover = OpCmpBranch(kCondNe, check_class, object_class, NULL); + LoadConstant(result_reg, 1); // eq case - load true + } + LIR* target = NewLIR0(kPseudoTargetLabel); + null_branchover->target = target; + if (ne_branchover != NULL) { + ne_branchover->target = target; + } + FreeTemp(object_class); + FreeTemp(check_class); + if (IsTemp(result_reg)) { + OpRegCopy(rl_result.low_reg, result_reg); + FreeTemp(result_reg); + } + StoreValue(rl_dest, rl_result); +} + +void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final, + bool type_known_abstract, bool use_declaring_class, + bool can_assume_type_is_in_dex_cache, + uint32_t type_idx, RegLocation rl_dest, + RegLocation rl_src) { FlushAllRegs(); // May generate a call - use explicit registers LockCallTemps(); LoadCurrMethodDirect(TargetReg(kArg1)); // kArg1 <= current Method* int class_reg = TargetReg(kArg2); // kArg2 will hold the Class* - if (!cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, - *cu_->dex_file, - type_idx)) { + if (needs_access_check) { // Check we have access to type_idx and if not throw IllegalAccessError, // returns Class* in kArg0 CallRuntimeHelperImm(ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode), type_idx, true); OpRegCopy(class_reg, TargetReg(kRet0)); // Align usage with fast path LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref + } else if (use_declaring_class) { + LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref + LoadWordDisp(TargetReg(kArg1), + mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), class_reg); } else { // Load dex cache entry into class_reg (kArg2) LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref @@ -909,8 +967,7 @@ void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*) * type_idx); LoadWordDisp(class_reg, offset_of_type, class_reg); - if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache( - *cu_->dex_file, type_idx)) { + if (!can_assume_type_is_in_dex_cache) { // Need to test presence of type in dex cache at runtime LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL); // Not resolved @@ -926,47 +983,88 @@ void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, /* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result */ RegLocation rl_result = GetReturn(false); if (cu_->instruction_set == kMips) { - LoadConstant(rl_result.low_reg, 0); // store false result for if branch is taken + // On MIPS rArg0 != rl_result, place false in result if branch is taken. + LoadConstant(rl_result.low_reg, 0); } LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0), 0, NULL); + /* load object->klass_ */ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0); LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1)); /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */ LIR* branchover = NULL; - if (cu_->instruction_set == kThumb2) { - /* Uses conditional nullification */ - int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); - OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2)); // Same? - OpIT(kCondEq, "EE"); // if-convert the test - LoadConstant(TargetReg(kArg0), 1); // .eq case - load true - OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .ne case - arg0 <= class - OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class) - FreeTemp(r_tgt); + if (type_known_final) { + // rl_result == ref == null == 0. + if (cu_->instruction_set == kThumb2) { + OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2)); // Same? + OpIT(kCondEq, "E"); // if-convert the test + LoadConstant(rl_result.low_reg, 1); // .eq case - load true + LoadConstant(rl_result.low_reg, 0); // .ne case - load false + } else { + LoadConstant(rl_result.low_reg, 0); // ne case - load false + branchover = OpCmpBranch(kCondNe, TargetReg(kArg1), TargetReg(kArg2), NULL); + LoadConstant(rl_result.low_reg, 1); // eq case - load true + } } else { - /* Uses branchovers */ - LoadConstant(rl_result.low_reg, 1); // assume true - branchover = OpCmpBranch(kCondEq, TargetReg(kArg1), TargetReg(kArg2), NULL); - if (cu_->instruction_set != kX86) { + if (cu_->instruction_set == kThumb2) { int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + if (!type_known_abstract) { + /* Uses conditional nullification */ + OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2)); // Same? + OpIT(kCondEq, "EE"); // if-convert the test + LoadConstant(TargetReg(kArg0), 1); // .eq case - load true + } OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .ne case - arg0 <= class OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class) FreeTemp(r_tgt); } else { - OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); - OpThreadMem(kOpBlx, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + if (!type_known_abstract) { + /* Uses branchovers */ + LoadConstant(rl_result.low_reg, 1); // assume true + branchover = OpCmpBranch(kCondEq, TargetReg(kArg1), TargetReg(kArg2), NULL); + } + if (cu_->instruction_set != kX86) { + int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .ne case - arg0 <= class + OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class) + FreeTemp(r_tgt); + } else { + OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); + OpThreadMem(kOpBlx, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode)); + } } } + // TODO: only clobber when type isn't final? ClobberCalleeSave(); /* branch targets here */ LIR* target = NewLIR0(kPseudoTargetLabel); StoreValue(rl_dest, rl_result); branch1->target = target; - if (cu_->instruction_set != kThumb2) { + if (branchover != NULL) { branchover->target = target; } } +void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src) { + bool type_known_final, type_known_abstract, use_declaring_class; + bool needs_access_check = !cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, + *cu_->dex_file, + type_idx, + &type_known_final, + &type_known_abstract, + &use_declaring_class); + bool can_assume_type_is_in_dex_cache = !needs_access_check && + cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx); + + if ((use_declaring_class || can_assume_type_is_in_dex_cache) && type_known_final) { + GenInstanceofFinal(use_declaring_class, type_idx, rl_dest, rl_src); + } else { + GenInstanceofCallingHelper(needs_access_check, type_known_final, type_known_abstract, + use_declaring_class, can_assume_type_is_in_dex_cache, + type_idx, rl_dest, rl_src); + } +} + void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_src) { DexCompilationUnit* cu = mir_graph_->GetCurrentDexCompilationUnit(); diff --git a/src/compiler/dex/quick/mir_to_lir.h b/src/compiler/dex/quick/mir_to_lir.h index 1df78cf0b0..9eb4524d0d 100644 --- a/src/compiler/dex/quick/mir_to_lir.h +++ b/src/compiler/dex/quick/mir_to_lir.h @@ -699,6 +699,14 @@ class Mir2Lir : public Backend { } private: + void GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest, + RegLocation rl_src); + void GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final, + bool type_known_abstract, bool use_declaring_class, + bool can_assume_type_is_in_dex_cache, + uint32_t type_idx, RegLocation rl_dest, + RegLocation rl_src); + void ClobberBody(RegisterInfo* p); void ResetDefBody(RegisterInfo* p) { p->def_start = NULL; diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 3dddc751be..1b8f87d51f 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -577,7 +577,18 @@ bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, } bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file, - uint32_t type_idx) { + uint32_t type_idx, + bool* type_known_final, bool* type_known_abstract, + bool* equals_referrers_class) { + if (type_known_final != NULL) { + *type_known_final = false; + } + if (type_known_abstract != NULL) { + *type_known_abstract = false; + } + if (equals_referrers_class != NULL) { + *equals_referrers_class = false; + } ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); // Get type from dex cache assuming it was populated by the verifier @@ -587,6 +598,9 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const Dex return false; // Unknown class needs access checks. } const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx); + if (equals_referrers_class != NULL) { + *equals_referrers_class = (method_id.class_idx_ == type_idx); + } mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); if (referrer_class == NULL) { stats_->TypeNeedsAccessCheck(); @@ -597,6 +611,12 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const Dex bool result = referrer_class->CanAccess(resolved_class); if (result) { stats_->TypeDoesntNeedAccessCheck(); + if (type_known_final != NULL) { + *type_known_final = resolved_class->IsFinal() && !resolved_class->IsArrayClass(); + } + if (type_known_abstract != NULL) { + *type_known_abstract = resolved_class->IsAbstract(); + } } else { stats_->TypeNeedsAccessCheck(); } diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index 1b5bd0d84c..9fd3c81c21 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -138,7 +138,9 @@ class CompilerDriver { // Are runtime access checks necessary in the compiled code? bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file, - uint32_t type_idx) + uint32_t type_idx, bool* type_known_final = NULL, + bool* type_known_abstract = NULL, + bool* equals_referrers_class = NULL) LOCKS_EXCLUDED(Locks::mutator_lock_); // Are runtime access and instantiable checks necessary in the code? diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc index 2798ab3fa7..5eec58b891 100644 --- a/src/verifier/method_verifier.cc +++ b/src/verifier/method_verifier.cc @@ -1253,6 +1253,11 @@ bool MethodVerifier::CodeFlowVerifyMethod() { if (dead_start >= 0) { LogVerifyInfo() << "dead code " << reinterpret_cast(dead_start) << "-" << reinterpret_cast(insn_idx - 1); } + // To dump the state of the verify after a method, do something like: + // if (PrettyMethod(dex_method_idx_, *dex_file_) == + // "boolean java.lang.String.equals(java.lang.Object)") { + // LOG(INFO) << info_messages_.str(); + // } } return true; } @@ -2528,12 +2533,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { if (!CheckNotMoveException(code_item_->insns_, next_insn_idx)) { return false; } + if (NULL != fallthrough_line.get()) { + // Make workline consistent with fallthrough computed from peephole optimization. + work_line_->CopyFromLine(fallthrough_line.get()); + } RegisterLine* next_line = reg_table_.GetLine(next_insn_idx); if (next_line != NULL) { - if (NULL != fallthrough_line.get()) { - // Make workline consistent with fallthrough computed from peephole optimization. - work_line_->CopyFromLine(fallthrough_line.get()); - } // Merge registers into what we have for the next instruction, // and set the "changed" flag if needed. if (!UpdateRegisters(next_insn_idx, work_line_.get())) { -- cgit v1.2.3-59-g8ed1b From 0aba0ba155bef7346bde19e53581200b89ae8a7a Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Mon, 3 Jun 2013 14:49:28 -0700 Subject: Created compiled stubs in image. Saves class linker from having to set code pointers when loading from an image. Added stubs for quick and portable resolution trampolines, and interpreter-to-interpreter and interpreter-to-quick entry points. Also added sizing stats output for oat writer. Change-Id: I3905fae81047742c23d1cf0ca001db798db971b1 --- build/Android.common.mk | 2 + src/class_linker.cc | 43 ++-- src/class_linker.h | 11 + src/compiler/driver/compiler_driver.cc | 61 +++++ src/compiler/driver/compiler_driver.h | 10 + src/compiler/stubs/portable/stubs.cc | 136 +++++++++++ src/compiler/stubs/quick/stubs.cc | 262 +++++++++++++++++++++ src/compiler/stubs/stubs.h | 59 +++++ src/dex2oat.cc | 5 +- src/image_test.cc | 3 +- src/image_writer.cc | 29 ++- src/image_writer.h | 12 +- src/instrumentation.cc | 4 +- src/interpreter/interpreter.cc | 8 +- src/interpreter/interpreter.h | 6 +- src/mirror/abstract_method-inl.h | 3 +- src/oat.cc | 102 +++++++- src/oat.h | 18 +- src/oat/runtime/arm/oat_support_entrypoints_arm.cc | 20 ++ src/oat/runtime/arm/runtime_support_arm.S | 42 ---- .../runtime/mips/oat_support_entrypoints_mips.cc | 20 ++ src/oat/runtime/mips/runtime_support_mips.S | 65 ----- src/oat/runtime/oat_support_entrypoints.h | 15 ++ src/oat/runtime/support_interpreter.cc | 5 +- src/oat/runtime/support_stubs.cc | 4 +- src/oat/runtime/x86/oat_support_entrypoints_x86.cc | 20 ++ src/oat/runtime/x86/runtime_support_x86.S | 50 ---- src/oat_test.cc | 16 +- src/oat_writer.cc | 192 ++++++++++++++- src/oat_writer.h | 37 ++- src/oatdump.cc | 2 +- src/runtime.cc | 4 +- src/runtime_support.h | 18 +- src/thread.cc | 2 + 34 files changed, 1032 insertions(+), 254 deletions(-) create mode 100644 src/compiler/stubs/portable/stubs.cc create mode 100644 src/compiler/stubs/quick/stubs.cc create mode 100644 src/compiler/stubs/stubs.h (limited to 'src/compiler/driver/compiler_driver.h') diff --git a/build/Android.common.mk b/build/Android.common.mk index 43f7ff5296..ba5fe9f2ed 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -172,6 +172,8 @@ LIBART_COMMON_SRC_FILES := \ src/compiled_method.cc \ src/compiler/driver/compiler_driver.cc \ src/compiler/llvm/runtime_support_llvm.cc \ + src/compiler/stubs/portable/stubs.cc \ + src/compiler/stubs/quick/stubs.cc \ src/debugger.cc \ src/dex_file.cc \ src/dex_file_verifier.cc \ diff --git a/src/class_linker.cc b/src/class_linker.cc index 9faf3de190..0fae4248e9 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -74,8 +74,9 @@ namespace art { -void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); static void ThrowNoClassDefFoundError(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2))) @@ -201,7 +202,9 @@ ClassLinker::ClassLinker(InternTable* intern_table) array_iftable_(NULL), init_done_(false), is_dirty_(false), - intern_table_(intern_table) { + intern_table_(intern_table), + portable_resolution_trampoline_(NULL), + quick_resolution_trampoline_(NULL) { CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax)); } @@ -952,6 +955,8 @@ void ClassLinker::InitFromImage() { CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U); CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U); CHECK(oat_file->GetOatHeader().GetImageFileLocation().empty()); + portable_resolution_trampoline_ = oat_file->GetOatHeader().GetPortableResolutionTrampoline(); + quick_resolution_trampoline_ = oat_file->GetOatHeader().GetQuickResolutionTrampoline(); mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); mirror::ObjectArray* dex_caches = dex_caches_object->AsObjectArray(); @@ -1038,19 +1043,12 @@ void ClassLinker::InitFromImageCallback(mirror::Object* obj, void* arg) { return; } - // Check if object is a method without its code set and point it to the resolution trampoline. + // Set entry points to interpreter for methods in interpreter only mode. if (obj->IsMethod()) { mirror::AbstractMethod* method = obj->AsMethod(); - // Install entry point from interpreter. - if (!method->IsNative() && !method->IsProxyMethod() && - (method->GetEntryPointFromCompiledCode() == NULL || - Runtime::Current()->GetInstrumentation()->InterpretOnly())) { - method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter); - } else { - method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry); - } - if (method->GetEntryPointFromCompiledCode() == NULL) { - method->SetEntryPointFromCompiledCode(GetResolutionTrampoline()); + if (Runtime::Current()->GetInstrumentation()->InterpretOnly() && !method->IsNative()) { + method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterEntry); + method->SetEntryPointFromCompiledCode(GetInterpreterEntryPoint()); } } } @@ -1612,10 +1610,11 @@ static void LinkCode(SirtRef& method, const OatFile::Oat // Install entry point from interpreter. Runtime* runtime = Runtime::Current(); - if (!method->IsNative() && !method->IsProxyMethod() && - (method->GetEntryPointFromCompiledCode() == NULL || - runtime->GetInstrumentation()->InterpretOnly())) { - method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter); + bool enter_interpreter = method->GetEntryPointFromCompiledCode() == NULL || + (runtime->GetInstrumentation()->InterpretOnly() && + !method->IsNative() && !method->IsProxyMethod()); + if (enter_interpreter) { + method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterEntry); } else { method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry); } @@ -1627,7 +1626,7 @@ static void LinkCode(SirtRef& method, const OatFile::Oat if (method->IsStatic() && !method->IsConstructor()) { // For static methods excluding the class initializer, install the trampoline. - method->SetEntryPointFromCompiledCode(GetResolutionTrampoline()); + method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker())); } if (method->IsNative()) { @@ -1635,10 +1634,8 @@ static void LinkCode(SirtRef& method, const OatFile::Oat method->UnregisterNative(Thread::Current()); } - if (method->GetEntryPointFromCompiledCode() == NULL || - (runtime->GetInstrumentation()->InterpretOnly() && !method->IsNative() && - !method->IsProxyMethod())) { - // No code? You must mean to go into the interpreter. + if (enter_interpreter) { + // Set entry point from compiled code if there's no code or in interpreter only mode. method->SetEntryPointFromCompiledCode(GetInterpreterEntryPoint()); } diff --git a/src/class_linker.h b/src/class_linker.h index 79fa8bafbb..eab1fcc814 100644 --- a/src/class_linker.h +++ b/src/class_linker.h @@ -334,6 +334,14 @@ class ClassLinker { is_dirty_ = true; } + const void* GetPortableResolutionTrampoline() const { + return portable_resolution_trampoline_; + } + + const void* GetQuickResolutionTrampoline() const { + return quick_resolution_trampoline_; + } + private: explicit ClassLinker(InternTable*); @@ -593,6 +601,9 @@ class ClassLinker { InternTable* intern_table_; + const void* portable_resolution_trampoline_; + const void* quick_resolution_trampoline_; + friend class CommonTest; friend class ImageWriter; // for GetClassRoots friend class ObjectTest; diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 1b8f87d51f..834be64d74 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -24,6 +24,7 @@ #include "base/stl_util.h" #include "base/timing_logger.h" #include "class_linker.h" +#include "compiler/stubs/stubs.h" #include "dex_compilation_unit.h" #include "dex_file-inl.h" #include "jni_internal.h" @@ -448,6 +449,66 @@ CompilerTls* CompilerDriver::GetTls() { return res; } +const std::vector* CompilerDriver::CreatePortableResolutionTrampoline() const { + switch (instruction_set_) { + case kArm: + case kThumb2: + return arm::CreatePortableResolutionTrampoline(); + case kMips: + return mips::CreatePortableResolutionTrampoline(); + case kX86: + return x86::CreatePortableResolutionTrampoline(); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + return NULL; + } +} + +const std::vector* CompilerDriver::CreateQuickResolutionTrampoline() const { + switch (instruction_set_) { + case kArm: + case kThumb2: + return arm::CreateQuickResolutionTrampoline(); + case kMips: + return mips::CreateQuickResolutionTrampoline(); + case kX86: + return x86::CreateQuickResolutionTrampoline(); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + return NULL; + } +} + +const std::vector* CompilerDriver::CreateInterpreterToInterpreterEntry() const { + switch (instruction_set_) { + case kArm: + case kThumb2: + return arm::CreateInterpreterToInterpreterEntry(); + case kMips: + return mips::CreateInterpreterToInterpreterEntry(); + case kX86: + return x86::CreateInterpreterToInterpreterEntry(); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + return NULL; + } +} + +const std::vector* CompilerDriver::CreateInterpreterToQuickEntry() const { + switch (instruction_set_) { + case kArm: + case kThumb2: + return arm::CreateInterpreterToQuickEntry(); + case kMips: + return mips::CreateInterpreterToQuickEntry(); + case kX86: + return x86::CreateInterpreterToQuickEntry(); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set_; + return NULL; + } +} + void CompilerDriver::CompileAll(jobject class_loader, const std::vector& dex_files) { DCHECK(!Runtime::Current()->IsStarted()); diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index 9fd3c81c21..4f77bdb7a7 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -98,6 +98,16 @@ class CompilerDriver { CompilerTls* GetTls(); + // Generate the trampolines that are invoked by unresolved direct methods. + const std::vector* CreatePortableResolutionTrampoline() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const std::vector* CreateQuickResolutionTrampoline() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const std::vector* CreateInterpreterToInterpreterEntry() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const std::vector* CreateInterpreterToQuickEntry() const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // A class is uniquely located by its DexFile and the class_defs_ table index into that DexFile typedef std::pair ClassReference; diff --git a/src/compiler/stubs/portable/stubs.cc b/src/compiler/stubs/portable/stubs.cc new file mode 100644 index 0000000000..db551bf368 --- /dev/null +++ b/src/compiler/stubs/portable/stubs.cc @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011 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 "compiler/stubs/stubs.h" +#include "jni_internal.h" +#include "oat/utils/arm/assembler_arm.h" +#include "oat/utils/mips/assembler_mips.h" +#include "oat/utils/x86/assembler_x86.h" +#include "oat/runtime/oat_support_entrypoints.h" +#include "stack_indirect_reference_table.h" +#include "sirt_ref.h" + +#define __ assembler-> + +namespace art { + +namespace arm { +const std::vector* CreatePortableResolutionTrampoline() { + UniquePtr assembler(static_cast(Assembler::Create(kArm))); + RegList save = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << LR); + + __ PushList(save); + __ LoadFromOffset(kLoadWord, R12, TR, ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode)); + __ mov(R3, ShifterOperand(TR)); // Pass Thread::Current() in R3 + __ mov(R2, ShifterOperand(SP)); // Pass sp for Method** callee_addr + __ IncreaseFrameSize(12); // 3 words of space for alignment + // Call to resolution trampoline (callee, receiver, callee_addr, Thread*) + __ blx(R12); + __ mov(R12, ShifterOperand(R0)); // Save code address returned into R12 + __ DecreaseFrameSize(12); + __ PopList(save); + __ cmp(R12, ShifterOperand(0)); + __ bx(R12, NE); // If R12 != 0 tail call method's code + __ bx(LR); // Return to caller to handle exception + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr > resolution_trampoline(new std::vector(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} +} // namespace arm + +namespace mips { +const std::vector* CreatePortableResolutionTrampoline() { + UniquePtr assembler(static_cast(Assembler::Create(kMips))); + // Build frame and save argument registers and RA. + __ AddConstant(SP, SP, -32); + __ StoreToOffset(kStoreWord, RA, SP, 28); + __ StoreToOffset(kStoreWord, A3, SP, 12); + __ StoreToOffset(kStoreWord, A2, SP, 8); + __ StoreToOffset(kStoreWord, A1, SP, 4); + __ StoreToOffset(kStoreWord, A0, SP, 0); + + __ LoadFromOffset(kLoadWord, T9, S1, + ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode)); + __ Move(A3, S1); // Pass Thread::Current() in A3 + __ Move(A2, SP); // Pass SP for Method** callee_addr + __ Jalr(T9); // Call to resolution trampoline (callee, receiver, callee_addr, Thread*) + + // Restore frame, argument registers, and RA. + __ LoadFromOffset(kLoadWord, A0, SP, 0); + __ LoadFromOffset(kLoadWord, A1, SP, 4); + __ LoadFromOffset(kLoadWord, A2, SP, 8); + __ LoadFromOffset(kLoadWord, A3, SP, 12); + __ LoadFromOffset(kLoadWord, RA, SP, 28); + __ AddConstant(SP, SP, 32); + + Label resolve_fail; + __ EmitBranch(V0, ZERO, &resolve_fail, true); + __ Jr(V0); // If V0 != 0 tail call method's code + __ Bind(&resolve_fail, false); + __ Jr(RA); // Return to caller to handle exception + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr > resolution_trampoline(new std::vector(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} +} // namespace mips + +namespace x86 { +const std::vector* CreatePortableResolutionTrampoline() { + UniquePtr assembler(static_cast(Assembler::Create(kX86))); + + __ pushl(EBP); + __ movl(EBP, ESP); // save ESP + __ subl(ESP, Immediate(8)); // Align stack + __ movl(EAX, Address(EBP, 8)); // Method* called + __ leal(EDX, Address(EBP, 8)); // Method** called_addr + __ fs()->pushl(Address::Absolute(Thread::SelfOffset())); // pass thread + __ pushl(EDX); // pass called_addr + __ pushl(ECX); // pass receiver + __ pushl(EAX); // pass called + // Call to resolve method. + __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pPortableResolutionTrampolineFromCode)), + X86ManagedRegister::FromCpuRegister(ECX)); + __ leave(); + + Label resolve_fail; // forward declaration + __ cmpl(EAX, Immediate(0)); + __ j(kEqual, &resolve_fail); + __ jmp(EAX); + // Tail call to intended method. + __ Bind(&resolve_fail); + __ ret(); + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr > resolution_trampoline(new std::vector(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} +} // namespace x86 + +} // namespace art diff --git a/src/compiler/stubs/quick/stubs.cc b/src/compiler/stubs/quick/stubs.cc new file mode 100644 index 0000000000..a8e691f35b --- /dev/null +++ b/src/compiler/stubs/quick/stubs.cc @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2011 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 "compiler/stubs/stubs.h" +#include "jni_internal.h" +#include "oat/utils/arm/assembler_arm.h" +#include "oat/utils/mips/assembler_mips.h" +#include "oat/utils/x86/assembler_x86.h" +#include "oat/runtime/oat_support_entrypoints.h" +#include "stack_indirect_reference_table.h" +#include "sirt_ref.h" + +#define __ assembler-> + +namespace art { + +namespace arm { +const std::vector* CreateQuickResolutionTrampoline() { + UniquePtr assembler(static_cast(Assembler::Create(kArm))); + // | Out args | + // | Method* | <- SP on entry + // | LR | return address into caller + // | ... | callee saves + // | R3 | possible argument + // | R2 | possible argument + // | R1 | possible argument + // | R0 | junk on call to QuickResolutionTrampolineFromCode, holds result Method* + // | Method* | Callee save Method* set up by QuickResoltuionTrampolineFromCode + // Save callee saves and ready frame for exception delivery + RegList save = (1 << R1) | (1 << R2) | (1 << R3) | (1 << R5) | (1 << R6) | (1 << R7) | (1 << R8) | + (1 << R10) | (1 << R11) | (1 << LR); + // TODO: enable when GetCalleeSaveMethod is available at stub generation time + // DCHECK_EQ(save, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetCoreSpillMask()); + __ PushList(save); + __ LoadFromOffset(kLoadWord, R12, TR, ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode)); + __ mov(R3, ShifterOperand(TR)); // Pass Thread::Current() in R3 + __ IncreaseFrameSize(8); // 2 words of space for alignment + __ mov(R2, ShifterOperand(SP)); // Pass SP + // Call to resolution trampoline (method_idx, receiver, sp, Thread*) + __ blx(R12); + __ mov(R12, ShifterOperand(R0)); // Save code address returned into R12 + // Restore registers which may have been modified by GC, "R0" will hold the Method* + __ DecreaseFrameSize(4); + __ PopList((1 << R0) | save); + __ bx(R12); // Leaf call to method's code + __ bkpt(0); + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr > resolution_trampoline(new std::vector(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} + +const std::vector* CreateInterpreterToInterpreterEntry() { + UniquePtr assembler(static_cast(Assembler::Create(kArm))); + + __ LoadFromOffset(kLoadWord, PC, R0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry)); + __ bkpt(0); + + size_t cs = assembler->CodeSize(); + UniquePtr > entry_stub(new std::vector(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} + +const std::vector* CreateInterpreterToQuickEntry() { + UniquePtr assembler(static_cast(Assembler::Create(kArm))); + + __ LoadFromOffset(kLoadWord, PC, R0, ENTRYPOINT_OFFSET(pInterpreterToQuickEntry)); + __ bkpt(0); + + size_t cs = assembler->CodeSize(); + UniquePtr > entry_stub(new std::vector(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} +} // namespace arm + +namespace mips { +const std::vector* CreateQuickResolutionTrampoline() { + UniquePtr assembler(static_cast(Assembler::Create(kMips))); + // | Out args | + // | Method* | <- SP on entry + // | RA | return address into caller + // | ... | callee saves + // | A3 | possible argument + // | A2 | possible argument + // | A1 | possible argument + // | A0/Method* | Callee save Method* set up by UnresolvedDirectMethodTrampolineFromCode + // Save callee saves and ready frame for exception delivery + __ AddConstant(SP, SP, -64); + __ StoreToOffset(kStoreWord, RA, SP, 60); + __ StoreToOffset(kStoreWord, FP, SP, 56); + __ StoreToOffset(kStoreWord, GP, SP, 52); + __ StoreToOffset(kStoreWord, S7, SP, 48); + __ StoreToOffset(kStoreWord, S6, SP, 44); + __ StoreToOffset(kStoreWord, S5, SP, 40); + __ StoreToOffset(kStoreWord, S4, SP, 36); + __ StoreToOffset(kStoreWord, S3, SP, 32); + __ StoreToOffset(kStoreWord, S2, SP, 28); + __ StoreToOffset(kStoreWord, A3, SP, 12); + __ StoreToOffset(kStoreWord, A2, SP, 8); + __ StoreToOffset(kStoreWord, A1, SP, 4); + + __ LoadFromOffset(kLoadWord, T9, S1, ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode)); + __ Move(A3, S1); // Pass Thread::Current() in A3 + __ Move(A2, SP); // Pass SP for Method** callee_addr + __ Jalr(T9); // Call to resolution trampoline (method_idx, receiver, sp, Thread*) + + // Restore registers which may have been modified by GC + __ LoadFromOffset(kLoadWord, A0, SP, 0); + __ LoadFromOffset(kLoadWord, A1, SP, 4); + __ LoadFromOffset(kLoadWord, A2, SP, 8); + __ LoadFromOffset(kLoadWord, A3, SP, 12); + __ LoadFromOffset(kLoadWord, S2, SP, 28); + __ LoadFromOffset(kLoadWord, S3, SP, 32); + __ LoadFromOffset(kLoadWord, S4, SP, 36); + __ LoadFromOffset(kLoadWord, S5, SP, 40); + __ LoadFromOffset(kLoadWord, S6, SP, 44); + __ LoadFromOffset(kLoadWord, S7, SP, 48); + __ LoadFromOffset(kLoadWord, GP, SP, 52); + __ LoadFromOffset(kLoadWord, FP, SP, 56); + __ LoadFromOffset(kLoadWord, RA, SP, 60); + __ AddConstant(SP, SP, 64); + + __ Move(T9, V0); // Put method's code in T9 + __ Jr(T9); // Leaf call to method's code + + __ Break(); + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr > resolution_trampoline(new std::vector(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} + +const std::vector* CreateInterpreterToInterpreterEntry() { + UniquePtr assembler(static_cast(Assembler::Create(kMips))); + + __ LoadFromOffset(kLoadWord, T9, A0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry)); + __ Jr(T9); + __ Break(); + + size_t cs = assembler->CodeSize(); + UniquePtr > entry_stub(new std::vector(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} + +const std::vector* CreateInterpreterToQuickEntry() { + UniquePtr assembler(static_cast(Assembler::Create(kMips))); + + __ LoadFromOffset(kLoadWord, T9, A0, ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry)); + __ Jr(T9); + __ Break(); + + size_t cs = assembler->CodeSize(); + UniquePtr > entry_stub(new std::vector(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} +} // namespace mips + +namespace x86 { +const std::vector* CreateQuickResolutionTrampoline() { + UniquePtr assembler(static_cast(Assembler::Create(kX86))); + // Set up the callee save frame to conform with Runtime::CreateCalleeSaveMethod(kRefsAndArgs) + // return address + __ pushl(EDI); + __ pushl(ESI); + __ pushl(EBP); + __ pushl(EBX); + __ pushl(EDX); + __ pushl(ECX); + __ pushl(EAX); // <-- callee save Method* to go here + __ movl(EDX, ESP); // save ESP + __ fs()->pushl(Address::Absolute(Thread::SelfOffset())); // pass Thread* + __ pushl(EDX); // pass ESP for Method* + __ pushl(ECX); // pass receiver + __ pushl(EAX); // pass Method* + + // Call to resolve method. + __ Call(ThreadOffset(ENTRYPOINT_OFFSET(pQuickResolutionTrampolineFromCode)), + X86ManagedRegister::FromCpuRegister(ECX)); + + __ movl(EDI, EAX); // save code pointer in EDI + __ addl(ESP, Immediate(16)); // Pop arguments + __ popl(EAX); // Restore args. + __ popl(ECX); + __ popl(EDX); + __ popl(EBX); + __ popl(EBP); // Restore callee saves. + __ popl(ESI); + // Swap EDI callee save with code pointer + __ xchgl(EDI, Address(ESP, 0)); + // Tail call to intended method. + __ ret(); + + assembler->EmitSlowPaths(); + size_t cs = assembler->CodeSize(); + UniquePtr > resolution_trampoline(new std::vector(cs)); + MemoryRegion code(&(*resolution_trampoline)[0], resolution_trampoline->size()); + assembler->FinalizeInstructions(code); + + return resolution_trampoline.release(); +} + +const std::vector* CreateInterpreterToInterpreterEntry() { + UniquePtr assembler(static_cast(Assembler::Create(kX86))); + + __ fs()->jmp(Address::Absolute(ThreadOffset(ENTRYPOINT_OFFSET(pInterpreterToInterpreterEntry)))); + + size_t cs = assembler->CodeSize(); + UniquePtr > entry_stub(new std::vector(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} + +const std::vector* CreateInterpreterToQuickEntry() { + UniquePtr assembler(static_cast(Assembler::Create(kX86))); + + __ fs()->jmp(Address::Absolute(ThreadOffset(ENTRYPOINT_OFFSET(pInterpreterToQuickEntry)))); + + size_t cs = assembler->CodeSize(); + UniquePtr > entry_stub(new std::vector(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} +} // namespace x86 + +} // namespace art diff --git a/src/compiler/stubs/stubs.h b/src/compiler/stubs/stubs.h new file mode 100644 index 0000000000..ebe761df35 --- /dev/null +++ b/src/compiler/stubs/stubs.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 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_SRC_COMPILER_STUBS_STUBS_H_ +#define ART_SRC_COMPILER_STUBS_STUBS_H_ + +#include "runtime.h" + +namespace art { + +namespace arm { +const std::vector* CreatePortableResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector* CreateQuickResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector* CreateInterpreterToInterpreterEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector* CreateInterpreterToQuickEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +} + +namespace mips { +const std::vector* CreatePortableResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector* CreateQuickResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector* CreateInterpreterToInterpreterEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector* CreateInterpreterToQuickEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +} + +namespace x86 { +const std::vector* CreatePortableResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector* CreateQuickResolutionTrampoline() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector* CreateInterpreterToInterpreterEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +const std::vector* CreateInterpreterToQuickEntry() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +} + +} // namespace art + +#endif // ART_SRC_COMPILER_STUBS_STUBS_H_ diff --git a/src/dex2oat.cc b/src/dex2oat.cc index 3f13d5939f..2f3edc443b 100644 --- a/src/dex2oat.cc +++ b/src/dex2oat.cc @@ -280,9 +280,8 @@ class Dex2Oat { std::string image_file_location; uint32_t image_file_location_oat_checksum = 0; uint32_t image_file_location_oat_data_begin = 0; - Heap* heap = Runtime::Current()->GetHeap(); - if (heap->GetSpaces().size() > 1) { - ImageSpace* image_space = heap->GetImageSpace(); + if (!driver->IsImage()) { + ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum(); image_file_location_oat_data_begin = reinterpret_cast(image_space->GetImageHeader().GetOatDataBegin()); diff --git a/src/image_test.cc b/src/image_test.cc index 8066a90957..cd1a34fa56 100644 --- a/src/image_test.cc +++ b/src/image_test.cc @@ -48,7 +48,8 @@ TEST_F(ImageTest, WriteRead) { dex_files.push_back(java_lang_dex_file_); dex_files.push_back(conscrypt_file_); VectorOutputStream output_stream(tmp_elf.GetFilename(), oat_contents); - bool success_oat = OatWriter::Create(output_stream, dex_files, 0, 0, "", *compiler_driver_.get()); + bool success_oat = OatWriter::Create(output_stream, dex_files, 0, 0, "", + *compiler_driver_.get()); ASSERT_TRUE(success_oat); // Force all system classes into memory diff --git a/src/image_writer.cc b/src/image_writer.cc index 12b756e3e0..4eea1cab29 100644 --- a/src/image_writer.cc +++ b/src/image_writer.cc @@ -81,6 +81,10 @@ bool ImageWriter::Write(const std::string& image_filename, } oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location); class_linker->RegisterOatFile(*oat_file_); + interpreter_to_interpreter_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToInterpreterEntryOffset(); + interpreter_to_quick_entry_offset_ = oat_file_->GetOatHeader().GetInterpreterToQuickEntryOffset(); + portable_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset(); + quick_resolution_trampoline_offset_ = oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset(); { Thread::Current()->TransitionFromSuspendedToRunnable(); @@ -507,17 +511,34 @@ void ImageWriter::FixupMethod(const AbstractMethod* orig, AbstractMethod* copy) if (orig->IsAbstract()) { // Code for abstract methods is set to the abstract method error stub when we load the image. copy->SetEntryPointFromCompiledCode(NULL); + copy->SetEntryPointFromInterpreter(reinterpret_cast + (GetOatAddress(interpreter_to_interpreter_entry_offset_))); return; + } else { + copy->SetEntryPointFromInterpreter(reinterpret_cast + (GetOatAddress(interpreter_to_quick_entry_offset_))); } if (orig == Runtime::Current()->GetResolutionMethod()) { - // The resolution method's code is set to the resolution trampoline when we load the image. - copy->SetEntryPointFromCompiledCode(NULL); +#if defined(ART_USE_PORTABLE_COMPILER) + copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_)); +#else + copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_)); +#endif return; } - // Non-abstract methods have code - copy->SetEntryPointFromCompiledCode(GetOatAddress(orig->GetOatCodeOffset())); + // Use original code if it exists. Otherwise, set the code pointer to the resolution trampoline. + const byte* code = GetOatAddress(orig->GetOatCodeOffset()); + if (code != NULL) { + copy->SetEntryPointFromCompiledCode(code); + } else { +#if defined(ART_USE_PORTABLE_COMPILER) + copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_)); +#else + copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_)); +#endif + } if (orig->IsNative()) { // The native method's pointer is set to a stub to lookup via dlsym when we load the image. diff --git a/src/image_writer.h b/src/image_writer.h index 0cccf69a16..1de31491dd 100644 --- a/src/image_writer.h +++ b/src/image_writer.h @@ -39,7 +39,9 @@ class ImageWriter { public: explicit ImageWriter(std::set* image_classes) : oat_file_(NULL), image_end_(0), image_begin_(NULL), image_classes_(image_classes), - oat_data_begin_(NULL) {} + oat_data_begin_(NULL), interpreter_to_interpreter_entry_offset_(0), + interpreter_to_quick_entry_offset_(0), portable_resolution_trampoline_offset_(0), + quick_resolution_trampoline_offset_(0) {} ~ImageWriter() {} @@ -196,7 +198,13 @@ class ImageWriter { // Beginning target oat address for the pointers from the output image to its oat file. const byte* oat_data_begin_; - // DexCaches seen while scanning for fixing up CodeAndDirectMethods. + // Offset from oat_data_begin_ to the stubs. + uint32_t interpreter_to_interpreter_entry_offset_; + uint32_t interpreter_to_quick_entry_offset_; + uint32_t portable_resolution_trampoline_offset_; + uint32_t quick_resolution_trampoline_offset_; + + // DexCaches seen while scanning for fixing up CodeAndDirectMethods typedef std::set Set; Set dex_caches_; }; diff --git a/src/instrumentation.cc b/src/instrumentation.cc index 39fd37766a..8af0885ef4 100644 --- a/src/instrumentation.cc +++ b/src/instrumentation.cc @@ -62,7 +62,7 @@ bool Instrumentation::InstallStubsForClass(mirror::Class* klass) { if (is_initialized || !method->IsStatic() || method->IsConstructor()) { new_code = class_linker->GetOatCodeFor(method); } else { - new_code = GetResolutionTrampoline(); + new_code = GetResolutionTrampoline(class_linker); } } else { // !uninstall if (!interpreter_stubs_installed_ || method->IsNative()) { @@ -380,7 +380,7 @@ const void* Instrumentation::GetQuickCodeFor(const mirror::AbstractMethod* metho if (LIKELY(!instrumentation_stubs_installed_)) { const void* code = method->GetEntryPointFromCompiledCode(); DCHECK(code != NULL); - if (LIKELY(code != GetResolutionTrampoline())) { + if (LIKELY(code != GetResolutionTrampoline(runtime->GetClassLinker()))) { return code; } } diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index 94237a01bc..c7b9c58c74 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -133,7 +133,7 @@ static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, } } else { // Not special, continue with regular interpreter execution. - EnterInterpreterFromInterpreter(self, mh, code_item, shadow_frame, result); + artInterpreterToInterpreterEntry(self, mh, code_item, shadow_frame, result); } } @@ -2816,9 +2816,9 @@ JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::C return Execute(self, mh, code_item, shadow_frame, JValue()); } -void EnterInterpreterFromInterpreter(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, - JValue* result) +void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { ThrowStackOverflowError(self); diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index bae3b65c89..20166ac545 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -47,9 +47,9 @@ extern JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, ShadowFrame& shadow_frame) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -extern void EnterInterpreterFromInterpreter(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result) +extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); } // namespace interpreter diff --git a/src/mirror/abstract_method-inl.h b/src/mirror/abstract_method-inl.h index d4f0f2c6bc..a8238867aa 100644 --- a/src/mirror/abstract_method-inl.h +++ b/src/mirror/abstract_method-inl.h @@ -117,7 +117,8 @@ inline void AbstractMethod::AssertPcIsWithinCode(uintptr_t pc) const { if (GetEntryPointFromCompiledCode() == GetInterpreterEntryPoint()) { return; } - if (GetEntryPointFromCompiledCode() == GetResolutionTrampoline()) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (GetEntryPointFromCompiledCode() == GetResolutionTrampoline(class_linker)) { return; } DCHECK(IsWithinCode(pc)) diff --git a/src/oat.cc b/src/oat.cc index 4eb97f5e41..e606953ed5 100644 --- a/src/oat.cc +++ b/src/oat.cc @@ -22,7 +22,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '0', '5', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '0', '6', '\0' }; OatHeader::OatHeader() { memset(this, 0, sizeof(*this)); @@ -57,6 +57,10 @@ OatHeader::OatHeader(InstructionSet instruction_set, UpdateChecksum(image_file_location.data(), image_file_location_size_); executable_offset_ = 0; + interpreter_to_interpreter_entry_offset_ = 0; + interpreter_to_quick_entry_offset_ = 0; + portable_resolution_trampoline_offset_ = 0; + quick_resolution_trampoline_offset_ = 0; } bool OatHeader::IsValid() const { @@ -97,6 +101,92 @@ uint32_t OatHeader::GetExecutableOffset() const { return executable_offset_; } +void OatHeader::SetExecutableOffset(uint32_t executable_offset) { + DCHECK_ALIGNED(executable_offset, kPageSize); + CHECK_GT(executable_offset, sizeof(OatHeader)); + DCHECK(IsValid()); + DCHECK_EQ(executable_offset_, 0U); + + executable_offset_ = executable_offset; + UpdateChecksum(&executable_offset_, sizeof(executable_offset)); +} + +const void* OatHeader::GetInterpreterToInterpreterEntry() const { + return reinterpret_cast(this) + GetInterpreterToInterpreterEntryOffset(); +} + +uint32_t OatHeader::GetInterpreterToInterpreterEntryOffset() const { + DCHECK(IsValid()); + CHECK_GE(interpreter_to_interpreter_entry_offset_, executable_offset_); + return interpreter_to_interpreter_entry_offset_; +} + +void OatHeader::SetInterpreterToInterpreterEntryOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= executable_offset_); + DCHECK(IsValid()); + DCHECK_EQ(interpreter_to_interpreter_entry_offset_, 0U) << offset; + + interpreter_to_interpreter_entry_offset_ = offset; + UpdateChecksum(&interpreter_to_interpreter_entry_offset_, sizeof(offset)); +} + +const void* OatHeader::GetInterpreterToQuickEntry() const { + return reinterpret_cast(this) + GetInterpreterToQuickEntryOffset(); +} + +uint32_t OatHeader::GetInterpreterToQuickEntryOffset() const { + DCHECK(IsValid()); + CHECK_GE(interpreter_to_quick_entry_offset_, interpreter_to_interpreter_entry_offset_); + return interpreter_to_quick_entry_offset_; +} + +void OatHeader::SetInterpreterToQuickEntryOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= interpreter_to_interpreter_entry_offset_); + DCHECK(IsValid()); + DCHECK_EQ(interpreter_to_quick_entry_offset_, 0U) << offset; + + interpreter_to_quick_entry_offset_ = offset; + UpdateChecksum(&interpreter_to_quick_entry_offset_, sizeof(offset)); +} + +const void* OatHeader::GetPortableResolutionTrampoline() const { + return reinterpret_cast(this) + GetPortableResolutionTrampolineOffset(); +} + +uint32_t OatHeader::GetPortableResolutionTrampolineOffset() const { + DCHECK(IsValid()); + CHECK_GE(portable_resolution_trampoline_offset_, interpreter_to_quick_entry_offset_); + return portable_resolution_trampoline_offset_; +} + +void OatHeader::SetPortableResolutionTrampolineOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= interpreter_to_quick_entry_offset_); + DCHECK(IsValid()); + DCHECK_EQ(portable_resolution_trampoline_offset_, 0U) << offset; + + portable_resolution_trampoline_offset_ = offset; + UpdateChecksum(&portable_resolution_trampoline_offset_, sizeof(offset)); +} + +const void* OatHeader::GetQuickResolutionTrampoline() const { + return reinterpret_cast(this) + GetQuickResolutionTrampolineOffset(); +} + +uint32_t OatHeader::GetQuickResolutionTrampolineOffset() const { + DCHECK(IsValid()); + CHECK_GE(quick_resolution_trampoline_offset_, portable_resolution_trampoline_offset_); + return quick_resolution_trampoline_offset_; +} + +void OatHeader::SetQuickResolutionTrampolineOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= portable_resolution_trampoline_offset_); + DCHECK(IsValid()); + DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset; + + quick_resolution_trampoline_offset_ = offset; + UpdateChecksum(&quick_resolution_trampoline_offset_, sizeof(offset)); +} + uint32_t OatHeader::GetImageFileLocationOatChecksum() const { CHECK(IsValid()); return image_file_location_oat_checksum_; @@ -123,16 +213,6 @@ std::string OatHeader::GetImageFileLocation() const { GetImageFileLocationSize()); } -void OatHeader::SetExecutableOffset(uint32_t executable_offset) { - DCHECK_ALIGNED(executable_offset, kPageSize); - CHECK_GT(executable_offset, sizeof(OatHeader)); - DCHECK(IsValid()); - DCHECK_EQ(executable_offset_, 0U); - - executable_offset_ = executable_offset; - UpdateChecksum(&executable_offset_, sizeof(executable_offset)); -} - OatMethodOffsets::OatMethodOffsets() : code_offset_(0), frame_size_in_bytes_(0), diff --git a/src/oat.h b/src/oat.h index cf988918f0..c67a1a6630 100644 --- a/src/oat.h +++ b/src/oat.h @@ -43,8 +43,20 @@ class PACKED(4) OatHeader { return dex_file_count_; } uint32_t GetExecutableOffset() const; - InstructionSet GetInstructionSet() const; void SetExecutableOffset(uint32_t executable_offset); + const void* GetInterpreterToInterpreterEntry() const; + uint32_t GetInterpreterToInterpreterEntryOffset() const; + void SetInterpreterToInterpreterEntryOffset(uint32_t offset); + const void* GetInterpreterToQuickEntry() const; + uint32_t GetInterpreterToQuickEntryOffset() const; + void SetInterpreterToQuickEntryOffset(uint32_t offset); + const void* GetPortableResolutionTrampoline() const; + uint32_t GetPortableResolutionTrampolineOffset() const; + void SetPortableResolutionTrampolineOffset(uint32_t offset); + const void* GetQuickResolutionTrampoline() const; + uint32_t GetQuickResolutionTrampolineOffset() const; + void SetQuickResolutionTrampolineOffset(uint32_t offset); + InstructionSet GetInstructionSet() const; uint32_t GetImageFileLocationOatChecksum() const; uint32_t GetImageFileLocationOatDataBegin() const; uint32_t GetImageFileLocationSize() const; @@ -62,6 +74,10 @@ class PACKED(4) OatHeader { InstructionSet instruction_set_; uint32_t dex_file_count_; uint32_t executable_offset_; + uint32_t interpreter_to_interpreter_entry_offset_; + uint32_t interpreter_to_quick_entry_offset_; + uint32_t portable_resolution_trampoline_offset_; + uint32_t quick_resolution_trampoline_offset_; uint32_t image_file_location_oat_checksum_; uint32_t image_file_location_oat_data_begin_; diff --git a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc index 1a5fe47e58..2e9453ce9c 100644 --- a/src/oat/runtime/arm/oat_support_entrypoints_arm.cc +++ b/src/oat/runtime/arm/oat_support_entrypoints_arm.cc @@ -91,12 +91,26 @@ extern "C" uint64_t art_quick_shl_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t); +// Interpreter entrypoints. +extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + // Intrinsic entrypoints. extern "C" int32_t __memcmp16(void*, void*, int32_t); extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); extern "C" int32_t art_quick_string_compareto(void*, void*); // Invoke entrypoints. +extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); +extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); @@ -187,6 +201,10 @@ void InitEntryPoints(EntryPoints* points) { points->pShrLong = art_quick_shr_long; points->pUshrLong = art_quick_ushr_long; + // Interpreter + points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry; + points->pInterpreterToQuickEntry = artInterpreterToQuickEntry; + // Intrinsics points->pIndexOf = art_quick_indexof; points->pMemcmp16 = __memcmp16; @@ -194,6 +212,8 @@ void InitEntryPoints(EntryPoints* points) { points->pMemcpy = memcpy; // Invocation + points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline; + points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline; points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline; points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; diff --git a/src/oat/runtime/arm/runtime_support_arm.S b/src/oat/runtime/arm/runtime_support_arm.S index b8d2265226..f19e8bada0 100644 --- a/src/oat/runtime/arm/runtime_support_arm.S +++ b/src/oat/runtime/arm/runtime_support_arm.S @@ -246,48 +246,6 @@ INVOKE_TRAMPOLINE art_quick_invoke_direct_trampoline_with_access_check, artInvok INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck - /* - * Portable resolution trampoline. - */ - .extern artPortableResolutionTrampoline -ENTRY art_portable_resolution_trampoline - push {r0, r1, r2, r3, lr} @ spill regs - .save {r0, r1, r2, r3, lr} - .pad #20 - .cfi_adjust_cfa_offset 20 - sub sp, #12 @ pad stack pointer to align frame - .pad #12 - .cfi_adjust_cfa_offset 12 - mov r3, r9 @ pass Thread::Current - mov r2, sp @ pass stack pointer - blx artPortableResolutionTrampoline @ (method, receiver, sp, Thread*) - mov r12, r0 @ save method code pointer result - add sp, #12 @ remove padding from stack pointer - .cfi_adjust_cfa_offset -12 - pop {r0, r1, r2, r3, lr} @ restore regs - .cfi_adjust_cfa_offset -20 - cmp r12, #0 @ is method code null? - bxne r12 @ if non-null, tail call to method's code - bx lr @ otherwise, return to caller to handle exception -END art_portable_resolution_trampoline - - /* - * Quick resolution trampoline. - */ - .extern artQuickResolutionTrampoline -ENTRY art_quick_resolution_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME @ save callee saves in case allocation triggers GC - mov r3, r9 @ pass Thread::Current - mov r2, sp @ pass stack pointer - blx artQuickResolutionTrampoline @ (called method, receiver, sp, Thread*) - mov r12, r0 @ save method code pointer result - add sp, #4 @ set up stack pointer - .cfi_adjust_cfa_offset -4 - pop {r0-r3, r5-r8, r10-r11, lr} @ 11 words, r0 will hold method* - .cfi_adjust_cfa_offset -44 - bx r12 @ leaf call to method code -END art_quick_resolution_trampoline - /* * Portable invocation stub. * On entry: diff --git a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc index eb82c42894..8e066118cd 100644 --- a/src/oat/runtime/mips/oat_support_entrypoints_mips.cc +++ b/src/oat/runtime/mips/oat_support_entrypoints_mips.cc @@ -93,12 +93,26 @@ extern "C" uint64_t art_quick_shl_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t); +// Interpreter entrypoints. +extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + // Intrinsic entrypoints. extern "C" int32_t __memcmp16(void*, void*, int32_t); extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); extern "C" int32_t art_quick_string_compareto(void*, void*); // Invoke entrypoints. +extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); +extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); @@ -188,6 +202,10 @@ void InitEntryPoints(EntryPoints* points) { points->pShrLong = art_quick_shr_long; points->pUshrLong = art_quick_ushr_long; + // Interpreter + points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry; + points->pInterpreterToQuickEntry = artInterpreterToQuickEntry; + // Intrinsics points->pIndexOf = art_quick_indexof; points->pMemcmp16 = __memcmp16; @@ -195,6 +213,8 @@ void InitEntryPoints(EntryPoints* points) { points->pMemcpy = memcpy; // Invocation + points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline; + points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline; points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline; points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; diff --git a/src/oat/runtime/mips/runtime_support_mips.S b/src/oat/runtime/mips/runtime_support_mips.S index 2eeb662e35..45d583e097 100644 --- a/src/oat/runtime/mips/runtime_support_mips.S +++ b/src/oat/runtime/mips/runtime_support_mips.S @@ -412,71 +412,6 @@ INVOKE_TRAMPOLINE art_quick_invoke_direct_trampoline_with_access_check, artInvok INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck - /* - * Portable resolution trampoline. - */ - .extern artPortableResolutionTrampoline -ENTRY art_portable_resolution_trampoline - GENERATE_GLOBAL_POINTER - addiu $sp, $sp, -32 # leave room for $a0, $a1, $a2, $a3, and $ra - .cfi_adjust_cfa_offset 32 - sw $ra, 16($sp) - .cfi_rel_offset 31, 16 - sw $a3, 12($sp) - .cfi_rel_offset 7, 12 - sw $a2, 8($sp) - .cfi_rel_offset 6, 8 - sw $a1, 4($sp) - .cfi_rel_offset 5, 4 - sw $a0, 0($sp) - .cfi_rel_offset 4, 0 - move $a3, $s1 # pass Thread::Current() - jal artPortableResolutionTrampoline # (method, receiver, sp, Thread*) - move $a2, $sp # pass stack pointer - lw $a0, 0($sp) # restore registers from stack - lw $a1, 4($sp) - lw $a2, 8($sp) - lw $a3, 12($sp) - lw $ra, 16($sp) - beq $v0, $zero, resolve_fail - addiu $sp, $sp, 32 # restore the stack - .cfi_adjust_cfa_offset -32 - jr $t9 # leaf call to method's code - move $t9, $v0 # put method code result in $t9 -resolve_fail: - jr $ra - nop -END art_portable_resolution_trampoline - - /* - * Quick resolution trampoline. - */ - .extern artQuickResolutionTrampoline -ENTRY art_quick_resolution_trampoline - GENERATE_GLOBAL_POINTER - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - move $a3, $s1 # pass Thread::Current() - jal artQuickResolutionTrampoline # (method, receiver, sp, Thread*) - move $a2, $sp # pass stack pointer - move $t9, $v0 # put method code result in $t9 - lw $a0, 0($sp) # restore registers from stack - lw $a1, 4($sp) - lw $a2, 8($sp) - lw $a3, 12($sp) - lw $s2, 28($sp) - lw $s3, 32($sp) - lw $s4, 36($sp) - lw $s5, 40($sp) - lw $s6, 44($sp) - lw $s7, 48($sp) - lw $gp, 52($sp) - lw $fp, 56($sp) - lw $ra, 60($sp) - jr $t9 # leaf call to method's code - addiu $sp, $sp, 64 # restore the stack - .cfi_adjust_cfa_offset -64 -END art_quick_resolution_trampoline - /* * Common invocation stub for portable and quick. * On entry: diff --git a/src/oat/runtime/oat_support_entrypoints.h b/src/oat/runtime/oat_support_entrypoints.h index 75c93783b5..c1a2587c45 100644 --- a/src/oat/runtime/oat_support_entrypoints.h +++ b/src/oat/runtime/oat_support_entrypoints.h @@ -17,6 +17,7 @@ #ifndef ART_SRC_OAT_RUNTIME_OAT_SUPPORT_ENTRYPOINTS_H_ #define ART_SRC_OAT_RUNTIME_OAT_SUPPORT_ENTRYPOINTS_H_ +#include "dex_file-inl.h" #include "runtime.h" #define ENTRYPOINT_OFFSET(x) \ @@ -30,6 +31,8 @@ class Class; class Object; } // namespace mirror class DvmDex; +class MethodHelper; +class ShadowFrame; class Thread; struct PACKED(4) EntryPoints { @@ -104,6 +107,14 @@ struct PACKED(4) EntryPoints { uint64_t (*pShrLong)(uint64_t, uint32_t); uint64_t (*pUshrLong)(uint64_t, uint32_t); + // Interpreter + void (*pInterpreterToInterpreterEntry)(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + void (*pInterpreterToQuickEntry)(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + // Intrinsics int32_t (*pIndexOf)(void*, uint32_t, uint32_t, uint32_t); int32_t (*pMemcmp16)(void*, void*, int32_t); @@ -111,6 +122,10 @@ struct PACKED(4) EntryPoints { void* (*pMemcpy)(void*, const void*, size_t); // Invocation + const void* (*pPortableResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*, + mirror::AbstractMethod**, Thread*); + const void* (*pQuickResolutionTrampolineFromCode)(mirror::AbstractMethod*, mirror::Object*, + mirror::AbstractMethod**, Thread*); void (*pInvokeDirectTrampolineWithAccessCheck)(uint32_t, void*); void (*pInvokeInterfaceTrampoline)(uint32_t, void*); void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*); diff --git a/src/oat/runtime/support_interpreter.cc b/src/oat/runtime/support_interpreter.cc index 7060a41a6c..55be54f2c2 100644 --- a/src/oat/runtime/support_interpreter.cc +++ b/src/oat/runtime/support_interpreter.cc @@ -110,8 +110,9 @@ extern "C" uint64_t artInterpreterEntry(mirror::AbstractMethod* method, Thread* return result.GetJ(); } -void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result) +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::AbstractMethod* method = shadow_frame->GetMethod(); uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_; diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc index 97d5f66079..71b67d06bb 100644 --- a/src/oat/runtime/support_stubs.cc +++ b/src/oat/runtime/support_stubs.cc @@ -144,7 +144,7 @@ extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* c // Expect class to at least be initializing. DCHECK(called->GetDeclaringClass()->IsInitializing()); // Don't want infinite recursion. - DCHECK(code != GetResolutionTrampoline()); + DCHECK(code != GetResolutionTrampoline(linker)); // Set up entry into main method *called_addr = called; } @@ -393,7 +393,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* call // Expect class to at least be initializing. DCHECK(called->GetDeclaringClass()->IsInitializing()); // Don't want infinite recursion. - DCHECK(code != GetResolutionTrampoline()); + DCHECK(code != GetResolutionTrampoline(linker)); // Set up entry into main method regs[0] = reinterpret_cast(called); } diff --git a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc index 357bbe0819..a90a583e9f 100644 --- a/src/oat/runtime/x86/oat_support_entrypoints_x86.cc +++ b/src/oat/runtime/x86/oat_support_entrypoints_x86.cc @@ -75,6 +75,14 @@ extern "C" uint64_t art_quick_lshl_from_code(uint64_t, uint32_t); extern "C" uint64_t art_quick_lshr_from_code(uint64_t, uint32_t); extern "C" uint64_t art_quick_lushr_from_code(uint64_t, uint32_t); +// Interpreter entrypoints. +extern "C" void artInterpreterToInterpreterEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); +extern "C" void artInterpreterToQuickEntry(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + // Intrinsic entrypoints. extern "C" int32_t art_quick_memcmp16(void*, void*, int32_t); extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); @@ -82,6 +90,12 @@ extern "C" int32_t art_quick_string_compareto(void*, void*); extern "C" void* art_quick_memcpy(void*, const void*, size_t); // Invoke entrypoints. +extern "C" const void* artPortableResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); +extern "C" const void* artQuickResolutionTrampoline(mirror::AbstractMethod* called, + mirror::Object* receiver, + mirror::AbstractMethod** sp, Thread* thread); extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*); extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); @@ -171,6 +185,10 @@ void InitEntryPoints(EntryPoints* points) { points->pShrLong = art_quick_lshr_from_code; points->pUshrLong = art_quick_lushr_from_code; + // Interpreter + points->pInterpreterToInterpreterEntry = artInterpreterToInterpreterEntry; + points->pInterpreterToQuickEntry = artInterpreterToQuickEntry; + // Intrinsics points->pIndexOf = art_quick_indexof; points->pMemcmp16 = art_quick_memcmp16; @@ -178,6 +196,8 @@ void InitEntryPoints(EntryPoints* points) { points->pMemcpy = art_quick_memcpy; // Invocation + points->pPortableResolutionTrampolineFromCode = artPortableResolutionTrampoline; + points->pQuickResolutionTrampolineFromCode = artQuickResolutionTrampoline; points->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; points->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline; points->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; diff --git a/src/oat/runtime/x86/runtime_support_x86.S b/src/oat/runtime/x86/runtime_support_x86.S index 734aad1e3d..ee6db0c3f8 100644 --- a/src/oat/runtime/x86/runtime_support_x86.S +++ b/src/oat/runtime/x86/runtime_support_x86.S @@ -301,56 +301,6 @@ INVOKE_TRAMPOLINE art_quick_invoke_direct_trampoline_with_access_check, artInvok INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck - /* - * Portable resolution trampoline. - */ -DEFINE_FUNCTION art_portable_resolution_trampoline - PUSH ebp // stash %ebp - movl %esp, %ebp // save %esp - .cfi_def_cfa_register ebp - subl LITERAL(8), %esp // align stack - movl 8(%ebp), %eax // load the called method* into %eax - leal 8(%ebp), %edx // put the called method* address in %edx - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - pushl %edx // pass called method* address - pushl 12(%ebp) // pass receiver - pushl 8(%ebp) // pass method* - call SYMBOL(artPortableResolutionTrampoline) // (method, receiver, sp, Thread*) - leave // restore the stack and %ebp - .cfi_def_cfa esp, 4 - .cfi_restore ebp - cmpl LITERAL(0), %eax // check if returned method code is null - je resolve_fail // if null, jump to return to handle - jmp *%eax // otherwise, tail call to intended method -resolve_fail: - ret -END_FUNCTION art_portable_resolution_trampoline - - /* - * Quick resolution trampoline. - */ -DEFINE_FUNCTION art_quick_resolution_trampoline - SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME - movl %esp, %edx // save stack pointer - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - .cfi_adjust_cfa_offset 4 - PUSH edx // pass stack pointer - PUSH ecx // pass receiver - PUSH eax // pass method* - call SYMBOL(artQuickResolutionTrampoline) // (method_idx, sp, Thread*) - movl %eax, %edi // save returned code pointer in %edi - addl LITERAL(16), %esp // pop arguments - .cfi_adjust_cfa_offset -16 - POP eax // restore registers - POP ecx - POP edx - POP ebx - POP ebp - POP esi - xchgl %edi, (%esp) // swap %edi and code pointer - ret // tail call to intended method -END_FUNCTION art_quick_resolution_trampoline - /* * Portable invocation stub. * On entry: diff --git a/src/oat_test.cc b/src/oat_test.cc index dd336d9a9b..c7c063a9d1 100644 --- a/src/oat_test.cc +++ b/src/oat_test.cc @@ -68,16 +68,16 @@ TEST_F(OatTest, WriteRead) { const bool compile = false; // DISABLED_ due to the time to compile libcore ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - jobject class_loader = NULL; - if (compile) { - // TODO: make selectable + // TODO: make selectable #if defined(ART_USE_PORTABLE_COMPILER) - CompilerBackend compiler_backend = kPortable; + CompilerBackend compiler_backend = kPortable; #else - CompilerBackend compiler_backend = kQuick; + CompilerBackend compiler_backend = kQuick; #endif - compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, 2, false, - NULL, true, true)); + compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, 2, false, + NULL, true, true)); + jobject class_loader = NULL; + if (compile) { compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath()); } @@ -143,7 +143,7 @@ TEST_F(OatTest, WriteRead) { TEST_F(OatTest, OatHeaderSizeCheck) { // If this test is failing and you have to update these constants, // it is time to update OatHeader::kOatVersion - EXPECT_EQ(36U, sizeof(OatHeader)); + EXPECT_EQ(52U, sizeof(OatHeader)); EXPECT_EQ(28U, sizeof(OatMethodOffsets)); } diff --git a/src/oat_writer.cc b/src/oat_writer.cc index 8acbfe9ca5..a4bd87d4c0 100644 --- a/src/oat_writer.cc +++ b/src/oat_writer.cc @@ -54,13 +54,35 @@ OatWriter::OatWriter(const std::vector& dex_files, uint32_t image_file_location_oat_begin, const std::string& image_file_location, const CompilerDriver* compiler) - : compiler_driver_(compiler) { - image_file_location_oat_checksum_ = image_file_location_oat_checksum; - image_file_location_oat_begin_ = image_file_location_oat_begin; - image_file_location_ = image_file_location; - dex_files_ = &dex_files; - oat_header_ = NULL; - executable_offset_padding_length_ = 0; + : compiler_driver_(compiler), + dex_files_(&dex_files), + image_file_location_oat_checksum_(image_file_location_oat_checksum), + image_file_location_oat_begin_(image_file_location_oat_begin), + image_file_location_(image_file_location), + oat_header_(NULL), + size_dex_file_alignment_(0), + size_executable_offset_alignment_(0), + size_oat_header_(0), + size_oat_header_image_file_location_(0), + size_dex_file_(0), + size_interpreter_to_interpreter_entry_(0), + size_interpreter_to_quick_entry_(0), + size_portable_resolution_trampoline_(0), + size_quick_resolution_trampoline_(0), + size_stubs_alignment_(0), + size_code_size_(0), + size_code_(0), + size_code_alignment_(0), + size_mapping_table_(0), + size_vmap_table_(0), + size_gc_map_(0), + size_oat_dex_file_location_size_(0), + size_oat_dex_file_location_data_(0), + size_oat_dex_file_location_checksum_(0), + size_oat_dex_file_offset_(0), + size_oat_dex_file_methods_offsets_(0), + size_oat_class_status_(0), + size_oat_class_method_offsets_(0) { size_t offset = InitOatHeader(); offset = InitOatDexFiles(offset); @@ -70,6 +92,7 @@ OatWriter::OatWriter(const std::vector& dex_files, offset = InitOatCodeDexFiles(offset); CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); + CHECK(image_file_location.empty() == compiler->IsImage()); } OatWriter::~OatWriter() { @@ -106,7 +129,9 @@ size_t OatWriter::InitDexFiles(size_t offset) { // calculate the offsets within OatDexFiles to the DexFiles for (size_t i = 0; i != dex_files_->size(); ++i) { // dex files are required to be 4 byte aligned + size_t original_offset = offset; offset = RoundUp(offset, 4); + size_dex_file_alignment_ += offset - original_offset; // set offset in OatDexFile to DexFile oat_dex_files_[i]->dex_file_offset_ = offset; @@ -162,7 +187,33 @@ size_t OatWriter::InitOatCode(size_t offset) { // required to be on a new page boundary offset = RoundUp(offset, kPageSize); oat_header_->SetExecutableOffset(offset); - executable_offset_padding_length_ = offset - old_offset; + size_executable_offset_alignment_ = offset - old_offset; + if (compiler_driver_->IsImage()) { + InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); + oat_header_->SetInterpreterToInterpreterEntryOffset(offset); + interpreter_to_interpreter_entry_.reset(compiler_driver_->CreateInterpreterToInterpreterEntry()); + offset += interpreter_to_interpreter_entry_->size(); + + offset = CompiledCode::AlignCode(offset, instruction_set); + oat_header_->SetInterpreterToQuickEntryOffset(offset); + interpreter_to_quick_entry_.reset(compiler_driver_->CreateInterpreterToQuickEntry()); + offset += interpreter_to_quick_entry_->size(); + + offset = CompiledCode::AlignCode(offset, instruction_set); + oat_header_->SetPortableResolutionTrampolineOffset(offset); + portable_resolution_trampoline_.reset(compiler_driver_->CreatePortableResolutionTrampoline()); + offset += portable_resolution_trampoline_->size(); + + offset = CompiledCode::AlignCode(offset, instruction_set); + oat_header_->SetQuickResolutionTrampolineOffset(offset); + quick_resolution_trampoline_.reset(compiler_driver_->CreateQuickResolutionTrampoline()); + offset += quick_resolution_trampoline_->size(); + } else { + oat_header_->SetInterpreterToInterpreterEntryOffset(0); + oat_header_->SetInterpreterToQuickEntryOffset(0); + oat_header_->SetPortableResolutionTrampolineOffset(0); + oat_header_->SetQuickResolutionTrampolineOffset(0); + } return offset; } @@ -389,11 +440,13 @@ bool OatWriter::Write(OutputStream& out) { PLOG(ERROR) << "Failed to write oat header to " << out.GetLocation(); return false; } + size_oat_header_ += sizeof(*oat_header_); if (!out.WriteFully(image_file_location_.data(), image_file_location_.size())) { PLOG(ERROR) << "Failed to write oat header image file location to " << out.GetLocation(); return false; } + size_oat_header_image_file_location_ += image_file_location_.size(); if (!WriteTables(out)) { LOG(ERROR) << "Failed to write oat tables to " << out.GetLocation(); @@ -412,12 +465,64 @@ bool OatWriter::Write(OutputStream& out) { return false; } + LOG(INFO) << "size_dex_file_alignment_=" << size_dex_file_alignment_; + LOG(INFO) << "size_executable_offset_alignment_=" << size_executable_offset_alignment_; + LOG(INFO) << "size_oat_header_=" << size_oat_header_; + LOG(INFO) << "size_oat_header_image_file_location_=" << size_oat_header_image_file_location_; + LOG(INFO) << "size_dex_file_=" << size_dex_file_; + LOG(INFO) << "size_interpreter_to_interpreter_entry_=" << size_interpreter_to_interpreter_entry_; + LOG(INFO) << "size_interpreter_to_quick_entry_=" << size_interpreter_to_quick_entry_; + LOG(INFO) << "size_portable_resolution_trampoline_=" << size_portable_resolution_trampoline_; + LOG(INFO) << "size_quick_resolution_trampoline_=" << size_quick_resolution_trampoline_; + LOG(INFO) << "size_stubs_alignment_=" << size_stubs_alignment_; + LOG(INFO) << "size_code_size_=" << size_code_size_; + LOG(INFO) << "size_code_=" << size_code_; + LOG(INFO) << "size_code_alignment_=" << size_code_alignment_; + LOG(INFO) << "size_mapping_table_=" << size_mapping_table_; + LOG(INFO) << "size_vmap_table_=" << size_vmap_table_; + LOG(INFO) << "size_gc_map_=" << size_gc_map_; + LOG(INFO) << "size_oat_dex_file_location_size_=" << size_oat_dex_file_location_size_; + LOG(INFO) << "size_oat_dex_file_location_data=" << size_oat_dex_file_location_data_; + LOG(INFO) << "size_oat_dex_file_location_checksum_=" << size_oat_dex_file_location_checksum_; + LOG(INFO) << "size_oat_dex_file_offset_=" << size_oat_dex_file_offset_; + LOG(INFO) << "size_oat_dex_file_methods_offsets_=" << size_oat_dex_file_methods_offsets_; + LOG(INFO) << "size_oat_class_status_=" << size_oat_class_status_; + LOG(INFO) << "size_oat_class_method_offsets=" << size_oat_class_method_offsets_; + + uint32_t size_total = + size_dex_file_alignment_ + + size_executable_offset_alignment_ + + size_oat_header_ + + size_oat_header_image_file_location_ + + size_dex_file_ + + size_interpreter_to_interpreter_entry_ + + size_interpreter_to_quick_entry_ + + size_portable_resolution_trampoline_ + + size_quick_resolution_trampoline_ + + size_stubs_alignment_ + + size_code_size_ + + size_code_ + + size_code_alignment_ + + size_mapping_table_ + + size_vmap_table_ + + size_gc_map_ + + size_oat_dex_file_location_size_ + + size_oat_dex_file_location_data_ + + size_oat_dex_file_location_checksum_ + + size_oat_dex_file_offset_ + + size_oat_dex_file_methods_offsets_ + + size_oat_class_status_ + + size_oat_class_method_offsets_; + + LOG(INFO) << "size_total=" << size_total; + DCHECK_EQ(size_total, static_cast(out.Seek(0, kSeekCurrent))); + return true; } bool OatWriter::WriteTables(OutputStream& out) { for (size_t i = 0; i != oat_dex_files_.size(); ++i) { - if (!oat_dex_files_[i]->Write(out)) { + if (!oat_dex_files_[i]->Write(this, out)) { PLOG(ERROR) << "Failed to write oat dex information to " << out.GetLocation(); return false; } @@ -436,9 +541,10 @@ bool OatWriter::WriteTables(OutputStream& out) { PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation() << " to " << out.GetLocation(); return false; } + size_dex_file_ += dex_file->GetHeader().file_size_; } for (size_t i = 0; i != oat_classes_.size(); ++i) { - if (!oat_classes_[i]->Write(out)) { + if (!oat_classes_[i]->Write(this, out)) { PLOG(ERROR) << "Failed to write oat methods information to " << out.GetLocation(); return false; } @@ -448,13 +554,59 @@ bool OatWriter::WriteTables(OutputStream& out) { size_t OatWriter::WriteCode(OutputStream& out) { uint32_t offset = oat_header_->GetExecutableOffset(); - off_t new_offset = out.Seek(executable_offset_padding_length_, kSeekCurrent); + off_t new_offset = out.Seek(size_executable_offset_alignment_, kSeekCurrent); if (static_cast(new_offset) != offset) { PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset << " Expected: " << offset << " File: " << out.GetLocation(); return 0; } DCHECK_OFFSET(); + if (compiler_driver_->IsImage()) { + InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); + if (!out.WriteFully(&(*interpreter_to_interpreter_entry_)[0], interpreter_to_interpreter_entry_->size())) { + PLOG(ERROR) << "Failed to write interpreter to interpreter entry to " << out.GetLocation(); + return false; + } + size_interpreter_to_interpreter_entry_ += interpreter_to_interpreter_entry_->size(); + offset += interpreter_to_interpreter_entry_->size(); + DCHECK_OFFSET(); + + uint32_t aligned_offset = CompiledCode::AlignCode(offset, instruction_set); + uint32_t alignment_padding = aligned_offset - offset; + out.Seek(alignment_padding, kSeekCurrent); + size_stubs_alignment_ += alignment_padding; + if (!out.WriteFully(&(*interpreter_to_quick_entry_)[0], interpreter_to_quick_entry_->size())) { + PLOG(ERROR) << "Failed to write interpreter to quick entry to " << out.GetLocation(); + return false; + } + size_interpreter_to_quick_entry_ += interpreter_to_quick_entry_->size(); + offset += alignment_padding + interpreter_to_quick_entry_->size(); + DCHECK_OFFSET(); + + aligned_offset = CompiledCode::AlignCode(offset, instruction_set); + alignment_padding = aligned_offset - offset; + out.Seek(alignment_padding, kSeekCurrent); + size_stubs_alignment_ += alignment_padding; + if (!out.WriteFully(&(*portable_resolution_trampoline_)[0], portable_resolution_trampoline_->size())) { + PLOG(ERROR) << "Failed to write portable resolution trampoline to " << out.GetLocation(); + return false; + } + size_portable_resolution_trampoline_ += portable_resolution_trampoline_->size(); + offset += alignment_padding + portable_resolution_trampoline_->size(); + DCHECK_OFFSET(); + + aligned_offset = CompiledCode::AlignCode(offset, instruction_set); + alignment_padding = aligned_offset - offset; + out.Seek(alignment_padding, kSeekCurrent); + size_stubs_alignment_ += alignment_padding; + if (!out.WriteFully(&(*quick_resolution_trampoline_)[0], quick_resolution_trampoline_->size())) { + PLOG(ERROR) << "Failed to write quick resolution trampoline to " << out.GetLocation(); + return false; + } + size_quick_resolution_trampoline_ += quick_resolution_trampoline_->size(); + offset += alignment_padding + quick_resolution_trampoline_->size(); + DCHECK_OFFSET(); + } return offset; } @@ -547,6 +699,7 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c uint32_t aligned_code_delta = aligned_offset - offset; if (aligned_code_delta != 0) { off_t new_offset = out.Seek(aligned_code_delta, kSeekCurrent); + size_code_alignment_ += aligned_code_delta; if (static_cast(new_offset) != aligned_offset) { PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset << " Expected: " << aligned_offset << " File: " << out.GetLocation(); @@ -572,12 +725,14 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c ReportWriteFailure("method code size", method_idx, dex_file, out); return 0; } + size_code_size_ += sizeof(code_size); offset += sizeof(code_size); DCHECK_OFFSET(); if (!out.WriteFully(&code[0], code_size)) { ReportWriteFailure("method code", method_idx, dex_file, out); return 0; } + size_code_ += code_size; offset += code_size; } DCHECK_OFFSET(); @@ -602,6 +757,7 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c ReportWriteFailure("mapping table", method_idx, dex_file, out); return 0; } + size_mapping_table_ += mapping_table_size; offset += mapping_table_size; } DCHECK_OFFSET(); @@ -625,6 +781,7 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c ReportWriteFailure("vmap table", method_idx, dex_file, out); return 0; } + size_vmap_table_ += vmap_table_size; offset += vmap_table_size; } DCHECK_OFFSET(); @@ -648,6 +805,7 @@ size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_c ReportWriteFailure("GC map", method_idx, dex_file, out); return 0; } + size_gc_map_ += gc_map_size; offset += gc_map_size; } DCHECK_OFFSET(); @@ -683,29 +841,35 @@ void OatWriter::OatDexFile::UpdateChecksum(OatHeader& oat_header) const { sizeof(methods_offsets_[0]) * methods_offsets_.size()); } -bool OatWriter::OatDexFile::Write(OutputStream& out) const { +bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream& out) const { DCHECK_OFFSET_(); if (!out.WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) { PLOG(ERROR) << "Failed to write dex file location length to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_); if (!out.WriteFully(dex_file_location_data_, dex_file_location_size_)) { PLOG(ERROR) << "Failed to write dex file location data to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_; if (!out.WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) { PLOG(ERROR) << "Failed to write dex file location checksum to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_); if (!out.WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) { PLOG(ERROR) << "Failed to write dex file offset to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_); if (!out.WriteFully(&methods_offsets_[0], sizeof(methods_offsets_[0]) * methods_offsets_.size())) { PLOG(ERROR) << "Failed to write methods offsets to " << out.GetLocation(); return false; } + oat_writer->size_oat_dex_file_methods_offsets_ += + sizeof(methods_offsets_[0]) * methods_offsets_.size(); return true; } @@ -736,12 +900,13 @@ void OatWriter::OatClass::UpdateChecksum(OatHeader& oat_header) const { sizeof(method_offsets_[0]) * method_offsets_.size()); } -bool OatWriter::OatClass::Write(OutputStream& out) const { +bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream& out) const { DCHECK_OFFSET_(); if (!out.WriteFully(&status_, sizeof(status_))) { PLOG(ERROR) << "Failed to write class status to " << out.GetLocation(); return false; } + oat_writer->size_oat_class_status_ += sizeof(status_); DCHECK_EQ(static_cast(GetOatMethodOffsetsOffsetFromOatHeader(0)), out.Seek(0, kSeekCurrent)); if (!out.WriteFully(&method_offsets_[0], @@ -749,6 +914,7 @@ bool OatWriter::OatClass::Write(OutputStream& out) const { PLOG(ERROR) << "Failed to write method offsets to " << out.GetLocation(); return false; } + oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size(); DCHECK_EQ(static_cast(GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())), out.Seek(0, kSeekCurrent)); return true; diff --git a/src/oat_writer.h b/src/oat_writer.h index e1d76f459f..b201d6b4ee 100644 --- a/src/oat_writer.h +++ b/src/oat_writer.h @@ -83,7 +83,8 @@ class OatWriter { size_t InitOatDexFiles(size_t offset); size_t InitDexFiles(size_t offset); size_t InitOatClasses(size_t offset); - size_t InitOatCode(size_t offset); + size_t InitOatCode(size_t offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t InitOatCodeDexFiles(size_t offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t InitOatCodeDexFile(size_t offset, @@ -120,7 +121,7 @@ class OatWriter { explicit OatDexFile(size_t offset, const DexFile& dex_file); size_t SizeOf() const; void UpdateChecksum(OatHeader& oat_header) const; - bool Write(OutputStream& out) const; + bool Write(OatWriter* oat_writer, OutputStream& out) const; // Offset of start of OatDexFile from beginning of OatHeader. It is // used to validate file position when writing. @@ -144,7 +145,7 @@ class OatWriter { size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const; size_t SizeOf() const; void UpdateChecksum(OatHeader& oat_header) const; - bool Write(OutputStream& out) const; + bool Write(OatWriter* oat_writer, OutputStream& out) const; // Offset of start of OatClass from beginning of OatHeader. It is // used to validate file position when writing. For Portable, it @@ -175,7 +176,35 @@ class OatWriter { OatHeader* oat_header_; std::vector oat_dex_files_; std::vector oat_classes_; - uint32_t executable_offset_padding_length_; + UniquePtr > interpreter_to_interpreter_entry_; + UniquePtr > interpreter_to_quick_entry_; + UniquePtr > portable_resolution_trampoline_; + UniquePtr > quick_resolution_trampoline_; + + // output stats + uint32_t size_dex_file_alignment_; + uint32_t size_executable_offset_alignment_; + uint32_t size_oat_header_; + uint32_t size_oat_header_image_file_location_; + uint32_t size_dex_file_; + uint32_t size_interpreter_to_interpreter_entry_; + uint32_t size_interpreter_to_quick_entry_; + uint32_t size_portable_resolution_trampoline_; + uint32_t size_quick_resolution_trampoline_; + uint32_t size_stubs_alignment_; + uint32_t size_code_size_; + uint32_t size_code_; + uint32_t size_code_alignment_; + uint32_t size_mapping_table_; + uint32_t size_vmap_table_; + uint32_t size_gc_map_; + uint32_t size_oat_dex_file_location_size_; + uint32_t size_oat_dex_file_location_data_; + uint32_t size_oat_dex_file_location_checksum_; + uint32_t size_oat_dex_file_offset_; + uint32_t size_oat_dex_file_methods_offsets_; + uint32_t size_oat_class_status_; + uint32_t size_oat_class_method_offsets_; template struct MapCompare { public: diff --git a/src/oatdump.cc b/src/oatdump.cc index 7a99f8dc0e..538e1bb99a 100644 --- a/src/oatdump.cc +++ b/src/oatdump.cc @@ -880,7 +880,7 @@ class ImageDumper { const void* GetOatCodeBegin(mirror::AbstractMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const void* code = m->GetEntryPointFromCompiledCode(); - if (code == GetResolutionTrampoline()) { + if (code == GetResolutionTrampoline(Runtime::Current()->GetClassLinker())) { code = oat_dumper_->GetOatCode(m); } if (oat_dumper_->GetInstructionSet() == kThumb2) { diff --git a/src/runtime.cc b/src/runtime.cc index 45d2988e97..c21a1c4924 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -1109,7 +1109,9 @@ mirror::AbstractMethod* Runtime::CreateResolutionMethod() { // TODO: use a special method for resolution method saves method->SetDexMethodIndex(DexFile::kDexNoIndex16); // When compiling, the code pointer will get set later when the image is loaded. - method->SetEntryPointFromCompiledCode(Runtime::Current()->IsCompiler() ? NULL : GetResolutionTrampoline()); + Runtime* r = Runtime::Current(); + ClassLinker* cl = r->GetClassLinker(); + method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetResolutionTrampoline(cl)); return method.get(); } diff --git a/src/runtime_support.h b/src/runtime_support.h index 5fc8da53b6..094e23a0fd 100644 --- a/src/runtime_support.h +++ b/src/runtime_support.h @@ -34,14 +34,12 @@ extern "C" void art_interpreter_invoke_handler(); extern "C" void art_jni_dlsym_lookup_stub(); extern "C" void art_portable_abstract_method_error_stub(); extern "C" void art_portable_proxy_invoke_handler(); -extern "C" void art_portable_resolution_trampoline(); extern "C" void art_quick_abstract_method_error_stub(); extern "C" void art_quick_deoptimize(); extern "C" void art_quick_instrumentation_entry_from_code(void*); extern "C" void art_quick_instrumentation_exit_from_code(); extern "C" void art_quick_interpreter_entry(void*); extern "C" void art_quick_proxy_invoke_handler(); -extern "C" void art_quick_resolution_trampoline(); extern "C" void art_work_around_app_jni_bugs(); extern "C" double art_l2d(int64_t l); @@ -373,22 +371,20 @@ static inline void* GetInterpreterEntryPoint() { return reinterpret_cast(art_quick_interpreter_entry); } -// Return address of portable resolution trampoline stub. -static inline void* GetPortableResolutionTrampoline() { - return reinterpret_cast(art_portable_resolution_trampoline); +static inline const void* GetPortableResolutionTrampoline(ClassLinker* class_linker) { + return class_linker->GetPortableResolutionTrampoline(); } -// Return address of quick resolution trampoline stub. -static inline void* GetQuickResolutionTrampoline() { - return reinterpret_cast(art_quick_resolution_trampoline); +static inline const void* GetQuickResolutionTrampoline(ClassLinker* class_linker) { + return class_linker->GetQuickResolutionTrampoline(); } // Return address of resolution trampoline stub for defined compiler. -static inline void* GetResolutionTrampoline() { +static inline const void* GetResolutionTrampoline(ClassLinker* class_linker) { #if defined(ART_USE_PORTABLE_COMPILER) - return GetPortableResolutionTrampoline(); + return GetPortableResolutionTrampoline(class_linker); #else - return GetQuickResolutionTrampoline(); + return GetQuickResolutionTrampoline(class_linker); #endif } diff --git a/src/thread.cc b/src/thread.cc index 91f0d7bf00..9e865329f5 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -1648,6 +1648,8 @@ static const EntryPointInfo gThreadEntryPointInfo[] = { ENTRY_POINT_INFO(pMemcmp16), ENTRY_POINT_INFO(pStringCompareTo), ENTRY_POINT_INFO(pMemcpy), + ENTRY_POINT_INFO(pPortableResolutionTrampolineFromCode), + ENTRY_POINT_INFO(pQuickResolutionTrampolineFromCode), ENTRY_POINT_INFO(pInvokeDirectTrampolineWithAccessCheck), ENTRY_POINT_INFO(pInvokeInterfaceTrampoline), ENTRY_POINT_INFO(pInvokeInterfaceTrampolineWithAccessCheck), -- cgit v1.2.3-59-g8ed1b From 96391606d8adfc661e1c21703ded1e7a39377a76 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Thu, 13 Jun 2013 19:49:50 -0700 Subject: Move image class computation to the CompilerDriver Change-Id: Ia51cdc199cdeaf409755ab8da23323e204ce041e --- src/common_test.h | 10 +- src/compiler/driver/compiler_driver.cc | 267 +++++++++++++++++++++++++++++++-- src/compiler/driver/compiler_driver.h | 37 ++++- src/dex2oat.cc | 137 ++--------------- src/image_test.cc | 39 +++-- src/image_writer.cc | 66 ++------ src/image_writer.h | 17 +-- src/oat_test.cc | 4 +- 8 files changed, 343 insertions(+), 234 deletions(-) (limited to 'src/compiler/driver/compiler_driver.h') diff --git a/src/common_test.h b/src/common_test.h index 3baa77c55c..0c171a8a86 100644 --- a/src/common_test.h +++ b/src/common_test.h @@ -346,9 +346,11 @@ class CommonTest : public testing::Test { } } class_linker_->FixupDexCaches(runtime_->GetResolutionMethod()); - image_classes_.reset(new std::set); - compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set, true, 2, false, - image_classes_.get(), true, true)); + compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set, + true, new CompilerDriver::DescriptorSet, + 2, false, true, true)); + // We typically don't generate an image in unit tests, disable this optimization by default. + compiler_driver_->SetSupportBootImageFixup(false); // Create the heap thread pool so that the GC runs in parallel for tests. Normally, the thread // pool is created by the runtime. @@ -389,7 +391,6 @@ class CommonTest : public testing::Test { (*icu_cleanup_fn)(); compiler_driver_.reset(); - image_classes_.reset(); STLDeleteElements(&opened_dex_files_); Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test @@ -522,7 +523,6 @@ class CommonTest : public testing::Test { // Owned by the runtime ClassLinker* class_linker_; UniquePtr compiler_driver_; - UniquePtr > image_classes_; private: std::vector opened_dex_files_; diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 40cc4830d4..186cf0d4d3 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -324,8 +324,8 @@ static Fn FindFunction(const std::string& compiler_so_name, void* library, const } CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, - bool image, size_t thread_count, bool support_debugging, - const std::set* image_classes, + bool image, DescriptorSet* image_classes, + size_t thread_count, bool support_debugging, bool dump_stats, bool dump_timings) : compiler_backend_(compiler_backend), instruction_set_(instruction_set), @@ -333,19 +333,20 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet compiled_classes_lock_("compiled classes lock"), compiled_methods_lock_("compiled method lock"), image_(image), + image_classes_(image_classes), thread_count_(thread_count), support_debugging_(support_debugging), start_ns_(0), stats_(new AOTCompilationStats), dump_stats_(dump_stats), dump_timings_(dump_timings), - image_classes_(image_classes), compiler_library_(NULL), compiler_(NULL), compiler_context_(NULL), jni_compiler_(NULL), compiler_enable_auto_elf_loading_(NULL), - compiler_get_method_code_addr_(NULL) + compiler_get_method_code_addr_(NULL), + support_boot_image_fixup_(true) { std::string compiler_so_name(MakeCompilerSoName(compiler_backend_)); compiler_library_ = dlopen(compiler_so_name.c_str(), RTLD_LAZY); @@ -380,7 +381,7 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet CHECK(!Runtime::Current()->IsStarted()); if (!image_) { - CHECK(image_classes_ == NULL); + CHECK(image_classes_.get() == NULL); } } @@ -576,20 +577,199 @@ void CompilerDriver::Resolve(jobject class_loader, const std::vector& dex_files, ThreadPool& thread_pool, TimingLogger& timings) { + LoadImageClasses(timings); + Resolve(class_loader, dex_files, thread_pool, timings); Verify(class_loader, dex_files, thread_pool, timings); InitializeClasses(class_loader, dex_files, thread_pool, timings); + + UpdateImageClasses(timings); } bool CompilerDriver::IsImageClass(const char* descriptor) const { - if (image_classes_ == NULL) { - return false; + DCHECK(descriptor != NULL); + if (image_classes_.get() == NULL) { + return true; } return image_classes_->find(descriptor) != image_classes_->end(); } +static void ResolveExceptionsForMethod(MethodHelper* mh, + std::set >& exceptions_to_resolve) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::CodeItem* code_item = mh->GetCodeItem(); + if (code_item == NULL) { + return; // native or abstract method + } + if (code_item->tries_size_ == 0) { + return; // nothing to process + } + const byte* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0); + size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list); + for (size_t i = 0; i < num_encoded_catch_handlers; i++) { + int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list); + bool has_catch_all = false; + if (encoded_catch_handler_size <= 0) { + encoded_catch_handler_size = -encoded_catch_handler_size; + has_catch_all = true; + } + for (int32_t j = 0; j < encoded_catch_handler_size; j++) { + uint16_t encoded_catch_handler_handlers_type_idx = + DecodeUnsignedLeb128(&encoded_catch_handler_list); + // Add to set of types to resolve if not already in the dex cache resolved types + if (!mh->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { + exceptions_to_resolve.insert( + std::pair(encoded_catch_handler_handlers_type_idx, + &mh->GetDexFile())); + } + // ignore address associated with catch handler + DecodeUnsignedLeb128(&encoded_catch_handler_list); + } + if (has_catch_all) { + // ignore catch all address + DecodeUnsignedLeb128(&encoded_catch_handler_list); + } + } +} + +static bool ResolveCatchBlockExceptionsClassVisitor(mirror::Class* c, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + std::set >* exceptions_to_resolve = + reinterpret_cast >*>(arg); + MethodHelper mh; + for (size_t i = 0; i < c->NumVirtualMethods(); ++i) { + mirror::AbstractMethod* m = c->GetVirtualMethod(i); + mh.ChangeMethod(m); + ResolveExceptionsForMethod(&mh, *exceptions_to_resolve); + } + for (size_t i = 0; i < c->NumDirectMethods(); ++i) { + mirror::AbstractMethod* m = c->GetDirectMethod(i); + mh.ChangeMethod(m); + ResolveExceptionsForMethod(&mh, *exceptions_to_resolve); + } + return true; +} + +static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CompilerDriver::DescriptorSet* image_classes = + reinterpret_cast(arg); + image_classes->insert(ClassHelper(klass).GetDescriptor()); + return true; +} + +// Make a list of descriptors for classes to include in the image +void CompilerDriver::LoadImageClasses(TimingLogger& timings) + LOCKS_EXCLUDED(Locks::mutator_lock_) { + if (image_classes_.get() == NULL) { + return; + } + + // Make a first class to load all classes explicitly listed in the file + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + typedef DescriptorSet::iterator It; // TODO: C++0x auto + for (It it = image_classes_->begin(), end = image_classes_->end(); it != end;) { + std::string descriptor(*it); + SirtRef klass(self, class_linker->FindSystemClass(descriptor.c_str())); + if (klass.get() == NULL) { + image_classes_->erase(it++); + LOG(WARNING) << "Failed to find class " << descriptor; + Thread::Current()->ClearException(); + } else { + ++it; + } + } + + // Resolve exception classes referenced by the loaded classes. The catch logic assumes + // exceptions are resolved by the verifier when there is a catch block in an interested method. + // Do this here so that exception classes appear to have been specified image classes. + std::set > unresolved_exception_types; + SirtRef java_lang_Throwable(self, + class_linker->FindSystemClass("Ljava/lang/Throwable;")); + do { + unresolved_exception_types.clear(); + class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor, + &unresolved_exception_types); + typedef std::set >::const_iterator It; // TODO: C++0x auto + for (It it = unresolved_exception_types.begin(), + end = unresolved_exception_types.end(); + it != end; ++it) { + uint16_t exception_type_idx = it->first; + const DexFile* dex_file = it->second; + mirror::DexCache* dex_cache = class_linker->FindDexCache(*dex_file); + mirror:: ClassLoader* class_loader = NULL; + SirtRef klass(self, class_linker->ResolveType(*dex_file, exception_type_idx, + dex_cache, class_loader)); + if (klass.get() == NULL) { + const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); + const char* descriptor = dex_file->GetTypeDescriptor(type_id); + LOG(FATAL) << "Failed to resolve class " << descriptor; + } + DCHECK(java_lang_Throwable->IsAssignableFrom(klass.get())); + } + // Resolving exceptions may load classes that reference more exceptions, iterate until no + // more are found + } while (!unresolved_exception_types.empty()); + + // We walk the roots looking for classes so that we'll pick up the + // above classes plus any classes them depend on such super + // classes, interfaces, and the required ClassLinker roots. + class_linker->VisitClasses(RecordImageClassesVisitor, image_classes_.get()); + + CHECK_NE(image_classes_->size(), 0U); + timings.AddSplit("LoadImageClasses"); +} + +static void MaybeAddToImageClasses(mirror::Class* klass, CompilerDriver::DescriptorSet* image_classes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + while (!klass->IsObjectClass()) { + ClassHelper kh(klass); + const char* descriptor = kh.GetDescriptor(); + std::pair result = + image_classes->insert(descriptor); + if (result.second) { + LOG(INFO) << "Adding " << descriptor << " to image classes"; + } else { + return; + } + for (size_t i = 0; i < kh.NumDirectInterfaces(); ++i) { + MaybeAddToImageClasses(kh.GetDirectInterface(i), image_classes); + } + if (klass->IsArrayClass()) { + MaybeAddToImageClasses(klass->GetComponentType(), image_classes); + } + klass = klass->GetSuperClass(); + } +} + +void CompilerDriver::FindClinitImageClassesCallback(mirror::Object* object, void* arg) { + DCHECK(object != NULL); + DCHECK(arg != NULL); + CompilerDriver* compiler_driver = reinterpret_cast(arg); + MaybeAddToImageClasses(object->GetClass(), compiler_driver->image_classes_.get()); +} + +void CompilerDriver::UpdateImageClasses(TimingLogger& timings) { + if (image_classes_.get() == NULL) { + return; + } + + // Update image_classes_ with classes for objects created by methods. + Thread* self = Thread::Current(); + const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); + Heap* heap = Runtime::Current()->GetHeap(); + // TODO: Image spaces only? + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + heap->FlushAllocStack(); + heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this); + self->EndAssertNoThreadSuspension(old_cause); + timings.AddSplit("UpdateImageClasses"); +} + void CompilerDriver::RecordClassStatus(ClassReference ref, CompiledClass* compiled_class) { MutexLock mu(Thread::Current(), CompilerDriver::compiled_classes_lock_); compiled_classes_.Put(ref, compiled_class); @@ -914,8 +1094,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType s } bool compiling_boot = Runtime::Current()->GetHeap()->GetSpaces().size() == 1; if (compiling_boot) { - const bool kSupportBootImageFixup = true; - if (kSupportBootImageFixup) { + if (support_boot_image_fixup_) { MethodHelper mh(method); if (IsImageClass(mh.GetDeclaringClassDescriptor())) { // We can only branch directly to Methods that are resolved in the DexCache. @@ -1617,10 +1796,13 @@ static const char* class_initializer_black_list[] = { "Ljava/io/ObjectStreamClass;", // Calls to Class.forName -> java.io.FileDescriptor. "Ljava/io/ObjectStreamConstants;", // Instance of non-image class SerializablePermission. "Ljava/lang/ClassLoader$SystemClassLoader;", // Calls System.getProperty -> OsConstants.initConstants. + "Ljava/lang/HexStringParser;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. + "Ljava/lang/ProcessManager;", // Calls Thread.currentThread. "Ljava/lang/Runtime;", // Calls System.getProperty -> OsConstants.initConstants. "Ljava/lang/System;", // Calls OsConstants.initConstants. "Ljava/math/BigDecimal;", // Calls native ... -> java.math.NativeBN.BN_new(). "Ljava/math/BigInteger;", // Calls native ... -> java.math.NativeBN.BN_new(). + "Ljava/math/Primality;", // Calls native ... -> java.math.NativeBN.BN_new(). "Ljava/math/Multiplication;", // Calls native ... -> java.math.NativeBN.BN_new(). "Ljava/net/InetAddress;", // Requires libcore.io.OsConstants. "Ljava/net/Inet4Address;", // Sub-class of InetAddress. @@ -1629,23 +1811,57 @@ static const char* class_initializer_black_list[] = { "Ljava/nio/charset/Charset;", // Calls Charset.getDefaultCharset -> System.getProperty -> OsConstants.initConstants. "Ljava/nio/charset/CharsetICU;", // Sub-class of Charset. "Ljava/nio/charset/Charsets;", // Calls Charset.forName. + "Ljava/security/AlgorithmParameterGenerator;", // Calls OsConstants.initConstants. + "Ljava/security/KeyPairGenerator$KeyPairGeneratorImpl;", // Calls OsConstants.initConstants. "Ljava/security/KeyPairGenerator;", // Calls OsConstants.initConstants. "Ljava/security/Security;", // Tries to do disk IO for "security.properties". + "Ljava/security/spec/RSAKeyGenParameterSpec;", // java.math.NativeBN.BN_new() "Ljava/sql/Date;", // Calls OsConstants.initConstants. + "Ljava/sql/DriverManager;", // Calls OsConstants.initConstants. + "Ljava/sql/Time;", // Calls OsConstants.initConstants. + "Ljava/sql/Timestamp;", // Calls OsConstants.initConstants. "Ljava/util/Date;", // Calls Date. -> System.currentTimeMillis -> OsConstants.initConstants. + "Ljava/util/ListResourceBundle;", // Calls OsConstants.initConstants. "Ljava/util/Locale;", // Calls System.getProperty -> OsConstants.initConstants. + "Ljava/util/PropertyResourceBundle;", // Calls OsConstants.initConstants. + "Ljava/util/ResourceBundle;", // Calls OsConstants.initConstants. + "Ljava/util/ResourceBundle$MissingBundle;", // Calls OsConstants.initConstants. + "Ljava/util/Scanner;", // regex.Pattern.compileImpl. "Ljava/util/SimpleTimeZone;", // Sub-class of TimeZone. "Ljava/util/TimeZone;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. "Ljava/util/concurrent/ConcurrentHashMap$Segment;", // Calls Runtime.getRuntime().availableProcessors(). + "Ljava/util/concurrent/ConcurrentSkipListMap;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/Exchanger;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/ForkJoinPool;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/LinkedTransferQueue;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/Phaser;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/ScheduledThreadPoolExecutor;", // Calls AtomicLong.VMSupportsCS8() + "Ljava/util/concurrent/SynchronousQueue;", // Calls OsConstants.initConstants. + "Ljava/util/concurrent/atomic/AtomicLong;", // Calls AtomicLong.VMSupportsCS8() "Ljava/util/logging/LogManager;", // Calls System.getProperty -> OsConstants.initConstants. + "Ljava/util/prefs/AbstractPreferences;", // Calls OsConstants.initConstants. + "Ljava/util/prefs/FilePreferencesImpl;", // Calls OsConstants.initConstants. + "Ljava/util/prefs/FilePreferencesFactoryImpl;", // Calls OsConstants.initConstants. + "Ljava/util/prefs/Preferences;", // Calls OsConstants.initConstants. + "Ljavax/crypto/KeyAgreement;", // Calls OsConstants.initConstants. + "Ljavax/crypto/KeyGenerator;", // Calls OsConstants.initConstants. + "Ljavax/security/cert/X509Certificate;", // Calls VMClassLoader.getBootClassPathSize. + "Ljavax/security/cert/X509Certificate$1;", // Calls VMClassLoader.getBootClassPathSize. "Ljavax/microedition/khronos/egl/EGL10;", // Requires EGLContext. "Ljavax/microedition/khronos/egl/EGLContext;", // Requires com.google.android.gles_jni.EGLImpl. "Ljavax/net/ssl/HttpsURLConnection;", // Calls SSLSocketFactory.getDefault -> java.security.Security.getProperty. + "Ljavax/xml/datatype/DatatypeConstants;", // Calls OsConstants.initConstants. + "Ljavax/xml/datatype/FactoryFinder;", // Calls OsConstants.initConstants. + "Ljavax/xml/namespace/QName;", // Calls OsConstants.initConstants. + "Ljavax/xml/validation/SchemaFactoryFinder;", // Calls OsConstants.initConstants. + "Ljavax/xml/xpath/XPathConstants;", // Calls OsConstants.initConstants. + "Ljavax/xml/xpath/XPathFactoryFinder;", // Calls OsConstants.initConstants. "Llibcore/icu/LocaleData;", // Requires java.util.Locale. "Llibcore/icu/TimeZoneNames;", // Requires java.util.TimeZone. "Llibcore/io/IoUtils;", // Calls Random. -> System.currentTimeMillis -> FileDescriptor -> OsConstants.initConstants. "Llibcore/io/OsConstants;", // Platform specific. "Llibcore/net/MimeUtils;", // Calls libcore.net.MimeUtils.getContentTypesPropertiesStream -> System.getProperty. + "Llibcore/reflect/Types;", // Calls OsConstants.initConstants. "Llibcore/util/ZoneInfo;", // Sub-class of TimeZone. "Llibcore/util/ZoneInfoDB;", // Calls System.getenv -> OsConstants.initConstants. "Lorg/apache/commons/logging/LogFactory;", // Calls System.getProperty. @@ -1653,17 +1869,40 @@ static const char* class_initializer_black_list[] = { "Lorg/apache/harmony/security/provider/cert/X509CertFactoryImpl;", // Requires java.nio.charsets.Charsets. "Lorg/apache/harmony/security/provider/crypto/RandomBitsSupplier;", // Requires java.io.File. "Lorg/apache/harmony/security/utils/AlgNameMapper;", // Requires java.util.Locale. + "Lorg/apache/harmony/security/pkcs10/CertificationRequest;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs10/CertificationRequestInfo;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs7/AuthenticatedAttributes;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs7/SignedData;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs7/SignerInfo;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/pkcs8/PrivateKeyInfo;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl;", // Calls OsConstants.initConstants. "Lorg/apache/harmony/security/x501/AttributeTypeAndValue;", // Calls IntegralToString.convertInt -> Thread.currentThread. "Lorg/apache/harmony/security/x501/DirectoryString;", // Requires BigInteger. "Lorg/apache/harmony/security/x501/Name;", // Requires org.apache.harmony.security.x501.AttributeTypeAndValue. + "Lorg/apache/harmony/security/x509/AccessDescription;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/AuthorityKeyIdentifier;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/CRLDistributionPoints;", // Calls Thread.currentThread. "Lorg/apache/harmony/security/x509/Certificate;", // Requires org.apache.harmony.security.x509.TBSCertificate. - "Lorg/apache/harmony/security/x509/TBSCertificate;", // Requires org.apache.harmony.security.x501.Name. + "Lorg/apache/harmony/security/x509/CertificateIssuer;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/CertificateList;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/DistributionPoint;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/DistributionPointName;", // Calls Thread.currentThread. "Lorg/apache/harmony/security/x509/EDIPartyName;", // Calls native ... -> java.math.NativeBN.BN_new(). "Lorg/apache/harmony/security/x509/GeneralName;", // Requires org.apache.harmony.security.x501.Name. "Lorg/apache/harmony/security/x509/GeneralNames;", // Requires GeneralName. + "Lorg/apache/harmony/security/x509/GeneralSubtree;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/GeneralSubtrees;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/InfoAccessSyntax;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/IssuingDistributionPoint;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/NameConstraints;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/TBSCertList$RevokedCertificate;", // Calls NativeBN.BN_new(). + "Lorg/apache/harmony/security/x509/TBSCertList;", // Calls Thread.currentThread. + "Lorg/apache/harmony/security/x509/TBSCertificate;", // Requires org.apache.harmony.security.x501.Name. "Lorg/apache/harmony/security/x509/Time;", // Calls native ... -> java.math.NativeBN.BN_new(). "Lorg/apache/harmony/security/x509/Validity;", // Requires x509.Time. + "Lorg/apache/harmony/security/x509/tsp/TSTInfo;", // Calls Thread.currentThread. "Lorg/apache/harmony/xml/ExpatParser;", // Calls native ExpatParser.staticInitialize. + "Lorg/apache/harmony/xml/ExpatParser$EntityParser;", // Calls ExpatParser.staticInitialize. "Lorg/apache/http/conn/params/ConnRouteParams;", // Requires java.util.Locale. "Lorg/apache/http/conn/ssl/SSLSocketFactory;", // Calls java.security.Security.getProperty. "Lorg/apache/http/conn/util/InetAddressUtils;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. @@ -1693,6 +1932,9 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl // Only try to initialize classes that were successfully verified. if (klass->IsVerified()) { manager->GetClassLinker()->EnsureInitialized(klass, false, can_init_static_fields); + if (soa.Self()->IsExceptionPending()) { + soa.Self()->GetException(NULL)->Dump(); + } if (!klass->IsInitialized()) { if (can_init_static_fields) { bool is_black_listed = false; @@ -1730,7 +1972,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl compiled_class = new CompiledClass(status); manager->GetCompiler()->RecordClassStatus(ref, compiled_class); } else { - DCHECK_EQ(status, compiled_class->GetStatus()); + DCHECK_GE(status, compiled_class->GetStatus()) << descriptor; } } // Clear any class not found or verification exceptions. @@ -1854,7 +2096,8 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t } else if ((access_flags & kAccAbstract) != 0) { } else { // In small mode we only compile image classes. - bool dont_compile = Runtime::Current()->IsSmallMode() && ((image_classes_ == NULL) || (image_classes_->size() == 0)); + bool dont_compile = (Runtime::Current()->IsSmallMode() && + ((image_classes_.get() == NULL) || (image_classes_->size() == 0))); // Don't compile class initializers, ever. if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index 4f77bdb7a7..fbfcadb8dd 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -61,14 +61,16 @@ class CompilerTls { class CompilerDriver { public: + typedef std::set DescriptorSet; + // Create a compiler targeting the requested "instruction_set". // "image" should be true if image specific optimizations should be // enabled. "image_classes" lets the compiler know what classes it // can assume will be in the image, with NULL implying all available // classes. - explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, bool image, + explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set, + bool image, DescriptorSet* image_classes, size_t thread_count, bool support_debugging, - const std::set* image_classes, bool dump_stats, bool dump_timings); ~CompilerDriver(); @@ -96,6 +98,10 @@ class CompilerDriver { return image_; } + DescriptorSet* GetImageClasses() const { + return image_classes_.get(); + } + CompilerTls* GetTls(); // Generate the trampolines that are invoked by unresolved direct methods. @@ -197,6 +203,15 @@ class CompilerDriver { void SetBitcodeFileName(std::string const& filename); + bool GetSupportBootImageFixup() const { + return support_boot_image_fixup_; + } + + void SetSupportBootImageFixup(bool support_boot_image_fixup) { + support_boot_image_fixup_ = support_boot_image_fixup; + } + + // TODO: remove these Elf wrappers when libart links against LLVM (when separate compiler library is gone) bool WriteElf(const std::string& android_root, bool is_host, @@ -298,6 +313,8 @@ class CompilerDriver { ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_); + void LoadImageClasses(TimingLogger& timings); + // Attempt to resolve all type, methods, fields, and strings // referenced from code in the dex file following PathClassLoader // ordering semantics. @@ -321,6 +338,10 @@ class CompilerDriver { ThreadPool& thread_pool, TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_, compiled_classes_lock_); + void UpdateImageClasses(TimingLogger& timings); + static void FindClinitImageClassesCallback(mirror::Object* object, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void Compile(jobject class_loader, const std::vector& dex_files, ThreadPool& thread_pool, TimingLogger& timings); void CompileDexFile(jobject class_loader, const DexFile& dex_file, @@ -355,7 +376,13 @@ class CompilerDriver { mutable Mutex compiled_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; MethodTable compiled_methods_ GUARDED_BY(compiled_methods_lock_); - bool image_; + const bool image_; + + // If image_ is true, specifies the classes that will be included in + // the image. Note if image_classes_ is NULL, all classes are + // included in the image. + UniquePtr image_classes_; + size_t thread_count_; bool support_debugging_; uint64_t start_ns_; @@ -365,8 +392,6 @@ class CompilerDriver { bool dump_stats_; bool dump_timings_; - const std::set* image_classes_; - typedef void (*CompilerCallbackFn)(CompilerDriver& driver); typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver); @@ -395,6 +420,8 @@ class CompilerDriver { (const CompilerDriver& driver, const CompiledMethod* cm, const mirror::AbstractMethod* method); CompilerGetMethodCodeAddrFn compiler_get_method_code_addr_; + bool support_boot_image_fixup_; + DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; diff --git a/src/dex2oat.cc b/src/dex2oat.cc index 235c068044..f678ee9438 100644 --- a/src/dex2oat.cc +++ b/src/dex2oat.cc @@ -161,18 +161,15 @@ class Dex2Oat { } - // Make a list of descriptors for classes to include in the image - std::set* GetImageClassDescriptors(const char* image_classes_filename) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Reads the class names (java.lang.Object) and returns as set of class descriptors (Ljava/lang/Object;) + CompilerDriver::DescriptorSet* ReadImageClasses(const char* image_classes_filename) { UniquePtr image_classes_file(new std::ifstream(image_classes_filename, std::ifstream::in)); if (image_classes_file.get() == NULL) { LOG(ERROR) << "Failed to open image classes file " << image_classes_filename; return NULL; } - // Load all the classes specified in the file - ClassLinker* class_linker = runtime_->GetClassLinker(); - Thread* self = Thread::Current(); + UniquePtr image_classes(new CompilerDriver::DescriptorSet); while (image_classes_file->good()) { std::string dot; std::getline(*image_classes_file.get(), dot); @@ -180,51 +177,9 @@ class Dex2Oat { continue; } std::string descriptor(DotToDescriptor(dot.c_str())); - SirtRef klass(self, class_linker->FindSystemClass(descriptor.c_str())); - if (klass.get() == NULL) { - LOG(WARNING) << "Failed to find class " << descriptor; - Thread::Current()->ClearException(); - } + image_classes->insert(descriptor); } image_classes_file->close(); - - // Resolve exception classes referenced by the loaded classes. The catch logic assumes - // exceptions are resolved by the verifier when there is a catch block in an interested method. - // Do this here so that exception classes appear to have been specified image classes. - std::set > unresolved_exception_types; - SirtRef java_lang_Throwable(self, - class_linker->FindSystemClass("Ljava/lang/Throwable;")); - do { - unresolved_exception_types.clear(); - class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor, - &unresolved_exception_types); - typedef std::set >::const_iterator It; // TODO: C++0x auto - for (It it = unresolved_exception_types.begin(), - end = unresolved_exception_types.end(); - it != end; ++it) { - uint16_t exception_type_idx = it->first; - const DexFile* dex_file = it->second; - mirror::DexCache* dex_cache = class_linker->FindDexCache(*dex_file); - mirror:: ClassLoader* class_loader = NULL; - SirtRef klass(self, class_linker->ResolveType(*dex_file, exception_type_idx, - dex_cache, class_loader)); - if (klass.get() == NULL) { - const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); - const char* descriptor = dex_file->GetTypeDescriptor(type_id); - LOG(FATAL) << "Failed to resolve class " << descriptor; - } - DCHECK(java_lang_Throwable->IsAssignableFrom(klass.get())); - } - // Resolving exceptions may load classes that reference more exceptions, iterate until no - // more are found - } while (!unresolved_exception_types.empty()); - - // We walk the roots looking for classes so that we'll pick up the - // above classes plus any classes them depend on such super - // classes, interfaces, and the required ClassLinker roots. - UniquePtr image_classes(new ImageWriter::DescriptorSet); - class_linker->VisitClasses(RecordImageClassesVisitor, image_classes.get()); - CHECK_NE(image_classes->size(), 0U); return image_classes.release(); } @@ -236,7 +191,7 @@ class Dex2Oat { File* oat_file, const std::string& bitcode_filename, bool image, - const ImageWriter::DescriptorSet* image_classes, + UniquePtr& image_classes, bool dump_stats, bool dump_timings) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -260,9 +215,9 @@ class Dex2Oat { UniquePtr driver(new CompilerDriver(compiler_backend_, instruction_set_, image, + image_classes.release(), thread_count_, support_debugging_, - image_classes, dump_stats, dump_timings)); @@ -320,7 +275,6 @@ class Dex2Oat { bool CreateImageFile(const std::string& image_filename, uintptr_t image_base, - ImageWriter::DescriptorSet* image_classes, const std::string& oat_filename, const std::string& oat_location, const CompilerDriver& compiler) @@ -328,8 +282,8 @@ class Dex2Oat { uintptr_t oat_data_begin; { // ImageWriter is scoped so it can free memory before doing FixupElf - ImageWriter image_writer(image_classes); - if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location, compiler)) { + ImageWriter image_writer(compiler); + if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) { LOG(ERROR) << "Failed to create image file " << image_filename; return false; } @@ -380,72 +334,6 @@ class Dex2Oat { return true; } - static void ResolveExceptionsForMethod(MethodHelper* mh, - std::set >& exceptions_to_resolve) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = mh->GetCodeItem(); - if (code_item == NULL) { - return; // native or abstract method - } - if (code_item->tries_size_ == 0) { - return; // nothing to process - } - const byte* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0); - size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list); - for (size_t i = 0; i < num_encoded_catch_handlers; i++) { - int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list); - bool has_catch_all = false; - if (encoded_catch_handler_size <= 0) { - encoded_catch_handler_size = -encoded_catch_handler_size; - has_catch_all = true; - } - for (int32_t j = 0; j < encoded_catch_handler_size; j++) { - uint16_t encoded_catch_handler_handlers_type_idx = - DecodeUnsignedLeb128(&encoded_catch_handler_list); - // Add to set of types to resolve if not already in the dex cache resolved types - if (!mh->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { - exceptions_to_resolve.insert( - std::pair(encoded_catch_handler_handlers_type_idx, - &mh->GetDexFile())); - } - // ignore address associated with catch handler - DecodeUnsignedLeb128(&encoded_catch_handler_list); - } - if (has_catch_all) { - // ignore catch all address - DecodeUnsignedLeb128(&encoded_catch_handler_list); - } - } - } - - static bool ResolveCatchBlockExceptionsClassVisitor(mirror::Class* c, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::set >* exceptions_to_resolve = - reinterpret_cast >*>(arg); - MethodHelper mh; - for (size_t i = 0; i < c->NumVirtualMethods(); ++i) { - mirror::AbstractMethod* m = c->GetVirtualMethod(i); - mh.ChangeMethod(m); - ResolveExceptionsForMethod(&mh, *exceptions_to_resolve); - } - for (size_t i = 0; i < c->NumDirectMethods(); ++i) { - mirror::AbstractMethod* m = c->GetDirectMethod(i); - mh.ChangeMethod(m); - ResolveExceptionsForMethod(&mh, *exceptions_to_resolve); - } - return true; - } - - static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ImageWriter::DescriptorSet* image_classes = reinterpret_cast(arg); - if (klass->IsArrayClass() || klass->IsPrimitive()) { - return true; - } - image_classes->insert(ClassHelper(klass).GetDescriptor()); - return true; - } - // Appends to dex_files any elements of class_path that it doesn't already // contain. This will open those dex files as necessary. static void OpenClassPathFiles(const std::string& class_path, std::vector& dex_files) { @@ -929,7 +817,7 @@ static int dex2oat(int argc, char** argv) { #endif // ART_SMALL_MODE Dex2Oat* p_dex2oat; - if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set, thread_count, + if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set, thread_count, support_debugging)) { LOG(ERROR) << "Failed to create dex2oat"; return EXIT_FAILURE; @@ -943,9 +831,9 @@ static int dex2oat(int argc, char** argv) { ScopedObjectAccess soa(Thread::Current()); // If --image-classes was specified, calculate the full list of classes to include in the image - UniquePtr image_classes(NULL); + UniquePtr image_classes(NULL); if (image_classes_filename != NULL) { - image_classes.reset(dex2oat->GetImageClassDescriptors(image_classes_filename)); + image_classes.reset(dex2oat->ReadImageClasses(image_classes_filename)); if (image_classes.get() == NULL) { LOG(ERROR) << "Failed to create list of image classes from " << image_classes_filename; return EXIT_FAILURE; @@ -1001,7 +889,7 @@ static int dex2oat(int argc, char** argv) { oat_file.get(), bitcode_filename, image, - image_classes.get(), + image_classes, dump_stats, dump_timings)); @@ -1066,7 +954,6 @@ static int dex2oat(int argc, char** argv) { Thread::Current()->TransitionFromRunnableToSuspended(kNative); bool image_creation_success = dex2oat->CreateImageFile(image_filename, image_base, - image_classes.get(), oat_unstripped, oat_location, *compiler.get()); diff --git a/src/image_test.cc b/src/image_test.cc index cd1a34fa56..0769e21f00 100644 --- a/src/image_test.cc +++ b/src/image_test.cc @@ -43,28 +43,19 @@ TEST_F(ImageTest, WriteRead) { { std::vector oat_contents; { + jobject class_loader = NULL; + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath()); + ScopedObjectAccess soa(Thread::Current()); - std::vector dex_files; - dex_files.push_back(java_lang_dex_file_); - dex_files.push_back(conscrypt_file_); VectorOutputStream output_stream(tmp_elf.GetFilename(), oat_contents); - bool success_oat = OatWriter::Create(output_stream, dex_files, 0, 0, "", - *compiler_driver_.get()); + bool success_oat = OatWriter::Create(output_stream, class_linker->GetBootClassPath(), + 0, 0, "", *compiler_driver_.get()); ASSERT_TRUE(success_oat); - // Force all system classes into memory - for (size_t dex_file_index = 0; dex_file_index < dex_files.size(); ++dex_file_index) { - const DexFile* dex_file = dex_files[dex_file_index]; - for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const char* descriptor = dex_file->GetClassDescriptor(class_def); - mirror::Class* klass = class_linker_->FindSystemClass(descriptor); - EXPECT_TRUE(klass != NULL) << descriptor; - } - } bool success_elf = compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, - dex_files, + class_linker->GetBootClassPath(), oat_contents, tmp_elf.GetFile()); ASSERT_TRUE(success_elf); @@ -77,10 +68,9 @@ TEST_F(ImageTest, WriteRead) { ScratchFile tmp_image; const uintptr_t requested_image_base = ART_BASE_ADDRESS; { - ImageWriter writer(NULL); + ImageWriter writer(*compiler_driver_.get()); bool success_image = writer.Write(tmp_image.GetFilename(), requested_image_base, - tmp_oat->GetPath(), tmp_oat->GetPath(), - *compiler_driver_.get()); + tmp_oat->GetPath(), tmp_oat->GetPath()); ASSERT_TRUE(success_image); bool success_fixup = compiler_driver_->FixupElf(tmp_oat.get(), writer.GetOatDataBegin()); ASSERT_TRUE(success_fixup); @@ -102,6 +92,9 @@ TEST_F(ImageTest, WriteRead) { ASSERT_GE(sizeof(image_header) + space->Size(), static_cast(file->GetLength())); } + ASSERT_TRUE(compiler_driver_->GetImageClasses() != NULL); + CompilerDriver::DescriptorSet image_classes(*compiler_driver_->GetImageClasses()); + // Need to delete the compiler since it has worker threads which are attached to runtime. compiler_driver_.reset(); @@ -149,7 +142,13 @@ TEST_F(ImageTest, WriteRead) { mirror::Class* klass = class_linker_->FindSystemClass(descriptor); EXPECT_TRUE(klass != NULL) << descriptor; EXPECT_LT(image_begin, reinterpret_cast(klass)) << descriptor; - EXPECT_LT(reinterpret_cast(klass), image_end) << descriptor; + if (image_classes.find(descriptor) != image_classes.end()) { + // image classes should be located before the end of the image. + EXPECT_LT(reinterpret_cast(klass), image_end) << descriptor; + } else { + // non image classes should be in a space after the image. + EXPECT_GT(reinterpret_cast(klass), image_end) << descriptor; + } EXPECT_EQ(*klass->GetRawLockWordAddress(), 0); // address should have been removed from monitor } } diff --git a/src/image_writer.cc b/src/image_writer.cc index 4ba99fe3d0..5a1ebbbcfd 100644 --- a/src/image_writer.cc +++ b/src/image_writer.cc @@ -57,8 +57,7 @@ namespace art { bool ImageWriter::Write(const std::string& image_filename, uintptr_t image_begin, const std::string& oat_filename, - const std::string& oat_location, - const CompilerDriver& compiler_driver) { + const std::string& oat_location) { CHECK(!image_filename.empty()); CHECK_NE(image_begin, 0U); @@ -114,10 +113,10 @@ bool ImageWriter::Write(const std::string& image_filename, Thread::Current()->TransitionFromSuspendedToRunnable(); size_t oat_loaded_size = 0; size_t oat_data_offset = 0; - compiler_driver.GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset); + compiler_driver_.GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset); CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset); CopyAndFixupObjects(); - PatchOatCodeAndMethods(compiler_driver); + PatchOatCodeAndMethods(); Thread::Current()->TransitionFromRunnableToSuspended(kNative); UniquePtr image_file(OS::OpenFile(image_filename.c_str(), true)); @@ -199,42 +198,21 @@ void ImageWriter::ComputeEagerResolvedStrings() } bool ImageWriter::IsImageClass(const Class* klass) { - if (image_classes_ == NULL) { - return true; - } - while (klass->IsArrayClass()) { - klass = klass->GetComponentType(); - } - if (klass->IsPrimitive()) { - return true; - } - const std::string descriptor(ClassHelper(klass).GetDescriptor()); - return image_classes_->find(descriptor) != image_classes_->end(); + return compiler_driver_.IsImageClass(ClassHelper(klass).GetDescriptor()); } - struct NonImageClasses { ImageWriter* image_writer; std::set* non_image_classes; }; void ImageWriter::PruneNonImageClasses() { - if (image_classes_ == NULL) { + if (compiler_driver_.GetImageClasses() == NULL) { return; } Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - // Update image_classes_ with classes for objects created by methods. - Thread* self = Thread::Current(); - const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); - Heap* heap = Runtime::Current()->GetHeap(); - // TODO: Image spaces only? - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this); - self->EndAssertNoThreadSuspension(old_cause); - // Make a list of classes we would like to prune. std::set non_image_classes; NonImageClasses context; @@ -275,28 +253,6 @@ void ImageWriter::PruneNonImageClasses() { } } -void ImageWriter::FindClinitImageClassesCallback(Object* object, void* arg) { - DCHECK(object != NULL); - DCHECK(arg != NULL); - ImageWriter* image_writer = reinterpret_cast(arg); - Class* klass = object->GetClass(); - while (klass->IsArrayClass()) { - klass = klass->GetComponentType(); - } - if (klass->IsPrimitive()) { - return; - } - while (!klass->IsObjectClass()) { - ClassHelper kh(klass); - const char* descriptor = kh.GetDescriptor(); - std::pair result = image_writer->image_classes_->insert(descriptor); - if (result.second) { - LOG(INFO) << "Adding " << descriptor << " to image classes"; - } - klass = klass->GetSuperClass(); - } -} - bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { NonImageClasses* context = reinterpret_cast(arg); if (!context->image_writer->IsImageClass(klass)) { @@ -307,7 +263,7 @@ bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { void ImageWriter::CheckNonImageClassesRemoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (image_classes_ == NULL) { + if (compiler_driver_.GetImageClasses() == NULL) { return; } @@ -336,8 +292,10 @@ void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) { } void ImageWriter::DumpImageClasses() { + CompilerDriver::DescriptorSet* image_classes = compiler_driver_.GetImageClasses(); + CHECK(image_classes != NULL); typedef std::set::const_iterator It; // TODO: C++0x auto - for (It it = image_classes_->begin(), end = image_classes_->end(); it != end; ++it) { + for (It it = image_classes->begin(), end = image_classes->end(); it != end; ++it) { LOG(INFO) << " " << *it; } } @@ -659,13 +617,13 @@ static AbstractMethod* GetTargetMethod(const CompilerDriver::PatchInformation* p return method; } -void ImageWriter::PatchOatCodeAndMethods(const CompilerDriver& compiler) { +void ImageWriter::PatchOatCodeAndMethods() { Thread* self = Thread::Current(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); typedef std::vector Patches; - const Patches& code_to_patch = compiler.GetCodeToPatch(); + const Patches& code_to_patch = compiler_driver_.GetCodeToPatch(); for (size_t i = 0; i < code_to_patch.size(); i++) { const CompilerDriver::PatchInformation* patch = code_to_patch[i]; AbstractMethod* target = GetTargetMethod(patch); @@ -675,7 +633,7 @@ void ImageWriter::PatchOatCodeAndMethods(const CompilerDriver& compiler) { SetPatchLocation(patch, reinterpret_cast(GetOatAddress(code_offset))); } - const Patches& methods_to_patch = compiler.GetMethodsToPatch(); + const Patches& methods_to_patch = compiler_driver_.GetMethodsToPatch(); for (size_t i = 0; i < methods_to_patch.size(); i++) { const CompilerDriver::PatchInformation* patch = methods_to_patch[i]; AbstractMethod* target = GetTargetMethod(patch); diff --git a/src/image_writer.h b/src/image_writer.h index 4628e5a05f..4507592353 100644 --- a/src/image_writer.h +++ b/src/image_writer.h @@ -37,9 +37,8 @@ namespace art { // Write a Space built during compilation for use during execution. class ImageWriter { public: - typedef std::set DescriptorSet; - explicit ImageWriter(DescriptorSet* image_classes) - : oat_file_(NULL), image_end_(0), image_begin_(NULL), image_classes_(image_classes), + explicit ImageWriter(const CompilerDriver& compiler_driver) + : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL), oat_data_begin_(NULL), interpreter_to_interpreter_entry_offset_(0), interpreter_to_quick_entry_offset_(0), portable_resolution_trampoline_offset_(0), quick_resolution_trampoline_offset_(0) {} @@ -49,8 +48,7 @@ class ImageWriter { bool Write(const std::string& image_filename, uintptr_t image_begin, const std::string& oat_filename, - const std::string& oat_location, - const CompilerDriver& compiler_driver) + const std::string& oat_location) LOCKS_EXCLUDED(Locks::mutator_lock_); uintptr_t GetOatDataBegin() { @@ -132,8 +130,6 @@ class ImageWriter { // Remove unwanted classes from various roots. void PruneNonImageClasses() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void FindClinitImageClassesCallback(mirror::Object* object, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static bool NonImageClassesVisitor(mirror::Class* c, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -172,12 +168,14 @@ class ImageWriter { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Patches references in OatFile to expect runtime addresses. - void PatchOatCodeAndMethods(const CompilerDriver& compiler) + void PatchOatCodeAndMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const CompilerDriver& compiler_driver_; + // Map of Object to where it will be at runtime. SafeMap offsets_; @@ -193,9 +191,6 @@ class ImageWriter { // Beginning target image address for the output image. byte* image_begin_; - // Set of classes to be include in the image, or NULL for all. - DescriptorSet* image_classes_; - // Beginning target oat address for the pointers from the output image to its oat file. const byte* oat_data_begin_; diff --git a/src/oat_test.cc b/src/oat_test.cc index c7c063a9d1..29e2891b40 100644 --- a/src/oat_test.cc +++ b/src/oat_test.cc @@ -74,8 +74,8 @@ TEST_F(OatTest, WriteRead) { #else CompilerBackend compiler_backend = kQuick; #endif - compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, 2, false, - NULL, true, true)); + compiler_driver_.reset(new CompilerDriver(compiler_backend, kThumb2, false, NULL, 2, false, + true, true)); jobject class_loader = NULL; if (compile) { compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath()); -- cgit v1.2.3-59-g8ed1b From 2d6ba5158d7fd459db2870df47300b517dc4d08c Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Fri, 17 May 2013 11:31:37 +0200 Subject: Quickening support. This CL adds quickening support for methods which are interpreted at runtime. This CL introduces a DEX-to-DEX compiler. A method is now compiled in one of the two following modes: - Native compilation: the method is compiled by the Quick or Portable backends. At runtime, the generated native target-dependent code is executed. - DEX-to-DEX compilation: the method is executed by the interpreter at runtime. Its DEX code is compiled so some instructions can be replaced by special instructions only valid at runtime. No native code is generated. The quickening adds special instructions to improve runtime performance. They are "-quick" versions of the following instructions: - iget/iput - iget-wide/iput-wide - iget-object/iput-object - invoke-virtual/range. These special instructions cannot be treated by the verifier since they lose the field/method index referencing the field/method being accessed/invoked. To prevent this, the DEX-to-DEX compiler is run only on methods of preverified classes (without verification error at compilation time). The DEX-to-DEX compiler implements quickening support using the CompilerDriver interface like the native compiler does (Quick or Portable backends). To replace instructions, the DEX-to-DEX compiler must be able to modify the mmapped DEX file. Since it can be read-only protected, the DEX-to-DEX compiler must be able to temporarily change its protection to read-write mmapped file. To achieve this, this CL adds support for changing DEX file protection with DexFile::EnableWrite and DexFile::DisableWrite methods. Besides, it also adds a dedicated lock (DexFile::modification_lock) to ensure thread-safety and avoid concurrent DEX file protection change (from a parallel DEX-to-DEX compiler on the same DEX file). Change-Id: Iaafd103b9766810d7fc94a2c424a8fafba66e26a --- build/Android.libart-compiler.mk | 1 + src/common_throws.cc | 80 +++- src/common_throws.h | 5 + src/compiler/dex/dex_to_dex_compiler.cc | 258 +++++++++++++ src/compiler/driver/compiler_driver.cc | 68 +++- src/compiler/driver/compiler_driver.h | 5 +- src/dex_file.cc | 22 ++ src/dex_file.h | 17 + src/dex_instruction.cc | 36 ++ src/dex_instruction.h | 24 ++ src/dex_instruction_list.h | 16 +- src/interpreter/interpreter.cc | 204 +++++++++- src/mem_map.cc | 22 ++ src/mem_map.h | 2 + src/verifier/method_verifier.cc | 420 +++++++++++++++++++-- src/verifier/method_verifier.h | 41 +- .../src/Main.java | 17 +- 17 files changed, 1186 insertions(+), 52 deletions(-) create mode 100644 src/compiler/dex/dex_to_dex_compiler.cc (limited to 'src/compiler/driver/compiler_driver.h') diff --git a/build/Android.libart-compiler.mk b/build/Android.libart-compiler.mk index b73a32961e..708a488144 100644 --- a/build/Android.libart-compiler.mk +++ b/build/Android.libart-compiler.mk @@ -44,6 +44,7 @@ LIBART_COMPILER_SRC_FILES := \ src/compiler/dex/quick/x86/target_x86.cc \ src/compiler/dex/quick/x86/utility_x86.cc \ src/compiler/dex/portable/mir_to_gbc.cc \ + src/compiler/dex/dex_to_dex_compiler.cc \ src/compiler/dex/mir_dataflow.cc \ src/compiler/dex/mir_optimization.cc \ src/compiler/dex/frontend.cc \ diff --git a/src/common_throws.cc b/src/common_throws.cc index dc3627a0b2..1e114bb6dd 100644 --- a/src/common_throws.cc +++ b/src/common_throws.cc @@ -27,6 +27,7 @@ #include "mirror/object_array-inl.h" #include "object_utils.h" #include "thread.h" +#include "verifier/method_verifier.h" #include @@ -283,16 +284,34 @@ void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str()); } -void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx, - InvokeType type) { - mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache(); - const DexFile& dex_file = *dex_cache->GetDexFile(); +static void ThrowNullPointerExceptionForMethodAccessImpl(const ThrowLocation& throw_location, + uint32_t method_idx, + const DexFile& dex_file, + InvokeType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::ostringstream msg; msg << "Attempt to invoke " << type << " method '" << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference"; ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str()); } +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx, + InvokeType type) { + mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method_idx, + dex_file, type); +} + +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, + mirror::AbstractMethod* method, + InvokeType type) { + mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache(); + const DexFile& dex_file = *dex_cache->GetDexFile(); + ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method->GetDexMethodIndex(), + dex_file, type); +} + void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { const DexFile::CodeItem* code = MethodHelper(throw_location.GetMethod()).GetCodeItem(); uint32_t throw_dex_pc = throw_location.GetDexPc(); @@ -317,6 +336,23 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { case Instruction::INVOKE_INTERFACE_RANGE: ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_3rc(), kInterface); break; + case Instruction::INVOKE_VIRTUAL_QUICK: + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { + // Since we replaced the method index, we ask the verifier to tell us which + // method is invoked at this location. + mirror::AbstractMethod* method = + verifier::MethodVerifier::FindInvokedMethodAtDexPc(throw_location.GetMethod(), + throw_location.GetDexPc()); + if (method != NULL) { + // NPE with precise message. + ThrowNullPointerExceptionForMethodAccess(throw_location, method, kVirtual); + } else { + // NPE with imprecise message. + ThrowNullPointerException(&throw_location, + "Attempt to invoke a virtual method on a null object reference"); + } + break; + } case Instruction::IGET: case Instruction::IGET_WIDE: case Instruction::IGET_OBJECT: @@ -330,6 +366,24 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */); break; } + case Instruction::IGET_QUICK: + case Instruction::IGET_WIDE_QUICK: + case Instruction::IGET_OBJECT_QUICK: { + // Since we replaced the field index, we ask the verifier to tell us which + // field is accessed at this location. + mirror::Field* field = + verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(), + throw_location.GetDexPc()); + if (field != NULL) { + // NPE with precise message. + ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */); + } else { + // NPE with imprecise message. + ThrowNullPointerException(&throw_location, + "Attempt to read from a field on a null object reference"); + } + break; + } case Instruction::IPUT: case Instruction::IPUT_WIDE: case Instruction::IPUT_OBJECT: @@ -343,6 +397,24 @@ void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) { ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */); break; } + case Instruction::IPUT_QUICK: + case Instruction::IPUT_WIDE_QUICK: + case Instruction::IPUT_OBJECT_QUICK: { + // Since we replaced the field index, we ask the verifier to tell us which + // field is accessed at this location. + mirror::Field* field = + verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(), + throw_location.GetDexPc()); + if (field != NULL) { + // NPE with precise message. + ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */); + } else { + // NPE with imprecise message. + ThrowNullPointerException(&throw_location, + "Attempt to write to a field on a null object reference"); + } + break; + } case Instruction::AGET: case Instruction::AGET_WIDE: case Instruction::AGET_OBJECT: diff --git a/src/common_throws.h b/src/common_throws.h index 5555435051..00d89f45f6 100644 --- a/src/common_throws.h +++ b/src/common_throws.h @@ -153,6 +153,11 @@ void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_locatio InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, + mirror::AbstractMethod* method, + InvokeType type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/src/compiler/dex/dex_to_dex_compiler.cc b/src/compiler/dex/dex_to_dex_compiler.cc new file mode 100644 index 0000000000..afb29f4163 --- /dev/null +++ b/src/compiler/dex/dex_to_dex_compiler.cc @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2011 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 "base/logging.h" +#include "base/mutex.h" +#include "compiler/driver/compiler_driver.h" +#include "compiler/driver/dex_compilation_unit.h" +#include "dex_file-inl.h" +#include "dex_instruction-inl.h" +#include "mirror/abstract_method-inl.h" +#include "mirror/class-inl.h" +#include "mirror/dex_cache.h" +#include "mirror/field-inl.h" + +namespace art { +namespace optimizer { + +// Controls quickening activation. +const bool kEnableQuickening = true; +// Controls logging. +const bool kEnableLogging = false; + +class DexCompiler { + public: + DexCompiler(art::CompilerDriver& compiler, + const DexCompilationUnit& unit) + : driver_(compiler), + unit_(unit) {}; + + ~DexCompiler() {}; + + void Compile(); + + private: + const DexFile& GetDexFile() const { + return *unit_.GetDexFile(); + } + + // TODO: since the whole compilation pipeline uses a "const DexFile", we need + // to "unconst" here. The DEX-to-DEX compiler should work on a non-const DexFile. + DexFile& GetModifiableDexFile() { + return *const_cast(unit_.GetDexFile()); + } + + void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_put); + void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_range); + + CompilerDriver& driver_; + const DexCompilationUnit& unit_; + + DISALLOW_COPY_AND_ASSIGN(DexCompiler); +}; + +// Ensures write access to a part of DEX file. +// +// If a DEX file is read-only, it modifies its protection (mprotect) so it allows +// write access to the part of DEX file defined by an address and a length. +// In this case, it also takes the DexFile::modification_lock to prevent from +// concurrent protection modification from a parallel DEX-to-DEX compilation on +// the same DEX file. +// When the instance is destroyed, it recovers original protection and releases +// the lock. +// TODO: as this scoped class is similar to a MutexLock we should use annotalysis +// to capture the locking behavior. +class ScopedDexWriteAccess { + public: + ScopedDexWriteAccess(DexFile& dex_file, Instruction* inst, + size_t length) + : dex_file_(dex_file), + address_(reinterpret_cast(inst)), + length_(length), + is_read_only_(dex_file_.IsReadOnly()) { + if (is_read_only_) { + // We need to enable DEX write access. To avoid concurrent DEX write access + // modification, we take the DexFile::modification_lock before. + dex_file_.GetModificationLock().ExclusiveLock(Thread::Current()); + bool success = dex_file_.EnableWrite(address_, length_); + DCHECK(success) << "Failed to enable DEX write access"; + } + } + + ~ScopedDexWriteAccess() { + DCHECK_EQ(is_read_only_, dex_file_.IsReadOnly()); + if (is_read_only_) { + bool success = dex_file_.DisableWrite(address_, length_); + DCHECK(success) << "Failed to disable DEX write access"; + // Now we recovered original read-only protection, we can release the + // DexFile::modification_lock. + dex_file_.GetModificationLock().ExclusiveUnlock(Thread::Current()); + } + } + + private: + DexFile& dex_file_; + // TODO: make address_ const. + uint8_t* address_; + const size_t length_; + const bool is_read_only_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDexWriteAccess); +}; + +void DexCompiler::Compile() { + const DexFile::CodeItem* code_item = unit_.GetCodeItem(); + const uint16_t* insns = code_item->insns_; + const uint32_t insns_size = code_item->insns_size_in_code_units_; + Instruction* inst = const_cast(Instruction::At(insns)); + + for (uint32_t dex_pc = 0; dex_pc < insns_size; + inst = const_cast(inst->Next()), dex_pc = inst->GetDexPc(insns)) { + switch (inst->Opcode()) { + case Instruction::IGET: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false); + break; + case Instruction::IGET_WIDE: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false); + break; + + case Instruction::IGET_OBJECT: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false); + break; + + case Instruction::IPUT: + case Instruction::IPUT_BOOLEAN: + case Instruction::IPUT_BYTE: + case Instruction::IPUT_CHAR: + case Instruction::IPUT_SHORT: + // These opcodes have the same implementation in interpreter so group + // them under IPUT_QUICK. + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true); + break; + + case Instruction::IPUT_WIDE: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true); + break; + + case Instruction::IPUT_OBJECT: + CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true); + break; + + case Instruction::INVOKE_VIRTUAL: + CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false); + break; + + case Instruction::INVOKE_VIRTUAL_RANGE: + CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true); + break; + + default: + // No optimization. + break; + } + } +} + +void DexCompiler::CompileInstanceFieldAccess(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_put) { + if (!kEnableQuickening) { + return; + } + uint32_t field_idx = inst->VRegC_22c(); + int field_offset; + bool is_volatile; + bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset, + is_volatile, is_put); + if (fast_path && !is_volatile && IsUint(16, field_offset)) { + // TODO: use VLOG ? + if (kEnableLogging) { + LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode()) + << " to " << Instruction::Name(new_opcode) + << " by replacing field index " << field_idx + << " by field offset " << field_offset + << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + } + // We are modifying 4 consecutive bytes. + ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u); + inst->SetOpcode(new_opcode); + // Replace field index by field offset. + inst->SetVRegC_22c(static_cast(field_offset)); + } +} + +void DexCompiler::CompileInvokeVirtual(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_range) { + if (!kEnableQuickening) { + return; + } + uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + CompilerDriver::MethodReference target_method(&GetDexFile(), method_idx); + InvokeType invoke_type = kVirtual; + InvokeType original_invoke_type = invoke_type; + int vtable_idx; + uintptr_t direct_code; + uintptr_t direct_method; + bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type, + target_method, vtable_idx, + direct_code, direct_method, + false); + // TODO: support devirtualization. + if (fast_path && original_invoke_type == invoke_type) { + if (vtable_idx >= 0 && IsUint(16, vtable_idx)) { + // TODO: use VLOG ? + if (kEnableLogging) { + LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode()) + << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")" + << " to " << Instruction::Name(new_opcode) + << " by replacing method index " << method_idx + << " by vtable index " << vtable_idx + << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " + << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true); + } + // We are modifying 4 consecutive bytes. + ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u); + inst->SetOpcode(new_opcode); + // Replace method index by vtable index. + if (is_range) { + inst->SetVRegB_3rc(static_cast(vtable_idx)); + } else { + inst->SetVRegB_35c(static_cast(vtable_idx)); + } + } + } +} + +} // namespace optimizer +} // namespace art + +extern "C" art::CompiledMethod* + ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, + uint32_t access_flags, art::InvokeType invoke_type, + uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, + const art::DexFile& dex_file) { + art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(), + dex_file, code_item, class_def_idx, method_idx, access_flags); + art::optimizer::DexCompiler dex_compiler(compiler, unit); + dex_compiler.Compile(); + return NULL; +} diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 6050108d48..4a6eb962e3 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -372,6 +372,8 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet compiler_ = FindFunction(compiler_so_name, compiler_library_, "ArtQuickCompileMethod"); } + dex_to_dex_compiler_ = FindFunction(compiler_so_name, compiler_library_, "ArtCompileDEX"); + init_compiler_context(*this); if (compiler_backend_ == kPortable) { @@ -531,10 +533,33 @@ void CompilerDriver::CompileAll(jobject class_loader, } } +static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader, + const DexFile& dex_file, + const DexFile::ClassDef& class_def) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Do not allow DEX-to-DEX compilation of image classes. This is to prevent the + // verifier from passing on "quick" instruction at compilation time. It must + // only pass on quick instructions at runtime. + if (class_loader == NULL) { + return false; + } + const char* descriptor = dex_file.GetClassDescriptor(class_def); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::Class* klass = class_linker->FindClass(descriptor, class_loader); + if (klass == NULL) { + Thread* self = Thread::Current(); + CHECK(self->IsExceptionPending()); + self->ClearException(); + return false; + } + // DEX-to-DEX compilation is only allowed on preverified classes. + return klass->IsVerified(); +} + void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) { DCHECK(!Runtime::Current()->IsStarted()); Thread* self = Thread::Current(); - jobject class_loader; + jobject jclass_loader; const DexFile* dex_file; uint32_t class_def_idx; { @@ -542,7 +567,7 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) { ScopedLocalRef local_class_loader(soa.Env(), soa.AddLocalReference(method->GetDeclaringClass()->GetClassLoader())); - class_loader = soa.Env()->NewGlobalRef(local_class_loader.get()); + jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get()); // Find the dex_file MethodHelper mh(method); dex_file = &mh.GetDexFile(); @@ -555,14 +580,22 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) { UniquePtr thread_pool(new ThreadPool(1U)); TimingLogger timings("CompileOne", false); - PreCompile(class_loader, dex_files, *thread_pool.get(), timings); + PreCompile(jclass_loader, dex_files, *thread_pool.get(), timings); uint32_t method_idx = method->GetDexMethodIndex(); const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); + // Can we run DEX-to-DEX compiler on this class ? + bool allow_dex_compilation; + { + ScopedObjectAccess soa(Thread::Current()); + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); + mirror::ClassLoader* class_loader = soa.Decode(jclass_loader); + allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, *dex_file, class_def); + } CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(), - class_def_idx, method_idx, class_loader, *dex_file); + class_def_idx, method_idx, jclass_loader, *dex_file, allow_dex_compilation); - self->GetJniEnv()->DeleteGlobalRef(class_loader); + self->GetJniEnv()->DeleteGlobalRef(jclass_loader); self->TransitionFromSuspendedToRunnable(); } @@ -2015,12 +2048,12 @@ void CompilerDriver::Compile(jobject class_loader, const std::vectorGetClassLoader(); + jobject jclass_loader = manager->GetClassLoader(); const DexFile& dex_file = *manager->GetDexFile(); const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); { ScopedObjectAccess soa(Thread::Current()); - mirror::ClassLoader* class_loader = soa.Decode(manager->GetClassLoader()); + mirror::ClassLoader* class_loader = soa.Decode(jclass_loader); if (SkipClass(class_loader, dex_file, class_def)) { return; } @@ -2035,6 +2068,13 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz // empty class, probably a marker interface return; } + // Can we run DEX-to-DEX compiler on this class ? + bool allow_dex_compilation; + { + ScopedObjectAccess soa(Thread::Current()); + mirror::ClassLoader* class_loader = soa.Decode(jclass_loader); + allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, dex_file, class_def); + } ClassDataItemIterator it(dex_file, class_data); // Skip fields while (it.HasNextStaticField()) { @@ -2056,7 +2096,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz previous_direct_method_idx = method_idx; manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMethodInvokeType(class_def), class_def_index, - method_idx, class_loader, dex_file); + method_idx, jclass_loader, dex_file, allow_dex_compilation); it.Next(); } // Compile virtual methods @@ -2072,7 +2112,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz previous_virtual_method_idx = method_idx; manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMethodInvokeType(class_def), class_def_index, - method_idx, class_loader, dex_file); + method_idx, jclass_loader, dex_file, allow_dex_compilation); it.Next(); } DCHECK(!it.HasNext()); @@ -2088,7 +2128,8 @@ void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_fil void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, - const DexFile& dex_file) { + const DexFile& dex_file, + bool allow_dex_to_dex_compilation) { CompiledMethod* compiled_method = NULL; uint64_t start_ns = NanoTime(); @@ -2113,6 +2154,13 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t compiled_method = (*compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file); CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file); + } else if (allow_dex_to_dex_compilation) { + // TODO: add a mode to disable DEX-to-DEX compilation ? + compiled_method = (*dex_to_dex_compiler_)(*this, code_item, access_flags, + invoke_type, class_def_idx, + method_idx, class_loader, dex_file); + // No native code is generated. + CHECK(compiled_method == NULL) << PrettyMethod(method_idx, dex_file); } } uint64_t duration_ns = NanoTime() - start_ns; diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index fbfcadb8dd..fdd2149f7f 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -349,7 +349,8 @@ class CompilerDriver { LOCKS_EXCLUDED(Locks::mutator_lock_); void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, - jobject class_loader, const DexFile& dex_file) + jobject class_loader, const DexFile& dex_file, + bool allow_dex_to_dex_compilation) LOCKS_EXCLUDED(compiled_methods_lock_); static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index) @@ -404,6 +405,8 @@ class CompilerDriver { jobject class_loader, const DexFile& dex_file); CompilerFn compiler_; + CompilerFn dex_to_dex_compiler_; + void* compiler_context_; typedef CompiledMethod* (*JniCompilerFn)(CompilerDriver& driver, diff --git a/src/dex_file.cc b/src/dex_file.cc index dad083cc3f..80465f2fed 100644 --- a/src/dex_file.cc +++ b/src/dex_file.cc @@ -108,6 +108,28 @@ int DexFile::GetPermissions() const { } } +bool DexFile::IsReadOnly() const { + return GetPermissions() == PROT_READ; +} + +bool DexFile::EnableWrite(uint8_t* addr, size_t length) const { + CHECK(IsReadOnly()); + if (mem_map_.get() == NULL) { + return false; + } else { + return mem_map_->ProtectRegion(addr, length, PROT_READ | PROT_WRITE); + } +} + +bool DexFile::DisableWrite(uint8_t* addr, size_t length) const { + CHECK(!IsReadOnly()); + if (mem_map_.get() == NULL) { + return false; + } else { + return mem_map_->ProtectRegion(addr, length, PROT_READ); + } +} + const DexFile* DexFile::OpenFile(const std::string& filename, const std::string& location, bool verify) { diff --git a/src/dex_file.h b/src/dex_file.h index ecc985fb3a..e09270e018 100644 --- a/src/dex_file.h +++ b/src/dex_file.h @@ -21,6 +21,7 @@ #include #include "base/logging.h" +#include "base/mutex.h" #include "base/stringpiece.h" #include "globals.h" #include "invoke_type.h" @@ -384,6 +385,10 @@ class DexFile { return *header_; } + Mutex& GetModificationLock() { + return modification_lock; + } + // Decode the dex magic version uint32_t GetVersion() const; @@ -798,6 +803,12 @@ class DexFile { int GetPermissions() const; + bool IsReadOnly() const; + + bool EnableWrite(uint8_t* addr, size_t size) const; + + bool DisableWrite(uint8_t* addr, size_t size) const; + private: // Opens a .dex file static const DexFile* OpenFile(const std::string& filename, @@ -830,6 +841,7 @@ class DexFile { location_checksum_(location_checksum), mem_map_(mem_map), dex_object_(NULL), + modification_lock("DEX modification lock"), header_(0), string_ids_(0), type_ids_(0), @@ -890,6 +902,11 @@ class DexFile { // TODO: this is mutable as it shouldn't be here. We should move it to the dex cache or similar. mutable jobject dex_object_; + // The DEX-to-DEX compiler uses this lock to ensure thread safety when + // enabling write access to a read-only DEX file. + // TODO: move to Locks::dex_file_modification_lock. + Mutex modification_lock; + // Points to the header section. const Header* header_; diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc index b18b4d0226..e00272796c 100644 --- a/src/dex_instruction.cc +++ b/src/dex_instruction.cc @@ -354,6 +354,14 @@ std::string Instruction::DumpString(const DexFile* file) const { << PrettyField(field_idx, *file, true) << " // field@" << field_idx; break; } // else fall-through + case IGET_QUICK: + case IGET_OBJECT_QUICK: + if (file != NULL) { + uint32_t field_idx = VRegC_22c(); + os << opcode << " v" << static_cast(VRegA_22c()) << ", v" << static_cast(VRegB_22c()) << ", " + << "// offset@" << field_idx; + break; + } // else fall-through case IPUT: case IPUT_WIDE: case IPUT_OBJECT: @@ -367,6 +375,14 @@ std::string Instruction::DumpString(const DexFile* file) const { << PrettyField(field_idx, *file, true) << " // field@" << field_idx; break; } // else fall-through + case IPUT_QUICK: + case IPUT_OBJECT_QUICK: + if (file != NULL) { + uint32_t field_idx = VRegC_22c(); + os << opcode << " v" << static_cast(VRegA_22c()) << ", v" << static_cast(VRegB_22c()) << ", " + << "// offset@" << field_idx; + break; + } // else fall-through case INSTANCE_OF: if (file != NULL) { uint32_t type_idx = VRegC_22c(); @@ -413,6 +429,19 @@ std::string Instruction::DumpString(const DexFile* file) const { os << "}, " << PrettyMethod(method_idx, *file) << " // method@" << method_idx; break; } // else fall-through + case INVOKE_VIRTUAL_QUICK: + if (file != NULL) { + os << opcode << " {"; + uint32_t method_idx = VRegB_35c(); + for (size_t i = 0; i < VRegA_35c(); ++i) { + if (i != 0) { + os << ", "; + } + os << "v" << arg[i]; + } + os << "}, // vtable@" << method_idx; + break; + } // else fall-through default: os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2] << ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c(); @@ -433,6 +462,13 @@ std::string Instruction::DumpString(const DexFile* file) const { << PrettyMethod(method_idx, *file) << " // method@" << method_idx; break; } // else fall-through + case INVOKE_VIRTUAL_RANGE_QUICK: + if (file != NULL) { + uint32_t method_idx = VRegB_3rc(); + os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1)) + << "// vtable@" << method_idx; + break; + } // else fall-through default: os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc()); diff --git a/src/dex_instruction.h b/src/dex_instruction.h index adaada7f87..8a49216e4d 100644 --- a/src/dex_instruction.h +++ b/src/dex_instruction.h @@ -278,6 +278,30 @@ class Instruction { return static_cast(opcode); } + void SetOpcode(Code opcode) { + DCHECK_LT(static_cast(opcode), 256u); + uint16_t* insns = reinterpret_cast(this); + insns[0] = (insns[0] & 0xff00) | static_cast(opcode); + } + + void SetVRegB_3rc(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k3rc); + uint16_t* insns = reinterpret_cast(this); + insns[1] = val; + } + + void SetVRegB_35c(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k35c); + uint16_t* insns = reinterpret_cast(this); + insns[1] = val; + } + + void SetVRegC_22c(uint16_t val) { + DCHECK(FormatOf(Opcode()) == k22c); + uint16_t* insns = reinterpret_cast(this); + insns[1] = val; + } + // Returns the format of the given opcode. static Format FormatOf(Code opcode) { return kInstructionFormats[opcode]; diff --git a/src/dex_instruction_list.h b/src/dex_instruction_list.h index 3083d224e9..9daec61ba5 100644 --- a/src/dex_instruction_list.h +++ b/src/dex_instruction_list.h @@ -242,14 +242,14 @@ V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \ - V(0xE3, UNUSED_E3, "unused-e3", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE4, UNUSED_E4, "unused-e4", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE5, UNUSED_E5, "unused-e5", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE6, UNUSED_E6, "unused-e6", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE7, UNUSED_E7, "unused-e7", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE8, UNUSED_E8, "unused-e8", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xE9, UNUSED_E9, "unused-e9", k10x, false, kUnknown, 0, kVerifyError) \ - V(0xEA, UNUSED_EA, "unused-ea", k10x, false, kUnknown, 0, kVerifyError) \ + V(0xE3, IGET_QUICK, "iget-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE4, IGET_WIDE_QUICK, "iget-wide-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \ + V(0xE5, IGET_OBJECT_QUICK, "iget-object-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE6, IPUT_QUICK, "iput-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \ + V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \ + V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArg) \ + V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRange) \ V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \ V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \ V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \ diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index dd96f8dead..685ca219b2 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -470,6 +470,98 @@ static void DoInvoke(Thread* self, ShadowFrame& shadow_frame, } } +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) + NO_THREAD_SAFETY_ANALYSIS; + +template +static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst, JValue* result) { + uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); + Object* receiver = shadow_frame.GetVRegReference(vregC); + if (UNLIKELY(receiver == NULL)) { + // We lost the reference to the method index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return; + } + uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + AbstractMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx); + if (UNLIKELY(method == NULL)) { + CHECK(self->IsExceptionPending()); + result->SetJ(0); + return; + } + MethodHelper mh(method); + + const DexFile::CodeItem* code_item = mh.GetCodeItem(); + uint16_t num_regs; + uint16_t num_ins; + if (code_item != NULL) { + num_regs = code_item->registers_size_; + num_ins = code_item->ins_size_; + } else if (method->IsAbstract()) { + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;", + "abstract method \"%s\"", PrettyMethod(method).c_str()); + return; + } else { + DCHECK(method->IsNative() || method->IsProxyMethod()); + num_regs = num_ins = AbstractMethod::NumArgRegisters(mh.GetShorty()); + if (!method->IsStatic()) { + num_regs++; + num_ins++; + } + } + + void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); + ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, + method, 0, memory)); + size_t cur_reg = num_regs - num_ins; + if (receiver != NULL) { + new_shadow_frame->SetVRegReference(cur_reg, receiver); + ++cur_reg; + } + + size_t arg_offset = (receiver == NULL) ? 0 : 1; + const char* shorty = mh.GetShorty(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } + for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { + DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); + size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; + switch (shorty[shorty_pos + 1]) { + case 'L': { + Object* o = shadow_frame.GetVRegReference(arg_pos); + new_shadow_frame->SetVRegReference(cur_reg, o); + break; + } + case 'J': case 'D': { + uint64_t wide_value = (static_cast(shadow_frame.GetVReg(arg_pos + 1)) << 32) | + static_cast(shadow_frame.GetVReg(arg_pos)); + new_shadow_frame->SetVRegLong(cur_reg, wide_value); + cur_reg++; + arg_offset++; + break; + } + default: + new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); + break; + } + } + + if (LIKELY(Runtime::Current()->IsStarted())) { + (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); + } else { + UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); + } +} + // We use template functions to optimize compiler inlining process. Otherwise, // some parts of the code (like a switch statement) which depend on a constant // parameter would not be inlined while it should be. These constant parameters @@ -533,6 +625,41 @@ static inline void DoFieldGet(Thread* self, ShadowFrame& shadow_frame, } } +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +static void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template +static inline void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + // We lost the reference to the field index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return; + } + MemberOffset field_offset(inst->VRegC_22c()); + const bool is_volatile = false; // iget-x-quick only on non volatile fields. + const uint32_t vregA = inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimInt: + shadow_frame.SetVReg(vregA, static_cast(obj->GetField32(field_offset, is_volatile))); + break; + case Primitive::kPrimLong: + shadow_frame.SetVRegLong(vregA, static_cast(obj->GetField64(field_offset, is_volatile))); + break; + case Primitive::kPrimNot: + shadow_frame.SetVRegReference(vregA, obj->GetFieldObject(field_offset, is_volatile)); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } +} + // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template // specialization. template @@ -591,6 +718,41 @@ static inline void DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, } } +// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template +// specialization. +template +static void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) + NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE; + +template +static inline void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame, + const Instruction* inst) { + Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c()); + if (UNLIKELY(obj == NULL)) { + // We lost the reference to the field index so we cannot get a more + // precised exception message. + ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); + return; + } + MemberOffset field_offset(inst->VRegC_22c()); + const bool is_volatile = false; // iput-x-quick only on non volatile fields. + const uint32_t vregA = inst->VRegA_22c(); + switch (field_type) { + case Primitive::kPrimInt: + obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile); + break; + case Primitive::kPrimLong: + obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile); + break; + case Primitive::kPrimNot: + obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile); + break; + default: + LOG(FATAL) << "Unreachable: " << field_type; + } +} + static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Class* java_lang_string_class = String::GetJavaLangString(); @@ -1770,6 +1932,21 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte DoFieldGet(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; + case Instruction::IGET_QUICK: + PREAMBLE(); + DoIGetQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IGET_WIDE_QUICK: + PREAMBLE(); + DoIGetQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IGET_OBJECT_QUICK: + PREAMBLE(); + DoIGetQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; case Instruction::SGET_BOOLEAN: PREAMBLE(); DoFieldGet(self, shadow_frame, inst); @@ -1840,6 +2017,21 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte DoFieldPut(self, shadow_frame, inst); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); break; + case Instruction::IPUT_QUICK: + PREAMBLE(); + DoIPutQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IPUT_WIDE_QUICK: + PREAMBLE(); + DoIPutQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; + case Instruction::IPUT_OBJECT_QUICK: + PREAMBLE(); + DoIPutQuick(self, shadow_frame, inst); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx); + break; case Instruction::SPUT_BOOLEAN: PREAMBLE(); DoFieldPut(self, shadow_frame, inst); @@ -1925,6 +2117,16 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte DoInvoke(self, shadow_frame, inst, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); break; + case Instruction::INVOKE_VIRTUAL_QUICK: + PREAMBLE(); + DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); + break; + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: + PREAMBLE(); + DoInvokeVirtualQuick(self, shadow_frame, inst, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx); + break; case Instruction::NEG_INT: PREAMBLE(); shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x())); @@ -2715,7 +2917,7 @@ static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeIte inst = inst->Next_2xx(); break; case Instruction::UNUSED_3E ... Instruction::UNUSED_43: - case Instruction::UNUSED_E3 ... Instruction::UNUSED_FF: + case Instruction::UNUSED_EB ... Instruction::UNUSED_FF: case Instruction::UNUSED_73: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: diff --git a/src/mem_map.cc b/src/mem_map.cc index fb19424c48..c75dffa63c 100644 --- a/src/mem_map.cc +++ b/src/mem_map.cc @@ -183,4 +183,26 @@ bool MemMap::Protect(int prot) { return false; } +bool MemMap::ProtectRegion(uint8_t* addr, size_t length, int prot) { + CHECK_GE(addr, base_begin_); + CHECK_LT(addr + length, reinterpret_cast(base_begin_) + base_size_); + + /* + * Align "addr" to a page boundary and adjust "length" appropriately. + * (The address must be page-aligned, the length doesn't need to be, + * but we do need to ensure we cover the same range.) + */ + uint8_t* alignAddr = (uint8_t*) ((uintptr_t) addr & ~(kPageSize-1)); + size_t alignLength = length + (addr - alignAddr); + + if (mprotect(alignAddr, alignLength, prot) == 0) { + prot_ = prot; + return true; + } + + PLOG(ERROR) << "mprotect(" << reinterpret_cast(alignAddr) << ", " << alignLength << ", " + << prot << ") failed"; + return false; +} + } // namespace art diff --git a/src/mem_map.h b/src/mem_map.h index 7310f78ddf..2eb7772705 100644 --- a/src/mem_map.h +++ b/src/mem_map.h @@ -61,6 +61,8 @@ class MemMap { bool Protect(int prot); + bool ProtectRegion(uint8_t* addr, size_t length, int prot); + int GetProtect() const { return prot_; } diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc index 021e984001..87cc3286b3 100644 --- a/src/verifier/method_verifier.cc +++ b/src/verifier/method_verifier.cc @@ -278,6 +278,8 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca declaring_class_(NULL), interesting_dex_pc_(-1), monitor_enter_dex_pcs_(NULL), + accessed_field(NULL), + invoked_method(NULL), have_pending_hard_failure_(false), have_pending_runtime_throw_failure_(false), new_instance_count_(0), @@ -308,6 +310,54 @@ void MethodVerifier::FindLocksAtDexPc() { Verify(); } +mirror::Field* MethodVerifier::FindAccessedFieldAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) { + MethodHelper mh(m); + MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), + mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(), + m, m->GetAccessFlags(), false, true); + mirror::Field* field = NULL; + verifier.interesting_dex_pc_ = dex_pc; + verifier.accessed_field = &field; + verifier.FindAccessedFieldAtDexPc(); + return field; +} + +void MethodVerifier::FindAccessedFieldAtDexPc() { + CHECK(accessed_field != NULL); + CHECK(code_item_ != NULL); // This only makes sense for methods with code. + + // Strictly speaking, we ought to be able to get away with doing a subset of the full method + // verification. In practice, the phase we want relies on data structures set up by all the + // earlier passes, so we just run the full method verification and bail out early when we've + // got what we wanted. + Verify(); +} + +mirror::AbstractMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) { + MethodHelper mh(m); + MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), + mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(), + m, m->GetAccessFlags(), false, true); + mirror::AbstractMethod* method = NULL; + verifier.interesting_dex_pc_ = dex_pc; + verifier.invoked_method = &method; + verifier.FindInvokedMethodAtDexPc(); + return method; +} + +void MethodVerifier::FindInvokedMethodAtDexPc() { + CHECK(invoked_method != NULL); + CHECK(code_item_ != NULL); // This only makes sense for methods with code. + + // Strictly speaking, we ought to be able to get away with doing a subset of the full method + // verification. In practice, the phase we want relies on data structures set up by all the + // earlier passes, so we just run the full method verification and bail out early when we've + // got what we wanted. + Verify(); +} + bool MethodVerifier::Verify() { // If there aren't any instructions, make sure that's expected, then exit successfully. if (code_item_ == NULL) { @@ -2361,10 +2411,63 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, false); break; + // Special instructions. + // + // Note: the following instructions encode offsets derived from class linking. + // As such they use Class*/Field*/AbstractMethod* as these offsets only have + // meaning if the class linking and resolution were successful. + case Instruction::IGET_QUICK: + VerifyIGetQuick(inst, reg_types_.Integer(), true); + break; + case Instruction::IGET_WIDE_QUICK: + VerifyIGetQuick(inst, reg_types_.LongLo(), true); + break; + case Instruction::IGET_OBJECT_QUICK: + VerifyIGetQuick(inst, reg_types_.JavaLangObject(false), false); + break; + case Instruction::IPUT_QUICK: + VerifyIPutQuick(inst, reg_types_.Integer(), true); + break; + case Instruction::IPUT_WIDE_QUICK: + VerifyIPutQuick(inst, reg_types_.LongLo(), true); + break; + case Instruction::IPUT_OBJECT_QUICK: + VerifyIPutQuick(inst, reg_types_.JavaLangObject(false), false); + break; + case Instruction::INVOKE_VIRTUAL_QUICK: + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { + bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); + mirror::AbstractMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range); + if (called_method != NULL) { + const char* descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + if (!return_type.IsLowHalf()) { + work_line_->SetResultRegisterType(return_type); + } else { + work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); + } + just_set_result = true; + } + break; + } + /* These should never appear during verification. */ + case Instruction::UNUSED_3E: + case Instruction::UNUSED_3F: + case Instruction::UNUSED_40: + case Instruction::UNUSED_41: + case Instruction::UNUSED_42: + case Instruction::UNUSED_43: + case Instruction::UNUSED_73: + case Instruction::UNUSED_79: + case Instruction::UNUSED_7A: + case Instruction::UNUSED_EB: + case Instruction::UNUSED_EC: case Instruction::UNUSED_ED: case Instruction::UNUSED_EE: case Instruction::UNUSED_EF: + case Instruction::UNUSED_F0: + case Instruction::UNUSED_F1: case Instruction::UNUSED_F2: case Instruction::UNUSED_F3: case Instruction::UNUSED_F4: @@ -2375,30 +2478,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::UNUSED_F9: case Instruction::UNUSED_FA: case Instruction::UNUSED_FB: - case Instruction::UNUSED_F0: - case Instruction::UNUSED_F1: - case Instruction::UNUSED_E3: - case Instruction::UNUSED_E8: - case Instruction::UNUSED_E7: - case Instruction::UNUSED_E4: - case Instruction::UNUSED_E9: case Instruction::UNUSED_FC: - case Instruction::UNUSED_E5: - case Instruction::UNUSED_EA: case Instruction::UNUSED_FD: - case Instruction::UNUSED_E6: - case Instruction::UNUSED_EB: case Instruction::UNUSED_FE: - case Instruction::UNUSED_3E: - case Instruction::UNUSED_3F: - case Instruction::UNUSED_40: - case Instruction::UNUSED_41: - case Instruction::UNUSED_42: - case Instruction::UNUSED_43: - case Instruction::UNUSED_73: - case Instruction::UNUSED_79: - case Instruction::UNUSED_7A: - case Instruction::UNUSED_EC: case Instruction::UNUSED_FF: Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_); break; @@ -2802,7 +2884,7 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* } } // We use vAA as our expected arg count, rather than res_method->insSize, because we need to - // match the call to the signature. Also, we might might be calling through an abstract method + // match the call to the signature. Also, we might be calling through an abstract method // definition (which doesn't have register count values). const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); /* caught by static verifier */ @@ -2882,6 +2964,121 @@ mirror::AbstractMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* } } +mirror::AbstractMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, + bool is_range) { + DCHECK(Runtime::Current()->IsStarted()); + CHECK(invoked_method != NULL || accessed_field != NULL) + << "We should not be verifying " << inst->Name(); + const RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range); + if (actual_arg_type.IsConflict()) { // GetInvocationThis failed. + return NULL; + } + mirror::Class* this_class = NULL; + if (!actual_arg_type.IsUnresolvedTypes()) { + this_class = actual_arg_type.GetClass(); + } else { + const std::string& descriptor(actual_arg_type.GetDescriptor()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + this_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + if (this_class == NULL) { + Thread::Current()->ClearException(); + // Look for a system class + this_class = class_linker->FindClass(descriptor.c_str(), NULL); + } + } + CHECK(this_class != NULL) << "Cannot get Class* for type " << actual_arg_type; + mirror::ObjectArray* vtable = this_class->GetVTable(); + CHECK(vtable != NULL); + uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + CHECK(vtable_index < vtable->GetLength()); + mirror::AbstractMethod* res_method = vtable->Get(vtable_index); + CHECK(!Thread::Current()->IsExceptionPending()); + // TODO: we should move the code below to FindInvokedMethodAtDexPc. Once the + // method is verified, we could access the information we need from register + // lines for the dex pc we are looking for. + if (invoked_method != NULL && work_insn_idx_ == interesting_dex_pc_) { + // We've been requested to tell which method is invoked at this dex pc. + *invoked_method = res_method; + } + if (res_method == NULL) { + return NULL; + } + CHECK(!res_method->IsDirect() && !res_method->IsStatic()); + + // We use vAA as our expected arg count, rather than res_method->insSize, because we need to + // match the call to the signature. Also, we might be calling through an abstract method + // definition (which doesn't have register count values). + const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); + /* caught by static verifier */ + DCHECK(is_range || expected_args <= 5); + if (expected_args > code_item_->outs_size_) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args + << ") exceeds outsSize (" << code_item_->outs_size_ << ")"; + return NULL; + } + + /* + * Check the "this" argument, which must be an instance of the class that declared the method. + * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a + * rigorous check here (which is okay since we have to do it at runtime). + */ + if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; + return NULL; + } + if (!actual_arg_type.IsZero()) { + mirror::Class* klass = res_method->GetDeclaringClass(); + const RegType& res_method_class = + reg_types_.FromClass(ClassHelper(klass).GetDescriptor(), klass, + klass->CannotBeAssignedFromOtherTypes()); + if (!res_method_class.IsAssignableFrom(actual_arg_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type + << "' not instance of '" << res_method_class << "'"; + return NULL; + } + } + /* + * Process the target method's signature. This signature may or may not + * have been verified, so we can't assume it's properly formed. + */ + MethodHelper mh(res_method); + const DexFile::TypeList* params = mh.GetParameterTypeList(); + size_t params_size = params == NULL ? 0 : params->Size(); + uint32_t arg[5]; + if (!is_range) { + inst->GetArgs(arg); + } + size_t actual_args = 1; + for (size_t param_index = 0; param_index < params_size; param_index++) { + if (actual_args >= expected_args) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" << PrettyMethod(res_method) + << "'. Expected " << expected_args << " arguments, processing argument " << actual_args + << " (where longs/doubles count twice)."; + return NULL; + } + const char* descriptor = + mh.GetTypeDescriptorFromTypeIdx(params->GetTypeItem(param_index).type_idx_); + if (descriptor == NULL) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method) + << " missing signature component"; + return NULL; + } + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; + if (!work_line_->VerifyRegisterType(get_reg, reg_type)) { + return res_method; + } + actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1; + } + if (actual_args != expected_args) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method) + << " expected " << expected_args << " arguments, found " << actual_args; + return NULL; + } else { + return res_method; + } +} + void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range) { uint32_t type_idx; if (!is_filled) { @@ -3244,6 +3441,185 @@ void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_ty } } +// Look for an instance field with this offset. +// TODO: we may speed up the search if offsets are sorted by doing a quick search. +static mirror::Field* FindInstanceFieldWithOffset(mirror::Class* klass, + uint32_t field_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ObjectArray* instance_fields = klass->GetIFields(); + if (instance_fields != NULL) { + for (int32_t i = 0, e = instance_fields->GetLength(); i < e; ++i) { + mirror::Field* field = instance_fields->Get(i); + if (field->GetOffset().Uint32Value() == field_offset) { + return field; + } + } + } + if (klass->GetSuperClass() != NULL) { + return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset); + } else { + return NULL; + } +} + +void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) { + DCHECK(Runtime::Current()->IsStarted()); + CHECK(accessed_field != NULL || invoked_method != NULL) + << "We should not be verifying " << inst->Name(); + const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); + mirror::Class* object_class = NULL; + if (!object_type.IsUnresolvedTypes()) { + object_class = object_type.GetClass(); + } else { + // We need to resolve the class from its descriptor. + const std::string& descriptor(object_type.GetDescriptor()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + object_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + if (object_class == NULL) { + Thread::Current()->ClearException(); + // Look for a system class + object_class = class_linker->FindClass(descriptor.c_str(), NULL); + } + } + CHECK(object_class != NULL) << "Cannot get Class* for type " << object_type; + uint32_t field_offset = static_cast(inst->VRegC_22c()); + mirror::Field* field = FindInstanceFieldWithOffset(object_class, field_offset); + CHECK(field != NULL); + // TODO: we should move the code below to FindAccessedFieldAtDexPc. Once the + // method is verified, we could access the information we need from register + // lines for the dex pc we are looking for. + if (accessed_field != NULL && work_insn_idx_ == interesting_dex_pc_) { + // We've been requested to tell which field is accessed at this dex pc. + *accessed_field = field; + } + const char* descriptor = FieldHelper(field).GetTypeDescriptor(); + mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader(); + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); + const uint32_t vregA = inst->VRegA_22c(); + if (is_primitive) { + if (field_type.Equals(insn_type) || + (field_type.IsFloat() && insn_type.IsIntegralTypes()) || + (field_type.IsDouble() && insn_type.IsLongTypes())) { + // expected that read is of the correct primitive type or that int reads are reading + // floats or long reads are reading doubles + } else { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << field_type << "' in get"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << field_type + << "' in get-object"; + work_line_->SetRegisterType(vregA, reg_types_.Conflict()); + return; + } + } + if (!field_type.IsLowHalf()) { + work_line_->SetRegisterType(vregA, field_type); + } else { + work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(®_types_)); + } +} + +void MethodVerifier::VerifyIPutQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) { + DCHECK(Runtime::Current()->IsStarted()); + CHECK(accessed_field != NULL || invoked_method != NULL) + << "We should not be verifying " << inst->Name(); + const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); + mirror::Class* object_class = NULL; + if (!object_type.IsUnresolvedTypes()) { + object_class = object_type.GetClass(); + } else { + // We need to resolve the class from its descriptor. + const std::string& descriptor(object_type.GetDescriptor()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + object_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + if (object_class == NULL) { + Thread::Current()->ClearException(); + // Look for a system class + object_class = class_linker->FindClass(descriptor.c_str(), NULL); + } + } + CHECK(object_class != NULL) << "Cannot get Class* for type " << object_type; + uint32_t field_offset = static_cast(inst->VRegC_22c()); + mirror::Field* field = FindInstanceFieldWithOffset(object_class, field_offset); + CHECK(field != NULL); + // TODO: like VerifyIGetQuick, we should move the code below to + // FindAccessedFieldAtDexPc. + if (accessed_field != NULL && work_insn_idx_ == interesting_dex_pc_) { + // We've been requested to tell which field is accessed at this dex pc. + *accessed_field = field; + } + const char* descriptor = FieldHelper(field).GetTypeDescriptor(); + mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader(); + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); + if (field != NULL) { + if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { + Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) + << " from other class " << GetDeclaringClass(); + return; + } + } + const uint32_t vregA = inst->VRegA_22c(); + if (is_primitive) { + // Primitive field assignability rules are weaker than regular assignability rules + bool instruction_compatible; + bool value_compatible; + const RegType& value_type = work_line_->GetRegisterType(vregA); + if (field_type.IsIntegralTypes()) { + instruction_compatible = insn_type.IsIntegralTypes(); + value_compatible = value_type.IsIntegralTypes(); + } else if (field_type.IsFloat()) { + instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int + value_compatible = value_type.IsFloatTypes(); + } else if (field_type.IsLong()) { + instruction_compatible = insn_type.IsLong(); + value_compatible = value_type.IsLongTypes(); + } else if (field_type.IsDouble()) { + instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long + value_compatible = value_type.IsDoubleTypes(); + } else { + instruction_compatible = false; // reference field with primitive store + value_compatible = false; // unused + } + if (!instruction_compatible) { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << field_type + << "' in put"; + return; + } + if (!value_compatible) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA + << " of type " << value_type + << " but expected " << field_type + << " for store to " << PrettyField(field) << " in put"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << field_type + << "' in put-object"; + return; + } + work_line_->VerifyRegisterType(vregA, field_type); + } +} + bool MethodVerifier::CheckNotMoveException(const uint16_t* insns, int insn_idx) { if ((insns[insn_idx] & 0xff) == Instruction::MOVE_EXCEPTION) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid use of move-exception"; diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h index 198d8cb17b..da6497926d 100644 --- a/src/verifier/method_verifier.h +++ b/src/verifier/method_verifier.h @@ -197,11 +197,23 @@ class MethodVerifier { LOCKS_EXCLUDED(safecast_map_lock_); // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding - // to the locks held at 'dex_pc' in 'm'. + // to the locks held at 'dex_pc' in method 'm'. static void FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc, std::vector& monitor_enter_dex_pcs) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Returns the accessed field corresponding to the quick instruction's field + // offset at 'dex_pc' in method 'm'. + static mirror::Field* FindAccessedFieldAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Returns the invoked method corresponding to the quick instruction's vtable + // index at 'dex_pc' in method 'm'. + static mirror::AbstractMethod* FindInvokedMethodAtDexPc(mirror::AbstractMethod* m, + uint32_t dex_pc) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void Shutdown(); @@ -254,6 +266,10 @@ class MethodVerifier { void FindLocksAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void FindAccessedFieldAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void FindInvokedMethodAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + /* * Compute the width of the instruction at each address in the instruction stream, and store it in * insn_flags_. Addresses that are in the middle of an instruction, or that are part of switch @@ -484,6 +500,16 @@ class MethodVerifier { bool is_primitive, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Perform verification of an iget-quick instruction. + void VerifyIGetQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Perform verification of an iput-quick instruction. + void VerifyIPutQuick(const Instruction* inst, const RegType& insn_type, + bool is_primitive) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Resolves a class based on an index and performs access checks to ensure the referrer can // access the resolved class. const RegType& ResolveClassAndCheckAccess(uint32_t class_idx) @@ -532,6 +558,10 @@ class MethodVerifier { bool is_range, bool is_super) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::AbstractMethod* VerifyInvokeVirtualQuickArgs(const Instruction* inst, + bool is_range) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + /* * Verify that the target instruction is not "move-exception". It's important that the only way * to execute a move-exception is as the first instruction of an exception handler. @@ -646,11 +676,18 @@ class MethodVerifier { const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class. // Instruction widths and flags, one entry per code unit. UniquePtr insn_flags_; - // The dex PC of a FindLocksAtDexPc request, -1 otherwise. + // The dex PC of a FindLocksAtDexPc, FindAccessedFieldAtDexPc or + // FindInvokedMethodAtDexPc request, -1 otherwise. uint32_t interesting_dex_pc_; // The container into which FindLocksAtDexPc should write the registers containing held locks, // NULL if we're not doing FindLocksAtDexPc. std::vector* monitor_enter_dex_pcs_; + // The pointer into which FindAccessedFieldAtDexPc should write the accessed field, + // NULL if we're not doing FindAccessedFieldAtDexPc. + mirror::Field** accessed_field; + // The pointer into which FindInvokedMethodAtDexPc should write the invoked method, + // NULL if we're not doing FindInvokedMethodAtDexPc. + mirror::AbstractMethod** invoked_method; // The types of any error that occurs. std::vector failures_; diff --git a/test/201-built-in-exception-detail-messages/src/Main.java b/test/201-built-in-exception-detail-messages/src/Main.java index f8da6446cf..24ee6e0eb3 100644 --- a/test/201-built-in-exception-detail-messages/src/Main.java +++ b/test/201-built-in-exception-detail-messages/src/Main.java @@ -286,10 +286,19 @@ public class Main { } } + // Defeat the fact that null's are untyped for precise detail message creation with quickening. + private static Object returnNullObject() { + return null; + } + + private static A returnNullA() { + return null; + } + private static void nullPointers() throws Exception { // Invoke method. try { - Object o = null; + Object o = returnNullObject(); o.hashCode(); fail(); } catch (NullPointerException ex) { @@ -298,7 +307,7 @@ public class Main { // Read field. try { - A a = null; + A a = returnNullA(); int i = a.i; fail(); } catch (NullPointerException ex) { @@ -307,7 +316,7 @@ public class Main { // Write field. try { - A a = null; + A a = returnNullA(); a.i = 1; fail(); } catch (NullPointerException ex) { @@ -332,7 +341,7 @@ public class Main { assertEquals("Attempt to write to null array", ex.getMessage()); } - // Invoke method. + // Array length. try { int[] is = null; int i = is.length; -- cgit v1.2.3-59-g8ed1b From 7467ee05012e1fd9834df74663c1ebda46f5636b Mon Sep 17 00:00:00 2001 From: Dragos Sbirlea Date: Fri, 21 Jun 2013 09:20:34 -0700 Subject: Added support for SEA IR. - Modified makefile to take the existance of SEA_IR_ART file to mean "switch to sea ir mode". - Switching SEA IR mode on leads to the new compiler being fed the fibonacci methods only, if they are used as input. - Added partial support for the control flow subgraph of the SEA IR (instruction nodes and region nodes for conditional and unconditional branches). Change-Id: I29020b8e2df5a00fde75715c3683cc25038589f4 Conflicts: src/compiler/driver/compiler_driver.cc --- .gitignore | 3 + build/Android.common.mk | 18 ++++ build/Android.libart-compiler.mk | 5 + src/compiler/dex/frontend.cc | 5 + src/compiler/dex/frontend.h | 7 ++ src/compiler/driver/compiler_driver.cc | 21 +++- src/compiler/driver/compiler_driver.h | 1 + src/compiler/sea_ir/frontend.cc | 81 ++++++++++++++++ src/compiler/sea_ir/sea.cc | 172 +++++++++++++++++++++++++++++++++ src/compiler/sea_ir/sea.h | 102 +++++++++++++++++++ src/dex2oat.cc | 6 ++ src/runtime.cc | 4 + src/runtime.h | 13 +++ 13 files changed, 436 insertions(+), 2 deletions(-) create mode 100644 src/compiler/sea_ir/frontend.cc create mode 100644 src/compiler/sea_ir/sea.cc create mode 100644 src/compiler/sea_ir/sea.h (limited to 'src/compiler/driver/compiler_driver.h') diff --git a/.gitignore b/.gitignore index 5973a95e1f..1cdfed9231 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ USE_LLVM_COMPILER +USE_PORTABLE_COMPILER +SMALL_ART +SEA_IR_ART diff --git a/build/Android.common.mk b/build/Android.common.mk index a7bf944596..33c5ac68bf 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -23,6 +23,15 @@ ifeq ($(WITH_ART_SMALL_MODE), true) ART_SMALL_MODE := true endif +ART_SEA_IR_MODE := false +ifneq ($(wildcard art/SEA_IR_ART),) +$(info Enabling ART_SEA_IR_MODE because of existence of art/SEA_IR_ART) +ART_SEA_IR_MODE := true +endif +ifeq ($(WITH_ART_SEA_IR_MODE), true) +ART_SEA_IR_MODE := true +endif + ART_USE_PORTABLE_COMPILER := false ifneq ($(wildcard art/USE_PORTABLE_COMPILER),) $(info Enabling ART_USE_PORTABLE_COMPILER because of existence of art/USE_PORTABLE_COMPILER) @@ -71,6 +80,10 @@ ifeq ($(ART_SMALL_MODE),true) art_cflags += -DART_SMALL_MODE=1 endif +ifeq ($(ART_SEA_IR_MODE),true) + art_cflags += -DART_SEA_IR_MODE=1 +endif + # TODO: enable -std=gnu++0x for auto support when on Ubuntu 12.04 LTS (Precise Pangolin) # On 10.04 LTS (Lucid Lynx), it can cause dependencies on GLIBCXX_3.4.14 version symbols. @@ -302,6 +315,11 @@ LIBART_COMMON_SRC_FILES += \ src/oat/runtime/support_throw.cc \ src/oat/runtime/support_interpreter.cc +ifeq ($(ART_SEA_IR_MODE),true) +LIBART_COMMON_SRC_FILES += \ + src/compiler/sea_ir/sea.cc +endif + LIBART_TARGET_SRC_FILES := \ $(LIBART_COMMON_SRC_FILES) \ src/base/logging_android.cc \ diff --git a/build/Android.libart-compiler.mk b/build/Android.libart-compiler.mk index 708a488144..25e6997ffc 100644 --- a/build/Android.libart-compiler.mk +++ b/build/Android.libart-compiler.mk @@ -76,6 +76,11 @@ LIBART_COMPILER_SRC_FILES := \ src/elf_writer.cc \ src/elf_writer_quick.cc +ifeq ($(ART_SEA_IR_MODE),true) +LIBART_COMPILER_SRC_FILES += \ + src/compiler/sea_ir/frontend.cc +endif + LIBART_COMPILER_CFLAGS := ifeq ($(ART_USE_PORTABLE_COMPILER),true) LIBART_COMPILER_SRC_FILES += src/elf_writer_mclinker.cc diff --git a/src/compiler/dex/frontend.cc b/src/compiler/dex/frontend.cc index e015645584..c528d8680c 100644 --- a/src/compiler/dex/frontend.cc +++ b/src/compiler/dex/frontend.cc @@ -29,6 +29,8 @@ #include "backend.h" #include "base/logging.h" + + namespace { #if !defined(ART_USE_PORTABLE_COMPILER) pthread_once_t llvm_multi_init = PTHREAD_ONCE_INIT; @@ -104,6 +106,7 @@ static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes //(1 << kDebugShowSummaryMemoryUsage) | 0; + static CompiledMethod* CompileMethod(CompilerDriver& compiler, const CompilerBackend compiler_backend, const DexFile::CodeItem* code_item, @@ -277,6 +280,8 @@ CompiledMethod* CompileOneMethod(CompilerDriver& compiler, ); } + + } // namespace art extern "C" art::CompiledMethod* diff --git a/src/compiler/dex/frontend.h b/src/compiler/dex/frontend.h index dc57a23485..69d7f7728c 100644 --- a/src/compiler/dex/frontend.h +++ b/src/compiler/dex/frontend.h @@ -20,6 +20,11 @@ #include "dex_file.h" #include "dex_instruction.h" + + + + + namespace llvm { class Module; class LLVMContext; @@ -116,4 +121,6 @@ extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver, jobject class_loader, const art::DexFile& dex_file); + + #endif // ART_SRC_COMPILER_DEX_COMPILER_H_ diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc index 4a6eb962e3..122988aa5b 100644 --- a/src/compiler/driver/compiler_driver.cc +++ b/src/compiler/driver/compiler_driver.cc @@ -374,6 +374,11 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet dex_to_dex_compiler_ = FindFunction(compiler_so_name, compiler_library_, "ArtCompileDEX"); + sea_ir_compiler_ = NULL; + if (Runtime::Current()->IsSeaIRMode()) { + sea_ir_compiler_ = FindFunction(compiler_so_name, compiler_library_, "SeaIrCompileMethod"); + } + init_compiler_context(*this); if (compiler_backend_ == kPortable) { @@ -2149,10 +2154,22 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t // Do compile small methods. dont_compile = false; } - if (!dont_compile) { - compiled_method = (*compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, + bool use_sea = false; + + if (Runtime::Current()->IsSeaIRMode()) { + use_sea = true; + } + if (use_sea) { + use_sea = (std::string::npos != PrettyMethod(method_idx, dex_file).find("fibonacci")); + } + if (!use_sea) { + compiled_method = (*compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file); + } else { + compiled_method = (*sea_ir_compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx, + method_idx, class_loader, dex_file); + } CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file); } else if (allow_dex_to_dex_compilation) { // TODO: add a mode to disable DEX-to-DEX compilation ? diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h index fdd2149f7f..b37b74b042 100644 --- a/src/compiler/driver/compiler_driver.h +++ b/src/compiler/driver/compiler_driver.h @@ -404,6 +404,7 @@ class CompilerDriver { uint32_t class_dex_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file); CompilerFn compiler_; + CompilerFn sea_ir_compiler_; CompilerFn dex_to_dex_compiler_; diff --git a/src/compiler/sea_ir/frontend.cc b/src/compiler/sea_ir/frontend.cc new file mode 100644 index 0000000000..d4e1c7e740 --- /dev/null +++ b/src/compiler/sea_ir/frontend.cc @@ -0,0 +1,81 @@ + +#include + +#include "compiler/driver/compiler_driver.h" + + +#include "compiler/llvm/llvm_compilation_unit.h" +#include "compiler/dex/portable/mir_to_gbc.h" + +#include "leb128.h" +#include "mirror/object.h" +#include "runtime.h" +#include "base/logging.h" + +#ifdef ART_SEA_IR_MODE +#include "compiler/sea_ir/sea.h" +#endif + + + + +#ifdef ART_SEA_IR_MODE +#include "compiler/sea_ir/sea.h" +namespace art { + +static CompiledMethod* CompileMethodWithSeaIr(CompilerDriver& compiler, + const CompilerBackend compiler_backend, + const DexFile::CodeItem* code_item, + uint32_t access_flags, InvokeType invoke_type, + uint32_t class_def_idx, uint32_t method_idx, + jobject class_loader, const DexFile& dex_file +#if defined(ART_USE_PORTABLE_COMPILER) + , llvm::LlvmCompilationUnit* llvm_compilation_unit +#endif +) +{ + VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "..."; + sea_ir::SeaGraph* sg = sea_ir::SeaGraph::GetCurrentGraph(); + sg->CompileMethod(code_item, class_def_idx, method_idx, dex_file); + sg->DumpSea("/tmp/temp.dot"); + CHECK(0 && "No SEA compiled function exists yet."); + return NULL; +} + + +CompiledMethod* SeaIrCompileOneMethod(CompilerDriver& compiler, + const CompilerBackend backend, + const DexFile::CodeItem* code_item, + uint32_t access_flags, + InvokeType invoke_type, + uint32_t class_def_idx, + uint32_t method_idx, + jobject class_loader, + const DexFile& dex_file, + llvm::LlvmCompilationUnit* llvm_compilation_unit) +{ + return CompileMethodWithSeaIr(compiler, backend, code_item, access_flags, invoke_type, class_def_idx, + method_idx, class_loader, dex_file +#if defined(ART_USE_PORTABLE_COMPILER) + , llvm_compilation_unit +#endif + + ); +} + +extern "C" art::CompiledMethod* + SeaIrCompileMethod(art::CompilerDriver& compiler, + const art::DexFile::CodeItem* code_item, + uint32_t access_flags, art::InvokeType invoke_type, + uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, + const art::DexFile& dex_file) +{ + // TODO: check method fingerprint here to determine appropriate backend type. Until then, use build default + art::CompilerBackend backend = compiler.GetCompilerBackend(); + return art::SeaIrCompileOneMethod(compiler, backend, code_item, access_flags, invoke_type, + class_def_idx, method_idx, class_loader, dex_file, + NULL /* use thread llvm_info */); +} +#endif + +} // end namespace art diff --git a/src/compiler/sea_ir/sea.cc b/src/compiler/sea_ir/sea.cc new file mode 100644 index 0000000000..e08558fa2d --- /dev/null +++ b/src/compiler/sea_ir/sea.cc @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2013 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 "compiler/sea_ir/sea.h" +#include "file_output_stream.h" + + + +namespace sea_ir { + + +SeaGraph SeaGraph::graph_; +int SeaNode::current_max_node_id_ = 0; + +SeaGraph* SeaGraph::GetCurrentGraph() { + return &sea_ir::SeaGraph::graph_; +} + +void SeaGraph::DumpSea(std::string filename) const { + std::string result; + result += "digraph seaOfNodes {\n"; + for(std::vector::const_iterator cit = regions_.begin(); cit != regions_.end(); cit++) { + result += (*cit)->ToDot(); + } + result += "}\n"; + art::File* file = art::OS::OpenFile(filename.c_str(), true, true); + art::FileOutputStream fos(file); + fos.WriteFully(result.c_str(), result.size()); + LOG(INFO) << "Written SEA string to file..."; +} + +void SeaGraph::CompileMethod(const art::DexFile::CodeItem* code_item, + uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file) { + const uint16_t* code = code_item->insns_; + const size_t size_in_code_units = code_item->insns_size_in_code_units_; + + Region* r = NULL; + // This maps target instruction pointers to their corresponding region objects. + std::map target_regions; + size_t i = 0; + + // Pass 1: Find the start instruction of basic blocks, as targets and flow-though of branches. + while (i < size_in_code_units) { + const art::Instruction* inst = art::Instruction::At(&code[i]); + if (inst->IsBranch()||inst->IsUnconditional()) { + int32_t offset = inst->GetTargetOffset(); + if (target_regions.end() == target_regions.find(&code[i+offset])) { + Region* region = GetNewRegion(); + target_regions.insert(std::pair(&code[i+offset], region)); + } + if (inst->IsFlowthrough() && + (target_regions.end() == target_regions.find(&code[i+inst->SizeInCodeUnits()]))) { + Region* region = GetNewRegion(); + target_regions.insert(std::pair(&code[i+inst->SizeInCodeUnits()], region)); + } + } + i += inst->SizeInCodeUnits(); + } + + + // Pass 2: Assign instructions to region nodes and + // assign branches their control flow successors. + i = 0; + r = GetNewRegion(); + sea_ir::SeaNode* last_node = NULL; + sea_ir::SeaNode* node = NULL; + while (i < size_in_code_units) { + const art::Instruction* inst = art::Instruction::At(&code[i]); //TODO: find workaround for this + last_node = node; + node = new sea_ir::SeaNode(inst); + + if (inst->IsBranch() || inst->IsUnconditional()) { + int32_t offset = inst->GetTargetOffset(); + std::map::iterator it = target_regions.find(&code[i+offset]); + DCHECK(it != target_regions.end()); + node->AddSuccessor(it->second); + } + + std::map::iterator it = target_regions.find(&code[i]); + if (target_regions.end() != it) { + // Get the already created region because this is a branch target. + Region* nextRegion = it->second; + if (last_node->GetInstruction()->IsBranch() && last_node->GetInstruction()->IsFlowthrough()) { + last_node->AddSuccessor(nextRegion); + + } + r = nextRegion; + } + + LOG(INFO) << inst->GetDexPc(code) << "*** " << inst->DumpString(&dex_file) + << " region:" <StringId() << std::endl; + r->AddChild(node); + i += inst->SizeInCodeUnits(); + } + +} + + +Region* SeaGraph::GetNewRegion() { + Region* new_region = new Region(); + AddRegion(new_region); + return new_region; +} + +void SeaGraph::AddRegion(Region* r) { + DCHECK(r) << "Tried to add NULL region to SEA graph."; + regions_.push_back(r); +} +void Region::AddChild(sea_ir::SeaNode* instruction) { + DCHECK(inst) << "Tried to add NULL instruction to region node."; + instructions_.push_back(instruction); +} + +SeaNode* Region::GetLastChild() const { + if (instructions_.size()>0) { + return instructions_.back(); + } + return NULL; +} + +std::string SeaNode::ToDot() const { + std::string node = "// Instruction: \n" + StringId() + + " [label=\"" + instruction_->DumpString(NULL) + "\"];\n"; + + for(std::vector::const_iterator cit = successors_.begin(); + cit != successors_.end(); cit++) { + DCHECK(NULL != *cit) << "Null successor found for SeaNode" << StringId() << "."; + node += StringId() + " -> " + (*cit)->StringId() + ";\n\n"; + } + return node; +} + +std::string SeaNode::StringId() const { + std::stringstream ss; + ss << id_; + return ss.str(); +} + +std::string Region::ToDot() const { + std::string result = "// Region: \n" + + StringId() + " [label=\"region " + StringId() + "\"];"; + + for(std::vector::const_iterator cit = instructions_.begin(); + cit != instructions_.end(); cit++) { + result += (*cit)->ToDot(); + result += StringId() + " -> " + (*cit)->StringId() + ";\n"; + } + + result += "// End Region.\n"; + return result; +} + +void SeaNode::AddSuccessor(SeaNode* successor) { + DCHECK(successor) << "Tried to add NULL successor to SEA node."; + successors_.push_back(successor); + return; +} + +} // end namespace diff --git a/src/compiler/sea_ir/sea.h b/src/compiler/sea_ir/sea.h new file mode 100644 index 0000000000..0ebd4d02d4 --- /dev/null +++ b/src/compiler/sea_ir/sea.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013 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 "dex_file.h" +#include "dex_instruction.h" + +#ifndef SEA_IR_H_ +#define SEA_IR_H_ + +#include +#include + +namespace sea_ir { + + +class SeaNode { + public: + explicit SeaNode(const art::Instruction* in):id_(GetNewId()), instruction_(in), successors_() {}; + explicit SeaNode():id_(GetNewId()), instruction_(NULL) {}; + void AddSuccessor(SeaNode* successor); + const art::Instruction* GetInstruction() { + DCHECK(NULL != instruction_); + return instruction_; + } + std::string StringId() const; + // Returns a dot language formatted string representing the node and + // (by convention) outgoing edges, so that the composition of theToDot() of all nodes + // builds a complete dot graph (without prolog and epilog though). + virtual std::string ToDot() const; + virtual ~SeaNode(){}; + + protected: + // Returns the id of the current block as string + + static int GetNewId() { + return current_max_node_id_++; + } + + + private: + const int id_; + const art::Instruction* const instruction_; + std::vector successors_; + static int current_max_node_id_; +}; + + + +class Region : public SeaNode { + public: + explicit Region():SeaNode() {} + void AddChild(sea_ir::SeaNode* instruction); + SeaNode* GetLastChild() const; + + // Returns a dot language formatted string representing the node and + // (by convention) outgoing edges, so that the composition of theToDot() of all nodes + // builds a complete dot graph (without prolog and epilog though). + virtual std::string ToDot() const; + + private: + std::vector instructions_; +}; + + + +class SeaGraph { + public: + static SeaGraph* GetCurrentGraph(); + void CompileMethod(const art::DexFile::CodeItem* code_item, + uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file); + // Returns a string representation of the region and its Instruction children + void DumpSea(std::string filename) const; + /*** Static helper functions follow: ***/ + static int ParseInstruction(const uint16_t* code_ptr, + art::DecodedInstruction* decoded_instruction); + static bool IsInstruction(const uint16_t* code_ptr); + + private: + // Registers the parameter as a child region of the SeaGraph instance + void AddRegion(Region* r); + // Returns new region and registers it with the SeaGraph instance + Region* GetNewRegion(); + static SeaGraph graph_; + std::vector regions_; +}; + + +} // end namespace sea_ir +#endif diff --git a/src/dex2oat.cc b/src/dex2oat.cc index b5dc319306..7cf54b4eee 100644 --- a/src/dex2oat.cc +++ b/src/dex2oat.cc @@ -823,6 +823,12 @@ static int dex2oat(int argc, char** argv) { options.push_back(std::make_pair("-small", reinterpret_cast(NULL))); #endif // ART_SMALL_MODE + +#ifdef ART_SEA_IR_MODE + options.push_back(std::make_pair("-sea_ir", reinterpret_cast(NULL))); +#endif + + Dex2Oat* p_dex2oat; if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set, thread_count, support_debugging)) { diff --git a/src/runtime.cc b/src/runtime.cc index 1889d88114..0a38eb97ab 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -357,6 +357,7 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->small_mode_method_threshold_ = Runtime::kDefaultSmallModeMethodThreshold; parsed->small_mode_method_dex_size_limit_ = Runtime::kDefaultSmallModeMethodDexSizeLimit; + parsed->sea_ir_mode_ = false; // gLogVerbosity.class_linker = true; // TODO: don't check this in! // gLogVerbosity.compiler = true; // TODO: don't check this in! // gLogVerbosity.heap = true; // TODO: don't check this in! @@ -566,6 +567,8 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b Trace::SetDefaultClockSource(kProfilerClockSourceDual); } else if (option == "-small") { parsed->small_mode_ = true; + }else if (option == "-sea_ir") { + parsed->sea_ir_mode_ = true; } else if (StartsWith(option, "-small-mode-methods-max:")) { parsed->small_mode_method_threshold_ = ParseIntegerOrDie(option); } else if (StartsWith(option, "-small-mode-methods-size-max:")) { @@ -794,6 +797,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { small_mode_method_threshold_ = options->small_mode_method_threshold_; small_mode_method_dex_size_limit_ = options->small_mode_method_dex_size_limit_; + sea_ir_mode_ = options->sea_ir_mode_; vfprintf_ = options->hook_vfprintf_; exit_ = options->hook_exit_; abort_ = options->hook_abort_; diff --git a/src/runtime.h b/src/runtime.h index dfcd647369..0b893a341d 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -108,9 +108,12 @@ class Runtime { void (*hook_abort_)(); std::vector properties_; bool small_mode_; + size_t small_mode_method_threshold_; size_t small_mode_method_dex_size_limit_; + bool sea_ir_mode_; + private: ParsedOptions() {} }; @@ -131,6 +134,14 @@ class Runtime { return is_concurrent_gc_enabled_; } + bool IsSeaIRMode() const { + return sea_ir_mode_; + } + + void SetSeaIRMode(bool sea_ir_mode) { + sea_ir_mode_ = sea_ir_mode; + } + bool IsSmallMode() const { return small_mode_; } @@ -374,6 +385,8 @@ class Runtime { size_t small_mode_method_threshold_; size_t small_mode_method_dex_size_limit_; + bool sea_ir_mode_; + // The host prefix is used during cross compilation. It is removed // from the start of host paths such as: // $ANDROID_PRODUCT_OUT/system/framework/boot.oat -- cgit v1.2.3-59-g8ed1b