diff options
148 files changed, 3864 insertions, 1273 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 990262844c..1691dbb3bc 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -84,6 +84,7 @@ $(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $( # Dex file dependencies for each gtest. ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested +ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 3b273a2202..e297b4f531 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -215,24 +215,9 @@ define create-core-oat-target-rules $(4)TARGET_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name) else $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name) - ifdef ART_USE_VIXL_ARM_BACKEND - ifeq ($(1),optimizing) - # TODO(VIXL): The ARM VIXL backend is still work in progress. Therefore for now we do not - # compile the core image with the Optimizing backend when ART_USE_VIXL_ARM_BACKEND is - # defined. - core_compile_options += --compiler-filter=interpret-only - endif - endif endif else $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name) - ifdef ART_USE_VIXL_ARM_BACKEND - ifeq ($(1),optimizing) - # TODO(VIXL): The ARM VIXL backend is still work in progress. Therefore for now we do not - # compile the core image with the Optimizing backend when ART_USE_VIXL_ARM_BACKEND is defined. - core_compile_options += --compiler-filter=interpret-only - endif - endif endif $(4)TARGET_CORE_IMG_OUTS += $$(core_image_name) $(4)TARGET_CORE_OAT_OUTS += $$(core_oat_name) diff --git a/compiler/Android.bp b/compiler/Android.bp index b883e0881a..db55ea0ef7 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -349,6 +349,7 @@ art_cc_test { "optimizing/ssa_test.cc", "optimizing/stack_map_test.cc", "optimizing/suspend_check_test.cc", + "utils/atomic_method_ref_map_test.cc", "utils/dedupe_set_test.cc", "utils/intrusive_forward_list_test.cc", "utils/string_reference_test.cc", diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 174e85e1bf..bbf9eee0e5 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -315,11 +315,11 @@ class LinkerPatch { return target_dex_file_; } - uint32_t TargetStringIndex() const { + dex::StringIndex TargetStringIndex() const { DCHECK(patch_type_ == Type::kString || patch_type_ == Type::kStringRelative || patch_type_ == Type::kStringBssEntry); - return string_idx_; + return dex::StringIndex(string_idx_); } const DexFile* TargetDexCacheDexFile() const { diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 0a4f094494..30d4b47c6a 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -53,7 +53,7 @@ static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) { uint32_t parameters_size = DecodeUnsignedLeb128(&stream); for (uint32_t i = 0; i < parameters_size; ++i) { uint32_t id = DecodeUnsignedLeb128P1(&stream); - names.push_back(mi->dex_file->StringDataByIdx(id)); + names.push_back(mi->dex_file->StringDataByIdx(dex::StringIndex(id))); } } } diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 3fb10d89dd..669d8cd991 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -23,6 +23,7 @@ #include "driver/compiler_options.h" #include "thread.h" #include "thread-inl.h" +#include "utils/atomic_method_ref_map-inl.h" #include "verified_method.h" #include "verifier/method_verifier-inl.h" @@ -35,8 +36,11 @@ VerificationResults::VerificationResults(const CompilerOptions* compiler_options VerificationResults::~VerificationResults() { WriterMutexLock mu(Thread::Current(), verified_methods_lock_); - DeleteResults(preregistered_dex_files_); STLDeleteValues(&verified_methods_); + atomic_verified_methods_.Visit([](const MethodReference& ref ATTRIBUTE_UNUSED, + const VerifiedMethod* method) { + delete method; + }); } void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) { @@ -49,16 +53,17 @@ void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method // We'll punt this later. return; } - bool inserted; - DexFileMethodArray* const array = GetMethodArray(ref.dex_file); + AtomicMap::InsertResult result = atomic_verified_methods_.Insert(ref, + /*expected*/ nullptr, + verified_method.get()); const VerifiedMethod* existing = nullptr; - if (array != nullptr) { - DCHECK(array != nullptr); - Atomic<const VerifiedMethod*>* slot = &(*array)[ref.dex_method_index]; - inserted = slot->CompareExchangeStrongSequentiallyConsistent(nullptr, verified_method.get()); + bool inserted; + if (result != AtomicMap::kInsertResultInvalidDexFile) { + inserted = (result == AtomicMap::kInsertResultSuccess); if (!inserted) { - existing = slot->LoadSequentiallyConsistent(); - DCHECK_NE(verified_method.get(), existing); + // Rare case. + CHECK(atomic_verified_methods_.Get(ref, &existing)); + CHECK_NE(verified_method.get(), existing); } } else { WriterMutexLock mu(Thread::Current(), verified_methods_lock_); @@ -89,9 +94,9 @@ void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method } const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) { - DexFileMethodArray* array = GetMethodArray(ref.dex_file); - if (array != nullptr) { - return (*array)[ref.dex_method_index].LoadRelaxed(); + const VerifiedMethod* ret = nullptr; + if (atomic_verified_methods_.Get(ref, &ret)) { + return ret; } ReaderMutexLock mu(Thread::Current(), verified_methods_lock_); auto it = verified_methods_.find(ref); @@ -124,10 +129,8 @@ bool VerificationResults::IsCandidateForCompilation(MethodReference&, return true; } -void VerificationResults::PreRegisterDexFile(const DexFile* dex_file) { - CHECK(preregistered_dex_files_.find(dex_file) == preregistered_dex_files_.end()) - << dex_file->GetLocation(); - DexFileMethodArray array(dex_file->NumMethodIds()); +void VerificationResults::AddDexFile(const DexFile* dex_file) { + atomic_verified_methods_.AddDexFile(dex_file); WriterMutexLock mu(Thread::Current(), verified_methods_lock_); // There can be some verified methods that are already registered for the dex_file since we set // up well known classes earlier. Remove these and put them in the array so that we don't @@ -135,31 +138,13 @@ void VerificationResults::PreRegisterDexFile(const DexFile* dex_file) { for (auto it = verified_methods_.begin(); it != verified_methods_.end(); ) { MethodReference ref = it->first; if (ref.dex_file == dex_file) { - array[ref.dex_method_index].StoreSequentiallyConsistent(it->second); + CHECK(atomic_verified_methods_.Insert(ref, nullptr, it->second) == + AtomicMap::kInsertResultSuccess); it = verified_methods_.erase(it); } else { ++it; } } - preregistered_dex_files_.emplace(dex_file, std::move(array)); -} - -void VerificationResults::DeleteResults(DexFileResults& array) { - for (auto& pair : array) { - for (Atomic<const VerifiedMethod*>& method : pair.second) { - delete method.LoadSequentiallyConsistent(); - } - } - array.clear(); -} - -VerificationResults::DexFileMethodArray* VerificationResults::GetMethodArray( - const DexFile* dex_file) { - auto it = preregistered_dex_files_.find(dex_file); - if (it != preregistered_dex_files_.end()) { - return &it->second; - } - return nullptr; } } // namespace art diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h index b3356e0e10..ea38f4d537 100644 --- a/compiler/dex/verification_results.h +++ b/compiler/dex/verification_results.h @@ -26,6 +26,7 @@ #include "class_reference.h" #include "method_reference.h" #include "safe_map.h" +#include "utils/atomic_method_ref_map.h" namespace art { @@ -54,26 +55,22 @@ class VerificationResults { bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags); - // Add a dex file array to the preregistered_dex_files_ array. These dex files require no locks to - // access. It is not safe to call if other callers are calling GetVerifiedMethod concurrently. - void PreRegisterDexFile(const DexFile* dex_file) REQUIRES(!verified_methods_lock_); + // Add a dex file to enable using the atomic map. + void AddDexFile(const DexFile* dex_file) REQUIRES(!verified_methods_lock_); private: // Verified methods. The method array is fixed to avoid needing a lock to extend it. - using DexFileMethodArray = dchecked_vector<Atomic<const VerifiedMethod*>>; - using DexFileResults = std::map<const DexFile*, DexFileMethodArray>; + using AtomicMap = AtomicMethodRefMap<const VerifiedMethod*>; using VerifiedMethodMap = SafeMap<MethodReference, const VerifiedMethod*, MethodReferenceComparator>; - static void DeleteResults(DexFileResults& array); - - DexFileMethodArray* GetMethodArray(const DexFile* dex_file) REQUIRES(!verified_methods_lock_); VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_); const CompilerOptions* const compiler_options_; - // Dex2oat can preregister dex files to avoid locking when calling GetVerifiedMethod. - DexFileResults preregistered_dex_files_; + // Dex2oat can add dex files to atomic_verified_methods_ to avoid locking when calling + // GetVerifiedMethod. + AtomicMap atomic_verified_methods_; ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index ad75ec4604..6b62110b91 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -71,6 +71,7 @@ #include "thread_pool.h" #include "trampolines/trampoline_compiler.h" #include "transaction.h" +#include "utils/atomic_method_ref_map-inl.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "utils/swap_space.h" #include "vdex_file.h" @@ -287,8 +288,6 @@ CompilerDriver::CompilerDriver( instruction_set_features_(instruction_set_features), requires_constructor_barrier_lock_("constructor barrier lock"), compiled_classes_lock_("compiled classes lock"), - compiled_methods_lock_("compiled method lock"), - compiled_methods_(MethodTable::key_compare()), non_relative_linker_patch_count_(0u), image_classes_(image_classes), classes_to_compile_(compiled_classes), @@ -326,12 +325,12 @@ CompilerDriver::~CompilerDriver() { MutexLock mu(self, compiled_classes_lock_); STLDeleteValues(&compiled_classes_); } - { - MutexLock mu(self, compiled_methods_lock_); - for (auto& pair : compiled_methods_) { - CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, pair.second); + compiled_methods_.Visit([this](const MethodReference& ref ATTRIBUTE_UNUSED, + CompiledMethod* method) { + if (method != nullptr) { + CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, method); } - } + }); compiler_->UnInit(); } @@ -575,8 +574,7 @@ static void CompileMethod(Thread* self, const DexFile& dex_file, optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level, bool compilation_enabled, - Handle<mirror::DexCache> dex_cache) - REQUIRES(!driver->compiled_methods_lock_) { + Handle<mirror::DexCache> dex_cache) { DCHECK(driver != nullptr); CompiledMethod* compiled_method = nullptr; uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0; @@ -842,9 +840,9 @@ static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache, switch (inst->Opcode()) { case Instruction::CONST_STRING: case Instruction::CONST_STRING_JUMBO: { - uint32_t string_index = (inst->Opcode() == Instruction::CONST_STRING) + dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) ? inst->VRegB_21c() - : inst->VRegB_31c(); + : inst->VRegB_31c()); mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache); CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; break; @@ -940,6 +938,13 @@ void CompilerDriver::PreCompile(jobject class_loader, TimingLogger* timings) { CheckThreadPools(); + for (const DexFile* dex_file : dex_files) { + // Can be already inserted if the caller is CompileOne. This happens for gtests. + if (!compiled_methods_.HaveDexFile(dex_file)) { + compiled_methods_.AddDexFile(dex_file); + } + } + LoadImageClasses(timings); VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false); @@ -2616,30 +2621,15 @@ void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref, size_t non_relative_linker_patch_count) { DCHECK(GetCompiledMethod(method_ref) == nullptr) << method_ref.dex_file->PrettyMethod(method_ref.dex_method_index); - { - MutexLock mu(Thread::Current(), compiled_methods_lock_); - compiled_methods_.Put(method_ref, compiled_method); - non_relative_linker_patch_count_ += non_relative_linker_patch_count; - } + MethodTable::InsertResult result = compiled_methods_.Insert(method_ref, + /*expected*/ nullptr, + compiled_method); + CHECK(result == MethodTable::kInsertResultSuccess); + non_relative_linker_patch_count_.FetchAndAddRelaxed(non_relative_linker_patch_count); DCHECK(GetCompiledMethod(method_ref) != nullptr) << method_ref.dex_file->PrettyMethod(method_ref.dex_method_index); } -void CompilerDriver::RemoveCompiledMethod(const MethodReference& method_ref) { - CompiledMethod* compiled_method = nullptr; - { - MutexLock mu(Thread::Current(), compiled_methods_lock_); - auto it = compiled_methods_.find(method_ref); - if (it != compiled_methods_.end()) { - compiled_method = it->second; - compiled_methods_.erase(it); - } - } - if (compiled_method != nullptr) { - CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, compiled_method); - } -} - CompiledClass* CompilerDriver::GetCompiledClass(ClassReference ref) const { MutexLock mu(Thread::Current(), compiled_classes_lock_); ClassTable::const_iterator it = compiled_classes_.find(ref); @@ -2678,13 +2668,9 @@ void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status } CompiledMethod* CompilerDriver::GetCompiledMethod(MethodReference ref) const { - MutexLock mu(Thread::Current(), compiled_methods_lock_); - MethodTable::const_iterator it = compiled_methods_.find(ref); - if (it == compiled_methods_.end()) { - return nullptr; - } - CHECK(it->second != nullptr); - return it->second; + CompiledMethod* compiled_method = nullptr; + compiled_methods_.Get(ref, &compiled_method); + return compiled_method; } bool CompilerDriver::IsMethodVerifiedWithoutFailures(uint32_t method_idx, @@ -2713,8 +2699,7 @@ bool CompilerDriver::IsMethodVerifiedWithoutFailures(uint32_t method_idx, } size_t CompilerDriver::GetNonRelativeLinkerPatchCount() const { - MutexLock mu(Thread::Current(), compiled_methods_lock_); - return non_relative_linker_patch_count_; + return non_relative_linker_patch_count_.LoadRelaxed(); } void CompilerDriver::SetRequiresConstructorBarrier(Thread* self, diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 7418b006ef..cc50197140 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -41,6 +41,7 @@ #include "runtime.h" #include "safe_map.h" #include "thread_pool.h" +#include "utils/atomic_method_ref_map.h" #include "utils/dex_cache_arrays_layout.h" namespace art { @@ -131,7 +132,7 @@ class CompilerDriver { // Compile a single Method. void CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings) REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_); + REQUIRES(!compiled_classes_lock_, !dex_to_dex_references_lock_); VerificationResults* GetVerificationResults() const { DCHECK(Runtime::Current()->IsAotCompiler()); @@ -168,18 +169,12 @@ class CompilerDriver { CompiledClass* GetCompiledClass(ClassReference ref) const REQUIRES(!compiled_classes_lock_); - CompiledMethod* GetCompiledMethod(MethodReference ref) const - REQUIRES(!compiled_methods_lock_); - size_t GetNonRelativeLinkerPatchCount() const - REQUIRES(!compiled_methods_lock_); - + CompiledMethod* GetCompiledMethod(MethodReference ref) const; + size_t GetNonRelativeLinkerPatchCount() const; // Add a compiled method. void AddCompiledMethod(const MethodReference& method_ref, CompiledMethod* const compiled_method, - size_t non_relative_linker_patch_count) - REQUIRES(!compiled_methods_lock_); - // Remove and delete a compiled method. - void RemoveCompiledMethod(const MethodReference& method_ref) REQUIRES(!compiled_methods_lock_); + size_t non_relative_linker_patch_count); void SetRequiresConstructorBarrier(Thread* self, const DexFile* dex_file, @@ -519,18 +514,15 @@ class CompilerDriver { mutable Mutex compiled_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; ClassTable compiled_classes_ GUARDED_BY(compiled_classes_lock_); - typedef SafeMap<const MethodReference, CompiledMethod*, MethodReferenceComparator> MethodTable; - - public: - // Lock is public so that non-members can have lock annotations. - mutable Mutex compiled_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + typedef AtomicMethodRefMap<CompiledMethod*> MethodTable; private: // All method references that this compiler has compiled. - MethodTable compiled_methods_ GUARDED_BY(compiled_methods_lock_); + MethodTable compiled_methods_; + // Number of non-relative patches in all compiled methods. These patches need space // in the .oat_patches ELF section if requested in the compiler options. - size_t non_relative_linker_patch_count_ GUARDED_BY(compiled_methods_lock_); + Atomic<size_t> non_relative_linker_patch_count_; // 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. diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index f40c71283b..12684c09c0 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -111,7 +111,7 @@ TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(soa.Self(), dex); EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings()); for (size_t i = 0; i < dex_cache->NumStrings(); i++) { - const mirror::String* string = dex_cache->GetResolvedString(i); + const mirror::String* string = dex_cache->GetResolvedString(dex::StringIndex(i)); EXPECT_TRUE(string != nullptr) << "string_idx=" << i; } EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes()); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index a706697496..440796858e 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -838,19 +838,64 @@ bool ImageWriter::KeepClass(Class* klass) { return true; } -class ImageWriter::NonImageClassesVisitor : public ClassVisitor { +class ImageWriter::PruneClassesVisitor : public ClassVisitor { public: - explicit NonImageClassesVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {} + PruneClassesVisitor(ImageWriter* image_writer, ObjPtr<mirror::ClassLoader> class_loader) + : image_writer_(image_writer), + class_loader_(class_loader), + classes_to_prune_(), + defined_class_count_(0u) { } - bool operator()(ObjPtr<Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { if (!image_writer_->KeepClass(klass.Ptr())) { classes_to_prune_.insert(klass.Ptr()); + if (klass->GetClassLoader() == class_loader_) { + ++defined_class_count_; + } } return true; } + size_t Prune() REQUIRES_SHARED(Locks::mutator_lock_) { + ClassTable* class_table = + Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader_); + for (mirror::Class* klass : classes_to_prune_) { + std::string storage; + const char* descriptor = klass->GetDescriptor(&storage); + bool result = class_table->Remove(descriptor); + DCHECK(result); + } + return defined_class_count_; + } + + private: + ImageWriter* const image_writer_; + const ObjPtr<mirror::ClassLoader> class_loader_; std::unordered_set<mirror::Class*> classes_to_prune_; + size_t defined_class_count_; +}; + +class ImageWriter::PruneClassLoaderClassesVisitor : public ClassLoaderVisitor { + public: + explicit PruneClassLoaderClassesVisitor(ImageWriter* image_writer) + : image_writer_(image_writer), removed_class_count_(0) {} + + virtual void Visit(ObjPtr<mirror::ClassLoader> class_loader) OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) { + PruneClassesVisitor classes_visitor(image_writer_, class_loader); + ClassTable* class_table = + Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader); + class_table->Visit(classes_visitor); + removed_class_count_ += classes_visitor.Prune(); + } + + size_t GetRemovedClassCount() const { + return removed_class_count_; + } + + private: ImageWriter* const image_writer_; + size_t removed_class_count_; }; void ImageWriter::PruneNonImageClasses() { @@ -862,21 +907,13 @@ void ImageWriter::PruneNonImageClasses() { // path dex caches. class_linker->ClearClassTableStrongRoots(); - // Make a list of classes we would like to prune. - NonImageClassesVisitor visitor(this); - class_linker->VisitClasses(&visitor); - // Remove the undesired classes from the class roots. - VLOG(compiler) << "Pruning " << visitor.classes_to_prune_.size() << " classes"; - for (mirror::Class* klass : visitor.classes_to_prune_) { - std::string temp; - const char* name = klass->GetDescriptor(&temp); - VLOG(compiler) << "Pruning class " << name; - if (!compile_app_image_) { - DCHECK(IsBootClassLoaderClass(klass)); - } - bool result = class_linker->RemoveClass(name, klass->GetClassLoader()); - DCHECK(result); + { + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + PruneClassLoaderClassesVisitor class_loader_visitor(this); + class_loader_visitor.Visit(nullptr); // Visit boot class loader. + class_linker->VisitClassLoaders(&class_loader_visitor); + VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes"; } // Clear references to removed classes from the DexCaches. @@ -932,7 +969,8 @@ void ImageWriter::PruneNonImageClasses() { class_linker->DropFindArrayClassCache(); // Clear to save RAM. - prune_class_memo_.clear(); + // FIXME: This has been temporarily removed to provide extra debugging output. Bug 33231647. + // prune_class_memo_.clear(); } void ImageWriter::CheckNonImageClassesRemoved() { @@ -1104,7 +1142,40 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, DCHECK_NE(as_klass->GetStatus(), mirror::Class::kStatusError); if (compile_app_image_) { // Extra sanity, no boot loader classes should be left! - CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass(); + // FIXME: Remove the extra logging. Bug 33231647. + struct Dumper { + std::string ImageRanges() const { + std::ostringstream oss; + const char* separator = ""; + gc::Heap* const heap = Runtime::Current()->GetHeap(); + for (gc::space::ImageSpace* boot_image_space : heap->GetBootImageSpaces()) { + const uint8_t* image_begin = boot_image_space->Begin(); + // Real image end including ArtMethods and ArtField sections. + const uint8_t* image_end = + image_begin + boot_image_space->GetImageHeader().GetImageSize(); + oss << separator << static_cast<const void*>(image_begin) + << "-" << static_cast<const void*>(image_end); + separator = ":"; + } + return oss.str(); + } + std::string PruneMemo(ImageWriter* writer, + mirror::Class* klass, + const std::unordered_map<mirror::Class*, bool>& map) const + REQUIRES_SHARED(Locks::mutator_lock_) { + auto it = map.find(klass); + if (it == map.end()) { + return writer->PruneAppImageClass(klass) ? "missing/true" : "missing/false"; + } else { + return it->second ? "true" : "false"; + } + } + }; + CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass() + << " status:" << as_klass->GetStatus() + << " " << static_cast<const void*>(as_klass) + << " " << Dumper().ImageRanges() + << " prune_memo:" << Dumper().PruneMemo(this, as_klass, prune_class_memo_); } LengthPrefixedArray<ArtField>* fields[] = { as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(), @@ -1451,7 +1522,8 @@ void ImageWriter::CalculateNewObjectOffsets() { InternTable* const intern_table = runtime->GetInternTable(); for (size_t i = 0, count = dex_file->NumStringIds(); i < count; ++i) { uint32_t utf16_length; - const char* utf8_data = dex_file->StringDataAndUtf16LengthByIdx(i, &utf16_length); + const char* utf8_data = dex_file->StringDataAndUtf16LengthByIdx(dex::StringIndex(i), + &utf16_length); mirror::String* string = intern_table->LookupStrong(self, utf16_length, utf8_data).Ptr(); TryAssignBinSlot(work_stack, string, oat_index); } @@ -1507,8 +1579,10 @@ void ImageWriter::CalculateNewObjectOffsets() { } // Calculate the size of the class table. ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); - DCHECK_EQ(image_info.class_table_->NumZygoteClasses(), 0u); - if (image_info.class_table_->NumNonZygoteClasses() != 0u) { + CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u); + mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr; + DCHECK_EQ(image_info.class_table_->NumZygoteClasses(class_loader), 0u); + if (image_info.class_table_->NumNonZygoteClasses(class_loader) != 0u) { image_info.class_table_bytes_ += image_info.class_table_->WriteToMemory(nullptr); } } @@ -1853,8 +1927,10 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { // above comment for intern tables. ClassTable temp_class_table; temp_class_table.ReadFromMemory(class_table_memory_ptr); - CHECK_EQ(temp_class_table.NumZygoteClasses(), table->NumNonZygoteClasses() + - table->NumZygoteClasses()); + CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u); + mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr; + CHECK_EQ(temp_class_table.NumZygoteClasses(class_loader), + table->NumNonZygoteClasses(class_loader) + table->NumZygoteClasses(class_loader)); BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(&root_visitor, RootInfo(kRootUnknown)); temp_class_table.VisitRoots(buffered_visitor); diff --git a/compiler/image_writer.h b/compiler/image_writer.h index 24fad466e4..ad6ffd8f51 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -588,7 +588,8 @@ class ImageWriter FINAL { class FixupVisitor; class GetRootsVisitor; class NativeLocationVisitor; - class NonImageClassesVisitor; + class PruneClassesVisitor; + class PruneClassLoaderClassesVisitor; class VisitReferencesVisitor; DISALLOW_COPY_AND_ASSIGN(ImageWriter); diff --git a/compiler/intrinsics_list.h b/compiler/intrinsics_list.h index 555baf6de9..9bd25d8484 100644 --- a/compiler/intrinsics_list.h +++ b/compiler/intrinsics_list.h @@ -117,6 +117,12 @@ V(StringNewStringFromBytes, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringFactory;", "newStringFromBytes", "([BIII)Ljava/lang/String;") \ V(StringNewStringFromChars, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringFactory;", "newStringFromChars", "(II[C)Ljava/lang/String;") \ V(StringNewStringFromString, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringFactory;", "newStringFromString", "(Ljava/lang/String;)Ljava/lang/String;") \ + V(StringBufferAppend, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringBuffer;", "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;") \ + V(StringBufferLength, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kNoThrow, "Ljava/lang/StringBuffer;", "length", "()I") \ + V(StringBufferToString, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringBuffer;", "toString", "()Ljava/lang/String;") \ + V(StringBuilderAppend, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringBuilder;", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;") \ + V(StringBuilderLength, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/StringBuilder;", "length", "()I") \ + V(StringBuilderToString, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Ljava/lang/StringBuilder;", "toString", "()Ljava/lang/String;") \ V(UnsafeCASInt, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "compareAndSwapInt", "(Ljava/lang/Object;JII)Z") \ V(UnsafeCASLong, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z") \ V(UnsafeCASObject, kVirtual, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow, "Lsun/misc/Unsafe;", "compareAndSwapObject", "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z") \ diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index 015178980c..233daf4a39 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -163,7 +163,8 @@ class RelativePatcherTest : public testing::Test { offset + patch.LiteralOffset(), target_offset); } else if (patch.GetType() == LinkerPatch::Type::kStringRelative) { - uint32_t target_offset = string_index_to_offset_map_.Get(patch.TargetStringIndex()); + uint32_t target_offset = + string_index_to_offset_map_.Get(patch.TargetStringIndex().index_); patcher_->PatchPcRelativeReference(&patched_code_, patch, offset + patch.LiteralOffset(), diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 8a6b94e0ea..1f5981682b 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -430,7 +430,7 @@ class LoadStringSlowPathARM : public SlowPathCodeARM { LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); HLoadString* load = instruction_->AsLoadString(); - const uint32_t string_index = load->GetStringIndex(); + const uint32_t string_index = load->GetStringIndex().index_; Register out = locations->Out().AsRegister<Register>(); Register temp = locations->GetTemp(0).AsRegister<Register>(); constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); @@ -5946,7 +5946,7 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorARM::PcRelativePatchInfo* labels = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); __ BindTrackedLabel(&labels->movw_label); __ movw(out, /* placeholder */ 0u); __ BindTrackedLabel(&labels->movt_label); @@ -5965,7 +5965,7 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); Register temp = locations->GetTemp(0).AsRegister<Register>(); CodeGeneratorARM::PcRelativePatchInfo* labels = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); __ BindTrackedLabel(&labels->movw_label); __ movw(temp, /* placeholder */ 0u); __ BindTrackedLabel(&labels->movt_label); @@ -5994,7 +5994,7 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod); InvokeRuntimeCallingConvention calling_convention; DCHECK_EQ(calling_convention.GetRegisterAt(0), out); - __ LoadImmediate(calling_convention.GetRegisterAt(0), load->GetStringIndex()); + __ LoadImmediate(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_); codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); } @@ -7340,7 +7340,7 @@ CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativePatch( } Literal* CodeGeneratorARM::DeduplicateBootImageStringLiteral(const DexFile& dex_file, - uint32_t string_index) { + dex::StringIndex string_index) { return boot_image_string_patches_.GetOrCreate( StringReference(&dex_file, string_index), [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); @@ -7364,7 +7364,7 @@ Literal* CodeGeneratorARM::DeduplicateDexCacheAddressLiteral(uint32_t address) { } Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file, - uint32_t string_index) { + dex::StringIndex string_index) { jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u); return jit_string_patches_.GetOrCreate( StringReference(&dex_file, string_index), @@ -7436,7 +7436,7 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche uint32_t literal_offset = literal->GetLabel()->Position(); linker_patches->push_back(LinkerPatch::StringPatch(literal_offset, target_string.dex_file, - target_string.string_index)); + target_string.string_index.index_)); } if (!GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_, diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index a4ccb57c1f..8230512825 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -485,11 +485,12 @@ class CodeGeneratorARM : public CodeGenerator { PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); - Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); + Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, + dex::StringIndex string_index); Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); Literal* DeduplicateBootImageAddressLiteral(uint32_t address); Literal* DeduplicateDexCacheAddressLiteral(uint32_t address); - Literal* DeduplicateJitStringLiteral(const DexFile& dex_file, uint32_t string_index); + Literal* DeduplicateJitStringLiteral(const DexFile& dex_file, dex::StringIndex string_index); void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index a78b3da455..ab6a33fbd9 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -349,7 +349,7 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_; __ Mov(calling_convention.GetRegisterAt(0).W(), string_index); arm64_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); @@ -4132,7 +4132,7 @@ vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativePatch( } vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageStringLiteral( - const DexFile& dex_file, uint32_t string_index) { + const DexFile& dex_file, dex::StringIndex string_index) { return boot_image_string_patches_.GetOrCreate( StringReference(&dex_file, string_index), [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); @@ -4158,7 +4158,7 @@ vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateDexCacheAddress } vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLiteral( - const DexFile& dex_file, uint32_t string_index) { + const DexFile& dex_file, dex::StringIndex string_index) { jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u); return jit_string_patches_.GetOrCreate( StringReference(&dex_file, string_index), @@ -4246,7 +4246,7 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc vixl::aarch64::Literal<uint32_t>* literal = entry.second; linker_patches->push_back(LinkerPatch::StringPatch(literal->GetOffset(), target_string.dex_file, - target_string.string_index)); + target_string.string_index.index_)); } if (!GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_, @@ -4594,7 +4594,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { // Add ADRP with its PC-relative String patch. const DexFile& dex_file = load->GetDexFile(); - uint32_t string_index = load->GetStringIndex(); + uint32_t string_index = load->GetStringIndex().index_; DCHECK(codegen_->GetCompilerOptions().IsBootImage()); vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index); codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); @@ -4612,7 +4612,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { case HLoadString::LoadKind::kBssEntry: { // Add ADRP with its PC-relative String .bss entry patch. const DexFile& dex_file = load->GetDexFile(); - uint32_t string_index = load->GetStringIndex(); + uint32_t string_index = load->GetStringIndex().index_; DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); UseScratchRegisterScope temps(codegen_->GetVIXLAssembler()); Register temp = temps.AcquireX(); @@ -4653,7 +4653,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { // TODO: Re-add the compiler code to do string dex cache lookup again. InvokeRuntimeCallingConvention calling_convention; DCHECK_EQ(calling_convention.GetRegisterAt(0).GetCode(), out.GetCode()); - __ Mov(calling_convention.GetRegisterAt(0).W(), load->GetStringIndex()); + __ Mov(calling_convention.GetRegisterAt(0).W(), load->GetStringIndex().index_); codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 1545fd3860..868c8b07ed 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -560,14 +560,15 @@ class CodeGeneratorARM64 : public CodeGenerator { uint32_t element_offset, vixl::aarch64::Label* adrp_label = nullptr); - vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageStringLiteral(const DexFile& dex_file, - uint32_t string_index); + vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageStringLiteral( + const DexFile& dex_file, + dex::StringIndex string_index); vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address); vixl::aarch64::Literal<uint64_t>* DeduplicateDexCacheAddressLiteral(uint64_t address); vixl::aarch64::Literal<uint32_t>* DeduplicateJitStringLiteral(const DexFile& dex_file, - uint32_t string_index); + dex::StringIndex string_index); void EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg); void EmitAddPlaceholder(vixl::aarch64::Label* fixup_label, diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index e399f3228e..1ca439e8cf 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -1322,11 +1322,10 @@ void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) { } break; - // TODO(VIXL): https://android-review.googlesource.com/#/c/252265/ case Primitive::kPrimFloat: case Primitive::kPrimDouble: locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1))); if (!cond->IsEmittedAtUseSite()) { locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } @@ -1346,13 +1345,20 @@ void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) { return; } + Location right = cond->GetLocations()->InAt(1); vixl32::Register out = OutputRegister(cond); vixl32::Label true_label, false_label; switch (cond->InputAt(0)->GetType()) { default: { // Integer case. - __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1)); + if (right.IsRegister()) { + __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1)); + } else { + DCHECK(right.IsConstant()); + __ Cmp(InputRegisterAt(cond, 0), + CodeGenerator::GetInt32ValueOf(right.GetConstant())); + } AssemblerAccurateScope aas(GetVIXLAssembler(), kArmInstrMaxSizeInBytes * 3u, CodeBufferCheckScope::kMaximumSize); @@ -2215,10 +2221,9 @@ void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) { break; } - // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/ case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD)); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); break; } @@ -2248,11 +2253,15 @@ void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) { } break; - // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/ case Primitive::kPrimLong: { - DCHECK(second.IsRegisterPair()); - __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second)); - __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second)); + if (second.IsConstant()) { + uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant())); + GenerateAddLongConst(out, first, value); + } else { + DCHECK(second.IsRegisterPair()); + __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second)); + __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second)); + } break; } @@ -2277,10 +2286,9 @@ void LocationsBuilderARMVIXL::VisitSub(HSub* sub) { break; } - // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/ case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB)); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); break; } @@ -2307,11 +2315,15 @@ void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) { break; } - // TODO(VIXL): https://android-review.googlesource.com/#/c/254144/ case Primitive::kPrimLong: { - DCHECK(second.IsRegisterPair()); - __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second)); - __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second)); + if (second.IsConstant()) { + uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant())); + GenerateAddLongConst(out, first, -value); + } else { + DCHECK(second.IsRegisterPair()); + __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second)); + __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second)); + } break; } @@ -2478,7 +2490,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOpera int shift; CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift); - __ Mov(temp1, Operand::From(magic)); + // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed. + __ Mov(temp1, static_cast<int32_t>(magic)); __ Smull(temp2, temp1, dividend, temp1); if (imm > 0 && magic < 0) { @@ -2769,15 +2782,8 @@ void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) { void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) { - // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/ - LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() - ? LocationSummary::kCallOnSlowPath - : LocationSummary::kNoCall; - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); + LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); - if (instruction->HasUses()) { - locations->SetOut(Location::SameAsFirstInput()); - } } void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) { @@ -3949,15 +3955,8 @@ void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet( } void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) { - // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/ - LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() - ? LocationSummary::kCallOnSlowPath - : LocationSummary::kNoCall; - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); + LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction); locations->SetInAt(0, Location::RequiresRegister()); - if (instruction->HasUses()) { - locations->SetOut(Location::SameAsFirstInput()); - } } void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) { @@ -4215,6 +4214,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { } else { codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index)); } + temps.Release(temp); } break; } @@ -4254,6 +4254,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { __ Add(temp, obj, data_offset); } codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index)); + temps.Release(temp); codegen_->MaybeRecordImplicitNullCheck(instruction); // If read barriers are enabled, emit read barriers other than @@ -4275,6 +4276,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { vixl32::Register temp = temps.Acquire(); __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset); + temps.Release(temp); } break; } @@ -4288,6 +4290,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { vixl32::Register temp = temps.Acquire(); __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4)); GetAssembler()->LoadSFromOffset(out, temp, data_offset); + temps.Release(temp); } break; } @@ -4300,6 +4303,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { vixl32::Register temp = temps.Acquire(); __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset); + temps.Release(temp); } break; } @@ -4389,6 +4393,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { __ Add(temp, array, data_offset); } codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index)); + temps.Release(temp); } break; } @@ -4410,6 +4415,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { vixl32::Register temp = temps.Acquire(); __ Add(temp, array, data_offset); codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index)); + temps.Release(temp); } codegen_->MaybeRecordImplicitNullCheck(instruction); DCHECK(!needs_write_barrier); @@ -4443,6 +4449,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { vixl32::Register temp = temps.Acquire(); __ Add(temp, array, data_offset); codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index)); + temps.Release(temp); } codegen_->MaybeRecordImplicitNullCheck(instruction); __ B(&done); @@ -4512,6 +4519,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { LocationFrom(source), temp, RegisterFrom(index)); + temps.Release(temp); } if (!may_need_runtime_call_for_type_check) { @@ -4541,6 +4549,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { vixl32::Register temp = temps.Acquire(); __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset); + temps.Release(temp); } break; } @@ -4555,6 +4564,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { vixl32::Register temp = temps.Acquire(); __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4)); GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset); + temps.Release(temp); } break; } @@ -4569,6 +4579,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { vixl32::Register temp = temps.Acquire(); __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset); + temps.Release(temp); } break; } @@ -4678,8 +4689,9 @@ void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instructi } void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) { - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); - // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/ and related. + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); + locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) { @@ -4904,9 +4916,12 @@ void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) { } else if (source.IsStackSlot() && destination.IsRegister()) { Exchange(RegisterFrom(destination), source.GetStackIndex()); } else if (source.IsStackSlot() && destination.IsStackSlot()) { - TODO_VIXL32(FATAL); + Exchange(source.GetStackIndex(), destination.GetStackIndex()); } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { - TODO_VIXL32(FATAL); + vixl32::SRegister temp = temps.AcquireS(); + __ Vmov(temp, SRegisterFrom(source)); + __ Vmov(SRegisterFrom(source), SRegisterFrom(destination)); + __ Vmov(SRegisterFrom(destination), temp); } else if (source.IsRegisterPair() && destination.IsRegisterPair()) { vixl32::DRegister temp = temps.AcquireD(); __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source)); @@ -4929,9 +4944,27 @@ void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) { __ Vmov(first, second); __ Vmov(second, temp); } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) { - TODO_VIXL32(FATAL); + vixl32::DRegister reg = source.IsFpuRegisterPair() + ? DRegisterFrom(source) + : DRegisterFrom(destination); + int mem = source.IsFpuRegisterPair() + ? destination.GetStackIndex() + : source.GetStackIndex(); + vixl32::DRegister temp = temps.AcquireD(); + __ Vmov(temp, reg); + GetAssembler()->LoadDFromOffset(reg, sp, mem); + GetAssembler()->StoreDToOffset(temp, sp, mem); } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { - TODO_VIXL32(FATAL); + vixl32::SRegister reg = source.IsFpuRegister() + ? SRegisterFrom(source) + : SRegisterFrom(destination); + int mem = source.IsFpuRegister() + ? destination.GetStackIndex() + : source.GetStackIndex(); + vixl32::Register temp = temps.Acquire(); + __ Vmov(temp, reg); + GetAssembler()->LoadSFromOffset(reg, sp, mem); + GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem); } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { vixl32::DRegister temp1 = temps.AcquireD(); vixl32::DRegister temp2 = temps.AcquireD(); @@ -5116,7 +5149,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) { // TODO: Re-add the compiler code to do string dex cache lookup again. DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod); InvokeRuntimeCallingConventionARMVIXL calling_convention; - __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex()); + __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_); codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); } @@ -5160,14 +5193,27 @@ void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) { CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>(); } -static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { - return kEmitCompilerReadBarrier && - (kUseBakerReadBarrier || - type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck); +// Temp is used for read barrier. +static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) { + if (kEmitCompilerReadBarrier && + (kUseBakerReadBarrier || + type_check_kind == TypeCheckKind::kAbstractClassCheck || + type_check_kind == TypeCheckKind::kClassHierarchyCheck || + type_check_kind == TypeCheckKind::kArrayObjectCheck)) { + return 1; + } + return 0; } +// Interface case has 3 temps, one for holding the number of interfaces, one for the current +// interface pointer, one for loading the current interface. +// The other checks have one temp for loading the object's class. +static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) { + if (type_check_kind == TypeCheckKind::kInterfaceCheck) { + return 3; + } + return 1 + NumberOfInstanceOfTemps(type_check_kind); +} void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary::CallKind call_kind = LocationSummary::kNoCall; @@ -5198,11 +5244,7 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { // The "out" register is used as a temporary, so it overlaps with the inputs. // Note that TypeCheckSlowPathARM uses this register too. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); - // When read barriers are enabled, we need a temporary register for - // some cases. - if (TypeCheckNeedsATemporary(type_check_kind)) { - locations->AddTemp(Location::RequiresRegister()); - } + locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind)); } void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { @@ -5213,9 +5255,9 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) vixl32::Register cls = InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); vixl32::Register out = OutputRegister(instruction); - Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? - locations->GetTemp(0) : - Location::NoLocation(); + const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind); + DCHECK_LE(num_temps, 1u); + Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); @@ -5236,7 +5278,8 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, obj_loc, class_offset, - maybe_temp_loc); + maybe_temp_loc, + kCompilerReadBarrierOption); __ Cmp(out, cls); // Classes must be equal for the instanceof to succeed. __ B(ne, &zero); @@ -5251,13 +5294,18 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, obj_loc, class_offset, - maybe_temp_loc); + maybe_temp_loc, + kCompilerReadBarrierOption); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. vixl32::Label loop; __ Bind(&loop); // /* HeapReference<Class> */ out = out->super_class_ - GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); + GenerateReferenceLoadOneRegister(instruction, + out_loc, + super_offset, + maybe_temp_loc, + kCompilerReadBarrierOption); // If `out` is null, we use it for the result, and jump to `done`. __ CompareAndBranchIfZero(out, &done, /* far_target */ false); __ Cmp(out, cls); @@ -5275,14 +5323,19 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, obj_loc, class_offset, - maybe_temp_loc); + maybe_temp_loc, + kCompilerReadBarrierOption); // Walk over the class hierarchy to find a match. vixl32::Label loop, success; __ Bind(&loop); __ Cmp(out, cls); __ B(eq, &success); // /* HeapReference<Class> */ out = out->super_class_ - GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); + GenerateReferenceLoadOneRegister(instruction, + out_loc, + super_offset, + maybe_temp_loc, + kCompilerReadBarrierOption); __ CompareAndBranchIfNonZero(out, &loop); // If `out` is null, we use it for the result, and jump to `done`. __ B(&done); @@ -5300,14 +5353,19 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, obj_loc, class_offset, - maybe_temp_loc); + maybe_temp_loc, + kCompilerReadBarrierOption); // Do an exact check. vixl32::Label exact_check; __ Cmp(out, cls); __ B(eq, &exact_check); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ out = out->component_type_ - GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); + GenerateReferenceLoadOneRegister(instruction, + out_loc, + component_offset, + maybe_temp_loc, + kCompilerReadBarrierOption); // If `out` is null, we use it for the result, and jump to `done`. __ CompareAndBranchIfZero(out, &done, /* far_target */ false); GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); @@ -5320,12 +5378,14 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kArrayCheck: { + // No read barrier since the slow path will retry upon failure. // /* HeapReference<Class> */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - maybe_temp_loc); + maybe_temp_loc, + kWithoutReadBarrier); __ Cmp(out, cls); DCHECK(locations->OnlyCallsOnSlowPath()); slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction, @@ -5409,13 +5469,7 @@ void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - // Note that TypeCheckSlowPathARM uses this "temp" register too. - locations->AddTemp(Location::RequiresRegister()); - // When read barriers are enabled, we need an additional temporary - // register for some cases. - if (TypeCheckNeedsATemporary(type_check_kind)) { - locations->AddTemp(Location::RequiresRegister()); - } + locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind)); } void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { @@ -5426,20 +5480,31 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { vixl32::Register cls = InputRegisterAt(instruction, 1); Location temp_loc = locations->GetTemp(0); vixl32::Register temp = RegisterFrom(temp_loc); - Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? - locations->GetTemp(1) : - Location::NoLocation(); - uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); - uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); - uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); - uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); - - bool is_type_check_slow_path_fatal = - (type_check_kind == TypeCheckKind::kExactCheck || - type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck) && - !instruction->CanThrowIntoCatchBlock(); + const size_t num_temps = NumberOfCheckCastTemps(type_check_kind); + DCHECK_LE(num_temps, 3u); + Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation(); + Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation(); + const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); + const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); + const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); + const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value(); + const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value(); + const uint32_t object_array_data_offset = + mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); + + // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases + // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding + // read barriers is done for performance and code size reasons. + bool is_type_check_slow_path_fatal = false; + if (!kEmitCompilerReadBarrier) { + is_type_check_slow_path_fatal = + (type_check_kind == TypeCheckKind::kExactCheck || + type_check_kind == TypeCheckKind::kAbstractClassCheck || + type_check_kind == TypeCheckKind::kClassHierarchyCheck || + type_check_kind == TypeCheckKind::kArrayObjectCheck) && + !instruction->CanThrowIntoCatchBlock(); + } SlowPathCodeARMVIXL* type_check_slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARMVIXL(instruction, is_type_check_slow_path_fatal); @@ -5451,12 +5516,17 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ CompareAndBranchIfZero(obj, &done, /* far_target */ false); } - // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); - switch (type_check_kind) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kArrayCheck: { + // /* HeapReference<Class> */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + __ Cmp(temp, cls); // Jump to slow path for throwing the exception or doing a // more involved array check. @@ -5465,12 +5535,24 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + // /* HeapReference<Class> */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. vixl32::Label loop; __ Bind(&loop); // /* HeapReference<Class> */ temp = temp->super_class_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); + GenerateReferenceLoadOneRegister(instruction, + temp_loc, + super_offset, + maybe_temp2_loc, + kWithoutReadBarrier); // If the class reference currently in `temp` is null, jump to the slow path to throw the // exception. @@ -5483,6 +5565,14 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + // /* HeapReference<Class> */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + // Walk over the class hierarchy to find a match. vixl32::Label loop; __ Bind(&loop); @@ -5490,7 +5580,11 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ B(eq, &done); // /* HeapReference<Class> */ temp = temp->super_class_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); + GenerateReferenceLoadOneRegister(instruction, + temp_loc, + super_offset, + maybe_temp2_loc, + kWithoutReadBarrier); // If the class reference currently in `temp` is null, jump to the slow path to throw the // exception. @@ -5501,13 +5595,25 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + // /* HeapReference<Class> */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + // Do an exact check. __ Cmp(temp, cls); __ B(eq, &done); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ temp = temp->component_type_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); + GenerateReferenceLoadOneRegister(instruction, + temp_loc, + component_offset, + maybe_temp2_loc, + kWithoutReadBarrier); // If the component type is null, jump to the slow path to throw the exception. __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel()); // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type` @@ -5519,10 +5625,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { } case TypeCheckKind::kUnresolvedCheck: - case TypeCheckKind::kInterfaceCheck: - // We always go into the type check slow path for the unresolved - // and interface check cases. - // + // We always go into the type check slow path for the unresolved check case. // We cannot directly call the CheckCast runtime entry point // without resorting to a type checking slow path here (i.e. by // calling InvokeRuntime directly), as it would require to @@ -5530,8 +5633,45 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // instruction (following the runtime calling convention), which // might be cluttered by the potential first read barrier // emission at the beginning of this method. + __ B(type_check_slow_path->GetEntryLabel()); break; + + case TypeCheckKind::kInterfaceCheck: { + // Avoid read barriers to improve performance of the fast path. We can not get false + // positives by doing this. + // /* HeapReference<Class> */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + + // /* HeapReference<Class> */ temp = temp->iftable_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + temp_loc, + iftable_offset, + maybe_temp2_loc, + kWithoutReadBarrier); + // Iftable is never null. + __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset)); + // Loop through the iftable and check if any class matches. + vixl32::Label start_loop; + __ Bind(&start_loop); + __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc), + type_check_slow_path->GetEntryLabel()); + __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset)); + GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc)); + // Go to next interface. + __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize)); + __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2); + // Compare the classes and continue the loop if they do not match. + __ Cmp(cls, RegisterFrom(maybe_temp3_loc)); + __ B(ne, &start_loop); + break; + } } __ Bind(&done); @@ -5715,6 +5855,33 @@ void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out, __ Eor(out, first, value); } +void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out, + Location first, + uint64_t value) { + vixl32::Register out_low = LowRegisterFrom(out); + vixl32::Register out_high = HighRegisterFrom(out); + vixl32::Register first_low = LowRegisterFrom(first); + vixl32::Register first_high = HighRegisterFrom(first); + uint32_t value_low = Low32Bits(value); + uint32_t value_high = High32Bits(value); + if (value_low == 0u) { + if (!out_low.Is(first_low)) { + __ Mov(out_low, first_low); + } + __ Add(out_high, first_high, value_high); + return; + } + __ Adds(out_low, first_low, value_low); + if (GetAssembler()->ShifterOperandCanHold(ADC, value_high, kCcKeep)) { + __ Adc(out_high, first_high, value_high); + } else if (GetAssembler()->ShifterOperandCanHold(SBC, ~value_high, kCcKeep)) { + __ Sbc(out_high, first_high, ~value_high); + } else { + LOG(FATAL) << "Unexpected constant " << value_high; + UNREACHABLE(); + } +} + void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) { LocationSummary* locations = instruction->GetLocations(); Location first = locations->InAt(0); @@ -5795,7 +5962,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister( HInstruction* instruction ATTRIBUTE_UNUSED, Location out, uint32_t offset, - Location maybe_temp ATTRIBUTE_UNUSED) { + Location maybe_temp ATTRIBUTE_UNUSED, + ReadBarrierOption read_barrier_option ATTRIBUTE_UNUSED) { vixl32::Register out_reg = RegisterFrom(out); if (kEmitCompilerReadBarrier) { TODO_VIXL32(FATAL); @@ -5812,7 +5980,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters( Location out, Location obj, uint32_t offset, - Location maybe_temp ATTRIBUTE_UNUSED) { + Location maybe_temp ATTRIBUTE_UNUSED, + ReadBarrierOption read_barrier_option ATTRIBUTE_UNUSED) { vixl32::Register out_reg = RegisterFrom(out); vixl32::Register obj_reg = RegisterFrom(obj); if (kEmitCompilerReadBarrier) { @@ -6175,17 +6344,35 @@ void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type } } -void LocationsBuilderARMVIXL::VisitClassTableGet( - HClassTableGet* instruction ATTRIBUTE_UNUSED) { - TODO_VIXL32(FATAL); +void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorARMVIXL::VisitClassTableGet( - HClassTableGet* instruction ATTRIBUTE_UNUSED) { - TODO_VIXL32(FATAL); +void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { + uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset( + instruction->GetIndex(), kArmPointerSize).SizeValue(); + GetAssembler()->LoadFromOffset(kLoadWord, + OutputRegister(instruction), + InputRegisterAt(instruction, 0), + method_offset); + } else { + uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement( + instruction->GetIndex(), kArmPointerSize)); + GetAssembler()->LoadFromOffset(kLoadWord, + OutputRegister(instruction), + InputRegisterAt(instruction, 0), + mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value()); + GetAssembler()->LoadFromOffset(kLoadWord, + OutputRegister(instruction), + OutputRegister(instruction), + method_offset); + } } - #undef __ #undef QUICK_ENTRY_POINT #undef TODO_VIXL32 diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 38c756fb0f..bd91127121 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -386,6 +386,7 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value); + void GenerateAddLongConst(Location out, Location first, uint64_t value); void HandleBitwiseOperation(HBinaryOperation* operation); void HandleCondition(HCondition* condition); void HandleIntegerRotate(HRor* ror); @@ -421,7 +422,8 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateReferenceLoadOneRegister(HInstruction* instruction, Location out, uint32_t offset, - Location maybe_temp); + Location maybe_temp, + ReadBarrierOption read_barrier_option); // Generate a heap reference load using two different registers // `out` and `obj`: // @@ -436,7 +438,8 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { Location out, Location obj, uint32_t offset, - Location maybe_temp); + Location maybe_temp, + ReadBarrierOption read_barrier_option); // Generate a GC root reference load: // diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 8f94834333..572d900909 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -280,7 +280,7 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { InvokeRuntimeCallingConvention calling_convention; HLoadString* load = instruction_->AsLoadString(); - const uint32_t string_index = load->GetStringIndex(); + const uint32_t string_index = load->GetStringIndex().index_; __ LoadConst32(calling_convention.GetRegisterAt(0), string_index); mips_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); @@ -1047,7 +1047,7 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); linker_patches->push_back(LinkerPatch::StringPatch(literal_offset, target_string.dex_file, - target_string.string_index)); + target_string.string_index.index_)); } for (const auto& entry : boot_image_type_patches_) { const TypeReference& target_type = entry.first; @@ -1110,7 +1110,7 @@ Literal* CodeGeneratorMIPS::DeduplicateMethodCodeLiteral(MethodReference target_ } Literal* CodeGeneratorMIPS::DeduplicateBootImageStringLiteral(const DexFile& dex_file, - uint32_t string_index) { + dex::StringIndex string_index) { return boot_image_string_patches_.GetOrCreate( StringReference(&dex_file, string_index), [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); @@ -5743,7 +5743,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) { DCHECK(!kEmitCompilerReadBarrier); DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS::PcRelativePatchInfo* info = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); return; // No dex cache slow path. } @@ -5759,7 +5759,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) { case HLoadString::LoadKind::kBssEntry: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS::PcRelativePatchInfo* info = - codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); codegen_->EmitPcRelativeAddressPlaceholder(info, out, base_or_current_method_reg); __ LoadFromOffset(kLoadWord, out, out, 0); SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load); @@ -5775,7 +5775,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) { // TODO: Re-add the compiler code to do string dex cache lookup again. DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex()); + __ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_); codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); } diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index e225d20094..2273e52b06 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_MIPS_H_ #include "code_generator.h" +#include "dex_file_types.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" @@ -452,7 +453,8 @@ class CodeGeneratorMIPS : public CodeGenerator { PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); - Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); + Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, + dex::StringIndex string_index); Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); Literal* DeduplicateBootImageAddressLiteral(uint32_t address); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 02b01c85e5..b5e98714e6 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -234,7 +234,7 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_; __ LoadConst32(calling_convention.GetRegisterAt(0), string_index); mips64_codegen->InvokeRuntime(kQuickResolveString, instruction_, diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 51e902a824..12aa03c4af 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -225,7 +225,7 @@ class LoadStringSlowPathX86 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_; __ movl(calling_convention.GetRegisterAt(0), Immediate(string_index)); x86_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); @@ -4607,7 +4607,7 @@ void CodeGeneratorX86::RecordSimplePatch() { void CodeGeneratorX86::RecordBootStringPatch(HLoadString* load_string) { DCHECK(GetCompilerOptions().IsBootImage()); - string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex()); + string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex().index_); __ Bind(&string_patches_.back().label); } @@ -4618,7 +4618,7 @@ void CodeGeneratorX86::RecordTypePatch(HLoadClass* load_class) { Label* CodeGeneratorX86::NewStringBssEntryPatch(HLoadString* load_string) { DCHECK(!GetCompilerOptions().IsBootImage()); - string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex()); + string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex().index_); return &string_patches_.back().label; } @@ -6253,10 +6253,11 @@ void LocationsBuilderX86::VisitLoadString(HLoadString* load) { } } -Label* CodeGeneratorX86::NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index) { +Label* CodeGeneratorX86::NewJitRootStringPatch(const DexFile& dex_file, + dex::StringIndex dex_index) { jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u); // Add a patch entry and return the label. - jit_string_patches_.emplace_back(dex_file, dex_index); + jit_string_patches_.emplace_back(dex_file, dex_index.index_); PatchInfo<Label>* info = &jit_string_patches_.back(); return &info->label; } @@ -6313,7 +6314,7 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) { // TODO: Re-add the compiler code to do string dex cache lookup again. InvokeRuntimeCallingConvention calling_convention; DCHECK_EQ(calling_convention.GetRegisterAt(0), out); - __ movl(calling_convention.GetRegisterAt(0), Immediate(load->GetStringIndex())); + __ movl(calling_convention.GetRegisterAt(0), Immediate(load->GetStringIndex().index_)); codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); } @@ -7755,7 +7756,8 @@ void CodeGeneratorX86::MoveFromReturnRegister(Location target, Primitive::Type t void CodeGeneratorX86::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { for (const PatchInfo<Label>& info : jit_string_patches_) { - const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, info.index)); + const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, + dex::StringIndex(info.index))); DCHECK(it != jit_string_roots_.end()); size_t index_in_table = it->second; uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 16ea6b55d6..2ae3670bed 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -20,6 +20,7 @@ #include "arch/x86/instruction_set_features_x86.h" #include "base/enums.h" #include "code_generator.h" +#include "dex_file_types.h" #include "driver/compiler_options.h" #include "nodes.h" #include "parallel_move_resolver.h" @@ -414,7 +415,7 @@ class CodeGeneratorX86 : public CodeGenerator { void RecordTypePatch(HLoadClass* load_class); Label* NewStringBssEntryPatch(HLoadString* load_string); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); - Label* NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index); + Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 34673138bf..22f7f6b52b 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -300,7 +300,7 @@ class LoadStringSlowPathX86_64 : public SlowPathCode { __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_; // Custom calling convention: RAX serves as both input and output. __ movl(CpuRegister(RAX), Immediate(string_index)); x86_64_codegen->InvokeRuntime(kQuickResolveString, @@ -1106,7 +1106,7 @@ void CodeGeneratorX86_64::RecordSimplePatch() { void CodeGeneratorX86_64::RecordBootStringPatch(HLoadString* load_string) { DCHECK(GetCompilerOptions().IsBootImage()); - string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex()); + string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex().index_); __ Bind(&string_patches_.back().label); } @@ -1117,7 +1117,7 @@ void CodeGeneratorX86_64::RecordTypePatch(HLoadClass* load_class) { Label* CodeGeneratorX86_64::NewStringBssEntryPatch(HLoadString* load_string) { DCHECK(!GetCompilerOptions().IsBootImage()); - string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex()); + string_patches_.emplace_back(load_string->GetDexFile(), load_string->GetStringIndex().index_); return &string_patches_.back().label; } @@ -5660,10 +5660,11 @@ void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) { } } -Label* CodeGeneratorX86_64::NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index) { +Label* CodeGeneratorX86_64::NewJitRootStringPatch(const DexFile& dex_file, + dex::StringIndex dex_index) { jit_string_roots_.Overwrite(StringReference(&dex_file, dex_index), /* placeholder */ 0u); // Add a patch entry and return the label. - jit_string_patches_.emplace_back(dex_file, dex_index); + jit_string_patches_.emplace_back(dex_file, dex_index.index_); PatchInfo<Label>* info = &jit_string_patches_.back(); return &info->label; } @@ -5714,7 +5715,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) { // TODO: Re-add the compiler code to do string dex cache lookup again. // Custom calling convention: RAX serves as both input and output. - __ movl(CpuRegister(RAX), Immediate(load->GetStringIndex())); + __ movl(CpuRegister(RAX), Immediate(load->GetStringIndex().index_)); codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); @@ -7111,7 +7112,8 @@ void CodeGeneratorX86_64::MoveInt64ToAddress(const Address& addr_low, void CodeGeneratorX86_64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { for (const PatchInfo<Label>& info : jit_string_patches_) { - const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, info.index)); + const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, + dex::StringIndex(info.index))); DCHECK(it != jit_string_roots_.end()); size_t index_in_table = it->second; uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 0f70b15787..2f41f73da6 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -412,7 +412,7 @@ class CodeGeneratorX86_64 : public CodeGenerator { void RecordTypePatch(HLoadClass* load_class); Label* NewStringBssEntryPatch(HLoadString* load_string); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); - Label* NewJitRootStringPatch(const DexFile& dex_file, uint32_t dex_index); + Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc index c80e19ef15..9df5bf1017 100644 --- a/compiler/optimizing/escape.cc +++ b/compiler/optimizing/escape.cc @@ -23,16 +23,19 @@ namespace art { void CalculateEscape(HInstruction* reference, bool (*no_escape)(HInstruction*, HInstruction*), /*out*/ bool* is_singleton, - /*out*/ bool* is_singleton_and_non_escaping) { + /*out*/ bool* is_singleton_and_not_returned, + /*out*/ bool* is_singleton_and_not_deopt_visible) { // For references not allocated in the method, don't assume anything. if (!reference->IsNewInstance() && !reference->IsNewArray()) { *is_singleton = false; - *is_singleton_and_non_escaping = false; + *is_singleton_and_not_returned = false; + *is_singleton_and_not_deopt_visible = false; return; } // Assume the best until proven otherwise. *is_singleton = true; - *is_singleton_and_non_escaping = true; + *is_singleton_and_not_returned = true; + *is_singleton_and_not_deopt_visible = true; // Visit all uses to determine if this reference can escape into the heap, // a method call, an alias, etc. for (const HUseListNode<HInstruction*>& use : reference->GetUses()) { @@ -45,7 +48,8 @@ void CalculateEscape(HInstruction* reference, // for the uncommon cases. Similarly, null checks are eventually eliminated for explicit // allocations, but if we see one before it is simplified, assume an alias. *is_singleton = false; - *is_singleton_and_non_escaping = false; + *is_singleton_and_not_returned = false; + *is_singleton_and_not_deopt_visible = false; return; } else if (user->IsPhi() || user->IsSelect() || user->IsInvoke() || (user->IsInstanceFieldSet() && (reference == user->InputAt(1))) || @@ -56,7 +60,8 @@ void CalculateEscape(HInstruction* reference, // The reference is merged to HPhi/HSelect, passed to a callee, or stored to heap. // Hence, the reference is no longer the only name that can refer to its value. *is_singleton = false; - *is_singleton_and_non_escaping = false; + *is_singleton_and_not_returned = false; + *is_singleton_and_not_deopt_visible = false; return; } else if ((user->IsUnresolvedInstanceFieldGet() && (reference == user->InputAt(0))) || (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(0)))) { @@ -64,37 +69,35 @@ void CalculateEscape(HInstruction* reference, // Note that we could optimize this case and still perform some optimizations until // we hit the unresolved access, but the conservative assumption is the simplest. *is_singleton = false; - *is_singleton_and_non_escaping = false; + *is_singleton_and_not_returned = false; + *is_singleton_and_not_deopt_visible = false; return; } else if (user->IsReturn()) { - *is_singleton_and_non_escaping = false; + *is_singleton_and_not_returned = false; } } - // Need for further analysis? - if (!*is_singleton_and_non_escaping) { - return; - } - - // Look at the environment uses and if it's for HDeoptimize, it's treated the - // same as a return which escapes at the end of executing the compiled code. - // Other environment uses are fine, as long as all client optimizations that - // rely on this informations are disabled for debuggable. + // Look at the environment uses if it's for HDeoptimize. Other environment uses are fine, + // as long as client optimizations that rely on this information are disabled for debuggable. for (const HUseListNode<HEnvironment*>& use : reference->GetEnvUses()) { HEnvironment* user = use.GetUser(); if (user->GetHolder()->IsDeoptimize()) { - *is_singleton_and_non_escaping = false; + *is_singleton_and_not_deopt_visible = false; break; } } } -bool IsNonEscapingSingleton(HInstruction* reference, - bool (*no_escape)(HInstruction*, HInstruction*)) { - bool is_singleton = true; - bool is_singleton_and_non_escaping = true; - CalculateEscape(reference, no_escape, &is_singleton, &is_singleton_and_non_escaping); - return is_singleton_and_non_escaping; +bool DoesNotEscape(HInstruction* reference, bool (*no_escape)(HInstruction*, HInstruction*)) { + bool is_singleton = false; + bool is_singleton_and_not_returned = false; + bool is_singleton_and_not_deopt_visible = false; // not relevant for escape + CalculateEscape(reference, + no_escape, + &is_singleton, + &is_singleton_and_not_returned, + &is_singleton_and_not_deopt_visible); + return is_singleton_and_not_returned; } } // namespace art diff --git a/compiler/optimizing/escape.h b/compiler/optimizing/escape.h index 6514843247..75e37b0551 100644 --- a/compiler/optimizing/escape.h +++ b/compiler/optimizing/escape.h @@ -31,9 +31,18 @@ class HInstruction; * allocation. The method assigns true to parameter 'is_singleton' if the reference * is the only name that can refer to its value during the lifetime of the method, * meaning that the reference is not aliased with something else, is not stored to - * heap memory, and not passed to another method. The method assigns true to parameter - * 'is_singleton_and_non_escaping' if the reference is a singleton and is not returned - * to the caller or used as an environment local of an HDeoptimize instruction. + * heap memory, and not passed to another method. In addition, the method assigns + * true to parameter 'is_singleton_and_not_returned' if the reference is a singleton + * and not returned to the caller and to parameter 'is_singleton_and_not_deopt_visible' + * if the reference is a singleton and not used as an environment local of an + * HDeoptimize instruction (clients of the final value must run after BCE to ensure + * all such instructions have been introduced already). + * + * Note that being visible to a HDeoptimize instruction does not count for ordinary + * escape analysis, since switching between compiled code and interpreted code keeps + * non escaping references restricted to the lifetime of the method and the thread + * executing it. This property only concerns optimizations that are interested in + * escape analysis with respect to the *compiled* code (such as LSE). * * When set, the no_escape function is applied to any use of the allocation instruction * prior to any built-in escape analysis. This allows clients to define better escape @@ -45,14 +54,14 @@ class HInstruction; void CalculateEscape(HInstruction* reference, bool (*no_escape)(HInstruction*, HInstruction*), /*out*/ bool* is_singleton, - /*out*/ bool* is_singleton_and_non_escaping); + /*out*/ bool* is_singleton_and_not_returned, + /*out*/ bool* is_singleton_and_not_deopt_visible); /* - * Convenience method for testing singleton and non-escaping property at once. + * Convenience method for testing the singleton and not returned properties at once. * Callers should be aware that this method invokes the full analysis at each call. */ -bool IsNonEscapingSingleton(HInstruction* reference, - bool (*no_escape)(HInstruction*, HInstruction*)); +bool DoesNotEscape(HInstruction* reference, bool (*no_escape)(HInstruction*, HInstruction*)); } // namespace art diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 16a465a43d..01e89bb304 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -258,6 +258,40 @@ class ScopedProfilingInfoInlineUse { ProfilingInfo* const profiling_info_; }; +static bool IsMonomorphic(Handle<mirror::ObjectArray<mirror::Class>> classes) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_GE(InlineCache::kIndividualCacheSize, 2); + return classes->Get(0) != nullptr && classes->Get(1) == nullptr; +} + +static bool IsMegamorphic(Handle<mirror::ObjectArray<mirror::Class>> classes) + REQUIRES_SHARED(Locks::mutator_lock_) { + for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) { + if (classes->Get(i) == nullptr) { + return false; + } + } + return true; +} + +static mirror::Class* GetMonomorphicType(Handle<mirror::ObjectArray<mirror::Class>> classes) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(classes->Get(0) != nullptr); + return classes->Get(0); +} + +static bool IsUninitialized(Handle<mirror::ObjectArray<mirror::Class>> classes) + REQUIRES_SHARED(Locks::mutator_lock_) { + return classes->Get(0) == nullptr; +} + +static bool IsPolymorphic(Handle<mirror::ObjectArray<mirror::Class>> classes) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_GE(InlineCache::kIndividualCacheSize, 3); + return classes->Get(1) != nullptr && + classes->Get(InlineCache::kIndividualCacheSize - 1) == nullptr; +} + bool HInliner::TryInline(HInvoke* invoke_instruction) { if (invoke_instruction->IsInvokeUnresolved()) { return false; // Don't bother to move further if we know the method is unresolved. @@ -301,31 +335,48 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { ScopedProfilingInfoInlineUse spiis(caller, soa.Self()); ProfilingInfo* profiling_info = spiis.GetProfilingInfo(); if (profiling_info != nullptr) { - const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()); - if (ic.IsUninitialized()) { - VLOG(compiler) << "Interface or virtual call to " - << caller_dex_file.PrettyMethod(method_index) - << " is not hit and not inlined"; + StackHandleScope<1> hs(soa.Self()); + ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); + Handle<mirror::ObjectArray<mirror::Class>> inline_cache = hs.NewHandle( + mirror::ObjectArray<mirror::Class>::Alloc( + soa.Self(), + class_linker->GetClassRoot(ClassLinker::kClassArrayClass), + InlineCache::kIndividualCacheSize)); + if (inline_cache.Get() == nullptr) { + // We got an OOME. Just clear the exception, and don't inline. + DCHECK(soa.Self()->IsExceptionPending()); + soa.Self()->ClearException(); + VLOG(compiler) << "Out of memory in the compiler when trying to inline"; return false; - } else if (ic.IsMonomorphic()) { - MaybeRecordStat(kMonomorphicCall); - if (outermost_graph_->IsCompilingOsr()) { - // If we are compiling OSR, we pretend this call is polymorphic, as we may come from the - // interpreter and it may have seen different receiver types. - return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic); + } else { + Runtime::Current()->GetJit()->GetCodeCache()->CopyInlineCacheInto( + *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()), + inline_cache); + if (IsUninitialized(inline_cache)) { + VLOG(compiler) << "Interface or virtual call to " + << caller_dex_file.PrettyMethod(method_index) + << " is not hit and not inlined"; + return false; + } else if (IsMonomorphic(inline_cache)) { + MaybeRecordStat(kMonomorphicCall); + if (outermost_graph_->IsCompilingOsr()) { + // If we are compiling OSR, we pretend this call is polymorphic, as we may come from the + // interpreter and it may have seen different receiver types. + return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache); + } else { + return TryInlineMonomorphicCall(invoke_instruction, resolved_method, inline_cache); + } + } else if (IsPolymorphic(inline_cache)) { + MaybeRecordStat(kPolymorphicCall); + return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache); } else { - return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic); + DCHECK(IsMegamorphic(inline_cache)); + VLOG(compiler) << "Interface or virtual call to " + << caller_dex_file.PrettyMethod(method_index) + << " is megamorphic and not inlined"; + MaybeRecordStat(kMegamorphicCall); + return false; } - } else if (ic.IsPolymorphic()) { - MaybeRecordStat(kPolymorphicCall); - return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic); - } else { - DCHECK(ic.IsMegamorphic()); - VLOG(compiler) << "Interface or virtual call to " - << caller_dex_file.PrettyMethod(method_index) - << " is megamorphic and not inlined"; - MaybeRecordStat(kMegamorphicCall); - return false; } } } @@ -358,13 +409,13 @@ HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker, bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, ArtMethod* resolved_method, - const InlineCache& ic) { + Handle<mirror::ObjectArray<mirror::Class>> classes) { DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface()) << invoke_instruction->DebugName(); const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); dex::TypeIndex class_index = FindClassIndexIn( - ic.GetMonomorphicType(), caller_dex_file, caller_compilation_unit_.GetDexCache()); + GetMonomorphicType(classes), caller_dex_file, caller_compilation_unit_.GetDexCache()); if (!class_index.IsValid()) { VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method) << " from inline cache is not inlined because its class is not" @@ -375,11 +426,11 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); PointerSize pointer_size = class_linker->GetImagePointerSize(); if (invoke_instruction->IsInvokeInterface()) { - resolved_method = ic.GetMonomorphicType()->FindVirtualMethodForInterface( + resolved_method = GetMonomorphicType(classes)->FindVirtualMethodForInterface( resolved_method, pointer_size); } else { DCHECK(invoke_instruction->IsInvokeVirtual()); - resolved_method = ic.GetMonomorphicType()->FindVirtualMethodForVirtual( + resolved_method = GetMonomorphicType(classes)->FindVirtualMethodForVirtual( resolved_method, pointer_size); } DCHECK(resolved_method != nullptr); @@ -393,7 +444,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, // We successfully inlined, now add a guard. bool is_referrer = - (ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass()); + (GetMonomorphicType(classes) == outermost_graph_->GetArtMethod()->GetDeclaringClass()); AddTypeGuard(receiver, cursor, bb_cursor, @@ -457,11 +508,11 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, ArtMethod* resolved_method, - const InlineCache& ic) { + Handle<mirror::ObjectArray<mirror::Class>> classes) { DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface()) << invoke_instruction->DebugName(); - if (TryInlinePolymorphicCallToSameTarget(invoke_instruction, resolved_method, ic)) { + if (TryInlinePolymorphicCallToSameTarget(invoke_instruction, resolved_method, classes)) { return true; } @@ -472,16 +523,16 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, bool all_targets_inlined = true; bool one_target_inlined = false; for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) { - if (ic.GetTypeAt(i) == nullptr) { + if (classes->Get(i) == nullptr) { break; } ArtMethod* method = nullptr; if (invoke_instruction->IsInvokeInterface()) { - method = ic.GetTypeAt(i)->FindVirtualMethodForInterface( + method = classes->Get(i)->FindVirtualMethodForInterface( resolved_method, pointer_size); } else { DCHECK(invoke_instruction->IsInvokeVirtual()); - method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual( + method = classes->Get(i)->FindVirtualMethodForVirtual( resolved_method, pointer_size); } @@ -490,20 +541,20 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); dex::TypeIndex class_index = FindClassIndexIn( - ic.GetTypeAt(i), caller_dex_file, caller_compilation_unit_.GetDexCache()); + classes->Get(i), caller_dex_file, caller_compilation_unit_.GetDexCache()); HInstruction* return_replacement = nullptr; if (!class_index.IsValid() || !TryBuildAndInline(invoke_instruction, method, &return_replacement)) { all_targets_inlined = false; } else { one_target_inlined = true; - bool is_referrer = (ic.GetTypeAt(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass()); + bool is_referrer = (classes->Get(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass()); // If we have inlined all targets before, and this receiver is the last seen, // we deoptimize instead of keeping the original invoke instruction. bool deoptimize = all_targets_inlined && (i != InlineCache::kIndividualCacheSize - 1) && - (ic.GetTypeAt(i + 1) == nullptr); + (classes->Get(i + 1) == nullptr); if (outermost_graph_->IsCompilingOsr()) { // We do not support HDeoptimize in OSR methods. @@ -618,9 +669,10 @@ void HInliner::CreateDiamondPatternForPolymorphicInline(HInstruction* compare, merge, original_invoke_block, /* replace_if_back_edge */ true); } -bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, - ArtMethod* resolved_method, - const InlineCache& ic) { +bool HInliner::TryInlinePolymorphicCallToSameTarget( + HInvoke* invoke_instruction, + ArtMethod* resolved_method, + Handle<mirror::ObjectArray<mirror::Class>> classes) { // This optimization only works under JIT for now. DCHECK(Runtime::Current()->UseJitCompilation()); if (graph_->GetInstructionSet() == kMips64) { @@ -639,12 +691,12 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, // Check whether we are actually calling the same method among // the different types seen. for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) { - if (ic.GetTypeAt(i) == nullptr) { + if (classes->Get(i) == nullptr) { break; } ArtMethod* new_method = nullptr; if (invoke_instruction->IsInvokeInterface()) { - new_method = ic.GetTypeAt(i)->GetImt(pointer_size)->Get( + new_method = classes->Get(i)->GetImt(pointer_size)->Get( method_index, pointer_size); if (new_method->IsRuntimeMethod()) { // Bail out as soon as we see a conflict trampoline in one of the target's @@ -653,7 +705,7 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, } } else { DCHECK(invoke_instruction->IsInvokeVirtual()); - new_method = ic.GetTypeAt(i)->GetEmbeddedVTableEntry(method_index, pointer_size); + new_method = classes->Get(i)->GetEmbeddedVTableEntry(method_index, pointer_size); } DCHECK(new_method != nullptr); if (actual_method == nullptr) { diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 682393e697..a2b4fc96c4 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -28,7 +28,6 @@ class CompilerDriver; class DexCompilationUnit; class HGraph; class HInvoke; -class InlineCache; class OptimizingCompilerStats; class HInliner : public HOptimization { @@ -105,18 +104,18 @@ class HInliner : public HOptimization { // ... // inlined code bool TryInlineMonomorphicCall(HInvoke* invoke_instruction, ArtMethod* resolved_method, - const InlineCache& ic) + Handle<mirror::ObjectArray<mirror::Class>> classes) REQUIRES_SHARED(Locks::mutator_lock_); // Try to inline targets of a polymorphic call. bool TryInlinePolymorphicCall(HInvoke* invoke_instruction, ArtMethod* resolved_method, - const InlineCache& ic) + Handle<mirror::ObjectArray<mirror::Class>> classes) REQUIRES_SHARED(Locks::mutator_lock_); bool TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, ArtMethod* resolved_method, - const InlineCache& ic) + Handle<mirror::ObjectArray<mirror::Class>> classes) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 40de5ce0cc..b97581beb3 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -2625,7 +2625,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::CONST_STRING: { - uint32_t string_index = instruction.VRegB_21c(); + dex::StringIndex string_index(instruction.VRegB_21c()); AppendInstruction( new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc)); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); @@ -2633,7 +2633,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, } case Instruction::CONST_STRING_JUMBO: { - uint32_t string_index = instruction.VRegB_31c(); + dex::StringIndex string_index(instruction.VRegB_31c()); AppendInstruction( new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc)); UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction()); diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 85b461dcf6..658b80468e 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -16,6 +16,7 @@ #include "instruction_simplifier.h" +#include "escape.h" #include "intrinsics.h" #include "mirror/class-inl.h" #include "scoped_thread_state_change-inl.h" @@ -107,6 +108,8 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyStringCharAt(HInvoke* invoke); void SimplifyStringIsEmptyOrLength(HInvoke* invoke); void SimplifyNPEOnArgN(HInvoke* invoke, size_t); + void SimplifyReturnThis(HInvoke* invoke); + void SimplifyAllocationIntrinsic(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); OptimizingCompilerStats* stats_; @@ -1864,11 +1867,61 @@ void InstructionSimplifierVisitor::SimplifyStringIsEmptyOrLength(HInvoke* invoke // is provably non-null, we can clear the flag. void InstructionSimplifierVisitor::SimplifyNPEOnArgN(HInvoke* invoke, size_t n) { HInstruction* arg = invoke->InputAt(n); - if (!arg->CanBeNull()) { + if (invoke->CanThrow() && !arg->CanBeNull()) { invoke->SetCanThrow(false); } } +// Methods that return "this" can replace the returned value with the receiver. +void InstructionSimplifierVisitor::SimplifyReturnThis(HInvoke* invoke) { + if (invoke->HasUses()) { + HInstruction* receiver = invoke->InputAt(0); + invoke->ReplaceWith(receiver); + RecordSimplification(); + } +} + +// Helper method for StringBuffer escape analysis. +static bool NoEscapeForStringBufferReference(HInstruction* reference, HInstruction* user) { + if (user->IsInvokeStaticOrDirect()) { + // Any constructor on StringBuffer is okay. + return user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && + user->InputAt(0) == reference; + } else if (user->IsInvokeVirtual()) { + switch (user->AsInvokeVirtual()->GetIntrinsic()) { + case Intrinsics::kStringBufferLength: + case Intrinsics::kStringBufferToString: + DCHECK_EQ(user->InputAt(0), reference); + return true; + case Intrinsics::kStringBufferAppend: + // Returns "this", so only okay if no further uses. + DCHECK_EQ(user->InputAt(0), reference); + DCHECK_NE(user->InputAt(1), reference); + return !user->HasUses(); + default: + break; + } + } + return false; +} + +// Certain allocation intrinsics are not removed by dead code elimination +// because of potentially throwing an OOM exception or other side effects. +// This method removes such intrinsics when special circumstances allow. +void InstructionSimplifierVisitor::SimplifyAllocationIntrinsic(HInvoke* invoke) { + if (!invoke->HasUses()) { + // Instruction has no uses. If unsynchronized, we can remove right away, safely ignoring + // the potential OOM of course. Otherwise, we must ensure the receiver object of this + // call does not escape since only thread-local synchronization may be removed. + bool is_synchronized = invoke->GetIntrinsic() == Intrinsics::kStringBufferToString; + HInstruction* receiver = invoke->InputAt(0); + if (!is_synchronized || DoesNotEscape(receiver, NoEscapeForStringBufferReference)) { + invoke->GetBlock()->RemoveInstruction(invoke); + RecordSimplification(); + } + } +} + void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind) { uint32_t dex_pc = invoke->GetDexPc(); HMemoryBarrier* mem_barrier = new (GetGraph()->GetArena()) HMemoryBarrier(barrier_kind, dex_pc); @@ -1926,6 +1979,14 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kStringStringIndexOfAfter: SimplifyNPEOnArgN(instruction, 1); // 0th has own NullCheck break; + case Intrinsics::kStringBufferAppend: + case Intrinsics::kStringBuilderAppend: + SimplifyReturnThis(instruction); + break; + case Intrinsics::kStringBufferToString: + case Intrinsics::kStringBuilderToString: + SimplifyAllocationIntrinsic(instruction); + break; case Intrinsics::kUnsafeLoadFence: SimplifyMemBarrier(instruction, MemBarrierKind::kLoadAny); break; diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 8234b2467d..8f64faeac0 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -2613,6 +2613,12 @@ UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit) UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(ARM, StringStringIndexOfAfter); +UNIMPLEMENTED_INTRINSIC(ARM, StringBufferAppend); +UNIMPLEMENTED_INTRINSIC(ARM, StringBufferLength); +UNIMPLEMENTED_INTRINSIC(ARM, StringBufferToString); +UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderAppend); +UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderLength); +UNIMPLEMENTED_INTRINSIC(ARM, StringBuilderToString); // 1.8. UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 17a97da6cc..d8a896e926 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -2781,6 +2781,12 @@ UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit) UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOfAfter); +UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferAppend); +UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferLength); +UNIMPLEMENTED_INTRINSIC(ARM64, StringBufferToString); +UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderAppend); +UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderLength); +UNIMPLEMENTED_INTRINSIC(ARM64, StringBuilderToString); // 1.8. UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt) diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index c8e3534164..9e724474d0 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -677,7 +677,10 @@ static void GenUnsafeGet(HInvoke* invoke, vixl32::Register trg_lo = LowRegisterFrom(trg_loc); vixl32::Register trg_hi = HighRegisterFrom(trg_loc); if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) { - __ Ldrexd(trg_lo, trg_hi, MemOperand(base, offset)); + UseScratchRegisterScope temps(assembler->GetVIXLAssembler()); + const vixl32::Register temp_reg = temps.Acquire(); + __ Add(temp_reg, base, offset); + __ Ldrexd(trg_lo, trg_hi, MemOperand(temp_reg)); } else { __ Ldrd(trg_lo, trg_hi, MemOperand(base, offset)); } @@ -2703,6 +2706,12 @@ UNIMPLEMENTED_INTRINSIC(ARMVIXL, LongLowestOneBit) UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter); +UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferAppend); +UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferLength); +UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBufferToString); +UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderAppend); +UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderLength); +UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringBuilderToString); // 1.8. UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeGetAndAddInt) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 7c81588cda..9b5d7a02dd 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -2497,6 +2497,12 @@ UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh) UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter); +UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferAppend); +UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferLength); +UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferToString); +UNIMPLEMENTED_INTRINSIC(MIPS, StringBuilderAppend); +UNIMPLEMENTED_INTRINSIC(MIPS, StringBuilderLength); +UNIMPLEMENTED_INTRINSIC(MIPS, StringBuilderToString); // 1.8. UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddInt) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 2d4f417b14..5a998861eb 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1949,6 +1949,12 @@ UNIMPLEMENTED_INTRINSIC(MIPS64, MathTanh) UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter); +UNIMPLEMENTED_INTRINSIC(MIPS64, StringBufferAppend); +UNIMPLEMENTED_INTRINSIC(MIPS64, StringBufferLength); +UNIMPLEMENTED_INTRINSIC(MIPS64, StringBufferToString); +UNIMPLEMENTED_INTRINSIC(MIPS64, StringBuilderAppend); +UNIMPLEMENTED_INTRINSIC(MIPS64, StringBuilderLength); +UNIMPLEMENTED_INTRINSIC(MIPS64, StringBuilderToString); // 1.8. UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddInt) diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 06ab46f536..922c3bcac9 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -3331,6 +3331,12 @@ UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit) UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter); +UNIMPLEMENTED_INTRINSIC(X86, StringBufferAppend); +UNIMPLEMENTED_INTRINSIC(X86, StringBufferLength); +UNIMPLEMENTED_INTRINSIC(X86, StringBufferToString); +UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppend); +UNIMPLEMENTED_INTRINSIC(X86, StringBuilderLength); +UNIMPLEMENTED_INTRINSIC(X86, StringBuilderToString); // 1.8. UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddInt) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 2ea8670100..05d270a4e6 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -3000,6 +3000,12 @@ UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite) UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOfAfter); +UNIMPLEMENTED_INTRINSIC(X86_64, StringBufferAppend); +UNIMPLEMENTED_INTRINSIC(X86_64, StringBufferLength); +UNIMPLEMENTED_INTRINSIC(X86_64, StringBufferToString); +UNIMPLEMENTED_INTRINSIC(X86_64, StringBuilderAppend); +UNIMPLEMENTED_INTRINSIC(X86_64, StringBuilderLength); +UNIMPLEMENTED_INTRINSIC(X86_64, StringBuilderToString); // 1.8. UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddInt) diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index edecf17f33..2856c3ea11 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -37,8 +37,13 @@ class ReferenceInfo : public ArenaObject<kArenaAllocMisc> { : reference_(reference), position_(pos), is_singleton_(true), - is_singleton_and_non_escaping_(true) { - CalculateEscape(reference_, nullptr, &is_singleton_, &is_singleton_and_non_escaping_); + is_singleton_and_not_returned_(true), + is_singleton_and_not_deopt_visible_(true) { + CalculateEscape(reference_, + nullptr, + &is_singleton_, + &is_singleton_and_not_returned_, + &is_singleton_and_not_deopt_visible_); } HInstruction* GetReference() const { @@ -59,19 +64,17 @@ class ReferenceInfo : public ArenaObject<kArenaAllocMisc> { // Returns true if reference_ is a singleton and not returned to the caller or // used as an environment local of an HDeoptimize instruction. // The allocation and stores into reference_ may be eliminated for such cases. - bool IsSingletonAndNonEscaping() const { - return is_singleton_and_non_escaping_; + bool IsSingletonAndRemovable() const { + return is_singleton_and_not_returned_ && is_singleton_and_not_deopt_visible_; } private: HInstruction* const reference_; - const size_t position_; // position in HeapLocationCollector's ref_info_array_. - bool is_singleton_; // can only be referred to by a single name in the method. + const size_t position_; // position in HeapLocationCollector's ref_info_array_. - // reference_ is singleton and does not escape in the end either by - // returning to the caller, or being used as an environment local of an - // HDeoptimize instruction. - bool is_singleton_and_non_escaping_; + bool is_singleton_; // can only be referred to by a single name in the method, + bool is_singleton_and_not_returned_; // and not returned to caller, + bool is_singleton_and_not_deopt_visible_; // and not used as an environment local of HDeoptimize. DISALLOW_COPY_AND_ASSIGN(ReferenceInfo); }; @@ -623,7 +626,7 @@ class LSEVisitor : public HGraphVisitor { bool from_all_predecessors = true; ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo(); HInstruction* singleton_ref = nullptr; - if (ref_info->IsSingletonAndNonEscaping()) { + if (ref_info->IsSingletonAndRemovable()) { // We do more analysis of liveness when merging heap values for such // cases since stores into such references may potentially be eliminated. singleton_ref = ref_info->GetReference(); @@ -796,7 +799,7 @@ class LSEVisitor : public HGraphVisitor { } else if (index != nullptr) { // For array element, don't eliminate stores since it can be easily aliased // with non-constant index. - } else if (ref_info->IsSingletonAndNonEscaping()) { + } else if (ref_info->IsSingletonAndRemovable()) { // Store into a field of a singleton that's not returned. The value cannot be // killed due to aliasing/invocation. It can be redundant since future loads can // directly get the value set by this instruction. The value can still be killed due to @@ -970,7 +973,7 @@ class LSEVisitor : public HGraphVisitor { // new_instance isn't used for field accesses. No need to process it. return; } - if (ref_info->IsSingletonAndNonEscaping() && + if (ref_info->IsSingletonAndRemovable() && !new_instance->IsFinalizable() && !new_instance->NeedsAccessCheck()) { singleton_new_instances_.push_back(new_instance); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index eebc49c991..7ab04e15fc 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2072,6 +2072,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { #undef INSTRUCTION_TYPE_CHECK // Returns whether the instruction can be moved within the graph. + // TODO: this method is used by LICM and GVN with possibly different + // meanings? split and rename? virtual bool CanBeMoved() const { return false; } // Returns whether the two instructions are of the same kind. @@ -3789,7 +3791,7 @@ class HInvoke : public HInstruction { bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>(); } - bool CanBeMoved() const OVERRIDE { return IsIntrinsic(); } + bool CanBeMoved() const OVERRIDE { return IsIntrinsic() && !DoesAnyWrite(); } bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { return intrinsic_ != Intrinsics::kNone && intrinsic_ == other->AsInvoke()->intrinsic_; @@ -4181,6 +4183,19 @@ class HInvokeVirtual FINAL : public HInvoke { kVirtual), vtable_index_(vtable_index) {} + bool CanBeNull() const OVERRIDE { + switch (GetIntrinsic()) { + case Intrinsics::kThreadCurrentThread: + case Intrinsics::kStringBufferAppend: + case Intrinsics::kStringBufferToString: + case Intrinsics::kStringBuilderAppend: + case Intrinsics::kStringBuilderToString: + return false; + default: + return HInvoke::CanBeNull(); + } + } + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { // TODO: Add implicit null checks in intrinsics. return (obj == InputAt(0)) && !GetLocations()->Intrinsified(); @@ -5698,7 +5713,7 @@ class HLoadString FINAL : public HInstruction { }; HLoadString(HCurrentMethod* current_method, - uint32_t string_index, + dex::StringIndex string_index, const DexFile& dex_file, uint32_t dex_pc) : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc), @@ -5717,7 +5732,7 @@ class HLoadString FINAL : public HInstruction { void SetLoadKindWithStringReference(LoadKind load_kind, const DexFile& dex_file, - uint32_t string_index) { + dex::StringIndex string_index) { DCHECK(HasStringReference(load_kind)); load_data_.dex_file_ = &dex_file; string_index_ = string_index; @@ -5730,7 +5745,7 @@ class HLoadString FINAL : public HInstruction { const DexFile& GetDexFile() const; - uint32_t GetStringIndex() const { + dex::StringIndex GetStringIndex() const { DCHECK(HasStringReference(GetLoadKind()) || /* For slow paths. */ !IsInDexCache()); return string_index_; } @@ -5744,7 +5759,7 @@ class HLoadString FINAL : public HInstruction { bool InstructionDataEquals(const HInstruction* other) const OVERRIDE; - size_t ComputeHashCode() const OVERRIDE { return string_index_; } + size_t ComputeHashCode() const OVERRIDE { return string_index_.index_; } // Will call the runtime if we need to load the string through // the dex cache and the string is not guaranteed to be there yet. @@ -5823,7 +5838,7 @@ class HLoadString FINAL : public HInstruction { // String index serves also as the hash code and it's also needed for slow-paths, // so it must not be overwritten with other load data. - uint32_t string_index_; + dex::StringIndex string_index_; union { const DexFile* dex_file_; // For string reference. diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index a127708ab0..daf160a483 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -267,7 +267,7 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { DCHECK(!load_string->IsInDexCache()); const DexFile& dex_file = load_string->GetDexFile(); - uint32_t string_index = load_string->GetStringIndex(); + dex::StringIndex string_index = load_string->GetStringIndex(); HLoadString::LoadKind desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; uint64_t address = 0u; // String or dex cache element address. diff --git a/compiler/utils/atomic_method_ref_map-inl.h b/compiler/utils/atomic_method_ref_map-inl.h new file mode 100644 index 0000000000..70ea028b17 --- /dev/null +++ b/compiler/utils/atomic_method_ref_map-inl.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_INL_H_ +#define ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_INL_H_ + +#include "atomic_method_ref_map.h" + +#include "dex_file-inl.h" + +namespace art { + +template <typename T> +inline typename AtomicMethodRefMap<T>::InsertResult AtomicMethodRefMap<T>::Insert( + MethodReference ref, + const T& expected, + const T& desired) { + ElementArray* const array = GetArray(ref.dex_file); + if (array == nullptr) { + return kInsertResultInvalidDexFile; + } + return (*array)[ref.dex_method_index].CompareExchangeStrongSequentiallyConsistent( + expected, desired) + ? kInsertResultSuccess + : kInsertResultCASFailure; +} + +template <typename T> +inline bool AtomicMethodRefMap<T>::Get(MethodReference ref, T* out) const { + const ElementArray* const array = GetArray(ref.dex_file); + if (array == nullptr) { + return kInsertResultInvalidDexFile; + } + *out = (*array)[ref.dex_method_index].LoadRelaxed(); + return true; +} + +template <typename T> +inline void AtomicMethodRefMap<T>::AddDexFile(const DexFile* dex_file) { + arrays_.Put(dex_file, std::move(ElementArray(dex_file->NumMethodIds()))); +} + +template <typename T> +inline typename AtomicMethodRefMap<T>::ElementArray* AtomicMethodRefMap<T>::GetArray( + const DexFile* dex_file) { + auto it = arrays_.find(dex_file); + return (it != arrays_.end()) ? &it->second : nullptr; +} + +template <typename T> +inline const typename AtomicMethodRefMap<T>::ElementArray* AtomicMethodRefMap<T>::GetArray( + const DexFile* dex_file) const { + auto it = arrays_.find(dex_file); + return (it != arrays_.end()) ? &it->second : nullptr; +} + +template <typename T> template <typename Visitor> +inline void AtomicMethodRefMap<T>::Visit(const Visitor& visitor) { + for (auto& pair : arrays_) { + const DexFile* dex_file = pair.first; + const ElementArray& elements = pair.second; + for (size_t i = 0; i < elements.size(); ++i) { + visitor(MethodReference(dex_file, i), elements[i].LoadRelaxed()); + } + } +} + +} // namespace art + +#endif // ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_INL_H_ diff --git a/compiler/utils/atomic_method_ref_map.h b/compiler/utils/atomic_method_ref_map.h new file mode 100644 index 0000000000..11ab211817 --- /dev/null +++ b/compiler/utils/atomic_method_ref_map.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_H_ +#define ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_H_ + +#include "base/dchecked_vector.h" +#include "method_reference.h" +#include "safe_map.h" + +namespace art { + +class DexFile; + +// Used by CompilerCallbacks to track verification information from the Runtime. +template <typename T> +class AtomicMethodRefMap { + public: + explicit AtomicMethodRefMap() {} + ~AtomicMethodRefMap() {} + + // Atomically swap the element in if the existing value matches expected. + enum InsertResult { + kInsertResultInvalidDexFile, + kInsertResultCASFailure, + kInsertResultSuccess, + }; + InsertResult Insert(MethodReference ref, const T& expected, const T& desired); + + // Retreive an item, returns false if the dex file is not added. + bool Get(MethodReference ref, T* out) const; + + // Dex files must be added before method references belonging to them can be used as keys. Not + // thread safe. + void AddDexFile(const DexFile* dex_file); + + bool HaveDexFile(const DexFile* dex_file) const { + return arrays_.find(dex_file) != arrays_.end(); + } + + // Visit all of the dex files and elements. + template <typename Visitor> + void Visit(const Visitor& visitor); + + private: + // Verified methods. The method array is fixed to avoid needing a lock to extend it. + using ElementArray = dchecked_vector<Atomic<T>>; + using DexFileArrays = SafeMap<const DexFile*, ElementArray>; + + const ElementArray* GetArray(const DexFile* dex_file) const; + ElementArray* GetArray(const DexFile* dex_file); + + DexFileArrays arrays_; +}; + +} // namespace art + +#endif // ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_H_ diff --git a/compiler/utils/atomic_method_ref_map_test.cc b/compiler/utils/atomic_method_ref_map_test.cc new file mode 100644 index 0000000000..9e5bf4bbe1 --- /dev/null +++ b/compiler/utils/atomic_method_ref_map_test.cc @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "atomic_method_ref_map-inl.h" + +#include <memory> + +#include "common_runtime_test.h" +#include "dex_file-inl.h" +#include "method_reference.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +class AtomicMethodRefMapTest : public CommonRuntimeTest {}; + +TEST_F(AtomicMethodRefMapTest, RunTests) { + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<const DexFile> dex(OpenTestDexFile("Interfaces")); + ASSERT_TRUE(dex != nullptr); + using Map = AtomicMethodRefMap<int>; + Map map; + int value = 123; + // Error case: Not already inserted. + EXPECT_FALSE(map.Get(MethodReference(dex.get(), 1), &value)); + EXPECT_FALSE(map.HaveDexFile(dex.get())); + // Error case: Dex file not registered. + EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 1), 0, 1) == Map::kInsertResultInvalidDexFile); + map.AddDexFile(dex.get()); + EXPECT_TRUE(map.HaveDexFile(dex.get())); + EXPECT_GT(dex->NumMethodIds(), 10u); + // After we have added the get should succeed but return the default value. + EXPECT_TRUE(map.Get(MethodReference(dex.get(), 1), &value)); + EXPECT_EQ(value, 0); + // Actually insert an item and make sure we can retreive it. + static const int kInsertValue = 44; + EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 1), 0, kInsertValue) == + Map::kInsertResultSuccess); + EXPECT_TRUE(map.Get(MethodReference(dex.get(), 1), &value)); + EXPECT_EQ(value, kInsertValue); + static const int kInsertValue2 = 123; + EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 2), 0, kInsertValue2) == + Map::kInsertResultSuccess); + EXPECT_TRUE(map.Get(MethodReference(dex.get(), 1), &value)); + EXPECT_EQ(value, kInsertValue); + EXPECT_TRUE(map.Get(MethodReference(dex.get(), 2), &value)); + EXPECT_EQ(value, kInsertValue2); + // Error case: Incorrect expected value for CAS. + EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 1), 0, kInsertValue + 1) == + Map::kInsertResultCASFailure); + // Correctly overwrite the value and verify. + EXPECT_TRUE(map.Insert(MethodReference(dex.get(), 1), kInsertValue, kInsertValue + 1) == + Map::kInsertResultSuccess); + EXPECT_TRUE(map.Get(MethodReference(dex.get(), 1), &value)); + EXPECT_EQ(value, kInsertValue + 1); +} + +} // namespace art diff --git a/compiler/utils/string_reference_test.cc b/compiler/utils/string_reference_test.cc index 0fd9e5ba53..90335eb048 100644 --- a/compiler/utils/string_reference_test.cc +++ b/compiler/utils/string_reference_test.cc @@ -18,6 +18,7 @@ #include <memory> +#include "dex_file_types.h" #include "gtest/gtest.h" #include "utils/test_dex_file_builder.h" @@ -34,15 +35,15 @@ TEST(StringReference, ValueComparator) { builder1.AddString("String1"); std::unique_ptr<const DexFile> dex_file1 = builder1.Build("dummy location 1"); ASSERT_EQ(1u, dex_file1->NumStringIds()); - ASSERT_STREQ("String1", dex_file1->GetStringData(dex_file1->GetStringId(0))); - StringReference sr1(dex_file1.get(), 0); + ASSERT_STREQ("String1", dex_file1->GetStringData(dex_file1->GetStringId(dex::StringIndex(0)))); + StringReference sr1(dex_file1.get(), dex::StringIndex(0)); TestDexFileBuilder builder2; builder2.AddString("String2"); std::unique_ptr<const DexFile> dex_file2 = builder2.Build("dummy location 2"); ASSERT_EQ(1u, dex_file2->NumStringIds()); - ASSERT_STREQ("String2", dex_file2->GetStringData(dex_file2->GetStringId(0))); - StringReference sr2(dex_file2.get(), 0); + ASSERT_STREQ("String2", dex_file2->GetStringData(dex_file2->GetStringId(dex::StringIndex(0)))); + StringReference sr2(dex_file2.get(), dex::StringIndex(0)); StringReferenceValueComparator cmp; EXPECT_TRUE(cmp(sr1, sr2)); // "String1" < "String2" is true. @@ -80,7 +81,8 @@ TEST(StringReference, ValueComparator2) { std::unique_ptr<const DexFile> dex_file1 = builder1.Build("dummy location 1"); ASSERT_EQ(arraysize(kDexFile1Strings), dex_file1->NumStringIds()); for (size_t index = 0; index != arraysize(kDexFile1Strings); ++index) { - ASSERT_STREQ(kDexFile1Strings[index], dex_file1->GetStringData(dex_file1->GetStringId(index))); + ASSERT_STREQ(kDexFile1Strings[index], + dex_file1->GetStringData(dex_file1->GetStringId(dex::StringIndex(index)))); } TestDexFileBuilder builder2; @@ -90,14 +92,15 @@ TEST(StringReference, ValueComparator2) { std::unique_ptr<const DexFile> dex_file2 = builder2.Build("dummy location 1"); ASSERT_EQ(arraysize(kDexFile2Strings), dex_file2->NumStringIds()); for (size_t index = 0; index != arraysize(kDexFile2Strings); ++index) { - ASSERT_STREQ(kDexFile2Strings[index], dex_file2->GetStringData(dex_file2->GetStringId(index))); + ASSERT_STREQ(kDexFile2Strings[index], + dex_file2->GetStringData(dex_file2->GetStringId(dex::StringIndex(index)))); } StringReferenceValueComparator cmp; for (size_t index1 = 0; index1 != arraysize(kDexFile1Strings); ++index1) { for (size_t index2 = 0; index2 != arraysize(kDexFile2Strings); ++index2) { - StringReference sr1(dex_file1.get(), index1); - StringReference sr2(dex_file2.get(), index2); + StringReference sr1(dex_file1.get(), dex::StringIndex(index1)); + StringReference sr2(dex_file2.get(), dex::StringIndex(index2)); EXPECT_EQ(expectedCmp12[index1][index2], cmp(sr1, sr2)) << index1 << " " << index2; EXPECT_EQ(expectedCmp21[index2][index1], cmp(sr2, sr1)) << index1 << " " << index2; } diff --git a/compiler/utils/test_dex_file_builder_test.cc b/compiler/utils/test_dex_file_builder_test.cc index 922f8b1dfa..c76739b3b1 100644 --- a/compiler/utils/test_dex_file_builder_test.cc +++ b/compiler/utils/test_dex_file_builder_test.cc @@ -49,7 +49,8 @@ TEST(TestDexFileBuilderTest, SimpleTest) { }; ASSERT_EQ(arraysize(expected_strings), dex_file->NumStringIds()); for (size_t i = 0; i != arraysize(expected_strings); ++i) { - EXPECT_STREQ(expected_strings[i], dex_file->GetStringData(dex_file->GetStringId(i))) << i; + EXPECT_STREQ(expected_strings[i], + dex_file->GetStringData(dex_file->GetStringId(dex::StringIndex(i)))) << i; } static const char* const expected_types[] = { diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 525a2ee293..90fe6da438 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -460,20 +460,20 @@ TEST_F(VerifierDepsTest, StringToId) { ScopedObjectAccess soa(Thread::Current()); LoadDexFile(&soa); - uint32_t id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;"); - ASSERT_LT(id_Main1, primary_dex_file_->NumStringIds()); + dex::StringIndex id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;"); + ASSERT_LT(id_Main1.index_, primary_dex_file_->NumStringIds()); ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main1)); - uint32_t id_Main2 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;"); - ASSERT_LT(id_Main2, primary_dex_file_->NumStringIds()); + dex::StringIndex id_Main2 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;"); + ASSERT_LT(id_Main2.index_, primary_dex_file_->NumStringIds()); ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main2)); - uint32_t id_Lorem1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "Lorem ipsum"); - ASSERT_GE(id_Lorem1, primary_dex_file_->NumStringIds()); + dex::StringIndex id_Lorem1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "Lorem ipsum"); + ASSERT_GE(id_Lorem1.index_, primary_dex_file_->NumStringIds()); ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Lorem1)); - uint32_t id_Lorem2 = verifier_deps_->GetIdFromString(*primary_dex_file_, "Lorem ipsum"); - ASSERT_GE(id_Lorem2, primary_dex_file_->NumStringIds()); + dex::StringIndex id_Lorem2 = verifier_deps_->GetIdFromString(*primary_dex_file_, "Lorem ipsum"); + ASSERT_GE(id_Lorem2.index_, primary_dex_file_->NumStringIds()); ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Lorem2)); ASSERT_EQ(id_Main1, id_Main2); @@ -1306,9 +1306,10 @@ TEST_F(VerifierDepsTest, VerifyDeps) { bool found = false; for (const auto& entry : deps->fields_) { if (!entry.IsResolved()) { + constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there. deps->fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */, VerifierDeps::kUnresolvedMarker - 1, - 0 /* we know there is a class there */)); + kStringIndexZero)); found = true; break; } @@ -1341,7 +1342,7 @@ TEST_F(VerifierDepsTest, VerifyDeps) { VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_); bool found = false; for (const auto& entry : deps->fields_) { - static constexpr uint32_t kNewTypeIndex = 0; + constexpr dex::StringIndex kNewTypeIndex(0); if (entry.GetDeclaringClassIndex() != kNewTypeIndex) { deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(), entry.GetAccessFlags(), @@ -1384,9 +1385,10 @@ TEST_F(VerifierDepsTest, VerifyDeps) { std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); for (const auto& entry : *methods) { if (!entry.IsResolved()) { + constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there. methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */, VerifierDeps::kUnresolvedMarker - 1, - 0 /* we know there is a class there */)); + kStringIndexZero)); found = true; break; } @@ -1421,7 +1423,7 @@ TEST_F(VerifierDepsTest, VerifyDeps) { bool found = false; std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind); for (const auto& entry : *methods) { - static constexpr uint32_t kNewTypeIndex = 0; + constexpr dex::StringIndex kNewTypeIndex(0); if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) { methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(), entry.GetAccessFlags(), diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 9e6032f83a..91a32f9353 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1636,7 +1636,7 @@ class Dex2Oat FINAL { soa.Decode<mirror::ClassLoader>(class_loader_).Ptr()))); // Pre-register dex files so that we can access verification results without locks during // compilation and verification. - verification_results_->PreRegisterDexFile(dex_file); + verification_results_->AddDexFile(dex_file); } return true; diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 03d6227ae2..916984c261 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -475,9 +475,9 @@ static void dumpEncodedValue(const DexFile* pDexFile, const u1** data, u1 type, case DexFile::kDexAnnotationString: { const u4 idx = static_cast<u4>(readVarWidth(data, arg, false)); if (gOptions.outputFormat == OUTPUT_PLAIN) { - dumpEscapedString(pDexFile->StringDataByIdx(idx)); + dumpEscapedString(pDexFile->StringDataByIdx(dex::StringIndex(idx))); } else { - dumpXmlAttribute(pDexFile->StringDataByIdx(idx)); + dumpXmlAttribute(pDexFile->StringDataByIdx(dex::StringIndex(idx))); } break; } @@ -518,7 +518,7 @@ static void dumpEncodedValue(const DexFile* pDexFile, const u1** data, u1 type, for (u4 i = 0; i < size; i++) { const u4 name_idx = DecodeUnsignedLeb128(data); fputc(' ', gOutFile); - fputs(pDexFile->StringDataByIdx(name_idx), gOutFile); + fputs(pDexFile->StringDataByIdx(dex::StringIndex(name_idx)), gOutFile); fputc('=', gOutFile); dumpEncodedValue(pDexFile, data); } @@ -599,7 +599,7 @@ static void dumpClassDef(const DexFile* pDexFile, int idx) { fprintf(gOutFile, "superclass_idx : %d\n", pClassDef.superclass_idx_.index_); fprintf(gOutFile, "interfaces_off : %d (0x%06x)\n", pClassDef.interfaces_off_, pClassDef.interfaces_off_); - fprintf(gOutFile, "source_file_idx : %d\n", pClassDef.source_file_idx_); + fprintf(gOutFile, "source_file_idx : %d\n", pClassDef.source_file_idx_.index_); fprintf(gOutFile, "annotations_off : %d (0x%06x)\n", pClassDef.annotations_off_, pClassDef.annotations_off_); fprintf(gOutFile, "class_data_off : %d (0x%06x)\n", @@ -842,7 +842,7 @@ static std::unique_ptr<char[]> indexString(const DexFile* pDexFile, break; case Instruction::kIndexStringRef: if (index < pDexFile->GetHeader().string_ids_size_) { - const char* st = pDexFile->StringDataByIdx(index); + const char* st = pDexFile->StringDataByIdx(dex::StringIndex(index)); outSize = snprintf(buf.get(), bufSize, "\"%s\" // string@%0*x", st, width, index); } else { outSize = snprintf(buf.get(), bufSize, "<string?> // string@%0*x", width, index); @@ -1564,13 +1564,13 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) { // End of class. if (gOptions.outputFormat == OUTPUT_PLAIN) { const char* fileName; - if (pClassDef.source_file_idx_ != DexFile::kDexNoIndex) { + if (pClassDef.source_file_idx_.IsValid()) { fileName = pDexFile->StringDataByIdx(pClassDef.source_file_idx_); } else { fileName = "unknown"; } fprintf(gOutFile, " source_file_idx : %d (%s)\n\n", - pClassDef.source_file_idx_, fileName); + pClassDef.source_file_idx_.index_, fileName); } else if (gOptions.outputFormat == OUTPUT_XML) { fprintf(gOutFile, "</class>\n"); } diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index fe2bcce843..b1e66be348 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -340,7 +340,7 @@ void Collections::ReadEncodedValue( } void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) { - const DexFile::StringId& disk_string_id = dex_file.GetStringId(i); + const DexFile::StringId& disk_string_id = dex_file.GetStringId(dex::StringIndex(i)); StringData* string_data = new StringData(dex_file.GetStringData(disk_string_id)); string_datas_.AddItem(string_data, disk_string_id.string_data_off_); @@ -350,7 +350,7 @@ void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) { void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) { const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i)); - TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_)); + TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_.index_)); type_ids_.AddIndexedItem(type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i); } @@ -359,7 +359,7 @@ void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id); TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_); - ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_), + ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_.index_), GetTypeId(disk_proto_id.return_type_idx_.index_), parameter_type_list); proto_ids_.AddIndexedItem(proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i); @@ -369,7 +369,7 @@ void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) { const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i); FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_.index_), GetTypeId(disk_field_id.type_idx_.index_), - GetStringId(disk_field_id.name_idx_)); + GetStringId(disk_field_id.name_idx_.index_)); field_ids_.AddIndexedItem(field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i); } @@ -377,7 +377,7 @@ void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i); MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_), GetProtoId(disk_method_id.proto_idx_), - GetStringId(disk_method_id.name_idx_)); + GetStringId(disk_method_id.name_idx_.index_)); method_ids_.AddIndexedItem(method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i); } @@ -390,7 +390,7 @@ void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) { const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def); TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_); - const StringId* source_file = GetStringIdOrNullPtr(disk_class_def.source_file_idx_); + const StringId* source_file = GetStringIdOrNullPtr(disk_class_def.source_file_idx_.index_); // Annotations. AnnotationsDirectoryItem* annotations = nullptr; const DexFile::AnnotationsDirectoryItem* disk_annotations_directory_item = diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index 68473c43ae..efe1aad7c6 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -140,7 +140,7 @@ void dumpClass(const DexFile* pDexFile, u4 idx) { const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx); const char* fileName; - if (pClassDef.source_file_idx_ == DexFile::kDexNoIndex) { + if (!pClassDef.source_file_idx_.IsValid()) { fileName = nullptr; } else { fileName = pDexFile->StringDataByIdx(pClassDef.source_file_idx_); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 3ad0f1e8ce..a1984a7d9f 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -703,13 +703,13 @@ class OatDumper { const Instruction* inst = Instruction::At(code_ptr); switch (inst->Opcode()) { case Instruction::CONST_STRING: { - const uint32_t string_index = inst->VRegB_21c(); + const dex::StringIndex string_index(inst->VRegB_21c()); unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index)); ++num_string_ids_from_code_; break; } case Instruction::CONST_STRING_JUMBO: { - const uint32_t string_index = inst->VRegB_31c(); + const dex::StringIndex string_index(inst->VRegB_31c()); unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index)); ++num_string_ids_from_code_; break; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 3a83eaf9c1..a71ab4b53c 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1109,62 +1109,7 @@ END art_quick_set64_instance */ ENTRY art_quick_resolve_string - push {r10-r12, lr} - .cfi_adjust_cfa_offset 16 - .cfi_rel_offset r10, 0 - .cfi_rel_offset r11, 4 - .cfi_rel_offset ip, 8 - .cfi_rel_offset lr, 12 - ldr r10, [sp, #16] @ load referrer - ldr r10, [r10, #ART_METHOD_DECLARING_CLASS_OFFSET] @ load declaring class - ldr r10, [r10, #DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET] @ load string dex cache - ubfx r11, r0, #0, #STRING_DEX_CACHE_HASH_BITS - add r10, r10, r11, LSL #STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT - ldrd r10, r11, [r10] @ load index into r11 and pointer into r10 - cmp r0, r11 - bne .Lart_quick_resolve_string_slow_path -#ifdef USE_READ_BARRIER - ldr r0, [rSELF, #THREAD_IS_GC_MARKING_OFFSET] - cbnz r0, .Lart_quick_resolve_string_marking -.Lart_quick_resolve_string_no_rb: -#endif - mov r0, r10 - pop {r10-r12, pc} - -#ifdef USE_READ_BARRIER -// GC is marking case, need to check the mark bit. -.Lart_quick_resolve_string_marking: - ldr r0, [r10, MIRROR_OBJECT_LOCK_WORD_OFFSET] - lsrs r0, #(LOCK_WORD_MARK_BIT_SHIFT + 1) - bcs .Lart_quick_resolve_string_no_rb - mov r0, r10 - .cfi_remember_state - pop {r10-r12, lr} - .cfi_adjust_cfa_offset -16 - .cfi_restore r10 - .cfi_restore r11 - .cfi_restore r12 - .cfi_restore lr - // Note: art_quick_read_barrier_mark_reg00 clobbers IP but the .Lslow_rb_* does not. - b .Lslow_rb_art_quick_read_barrier_mark_reg00 @ Get the marked string back. - .cfi_restore_state -#endif - -// Slow path case, the index did not match -.Lart_quick_resolve_string_slow_path: - push {r0-r9} @ 10 words of callee saves and args; {r10-r12, lr} already saved. - .cfi_adjust_cfa_offset 40 - .cfi_rel_offset r0, 0 - .cfi_rel_offset r1, 4 - .cfi_rel_offset r2, 8 - .cfi_rel_offset r3, 12 - .cfi_rel_offset r4, 16 - .cfi_rel_offset r5, 20 - .cfi_rel_offset r6, 24 - .cfi_rel_offset r7, 28 - .cfi_rel_offset r8, 32 - .cfi_rel_offset r9, 36 - SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED r1 @ save callee saves in case of GC + SETUP_SAVE_EVERYTHING_FRAME r1 @ save everything in case of GC mov r1, r9 @ pass Thread::Current bl artResolveStringFromCode @ (uint32_t type_idx, Thread*) cbz r0, 1f @ If result is null, deliver the OOME. diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index e397405b06..b88515f21f 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1653,44 +1653,7 @@ END art_quick_set64_static */ ENTRY art_quick_resolve_string - SAVE_TWO_REGS_INCREASE_FRAME x29, xLR, 2 * __SIZEOF_POINTER__ - ldr x29, [sp, #(2 * __SIZEOF_POINTER__)] // load referrer - ldr w29, [x29, #ART_METHOD_DECLARING_CLASS_OFFSET] // load declaring class - ldr x29, [x29, #DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET] // load string dex cache - ubfx lr, x0, #0, #STRING_DEX_CACHE_HASH_BITS // get masked string index into LR - ldr x29, [x29, lr, lsl #STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT] // load dex cache pair into x29 - cmp x0, x29, lsr #32 // compare against upper 32 bits - bne .Lart_quick_resolve_string_slow_path - ubfx x0, x29, #0, #32 // extract lower 32 bits into x0 -#ifdef USE_READ_BARRIER - // Most common case: GC is not marking. - ldr w29, [xSELF, #THREAD_IS_GC_MARKING_OFFSET] - cbnz x29, .Lart_quick_resolve_string_marking -.Lart_quick_resolve_string_no_rb: -#endif - .cfi_remember_state - RESTORE_TWO_REGS_DECREASE_FRAME x29, xLR, 2 * __SIZEOF_POINTER__ - ret - .cfi_restore_state - .cfi_def_cfa_offset 16 // workaround for clang bug: 31975598 - -#ifdef USE_READ_BARRIER -// GC is marking case, need to check the mark bit. -.Lart_quick_resolve_string_marking: - ldr x29, [x0, #MIRROR_OBJECT_LOCK_WORD_OFFSET] - tbnz x29, #LOCK_WORD_MARK_BIT_SHIFT, .Lart_quick_resolve_string_no_rb - .cfi_remember_state - RESTORE_TWO_REGS_DECREASE_FRAME x29, xLR, 2 * __SIZEOF_POINTER__ - // Note: art_quick_read_barrier_mark_reg00 clobbers IP0 but the .Lslow_rb_* does not. - b .Lslow_rb_art_quick_read_barrier_mark_reg00 // Get the marked string back. - .cfi_restore_state - .cfi_def_cfa_offset 16 // workaround for clang bug: 31975598 -#endif - -// Slow path case, the index did not match. -.Lart_quick_resolve_string_slow_path: - INCREASE_FRAME (FRAME_SIZE_SAVE_EVERYTHING - 2 * __SIZEOF_POINTER__) - SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR // save callee saves in case of GC + SETUP_SAVE_EVERYTHING_FRAME // save everything for stack crawl mov x1, xSELF // pass Thread::Current bl artResolveStringFromCode // (int32_t string_idx, Thread* self) cbz w0, 1f // If result is null, deliver the OOME. diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 6fbc9547e6..c6f4c0346f 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1148,51 +1148,17 @@ DEFINE_FUNCTION art_quick_alloc_object_region_tlab END_FUNCTION art_quick_alloc_object_region_tlab DEFINE_FUNCTION art_quick_resolve_string - PUSH edi - PUSH esi - // Save xmm0 at an aligned address on the stack. - subl MACRO_LITERAL(12), %esp - CFI_ADJUST_CFA_OFFSET(12) - movsd %xmm0, 0(%esp) - movl 24(%esp), %edi // get referrer - movl ART_METHOD_DECLARING_CLASS_OFFSET(%edi), %edi // get declaring class - movl DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET(%edi), %edi // get string dex cache - movl LITERAL(STRING_DEX_CACHE_SIZE_MINUS_ONE), %esi - andl %eax, %esi - movlps (%edi, %esi, STRING_DEX_CACHE_ELEMENT_SIZE), %xmm0 // load string idx and ptr to xmm0 - movd %xmm0, %edi // extract pointer - pshufd LITERAL(0x55), %xmm0, %xmm0 // shuffle index into lowest bits - movd %xmm0, %esi // extract index - // Restore xmm0 and remove it together with padding from the stack. - movsd 0(%esp), %xmm0 - addl MACRO_LITERAL(12), %esp - CFI_ADJUST_CFA_OFFSET(-12) - cmp %esi, %eax - jne .Lart_quick_resolve_string_slow_path - movl %edi, %eax - CFI_REMEMBER_STATE - POP esi - POP edi -#ifdef USE_READ_BARRIER - cmpl LITERAL(0), %fs:THREAD_IS_GC_MARKING_OFFSET - jne .Lnot_null_art_quick_read_barrier_mark_reg00 -#endif - ret - CFI_RESTORE_STATE - CFI_DEF_CFA(esp, 24) // workaround for clang bug: 31975598 - -.Lart_quick_resolve_string_slow_path: + SETUP_SAVE_EVERYTHING_FRAME ebx, ebx // Outgoing argument set up - SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED ebx, ebx - subl LITERAL(8), %esp // push padding + subl LITERAL(8), %esp // push padding CFI_ADJUST_CFA_OFFSET(8) - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() CFI_ADJUST_CFA_OFFSET(4) - PUSH eax // pass arg1 + PUSH eax // pass arg1 call SYMBOL(artResolveStringFromCode) - addl LITERAL(16), %esp // pop arguments + addl LITERAL(16), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-16) - testl %eax, %eax // If result is null, deliver the OOME. + testl %eax, %eax // If result is null, deliver the OOME. jz 1f CFI_REMEMBER_STATE RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index f8066e45fb..4c46b08a9e 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1354,34 +1354,7 @@ DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab END_FUNCTION art_quick_alloc_object_initialized_region_tlab DEFINE_FUNCTION art_quick_resolve_string - // Custom calling convention: RAX serves as both input and output. - PUSH r15 - PUSH r14 - movq 24(%rsp), %r15 // get referrer - movl ART_METHOD_DECLARING_CLASS_OFFSET(%r15), %r15d // get declaring class - movq DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET(%r15d), %r15 // get string dex cache - movl LITERAL(STRING_DEX_CACHE_SIZE_MINUS_ONE), %r14d - andl %eax, %r14d - movq (%r15, %r14, STRING_DEX_CACHE_ELEMENT_SIZE), %r14 - movl %r14d, %r15d - shrq LITERAL(32), %r14 - cmpl %r14d, %eax - jne .Lart_quick_resolve_string_slow_path - movl %r15d, %eax - CFI_REMEMBER_STATE - POP r14 - POP r15 -#ifdef USE_READ_BARRIER - cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET - jne .Lnot_null_art_quick_read_barrier_mark_reg00 -#endif - ret - CFI_RESTORE_STATE - CFI_DEF_CFA(rsp, 24) // workaround for clang bug: 31975598 - -// Slow path, the index did not match. -.Lart_quick_resolve_string_slow_path: - SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED + SETUP_SAVE_EVERYTHING_FRAME // Outgoing argument set up movl %eax, %edi // pass string index movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() diff --git a/runtime/art_field.cc b/runtime/art_field.cc index 25b8ed295b..a4a6e5a4fb 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -54,7 +54,7 @@ ObjPtr<mirror::Class> ArtField::ResolveGetType(dex::TypeIndex type_idx) { ObjPtr<mirror::String> ArtField::ResolveGetStringName(Thread* self, const DexFile& dex_file, - uint32_t string_idx, + dex::StringIndex string_idx, ObjPtr<mirror::DexCache> dex_cache) { StackHandleScope<1> hs(self); return Runtime::Current()->GetClassLinker()->ResolveString(dex_file, diff --git a/runtime/art_field.h b/runtime/art_field.h index cacb32450b..427e103749 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -221,7 +221,7 @@ class ArtField FINAL { REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr<mirror::String> ResolveGetStringName(Thread* self, const DexFile& dex_file, - uint32_t string_idx, + dex::StringIndex string_idx, ObjPtr<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 2dfdc16ac0..730a9c32fd 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -399,7 +399,11 @@ inline mirror::ClassLoader* ArtMethod::GetClassLoader() { inline mirror::DexCache* ArtMethod::GetDexCache() { DCHECK(!IsProxyMethod()); - return GetDeclaringClass()->GetDexCache(); + if (UNLIKELY(IsObsolete())) { + return GetObsoleteDexCache(); + } else { + return GetDeclaringClass()->GetDexCache(); + } } template<ReadBarrierOption kReadBarrierOption> @@ -466,20 +470,6 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, PointerSize pointer_size) { klass, this)); interface_method->VisitRoots(visitor, pointer_size); } - // We know we don't have profiling information if the class hasn't been verified. Note - // that this check also ensures the IsNative call can be made, as IsNative expects a fully - // created class (and not a retired one). - if (klass->IsVerified()) { - // Runtime methods and native methods use the same field as the profiling info for - // storing their own data (jni entrypoint for native methods, and ImtConflictTable for - // some runtime methods). - if (!IsNative<kReadBarrierOption>() && !IsRuntimeMethod()) { - ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size); - if (profiling_info != nullptr) { - profiling_info->VisitRoots(visitor); - } - } - } } } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index d1454b646a..eeece90be5 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -35,6 +35,7 @@ #include "jit/profiling_info.h" #include "jni_internal.h" #include "mirror/class-inl.h" +#include "mirror/class_ext.h" #include "mirror/executable.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" @@ -57,6 +58,28 @@ ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnabl return executable->GetArtMethod(); } +mirror::DexCache* ArtMethod::GetObsoleteDexCache() { + DCHECK(!Runtime::Current()->IsAotCompiler()) << PrettyMethod(); + DCHECK(IsObsolete()); + ObjPtr<mirror::ClassExt> ext(GetDeclaringClass()->GetExtData()); + CHECK(!ext.IsNull()); + ObjPtr<mirror::PointerArray> obsolete_methods(ext->GetObsoleteMethods()); + CHECK(!obsolete_methods.IsNull()); + DCHECK(ext->GetObsoleteDexCaches() != nullptr); + int32_t len = obsolete_methods->GetLength(); + DCHECK_EQ(len, ext->GetObsoleteDexCaches()->GetLength()); + // TODO I think this is fine since images should never have obsolete methods in them. + PointerSize pointer_size = kRuntimePointerSize; + DCHECK_EQ(kRuntimePointerSize, Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + for (int32_t i = 0; i < len; i++) { + if (this == obsolete_methods->GetElementPtrSize<ArtMethod*>(i, pointer_size)) { + return ext->GetObsoleteDexCaches()->Get(i); + } + } + LOG(FATAL) << "This method does not appear in the obsolete map of its class!"; + UNREACHABLE(); +} + mirror::String* ArtMethod::GetNameAsString(Thread* self) { CHECK(!IsProxyMethod()); StackHandleScope<1> hs(self); diff --git a/runtime/art_method.h b/runtime/art_method.h index 0e1d7e7303..00fab65241 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -227,6 +227,11 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccDefault) != 0; } + bool IsObsolete() { + // TODO Should maybe make this IsIntrinsic check not needed + return !IsIntrinsic() && (GetAccessFlags() & kAccObsoleteMethod) != 0; + } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsNative() { return (GetAccessFlags<kReadBarrierOption>() & kAccNative) != 0; @@ -557,6 +562,7 @@ class ArtMethod FINAL { mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_); mirror::DexCache* GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_); + mirror::DexCache* GetObsoleteDexCache() REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 81adaeb00a..7005c292c8 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -65,14 +65,15 @@ inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, return array_class.Ptr(); } -inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx, ArtMethod* referrer) { +inline mirror::String* ClassLinker::ResolveString(dex::StringIndex string_idx, + ArtMethod* referrer) { Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); // MethodVerifier refuses methods with string_idx out of bounds. - DCHECK_LT(string_idx, declaring_class->GetDexFile().NumStringIds()); + DCHECK_LT(string_idx.index_, declaring_class->GetDexFile().NumStringIds()); ObjPtr<mirror::String> string = mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(), - string_idx, + string_idx.index_, mirror::DexCache::kDexCacheStringCacheSize).Read(); if (UNLIKELY(string == nullptr)) { StackHandleScope<1> hs(Thread::Current()); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index f3aba970b9..f98f364bc7 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1949,13 +1949,36 @@ class VisitClassLoaderClassesVisitor : public ClassLoaderVisitor { void Visit(ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE { ClassTable* const class_table = class_loader->GetClassTable(); - if (!done_ && class_table != nullptr && !class_table->Visit(*visitor_)) { - // If the visitor ClassTable returns false it means that we don't need to continue. - done_ = true; + if (!done_ && class_table != nullptr) { + DefiningClassLoaderFilterVisitor visitor(class_loader, visitor_); + if (!class_table->Visit(visitor)) { + // If the visitor ClassTable returns false it means that we don't need to continue. + done_ = true; + } } } private: + // Class visitor that limits the class visits from a ClassTable to the classes with + // the provided defining class loader. This filter is used to avoid multiple visits + // of the same class which can be recorded for multiple initiating class loaders. + class DefiningClassLoaderFilterVisitor : public ClassVisitor { + public: + DefiningClassLoaderFilterVisitor(ObjPtr<mirror::ClassLoader> defining_class_loader, + ClassVisitor* visitor) + : defining_class_loader_(defining_class_loader), visitor_(visitor) { } + + bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + if (klass->GetClassLoader() != defining_class_loader_) { + return true; + } + return (*visitor_)(klass); + } + + ObjPtr<mirror::ClassLoader> const defining_class_loader_; + ClassVisitor* const visitor_; + }; + ClassVisitor* const visitor_; // If done is true then we don't need to do any more visiting. bool done_; @@ -2540,56 +2563,109 @@ mirror::Class* ClassLinker::FindClass(Thread* self, } } else { ScopedObjectAccessUnchecked soa(self); - ObjPtr<mirror::Class> cp_klass; - if (FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) { - // The chain was understood. So the value in cp_klass is either the class we were looking - // for, or not found. - if (cp_klass != nullptr) { - return cp_klass.Ptr(); - } - // TODO: We handle the boot classpath loader in FindClassInBaseDexClassLoader. Try to unify - // this and the branch above. TODO: throw the right exception here. - - // We'll let the Java-side rediscover all this and throw the exception with the right stack - // trace. - } + ObjPtr<mirror::Class> result_ptr; + bool descriptor_equals; + bool known_hierarchy = + FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr); + if (result_ptr != nullptr) { + // The chain was understood and we found the class. We still need to add the class to + // the class table to protect from racy programs that can try and redefine the path list + // which would change the Class<?> returned for subsequent evaluation of const-class. + DCHECK(known_hierarchy); + DCHECK(result_ptr->DescriptorEquals(descriptor)); + descriptor_equals = true; + } else { + // Either the chain wasn't understood or the class wasn't found. + // + // If the chain was understood but we did not find the class, let the Java-side + // rediscover all this and throw the exception with the right stack trace. Note that + // the Java-side could still succeed for racy programs if another thread is actively + // modifying the class loader's path list. - if (Runtime::Current()->IsAotCompiler()) { - // Oops, compile-time, can't run actual class-loader code. - ObjPtr<mirror::Throwable> pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); - self->SetException(pre_allocated); - return nullptr; - } + if (Runtime::Current()->IsAotCompiler()) { + // Oops, compile-time, can't run actual class-loader code. + ObjPtr<mirror::Throwable> pre_allocated = + Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return nullptr; + } - ScopedLocalRef<jobject> class_loader_object(soa.Env(), - soa.AddLocalReference<jobject>(class_loader.Get())); - std::string class_name_string(DescriptorToDot(descriptor)); - ScopedLocalRef<jobject> result(soa.Env(), nullptr); - { - ScopedThreadStateChange tsc(self, kNative); - ScopedLocalRef<jobject> class_name_object(soa.Env(), - soa.Env()->NewStringUTF(class_name_string.c_str())); - if (class_name_object.get() == nullptr) { - DCHECK(self->IsExceptionPending()); // OOME. + ScopedLocalRef<jobject> class_loader_object( + soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get())); + std::string class_name_string(DescriptorToDot(descriptor)); + ScopedLocalRef<jobject> result(soa.Env(), nullptr); + { + ScopedThreadStateChange tsc(self, kNative); + ScopedLocalRef<jobject> class_name_object( + soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str())); + if (class_name_object.get() == nullptr) { + DCHECK(self->IsExceptionPending()); // OOME. + return nullptr; + } + CHECK(class_loader_object.get() != nullptr); + result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(), + WellKnownClasses::java_lang_ClassLoader_loadClass, + class_name_object.get())); + } + if (self->IsExceptionPending()) { + // If the ClassLoader threw, pass that exception up. + // However, to comply with the RI behavior, first check if another thread succeeded. + result_ptr = LookupClass(self, descriptor, hash, class_loader.Get()); + if (result_ptr != nullptr && !result_ptr->IsErroneous()) { + self->ClearException(); + return EnsureResolved(self, descriptor, result_ptr); + } + return nullptr; + } else if (result.get() == nullptr) { + // broken loader - throw NPE to be compatible with Dalvik + ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s", + class_name_string.c_str()).c_str()); return nullptr; } - CHECK(class_loader_object.get() != nullptr); - result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(), - WellKnownClasses::java_lang_ClassLoader_loadClass, - class_name_object.get())); + result_ptr = soa.Decode<mirror::Class>(result.get()); + // Check the name of the returned class. + descriptor_equals = result_ptr->DescriptorEquals(descriptor); } - if (self->IsExceptionPending()) { - // If the ClassLoader threw, pass that exception up. - return nullptr; - } else if (result.get() == nullptr) { - // broken loader - throw NPE to be compatible with Dalvik - ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s", - class_name_string.c_str()).c_str()); + + // Try to insert the class to the class table, checking for mismatch. + ObjPtr<mirror::Class> old; + { + ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get()); + old = class_table->Lookup(descriptor, hash); + if (old == nullptr) { + old = result_ptr; // For the comparison below, after releasing the lock. + if (descriptor_equals) { + class_table->InsertWithHash(result_ptr.Ptr(), hash); + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); + } // else throw below, after releasing the lock. + } + } + if (UNLIKELY(old != result_ptr)) { + // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel + // capable class loaders. (All class loaders are considered parallel capable on Android.) + mirror::Class* loader_class = class_loader->GetClass(); + const char* loader_class_name = + loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex()); + LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name) + << " is not well-behaved; it returned a different Class for racing loadClass(\"" + << DescriptorToDot(descriptor) << "\")."; + return EnsureResolved(self, descriptor, old); + } + if (UNLIKELY(!descriptor_equals)) { + std::string result_storage; + const char* result_name = result_ptr->GetDescriptor(&result_storage); + std::string loader_storage; + const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage); + ThrowNoClassDefFoundError( + "Initiating class loader of type %s returned class %s instead of %s.", + DescriptorToDot(loader_class_name).c_str(), + DescriptorToDot(result_name).c_str(), + DescriptorToDot(descriptor).c_str()); return nullptr; - } else { - // success, return mirror::Class* - return soa.Decode<mirror::Class>(result.get()).Ptr(); } + // success, return mirror::Class* + return result_ptr.Ptr(); } UNREACHABLE(); } @@ -3670,12 +3746,6 @@ void ClassLinker::UpdateClassMethods(ObjPtr<mirror::Class> klass, Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass); } -bool ClassLinker::RemoveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> class_loader) { - WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - ClassTable* const class_table = ClassTableForClassLoader(class_loader); - return class_table != nullptr && class_table->Remove(descriptor); -} - mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash, @@ -3726,7 +3796,8 @@ class LookupClassesVisitor : public ClassLoaderVisitor { REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE { ClassTable* const class_table = class_loader->GetClassTable(); ObjPtr<mirror::Class> klass = class_table->Lookup(descriptor_, hash_); - if (klass != nullptr) { + // Add `klass` only if `class_loader` is its defining (not just initiating) class loader. + if (klass != nullptr && klass->GetClassLoader() == class_loader) { result_->push_back(klass); } } @@ -3745,6 +3816,7 @@ void ClassLinker::LookupClasses(const char* descriptor, const size_t hash = ComputeModifiedUtf8Hash(descriptor); ObjPtr<mirror::Class> klass = boot_class_table_.Lookup(descriptor, hash); if (klass != nullptr) { + DCHECK(klass->GetClassLoader() == nullptr); result.push_back(klass); } LookupClassesVisitor visitor(descriptor, hash, &result); @@ -7485,7 +7557,7 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) { } mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, - uint32_t string_idx, + dex::StringIndex string_idx, Handle<mirror::DexCache> dex_cache) { DCHECK(dex_cache.Get() != nullptr); Thread::PoisonObjectPointersIfDebug(); @@ -7501,7 +7573,7 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, } mirror::String* ClassLinker::LookupString(const DexFile& dex_file, - uint32_t string_idx, + dex::StringIndex string_idx, Handle<mirror::DexCache> dex_cache) { DCHECK(dex_cache.Get() != nullptr); ObjPtr<mirror::String> resolved = dex_cache->GetResolvedString(string_idx); @@ -7510,7 +7582,8 @@ mirror::String* ClassLinker::LookupString(const DexFile& dex_file, } uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); - ObjPtr<mirror::String> string = intern_table_->LookupStrong(Thread::Current(), utf16_length, utf8_data); + ObjPtr<mirror::String> string = + intern_table_->LookupStrong(Thread::Current(), utf16_length, utf8_data); if (string != nullptr) { dex_cache->SetResolvedString(string_idx, string); } @@ -8052,8 +8125,8 @@ class CountClassesVisitor : public ClassLoaderVisitor { REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE { ClassTable* const class_table = class_loader->GetClassTable(); if (class_table != nullptr) { - num_zygote_classes += class_table->NumZygoteClasses(); - num_non_zygote_classes += class_table->NumNonZygoteClasses(); + num_zygote_classes += class_table->NumZygoteClasses(class_loader); + num_non_zygote_classes += class_table->NumNonZygoteClasses(class_loader); } } @@ -8064,13 +8137,13 @@ class CountClassesVisitor : public ClassLoaderVisitor { size_t ClassLinker::NumZygoteClasses() const { CountClassesVisitor visitor; VisitClassLoaders(&visitor); - return visitor.num_zygote_classes + boot_class_table_.NumZygoteClasses(); + return visitor.num_zygote_classes + boot_class_table_.NumZygoteClasses(nullptr); } size_t ClassLinker::NumNonZygoteClasses() const { CountClassesVisitor visitor; VisitClassLoaders(&visitor); - return visitor.num_non_zygote_classes + boot_class_table_.NumNonZygoteClasses(); + return visitor.num_non_zygote_classes + boot_class_table_.NumNonZygoteClasses(nullptr); } size_t ClassLinker::NumLoadedClasses() { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 95634484fc..12050745c8 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -218,12 +218,6 @@ class ClassLinker { mirror::Class* FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_); - // General class unloading is not supported, this is used to prune - // unwanted classes during image writing. - bool RemoveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> class_loader) - REQUIRES(!Locks::classlinker_classes_lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - void DumpAllClasses(int flags) REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -237,18 +231,20 @@ class ClassLinker { // Resolve a String with the given index from the DexFile, storing the // result in the DexCache. The referrer is used to identify the // target DexCache and ClassLoader to use for resolution. - mirror::String* ResolveString(uint32_t string_idx, ArtMethod* referrer) + mirror::String* ResolveString(dex::StringIndex string_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a String with the given index from the DexFile, storing the // result in the DexCache. - mirror::String* ResolveString(const DexFile& dex_file, uint32_t string_idx, + mirror::String* ResolveString(const DexFile& dex_file, + dex::StringIndex string_idx, Handle<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); // Find a String with the given index from the DexFile, storing the // result in the DexCache if found. Return null if not found. - mirror::String* LookupString(const DexFile& dex_file, uint32_t string_idx, + mirror::String* LookupString(const DexFile& dex_file, + dex::StringIndex string_idx, Handle<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 9e17be2518..7c06ffedb8 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -614,6 +614,9 @@ struct ClassOffsets : public CheckOffsets<mirror::Class> { struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> { ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") { + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_dex_caches_), "obsoleteDexCaches"); + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_methods_), "obsoleteMethods"); + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_cache_), "originalDexCache"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, verify_error_), "verifyError"); } }; diff --git a/runtime/class_table.cc b/runtime/class_table.cc index 0fcce6b307..81cdce57a8 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -83,18 +83,29 @@ mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* kl #pragma clang diagnostic pop // http://b/31104323 -size_t ClassTable::NumZygoteClasses() const { +size_t ClassTable::CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader, + const ClassSet& set) const { + size_t count = 0; + for (const GcRoot<mirror::Class>& klass : set) { + if (klass.Read()->GetClassLoader() == defining_loader) { + ++count; + } + } + return count; +} + +size_t ClassTable::NumZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const { ReaderMutexLock mu(Thread::Current(), lock_); size_t sum = 0; for (size_t i = 0; i < classes_.size() - 1; ++i) { - sum += classes_[i].Size(); + sum += CountDefiningLoaderClasses(defining_loader, classes_[i]); } return sum; } -size_t ClassTable::NumNonZygoteClasses() const { +size_t ClassTable::NumNonZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const { ReaderMutexLock mu(Thread::Current(), lock_); - return classes_.back().Size(); + return CountDefiningLoaderClasses(defining_loader, classes_.back()); } mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) { @@ -102,7 +113,7 @@ mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) { for (ClassSet& class_set : classes_) { auto it = class_set.FindWithHash(descriptor, hash); if (it != class_set.end()) { - return it->Read(); + return it->Read(); } } return nullptr; @@ -142,7 +153,6 @@ uint32_t ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror:: bool ClassTable::ClassDescriptorHashEquals::operator()(const GcRoot<mirror::Class>& a, const GcRoot<mirror::Class>& b) const { - DCHECK_EQ(a.Read()->GetClassLoader(), b.Read()->GetClassLoader()); std::string temp; return a.Read()->DescriptorEquals(b.Read()->GetDescriptor(&temp)); } diff --git a/runtime/class_table.h b/runtime/class_table.h index 558c144013..92634a434a 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -84,10 +84,14 @@ class ClassTable { REQUIRES_SHARED(Locks::mutator_lock_); // Returns the number of classes in previous snapshots. - size_t NumZygoteClasses() const REQUIRES(!lock_); + size_t NumZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const + REQUIRES(!lock_) + REQUIRES_SHARED(Locks::mutator_lock_); // Returns all off the classes in the lastest snapshot. - size_t NumNonZygoteClasses() const REQUIRES(!lock_); + size_t NumNonZygoteClasses(ObjPtr<mirror::ClassLoader> defining_loader) const + REQUIRES(!lock_) + REQUIRES_SHARED(Locks::mutator_lock_); // Update a class in the table with the new class. Returns the existing class which was replaced. mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash) @@ -173,6 +177,11 @@ class ClassTable { private: void InsertWithoutLocks(ObjPtr<mirror::Class> klass) NO_THREAD_SAFETY_ANALYSIS; + size_t CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader, + const ClassSet& set) const + REQUIRES(lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + // Return true if we inserted the oat file, false if it already exists. bool InsertOatFileLocked(const OatFile* oat_file) REQUIRES(lock_) diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 77a63c11ed..e884e39c19 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -43,9 +43,9 @@ inline const char* DexFile::GetStringData(const StringId& string_id) const { return GetStringDataAndUtf16Length(string_id, &ignored); } -inline const char* DexFile::StringDataAndUtf16LengthByIdx(uint32_t idx, +inline const char* DexFile::StringDataAndUtf16LengthByIdx(dex::StringIndex idx, uint32_t* utf16_length) const { - if (idx == kDexNoIndex) { + if (!idx.IsValid()) { *utf16_length = 0; return nullptr; } @@ -53,7 +53,7 @@ inline const char* DexFile::StringDataAndUtf16LengthByIdx(uint32_t idx, return GetStringDataAndUtf16Length(string_id, utf16_length); } -inline const char* DexFile::StringDataByIdx(uint32_t idx) const { +inline const char* DexFile::StringDataByIdx(dex::StringIndex idx) const { uint32_t unicode_length; return StringDataAndUtf16LengthByIdx(idx, &unicode_length); } @@ -130,8 +130,8 @@ inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, u (RoundUp(reinterpret_cast<uintptr_t>(insns_end_), 4)) + offset; } -static inline bool DexFileStringEquals(const DexFile* df1, uint32_t sidx1, - const DexFile* df2, uint32_t sidx2) { +static inline bool DexFileStringEquals(const DexFile* df1, dex::StringIndex sidx1, + const DexFile* df2, dex::StringIndex sidx2) { uint32_t s1_len; // Note: utf16 length != mutf8 length. const char* s1_data = df1->StringDataAndUtf16LengthByIdx(sidx1, &s1_len); uint32_t s2_len; diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index cc544fd51c..aa8fb38fca 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -45,6 +45,8 @@ namespace art { +static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong"); +static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial"); static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial"); @@ -602,7 +604,7 @@ const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_kl const DexFile::TypeId& type) const { // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); - const uint32_t name_idx = GetIndexForStringId(name); + const dex::StringIndex name_idx = GetIndexForStringId(name); const dex::TypeIndex type_idx = GetIndexForTypeId(type); int32_t lo = 0; int32_t hi = NumFieldIds() - 1; @@ -637,7 +639,7 @@ const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_ const DexFile::ProtoId& signature) const { // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass); - const uint32_t name_idx = GetIndexForStringId(name); + const dex::StringIndex name_idx = GetIndexForStringId(name); const uint16_t proto_idx = GetIndexForProtoId(signature); int32_t lo = 0; int32_t hi = NumMethodIds() - 1; @@ -672,7 +674,7 @@ const DexFile::StringId* DexFile::FindStringId(const char* string) const { int32_t hi = NumStringIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; - const DexFile::StringId& str_id = GetStringId(mid); + const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid)); const char* str = GetStringData(str_id); int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str); if (compare > 0) { @@ -711,7 +713,7 @@ const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t le int32_t hi = NumStringIds() - 1; while (hi >= lo) { int32_t mid = (hi + lo) / 2; - const DexFile::StringId& str_id = GetStringId(mid); + const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid)); const char* str = GetStringData(str_id); int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string, length); if (compare > 0) { @@ -725,7 +727,7 @@ const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t le return nullptr; } -const DexFile::TypeId* DexFile::FindTypeId(uint32_t string_idx) const { +const DexFile::TypeId* DexFile::FindTypeId(dex::StringIndex string_idx) const { int32_t lo = 0; int32_t hi = NumTypeIds() - 1; while (hi >= lo) { @@ -912,7 +914,7 @@ bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, bool is_static, ui } uint32_t name_idx = DecodeUnsignedLeb128P1(&stream); const char* descriptor = it.GetDescriptor(); - local_in_reg[arg_reg].name_ = StringDataByIdx(name_idx); + local_in_reg[arg_reg].name_ = StringDataByIdx(dex::StringIndex(name_idx)); local_in_reg[arg_reg].descriptor_ = descriptor; local_in_reg[arg_reg].signature_ = nullptr; local_in_reg[arg_reg].start_address_ = 0; @@ -975,10 +977,10 @@ bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, bool is_static, ui local_cb(context, local_in_reg[reg]); } - local_in_reg[reg].name_ = StringDataByIdx(name_idx); + local_in_reg[reg].name_ = StringDataByIdx(dex::StringIndex(name_idx)); local_in_reg[reg].descriptor_ = StringByTypeIdx(dex::TypeIndex(dchecked_integral_cast<uint16_t>(descriptor_idx)));; - local_in_reg[reg].signature_ = StringDataByIdx(signature_idx); + local_in_reg[reg].signature_ = StringDataByIdx(dex::StringIndex(signature_idx)); local_in_reg[reg].start_address_ = address; local_in_reg[reg].reg_ = reg; local_in_reg[reg].is_live_ = true; @@ -1080,7 +1082,7 @@ bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, DexDebugNewPosi break; case DBG_SET_FILE: { uint32_t name_idx = DecodeUnsignedLeb128P1(&stream); - entry.source_file_ = StringDataByIdx(name_idx); + entry.source_file_ = StringDataByIdx(dex::StringIndex(name_idx)); break; } default: { @@ -1482,6 +1484,11 @@ void CatchHandlerIterator::Next() { namespace dex { +std::ostream& operator<<(std::ostream& os, const StringIndex& index) { + os << "StringIndex[" << index.index_ << "]"; + return os; +} + std::ostream& operator<<(std::ostream& os, const TypeIndex& index) { os << "TypeIndex[" << index.index_ << "]"; return os; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 2384eb614b..250795b21d 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -152,7 +152,7 @@ class DexFile { // Raw type_id_item. struct TypeId { - uint32_t descriptor_idx_; // index into string_ids + dex::StringIndex descriptor_idx_; // index into string_ids private: DISALLOW_COPY_AND_ASSIGN(TypeId); @@ -160,9 +160,9 @@ class DexFile { // Raw field_id_item. struct FieldId { - dex::TypeIndex class_idx_; // index into type_ids_ array for defining class - dex::TypeIndex type_idx_; // index into type_ids_ array for field type - uint32_t name_idx_; // index into string_ids_ array for field name + dex::TypeIndex class_idx_; // index into type_ids_ array for defining class + dex::TypeIndex type_idx_; // index into type_ids_ array for field type + dex::StringIndex name_idx_; // index into string_ids_ array for field name private: DISALLOW_COPY_AND_ASSIGN(FieldId); @@ -170,10 +170,10 @@ class DexFile { // Raw proto_id_item. struct ProtoId { - uint32_t shorty_idx_; // index into string_ids array for shorty descriptor + dex::StringIndex shorty_idx_; // index into string_ids array for shorty descriptor dex::TypeIndex return_type_idx_; // index into type_ids array for return type - uint16_t pad_; // padding = 0 - uint32_t parameters_off_; // file offset to type_list for parameter types + uint16_t pad_; // padding = 0 + uint32_t parameters_off_; // file offset to type_list for parameter types private: DISALLOW_COPY_AND_ASSIGN(ProtoId); @@ -182,8 +182,8 @@ class DexFile { // Raw method_id_item. struct MethodId { dex::TypeIndex class_idx_; // index into type_ids_ array for defining class - uint16_t proto_idx_; // index into proto_ids_ array for method prototype - uint32_t name_idx_; // index into string_ids_ array for method name + uint16_t proto_idx_; // index into proto_ids_ array for method prototype + dex::StringIndex name_idx_; // index into string_ids_ array for method name private: DISALLOW_COPY_AND_ASSIGN(MethodId); @@ -197,7 +197,7 @@ class DexFile { dex::TypeIndex superclass_idx_; // index into type_ids_ array for superclass uint16_t pad2_; // padding = 0 uint32_t interfaces_off_; // file offset to TypeList - uint32_t source_file_idx_; // index into string_ids_ for source file name + dex::StringIndex source_file_idx_; // index into string_ids_ for source file name uint32_t annotations_off_; // file offset to annotations_directory_item uint32_t class_data_off_; // file offset to class_data_item uint32_t static_values_off_; // file offset to EncodedArray @@ -501,15 +501,15 @@ class DexFile { } // Returns the StringId at the specified index. - const StringId& GetStringId(uint32_t idx) const { - DCHECK_LT(idx, NumStringIds()) << GetLocation(); - return string_ids_[idx]; + const StringId& GetStringId(dex::StringIndex idx) const { + DCHECK_LT(idx.index_, NumStringIds()) << GetLocation(); + return string_ids_[idx.index_]; } - uint32_t GetIndexForStringId(const StringId& string_id) const { + dex::StringIndex GetIndexForStringId(const StringId& string_id) const { CHECK_GE(&string_id, string_ids_) << GetLocation(); CHECK_LT(&string_id, string_ids_ + header_->string_ids_size_) << GetLocation(); - return &string_id - string_ids_; + return dex::StringIndex(&string_id - string_ids_); } int32_t GetStringLength(const StringId& string_id) const; @@ -522,9 +522,9 @@ class DexFile { const char* GetStringData(const StringId& string_id) const; // Index version of GetStringDataAndUtf16Length. - const char* StringDataAndUtf16LengthByIdx(uint32_t idx, uint32_t* utf16_length) const; + const char* StringDataAndUtf16LengthByIdx(dex::StringIndex idx, uint32_t* utf16_length) const; - const char* StringDataByIdx(uint32_t idx) const; + const char* StringDataByIdx(dex::StringIndex idx) const; // Looks up a string id for a given modified utf8 string. const StringId* FindStringId(const char* string) const; @@ -563,7 +563,7 @@ class DexFile { const char* GetTypeDescriptor(const TypeId& type_id) const; // Looks up a type for the given string index - const TypeId* FindTypeId(uint32_t string_idx) const; + const TypeId* FindTypeId(dex::StringIndex string_idx) const; // Returns the number of field identifiers in the .dex file. size_t NumFieldIds() const { @@ -963,7 +963,7 @@ class DexFile { void* context) const; const char* GetSourceFile(const ClassDef& class_def) const { - if (class_def.source_file_idx_ == 0xffffffff) { + if (!class_def.source_file_idx_.IsValid()) { return nullptr; } else { return StringDataByIdx(class_def.source_file_idx_); diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 3fe2c409ca..52b9f114d2 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -167,7 +167,8 @@ const uint8_t* SearchEncodedAnnotation(const DexFile& dex_file, while (size != 0) { uint32_t element_name_index = DecodeUnsignedLeb128(&annotation); - const char* element_name = dex_file.GetStringData(dex_file.GetStringId(element_name_index)); + const char* element_name = + dex_file.GetStringData(dex_file.GetStringId(dex::StringIndex(element_name_index))); if (strcmp(name, element_name) == 0) { return annotation; } @@ -357,7 +358,7 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, StackHandleScope<1> hs(self); Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache())); element_object = Runtime::Current()->GetClassLinker()->ResolveString( - klass->GetDexFile(), index, dex_cache); + klass->GetDexFile(), dex::StringIndex(index), dex_cache); set_object = true; if (element_object == nullptr) { return false; @@ -592,7 +593,7 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, ScopedObjectAccessUnchecked soa(self); StackHandleScope<5> hs(self); uint32_t element_name_index = DecodeUnsignedLeb128(annotation); - const char* name = dex_file.StringDataByIdx(element_name_index); + const char* name = dex_file.StringDataByIdx(dex::StringIndex(element_name_index)); Handle<mirror::String> string_name( hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, name))); @@ -1341,7 +1342,9 @@ void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) c case kDouble: field->SetDouble<kTransactionActive>(field->GetDeclaringClass(), jval_.d); break; case kNull: field->SetObject<kTransactionActive>(field->GetDeclaringClass(), nullptr); break; case kString: { - mirror::String* resolved = linker_->ResolveString(dex_file_, jval_.i, *dex_cache_); + mirror::String* resolved = linker_->ResolveString(dex_file_, + dex::StringIndex(jval_.i), + *dex_cache_); field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved); break; } diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index f94d07b252..0fec856865 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -418,7 +418,7 @@ TEST_F(DexFileTest, FindTypeId) { const char* type_str = java_lang_dex_file_->StringByTypeIdx(dex::TypeIndex(i)); const DexFile::StringId* type_str_id = java_lang_dex_file_->FindStringId(type_str); ASSERT_TRUE(type_str_id != nullptr); - uint32_t type_str_idx = java_lang_dex_file_->GetIndexForStringId(*type_str_id); + dex::StringIndex type_str_idx = java_lang_dex_file_->GetIndexForStringId(*type_str_id); const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId(type_str_idx); ASSERT_EQ(type_id, java_lang_dex_file_->FindTypeId(type_str)); ASSERT_TRUE(type_id != nullptr); diff --git a/runtime/dex_file_types.h b/runtime/dex_file_types.h index c6d95a1d19..bd779c4ab0 100644 --- a/runtime/dex_file_types.h +++ b/runtime/dex_file_types.h @@ -23,12 +23,47 @@ namespace art { namespace dex { +class StringIndex { + public: + uint32_t index_; + + constexpr StringIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} + explicit constexpr StringIndex(uint32_t idx) : index_(idx) {} + + bool IsValid() const { + return index_ != std::numeric_limits<decltype(index_)>::max(); + } + static StringIndex Invalid() { + return StringIndex(std::numeric_limits<decltype(index_)>::max()); + } + + bool operator==(const StringIndex& other) const { + return index_ == other.index_; + } + bool operator!=(const StringIndex& other) const { + return index_ != other.index_; + } + bool operator<(const StringIndex& other) const { + return index_ < other.index_; + } + bool operator<=(const StringIndex& other) const { + return index_ <= other.index_; + } + bool operator>(const StringIndex& other) const { + return index_ > other.index_; + } + bool operator>=(const StringIndex& other) const { + return index_ >= other.index_; + } +}; +std::ostream& operator<<(std::ostream& os, const StringIndex& index); + class TypeIndex { public: uint16_t index_; - TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} - explicit TypeIndex(uint16_t idx) : index_(idx) {} + constexpr TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {} + explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {} bool IsValid() const { return index_ != std::numeric_limits<decltype(index_)>::max(); @@ -63,6 +98,12 @@ std::ostream& operator<<(std::ostream& os, const TypeIndex& index); namespace std { +template<> struct hash<art::dex::StringIndex> { + size_t operator()(const art::dex::StringIndex& index) const { + return hash<uint32_t>()(index.index_); + } +}; + template<> struct hash<art::dex::TypeIndex> { size_t operator()(const art::dex::TypeIndex& index) const { return hash<uint16_t>()(index.index_); diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index ed507110bd..07f0fcae56 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -80,8 +80,8 @@ static bool IsDataSectionType(uint32_t map_type) { return true; } -const char* DexFileVerifier::CheckLoadStringByIdx(uint32_t idx, const char* error_string) { - if (UNLIKELY(!CheckIndex(idx, dex_file_->NumStringIds(), error_string))) { +const char* DexFileVerifier::CheckLoadStringByIdx(dex::StringIndex idx, const char* error_string) { + if (UNLIKELY(!CheckIndex(idx.index_, dex_file_->NumStringIds(), error_string))) { return nullptr; } return dex_file_->StringDataByIdx(idx); @@ -92,9 +92,7 @@ const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) { return nullptr; } - const DexFile::TypeId& type_id = dex_file_->GetTypeId(type_idx); - uint32_t idx = type_id.descriptor_idx_; - return CheckLoadStringByIdx(idx, error_string); + return CheckLoadStringByIdx(dex_file_->GetTypeId(type_idx).descriptor_idx_, error_string); } const DexFile::FieldId* DexFileVerifier::CheckLoadFieldId(uint32_t idx, const char* error_string) { @@ -1782,7 +1780,8 @@ bool DexFileVerifier::CheckInterTypeIdItem() { const DexFile::TypeId* prev_item = reinterpret_cast<const DexFile::TypeId*>(previous_item_); if (UNLIKELY(prev_item->descriptor_idx_ >= item->descriptor_idx_)) { ErrorStringPrintf("Out-of-order type_ids: %x then %x", - prev_item->descriptor_idx_, item->descriptor_idx_); + prev_item->descriptor_idx_.index_, + item->descriptor_idx_.index_); return false; } } @@ -2500,14 +2499,15 @@ static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) { static std::string GetStringOrError(const uint8_t* const begin, const DexFile::Header* const header, - uint32_t string_idx) { + dex::StringIndex string_idx) { // The `string_idx` is not guaranteed to be valid yet. - if (header->string_ids_size_ <= string_idx) { + if (header->string_ids_size_ <= string_idx.index_) { return "(error)"; } const DexFile::StringId* string_id = - reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx; + reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + + string_idx.index_; // Assume that the data is OK at this point. String data has been checked at this point. @@ -2664,7 +2664,7 @@ static bool FindMethodName(uint32_t method_index, } uint32_t string_idx = (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + - method_index)->name_idx_; + method_index)->name_idx_.index_; if (string_idx >= header->string_ids_size_) { *error_msg = "String index not available for method flags verification"; return false; diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h index 19a89de2f5..0327367059 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex_file_verifier.h @@ -150,7 +150,7 @@ class DexFileVerifier { // Load a string by (type) index. Checks whether the index is in bounds, printing the error if // not. If there is an error, null is returned. - const char* CheckLoadStringByIdx(uint32_t idx, const char* error_fmt); + const char* CheckLoadStringByIdx(dex::StringIndex idx, const char* error_fmt); const char* CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_fmt); // Load a field/method Id by index. Checks whether the index is in bounds, printing the error if diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index 0e0929f21f..f14b1d56cb 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -176,7 +176,7 @@ TEST_F(DexFileVerifierTest, MethodId) { "method_id_name_idx", [](DexFile* dex_file) { DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0)); - method_id->name_idx_ = 0xFF; + method_id->name_idx_ = dex::StringIndex(0xFF); }, "String index not available for method flags verification"); } @@ -247,7 +247,7 @@ static const uint8_t* FindMethodData(const DexFile* dex_file, while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { uint32_t method_index = it.GetMemberIndex(); - uint32_t name_index = dex_file->GetMethodId(method_index).name_idx_; + dex::StringIndex name_index = dex_file->GetMethodId(method_index).name_idx_; const DexFile::StringId& string_id = dex_file->GetStringId(name_index); const char* str = dex_file->GetStringData(string_id); if (strcmp(name, str) == 0) { @@ -635,7 +635,7 @@ TEST_F(DexFileVerifierTest, B28552165) { uint32_t method_idx; FindMethodData(dex_file, "foo", &method_idx); auto* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(method_idx)); - method_id->name_idx_ = dex_file->NumStringIds(); + method_id->name_idx_ = dex::StringIndex(dex_file->NumStringIds()); }, "Method may have only one of public/protected/private, LMethodFlags;.(error)"); } @@ -856,7 +856,7 @@ static const uint8_t* FindFieldData(const DexFile* dex_file, const char* name) { while (it.HasNextStaticField() || it.HasNextInstanceField()) { uint32_t field_index = it.GetMemberIndex(); - uint32_t name_index = dex_file->GetFieldId(field_index).name_idx_; + dex::StringIndex name_index = dex_file->GetFieldId(field_index).name_idx_; const DexFile::StringId& string_id = dex_file->GetStringId(name_index); const char* str = dex_file->GetStringData(string_id); if (strcmp(name, str) == 0) { @@ -1451,12 +1451,12 @@ TEST_F(DexFileVerifierTest, ProtoOrdering) { // Swap the proto parameters and shorties to break the ordering. std::swap(const_cast<uint32_t&>(proto1.parameters_off_), const_cast<uint32_t&>(proto2.parameters_off_)); - std::swap(const_cast<uint32_t&>(proto1.shorty_idx_), - const_cast<uint32_t&>(proto2.shorty_idx_)); + std::swap(const_cast<dex::StringIndex&>(proto1.shorty_idx_), + const_cast<dex::StringIndex&>(proto2.shorty_idx_)); } else { // Copy the proto parameters and shorty to create duplicate proto id. const_cast<uint32_t&>(proto1.parameters_off_) = proto2.parameters_off_; - const_cast<uint32_t&>(proto1.shorty_idx_) = proto2.shorty_idx_; + const_cast<dex::StringIndex&>(proto1.shorty_idx_) = proto2.shorty_idx_; } }, "Out-of-order proto_id arguments"); diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index 751bd51dab..99023893ba 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -191,10 +191,11 @@ std::string Instruction::DumpString(const DexFile* file) const { if (file != nullptr) { uint32_t string_idx = VRegB_21c(); if (string_idx < file->NumStringIds()) { - os << StringPrintf("const-string v%d, %s // string@%d", - VRegA_21c(), - PrintableString(file->StringDataByIdx(string_idx)).c_str(), - string_idx); + os << StringPrintf( + "const-string v%d, %s // string@%d", + VRegA_21c(), + PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(), + string_idx); } else { os << StringPrintf("const-string v%d, <<invalid-string-idx-%d>> // string@%d", VRegA_21c(), @@ -333,11 +334,12 @@ std::string Instruction::DumpString(const DexFile* file) const { uint32_t string_idx = VRegB_31c(); if (file != nullptr) { if (string_idx < file->NumStringIds()) { - os << StringPrintf("%s v%d, %s // string@%d", - opcode, - VRegA_31c(), - PrintableString(file->StringDataByIdx(string_idx)).c_str(), - string_idx); + os << StringPrintf( + "%s v%d, %s // string@%d", + opcode, + VRegA_31c(), + PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(), + string_idx); } else { os << StringPrintf("%s v%d, <<invalid-string-idx-%d>> // string@%d", opcode, diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index ac52f4e287..f6eeffca73 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -826,7 +826,7 @@ inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, return h_class.Get(); } -inline mirror::String* ResolveStringFromCode(ArtMethod* referrer, uint32_t string_idx) { +inline mirror::String* ResolveStringFromCode(ArtMethod* referrer, dex::StringIndex string_idx) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); return class_linker->ResolveString(string_idx, referrer); } diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index d87dc674bc..7cc136e227 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -188,7 +188,7 @@ inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -inline mirror::String* ResolveStringFromCode(ArtMethod* referrer, uint32_t string_idx) +inline mirror::String* ResolveStringFromCode(ArtMethod* referrer, dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index b1259e1b8d..5dad43e7fa 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -66,7 +66,7 @@ extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* // TODO: Change art_quick_resolve_string on MIPS and MIPS64 to kSaveEverything. (kRuntimeISA == kMips || kRuntimeISA == kMips64) ? Runtime::kSaveRefsOnly : Runtime::kSaveEverything); - mirror::String* result = ResolveStringFromCode(caller, string_idx); + mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); if (LIKELY(result != nullptr)) { // For AOT code, we need a write barrier for the class loader that holds // the GC roots in the .bss. diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 8353b26a9a..19ee0fbeab 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -2145,14 +2145,18 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref) { to_ref->SetReadBarrierState(ReadBarrier::GrayState()); } + // Do a fence to prevent the field CAS in ConcurrentCopying::Process from possibly reordering + // before the object copy. + QuasiAtomic::ThreadFenceRelease(); + LockWord new_lock_word = LockWord::FromForwardingAddress(reinterpret_cast<size_t>(to_ref)); // Try to atomically write the fwd ptr. - bool success = from_ref->CasLockWordWeakSequentiallyConsistent(old_lock_word, new_lock_word); + bool success = from_ref->CasLockWordWeakRelaxed(old_lock_word, new_lock_word); if (LIKELY(success)) { // The CAS succeeded. - objects_moved_.FetchAndAddSequentiallyConsistent(1); - bytes_moved_.FetchAndAddSequentiallyConsistent(region_space_alloc_size); + objects_moved_.FetchAndAddRelaxed(1); + bytes_moved_.FetchAndAddRelaxed(region_space_alloc_size); if (LIKELY(!fall_back_to_non_moving)) { DCHECK(region_space_->IsInToSpace(to_ref)); } else { diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index ae9741ffa5..5c219cc871 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2742,12 +2742,6 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, concurrent_start_bytes_ = std::numeric_limits<size_t>::max(); } - // It's time to clear all inline caches, in case some classes can be unloaded. - if (((gc_type == collector::kGcTypeFull) || (gc_type == collector::kGcTypePartial)) && - (runtime->GetJit() != nullptr)) { - runtime->GetJit()->GetCodeCache()->ClearGcRootsInInlineCaches(self); - } - CHECK(collector != nullptr) << "Could not find garbage collector with collector_type=" << static_cast<size_t>(collector_type_) << " and gc_type=" << gc_type; diff --git a/runtime/handle.h b/runtime/handle.h index 3db3be202a..e4b6d29a55 100644 --- a/runtime/handle.h +++ b/runtime/handle.h @@ -61,6 +61,10 @@ class Handle : public ValueObject { return down_cast<T*>(reference_->AsMirrorPtr()); } + ALWAYS_INLINE bool IsNull() const REQUIRES_SHARED(Locks::mutator_lock_) { + return Get() == nullptr; + } + ALWAYS_INLINE jobject ToJObject() const REQUIRES_SHARED(Locks::mutator_lock_) { if (UNLIKELY(reference_->AsMirrorPtr() == nullptr)) { // Special case so that we work with null handles. diff --git a/runtime/image.cc b/runtime/image.cc index bd5ba9350e..52c9f4edf0 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -25,7 +25,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '2', '\0' }; +const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '3', '\0' }; ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 9c26d24ab1..c9a5b44c54 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -236,7 +236,7 @@ bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint1 // java.lang.String class is initialized. static inline ObjPtr<mirror::String> ResolveString(Thread* self, ShadowFrame& shadow_frame, - uint32_t string_idx) + dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr<mirror::Class> java_lang_string_class = mirror::String::GetJavaLangString(); if (UNLIKELY(!java_lang_string_class->IsInitialized())) { @@ -251,11 +251,11 @@ static inline ObjPtr<mirror::String> ResolveString(Thread* self, ArtMethod* method = shadow_frame.GetMethod(); ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass(); // MethodVerifier refuses methods with string_idx out of bounds. - DCHECK_LT(string_idx % mirror::DexCache::kDexCacheStringCacheSize, + DCHECK_LT(string_idx.index_ % mirror::DexCache::kDexCacheStringCacheSize, declaring_class->GetDexFile().NumStringIds()); ObjPtr<mirror::String> string_ptr = mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(), - string_idx, + string_idx.index_, mirror::DexCache::kDexCacheStringCacheSize).Read(); if (UNLIKELY(string_ptr == nullptr)) { StackHandleScope<1> hs(self); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 22c0fe09be..52eacd564f 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -373,7 +373,9 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, break; case Instruction::CONST_STRING: { PREAMBLE(); - ObjPtr<mirror::String> s = ResolveString(self, shadow_frame, inst->VRegB_21c()); + ObjPtr<mirror::String> s = ResolveString(self, + shadow_frame, + dex::StringIndex(inst->VRegB_21c())); if (UNLIKELY(s == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { @@ -384,7 +386,9 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } case Instruction::CONST_STRING_JUMBO: { PREAMBLE(); - ObjPtr<mirror::String> s = ResolveString(self, shadow_frame, inst->VRegB_31c()); + ObjPtr<mirror::String> s = ResolveString(self, + shadow_frame, + dex::StringIndex(inst->VRegB_31c())); if (UNLIKELY(s == nullptr)) { HANDLE_PENDING_EXCEPTION(); } else { diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index fbfed402ee..c8c1563ff6 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -291,7 +291,7 @@ extern "C" size_t MterpConstString(uint32_t index, ShadowFrame* shadow_frame, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::String> s = ResolveString(self, *shadow_frame, index); + ObjPtr<mirror::String> s = ResolveString(self, *shadow_frame, dex::StringIndex(index)); if (UNLIKELY(s == nullptr)) { return true; } diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 3531852933..2ae989a239 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -133,7 +133,7 @@ JitCodeCache::JitCodeCache(MemMap* code_map, size_t max_capacity, bool garbage_collect_code) : lock_("Jit code cache", kJitCodeCacheLock), - lock_cond_("Jit code cache variable", lock_), + lock_cond_("Jit code cache condition variable", lock_), collection_in_progress_(false), code_map_(code_map), data_map_(data_map), @@ -152,7 +152,9 @@ JitCodeCache::JitCodeCache(MemMap* code_map, number_of_collections_(0), histogram_stack_map_memory_use_("Memory used for stack maps", 16), histogram_code_memory_use_("Memory used for compiled code", 16), - histogram_profiling_info_memory_use_("Memory used for profiling info", 16) { + histogram_profiling_info_memory_use_("Memory used for profiling info", 16), + is_weak_access_enabled_(true), + inline_cache_cond_("Jit inline cache condition variable", lock_) { DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity); code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/); @@ -327,6 +329,34 @@ void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { } } } + // Walk over inline caches to clear entries containing unloaded classes. + for (ProfilingInfo* info : profiling_infos_) { + for (size_t i = 0; i < info->number_of_inline_caches_; ++i) { + InlineCache* cache = &info->cache_[i]; + for (size_t j = 0; j < InlineCache::kIndividualCacheSize; ++j) { + // This does not need a read barrier because this is called by GC. + mirror::Class* cls = cache->classes_[j].Read<kWithoutReadBarrier>(); + if (cls != nullptr) { + // Look at the classloader of the class to know if it has been + // unloaded. + // This does not need a read barrier because this is called by GC. + mirror::Object* class_loader = + cls->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>(); + if (class_loader == nullptr || visitor->IsMarked(class_loader) != nullptr) { + // The class loader is live, update the entry if the class has moved. + mirror::Class* new_cls = down_cast<mirror::Class*>(visitor->IsMarked(cls)); + // Note that new_object can be null for CMS and newly allocated objects. + if (new_cls != nullptr && new_cls != cls) { + cache->classes_[j] = GcRoot<mirror::Class>(new_cls); + } + } else { + // The class loader is not live, clear the entry. + cache->classes_[j] = GcRoot<mirror::Class>(nullptr); + } + } + } + } + } } void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UNUSED) { @@ -375,11 +405,51 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { } } -void JitCodeCache::ClearGcRootsInInlineCaches(Thread* self) { +bool JitCodeCache::IsWeakAccessEnabled(Thread* self) const { + return kUseReadBarrier + ? self->GetWeakRefAccessEnabled() + : is_weak_access_enabled_.LoadSequentiallyConsistent(); +} + +void JitCodeCache::WaitUntilInlineCacheAccessible(Thread* self) { + if (IsWeakAccessEnabled(self)) { + return; + } + ScopedThreadSuspension sts(self, kWaitingWeakGcRootRead); MutexLock mu(self, lock_); - for (ProfilingInfo* info : profiling_infos_) { - if (!info->IsInUseByCompiler()) { - info->ClearGcRootsInInlineCaches(); + while (!IsWeakAccessEnabled(self)) { + inline_cache_cond_.Wait(self); + } +} + +void JitCodeCache::BroadcastForInlineCacheAccess() { + Thread* self = Thread::Current(); + MutexLock mu(self, lock_); + inline_cache_cond_.Broadcast(self); +} + +void JitCodeCache::AllowInlineCacheAccess() { + DCHECK(!kUseReadBarrier); + is_weak_access_enabled_.StoreSequentiallyConsistent(true); + BroadcastForInlineCacheAccess(); +} + +void JitCodeCache::DisallowInlineCacheAccess() { + DCHECK(!kUseReadBarrier); + is_weak_access_enabled_.StoreSequentiallyConsistent(false); +} + +void JitCodeCache::CopyInlineCacheInto(const InlineCache& ic, + Handle<mirror::ObjectArray<mirror::Class>> array) { + WaitUntilInlineCacheAccessible(Thread::Current()); + // Note that we don't need to lock `lock_` here, the compiler calling + // this method has already ensured the inline cache will not be deleted. + for (size_t in_cache = 0, in_array = 0; + in_cache < InlineCache::kIndividualCacheSize; + ++in_cache) { + mirror::Class* object = ic.classes_[in_cache].Read(); + if (object != nullptr) { + array->Set(in_array++, object); } } } @@ -837,8 +907,6 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { if (collect_profiling_info) { ScopedThreadSuspension sts(self, kSuspended); - gc::ScopedGCCriticalSection gcs( - self, gc::kGcCauseJitCodeCache, gc::kCollectorTypeJitCodeCache); MutexLock mu(self, lock_); // Free all profiling infos of methods not compiled nor being compiled. auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(), @@ -852,10 +920,6 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // code cache collection. if (ContainsPc(ptr) && info->GetMethod()->GetProfilingInfo(kRuntimePointerSize) == nullptr) { - // We clear the inline caches as classes in it might be stalled. - info->ClearGcRootsInInlineCaches(); - // Do a fence to make sure the clearing is seen before attaching to the method. - QuasiAtomic::ThreadFenceRelease(); info->GetMethod()->SetProfilingInfo(info); } else if (info->GetMethod()->GetProfilingInfo(kRuntimePointerSize) != info) { // No need for this ProfilingInfo object anymore. diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 40112feb05..be2cec5091 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -36,6 +36,7 @@ namespace art { class ArtMethod; class LinearAlloc; +class InlineCache; class ProfilingInfo; namespace jit { @@ -156,7 +157,9 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void ClearGcRootsInInlineCaches(Thread* self) REQUIRES(!lock_); + void CopyInlineCacheInto(const InlineCache& ic, Handle<mirror::ObjectArray<mirror::Class>> array) + REQUIRES(!lock_) + REQUIRES_SHARED(Locks::mutator_lock_); // Create a 'ProfileInfo' for 'method'. If 'retry_allocation' is true, // will collect and retry if the first allocation is unsuccessful. @@ -200,6 +203,12 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); + // The GC needs to disallow the reading of inline caches when it processes them, + // to avoid having a class being used while it is being deleted. + void AllowInlineCacheAccess() REQUIRES(!lock_); + void DisallowInlineCacheAccess() REQUIRES(!lock_); + void BroadcastForInlineCacheAccess() REQUIRES(!lock_); + private: // Take ownership of maps. JitCodeCache(MemMap* code_map, @@ -275,6 +284,11 @@ class JitCodeCache { void FreeData(uint8_t* data) REQUIRES(lock_); uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); + bool IsWeakAccessEnabled(Thread* self) const; + void WaitUntilInlineCacheAccessible(Thread* self) + REQUIRES(!lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -347,6 +361,14 @@ class JitCodeCache { // Histograms for keeping track of profiling info statistics. Histogram<uint64_t> histogram_profiling_info_memory_use_ GUARDED_BY(lock_); + // Whether the GC allows accessing weaks in inline caches. Note that this + // is not used by the concurrent collector, which uses + // Thread::SetWeakRefAccessEnabled instead. + Atomic<bool> is_weak_access_enabled_; + + // Condition to wait on for accessing inline caches. + ConditionVariable inline_cache_cond_ GUARDED_BY(lock_); + DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index 9ec46f0795..405280dd47 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -36,15 +36,6 @@ ProfilingInfo::ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& ent for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; } - if (method->IsCopied()) { - // GetHoldingClassOfCopiedMethod is expensive, but creating a profiling info for a copied method - // appears to happen very rarely in practice. - holding_class_ = GcRoot<mirror::Class>( - Runtime::Current()->GetClassLinker()->GetHoldingClassOfCopiedMethod(method)); - } else { - holding_class_ = GcRoot<mirror::Class>(method->GetDeclaringClass()); - } - DCHECK(!holding_class_.IsNull()); } bool ProfilingInfo::Create(Thread* self, ArtMethod* method, bool retry_allocation) { @@ -116,14 +107,6 @@ void ProfilingInfo::AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls) { --i; } else { // We successfully set `cls`, just return. - // Since the instrumentation is marked from the declaring class we need to mark the card so - // that mod-union tables and card rescanning know about the update. - // Note that the declaring class is not necessarily the holding class if the method is - // copied. We need the card mark to be in the holding class since that is from where we - // will visit the profiling info. - if (!holding_class_.IsNull()) { - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(holding_class_.Read()); - } return; } } diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index 1056fac5ef..9902bb584f 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -39,46 +39,13 @@ class Class; // Once the classes_ array is full, we consider the INVOKE to be megamorphic. class InlineCache { public: - bool IsMonomorphic() const { - DCHECK_GE(kIndividualCacheSize, 2); - return !classes_[0].IsNull() && classes_[1].IsNull(); - } - - bool IsMegamorphic() const { - for (size_t i = 0; i < kIndividualCacheSize; ++i) { - if (classes_[i].IsNull()) { - return false; - } - } - return true; - } - - mirror::Class* GetMonomorphicType() const REQUIRES_SHARED(Locks::mutator_lock_) { - // Note that we cannot ensure the inline cache is actually monomorphic - // at this point, as other threads may have updated it. - DCHECK(!classes_[0].IsNull()); - return classes_[0].Read(); - } - - bool IsUninitialized() const { - return classes_[0].IsNull(); - } - - bool IsPolymorphic() const { - DCHECK_GE(kIndividualCacheSize, 3); - return !classes_[1].IsNull() && classes_[kIndividualCacheSize - 1].IsNull(); - } - - mirror::Class* GetTypeAt(size_t i) const REQUIRES_SHARED(Locks::mutator_lock_) { - return classes_[i].Read(); - } - static constexpr uint16_t kIndividualCacheSize = 5; private: uint32_t dex_pc_; GcRoot<mirror::Class> classes_[kIndividualCacheSize]; + friend class jit::JitCodeCache; friend class ProfilingInfo; DISALLOW_COPY_AND_ASSIGN(InlineCache); @@ -102,18 +69,6 @@ class ProfilingInfo { REQUIRES(Roles::uninterruptible_) REQUIRES_SHARED(Locks::mutator_lock_); - // NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires. - template<typename RootVisitorType> - void VisitRoots(RootVisitorType& visitor) NO_THREAD_SAFETY_ANALYSIS { - visitor.VisitRootIfNonNull(holding_class_.AddressWithoutBarrier()); - for (size_t i = 0; i < number_of_inline_caches_; ++i) { - InlineCache* cache = &cache_[i]; - for (size_t j = 0; j < InlineCache::kIndividualCacheSize; ++j) { - visitor.VisitRootIfNonNull(cache->classes_[j].AddressWithoutBarrier()); - } - } - } - ArtMethod* GetMethod() const { return method_; } @@ -175,9 +130,6 @@ class ProfilingInfo { // Method this profiling info is for. ArtMethod* const method_; - // Holding class for the method in case method is a copied method. - GcRoot<mirror::Class> holding_class_; - // Whether the ArtMethod is currently being compiled. This flag // is implicitly guarded by the JIT code cache lock. // TODO: Make the JIT code cache lock global. diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index b11dad8203..7d7c1d7cfd 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -424,6 +424,29 @@ inline void PointerArray::Fixup(mirror::PointerArray* dest, } } +template<bool kUnchecked> +void PointerArray::Memcpy(int32_t dst_pos, + ObjPtr<PointerArray> src, + int32_t src_pos, + int32_t count, + PointerSize ptr_size) { + DCHECK(!Runtime::Current()->IsActiveTransaction()); + DCHECK(!src.IsNull()); + if (ptr_size == PointerSize::k64) { + LongArray* l_this = (kUnchecked ? down_cast<LongArray*>(static_cast<Object*>(this)) + : AsLongArray()); + LongArray* l_src = (kUnchecked ? down_cast<LongArray*>(static_cast<Object*>(src.Ptr())) + : src->AsLongArray()); + l_this->Memcpy(dst_pos, l_src, src_pos, count); + } else { + IntArray* i_this = (kUnchecked ? down_cast<IntArray*>(static_cast<Object*>(this)) + : AsIntArray()); + IntArray* i_src = (kUnchecked ? down_cast<IntArray*>(static_cast<Object*>(src.Ptr())) + : src->AsIntArray()); + i_this->Memcpy(dst_pos, i_src, src_pos, count); + } +} + template<typename T> inline void PrimitiveArray<T>::SetArrayClass(ObjPtr<Class> array_class) { CHECK(array_class_.IsNull()); diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 994e9b2616..19d300e1f4 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -208,6 +208,17 @@ class PointerArray : public Array { typename Visitor> void Fixup(mirror::PointerArray* dest, PointerSize pointer_size, const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); + + // Works like memcpy(), except we guarantee not to allow tearing of array values (ie using smaller + // than element size copies). Arguments are assumed to be within the bounds of the array and the + // arrays non-null. Cannot be called in an active transaction. + template<bool kUnchecked = false> + void Memcpy(int32_t dst_pos, + ObjPtr<PointerArray> src, + int32_t src_pos, + int32_t count, + PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_); }; } // namespace mirror diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index aa5da2e83b..5def65e4d4 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -65,8 +65,10 @@ inline Class* Class::GetSuperClass() { OFFSET_OF_OBJECT_MEMBER(Class, super_class_)); } +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline ClassLoader* Class::GetClassLoader() { - return GetFieldObject<ClassLoader>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_)); + return GetFieldObject<ClassLoader, kVerifyFlags, kReadBarrierOption>( + OFFSET_OF_OBJECT_MEMBER(Class, class_loader_)); } template<VerifyObjectFlags kVerifyFlags> diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 792f626eff..248c941d1c 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -674,6 +674,8 @@ class MANAGED Class FINAL : public Object { return MemberOffset(OFFSETOF_MEMBER(Class, super_class_)); } + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ClassLoader* GetClassLoader() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_); void SetClassLoader(ObjPtr<ClassLoader> new_cl) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index cc208e49dd..259bbbe174 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -34,6 +34,71 @@ namespace mirror { GcRoot<Class> ClassExt::dalvik_system_ClassExt_; +void ClassExt::SetObsoleteArrays(ObjPtr<PointerArray> methods, + ObjPtr<ObjectArray<DexCache>> dex_caches) { + DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId()) + << "Obsolete arrays are set without synchronization!"; + CHECK_EQ(methods.IsNull(), dex_caches.IsNull()); + auto obsolete_dex_cache_off = OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_dex_caches_); + auto obsolete_methods_off = OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_); + DCHECK(!Runtime::Current()->IsActiveTransaction()); + SetFieldObject<false>(obsolete_dex_cache_off, dex_caches.Ptr()); + SetFieldObject<false>(obsolete_methods_off, methods.Ptr()); +} + +// TODO We really need to be careful how we update this. If we ever in the future make it so that +// these arrays are written into without all threads being suspended we have a race condition! +bool ClassExt::ExtendObsoleteArrays(Thread* self, uint32_t increase) { + DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId()) + << "Obsolete arrays are set without synchronization!"; + StackHandleScope<5> hs(self); + Handle<ClassExt> h_this(hs.NewHandle(this)); + Handle<PointerArray> old_methods(hs.NewHandle(h_this->GetObsoleteMethods())); + Handle<ObjectArray<DexCache>> old_dex_caches(hs.NewHandle(h_this->GetObsoleteDexCaches())); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + size_t new_len; + if (old_methods.Get() == nullptr) { + CHECK(old_dex_caches.Get() == nullptr); + new_len = increase; + } else { + CHECK_EQ(old_methods->GetLength(), old_dex_caches->GetLength()); + new_len = increase + old_methods->GetLength(); + } + Handle<PointerArray> new_methods(hs.NewHandle<PointerArray>( + cl->AllocPointerArray(self, new_len))); + if (new_methods.IsNull()) { + // Fail. + self->AssertPendingOOMException(); + return false; + } + Handle<ObjectArray<DexCache>> new_dex_caches(hs.NewHandle<ObjectArray<DexCache>>( + ObjectArray<DexCache>::Alloc(self, + cl->FindClass(self, + "[Ljava/lang/DexCache;", + ScopedNullHandle<ClassLoader>()), + new_len))); + if (new_dex_caches.IsNull()) { + // Fail. + self->AssertPendingOOMException(); + return false; + } + + if (!old_methods.IsNull()) { + // Copy the old contents. + new_methods->Memcpy(0, + old_methods.Get(), + 0, + old_methods->GetLength(), + cl->GetImagePointerSize()); + new_dex_caches->AsObjectArray<Object>()->AssignableCheckingMemcpy<false>( + 0, old_dex_caches->AsObjectArray<Object>(), 0, old_dex_caches->GetLength(), false); + } + // Set the fields. + h_this->SetObsoleteArrays(new_methods.Get(), new_dex_caches.Get()); + + return true; +} + ClassExt* ClassExt::Alloc(Thread* self) { DCHECK(dalvik_system_ClassExt_.Read() != nullptr); return down_cast<ClassExt*>(dalvik_system_ClassExt_.Read()->AllocObject(self).Ptr()); diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h index 35eaae147a..91046314db 100644 --- a/runtime/mirror/class_ext.h +++ b/runtime/mirror/class_ext.h @@ -19,8 +19,11 @@ #include "class-inl.h" +#include "array.h" +#include "dex_cache.h" #include "gc_root.h" #include "object.h" +#include "object_array.h" #include "object_callbacks.h" #include "string.h" @@ -49,6 +52,22 @@ class MANAGED ClassExt : public Object { return GetFieldObject<ClassExt>(OFFSET_OF_OBJECT_MEMBER(ClassExt, verify_error_)); } + ObjectArray<DexCache>* GetObsoleteDexCaches() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldObject<ObjectArray<DexCache>>( + OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_dex_caches_)); + } + + PointerArray* GetObsoleteMethods() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldObject<PointerArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_)); + } + + void SetObsoleteArrays(ObjPtr<PointerArray> methods, ObjPtr<ObjectArray<DexCache>> dex_caches) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Extend the obsolete arrays by the given amount. + bool ExtendObsoleteArrays(Thread* self, uint32_t increase) + REQUIRES_SHARED(Locks::mutator_lock_); + static void SetClass(ObjPtr<Class> dalvik_system_ClassExt); static void ResetClass(); static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); @@ -57,6 +76,13 @@ class MANAGED ClassExt : public Object { private: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". + HeapReference<ObjectArray<DexCache>> obsolete_dex_caches_; + + HeapReference<PointerArray> obsolete_methods_; + + HeapReference<DexCache> original_dex_cache_; + + // The saved verification error of this class. HeapReference<Object> verify_error_; static GcRoot<Class> dalvik_system_ClassExt_; diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index d903f71604..be8815aba2 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -40,13 +40,14 @@ inline uint32_t DexCache::ClassSize(PointerSize pointer_size) { return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size); } -inline mirror::String* DexCache::GetResolvedString(uint32_t string_idx) { - DCHECK_LT(string_idx, GetDexFile()->NumStringIds()); - return StringDexCachePair::Lookup(GetStrings(), string_idx, NumStrings()).Read(); +inline mirror::String* DexCache::GetResolvedString(dex::StringIndex string_idx) { + DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds()); + return StringDexCachePair::Lookup(GetStrings(), string_idx.index_, NumStrings()).Read(); } -inline void DexCache::SetResolvedString(uint32_t string_idx, ObjPtr<mirror::String> resolved) { - StringDexCachePair::Assign(GetStrings(), string_idx, resolved.Ptr(), NumStrings()); +inline void DexCache::SetResolvedString(dex::StringIndex string_idx, + ObjPtr<mirror::String> resolved) { + StringDexCachePair::Assign(GetStrings(), string_idx.index_, resolved.Ptr(), NumStrings()); Runtime* const runtime = Runtime::Current(); if (UNLIKELY(runtime->IsActiveTransaction())) { DCHECK(runtime->IsAotCompiler()); @@ -56,12 +57,12 @@ inline void DexCache::SetResolvedString(uint32_t string_idx, ObjPtr<mirror::Stri runtime->GetHeap()->WriteBarrierEveryFieldOf(this); } -inline void DexCache::ClearString(uint32_t string_idx) { - const uint32_t slot_idx = string_idx % NumStrings(); +inline void DexCache::ClearString(dex::StringIndex string_idx) { + const uint32_t slot_idx = string_idx.index_ % NumStrings(); DCHECK(Runtime::Current()->IsAotCompiler()); StringDexCacheType* slot = &GetStrings()[slot_idx]; // This is racy but should only be called from the transactional interpreter. - if (slot->load(std::memory_order_relaxed).index == string_idx) { + if (slot->load(std::memory_order_relaxed).index == string_idx.index_) { StringDexCachePair cleared( nullptr, StringDexCachePair::InvalidIndexForSlot(slot_idx)); diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 7d82d3ad29..cc4d01a075 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -214,15 +214,15 @@ class MANAGED DexCache FINAL : public Object { return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_); } - mirror::String* GetResolvedString(uint32_t string_idx) ALWAYS_INLINE + mirror::String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_); - void SetResolvedString(uint32_t string_idx, ObjPtr<mirror::String> resolved) ALWAYS_INLINE + void SetResolvedString(dex::StringIndex string_idx, ObjPtr<mirror::String> resolved) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_); // Clear a string for a string_idx, used to undo string intern transactions to make sure // the string isn't kept live. - void ClearString(uint32_t string_idx) REQUIRES_SHARED(Locks::mutator_lock_); + void ClearString(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_); Class* GetResolvedType(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/modifiers.h b/runtime/modifiers.h index dd32df69d7..a1110d92a5 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -67,6 +67,10 @@ static constexpr uint32_t kAccCompileDontBother = 0x01000000; // method (ru // Set by the verifier for a method that could not be verified to follow structured locking. static constexpr uint32_t kAccMustCountLocks = 0x02000000; // method (runtime) +// Set to indicate that the ArtMethod is obsolete and has a different DexCache from it's declaring +// class. +// TODO Might want to re-arrange some of these so that we can have obsolete + intrinsic methods. +static constexpr uint32_t kAccObsoleteMethod = 0x04000000; // method (runtime) static constexpr uint32_t kAccIntrinsic = 0x80000000; // method (runtime) // Special runtime-only flags. diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 48feb11924..3058df442c 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -287,7 +287,7 @@ class PreloadDexCachesStringsVisitor : public SingleRootVisitor { // Based on ClassLinker::ResolveString. static void PreloadDexCachesResolveString( - Handle<mirror::DexCache> dex_cache, uint32_t string_idx, StringTable& strings) + Handle<mirror::DexCache> dex_cache, dex::StringIndex string_idx, StringTable& strings) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr<mirror::String> string = dex_cache->GetResolvedString(string_idx); if (string != nullptr) { @@ -450,7 +450,7 @@ static void PreloadDexCachesStatsFilled(DexCacheStats* filled) continue; } for (size_t j = 0; j < dex_cache->NumStrings(); j++) { - ObjPtr<mirror::String> string = dex_cache->GetResolvedString(j); + ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(j)); if (string != nullptr) { filled->num_strings++; } @@ -514,7 +514,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { if (kPreloadDexCachesStrings) { for (size_t j = 0; j < dex_cache->NumStrings(); j++) { - PreloadDexCachesResolveString(dex_cache, j, strings); + PreloadDexCachesResolveString(dex_cache, dex::StringIndex(j), strings); } } diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc index f6de593017..f1c350f23c 100644 --- a/runtime/native/java_lang_DexCache.cc +++ b/runtime/native/java_lang_DexCache.cc @@ -61,7 +61,8 @@ static jobject DexCache_getResolvedString(JNIEnv* env, jobject javaDexCache, jin ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache); CHECK_LT(static_cast<size_t>(string_index), dex_cache->GetDexFile()->NumStringIds()); - return soa.AddLocalReference<jobject>(dex_cache->GetResolvedString(string_index)); + return soa.AddLocalReference<jobject>( + dex_cache->GetResolvedString(dex::StringIndex(string_index))); } static void DexCache_setResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index, @@ -77,7 +78,7 @@ static void DexCache_setResolvedString(JNIEnv* env, jobject javaDexCache, jint s ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache); CHECK_LT(static_cast<size_t>(string_index), dex_cache->GetDexFile()->NumStringIds()); - dex_cache->SetResolvedString(string_index, soa.Decode<mirror::String>(string)); + dex_cache->SetResolvedString(dex::StringIndex(string_index), soa.Decode<mirror::String>(string)); } static JNINativeMethod gMethods[] = { diff --git a/runtime/object_lock.cc b/runtime/object_lock.cc index b8754a4093..39ab52fb2d 100644 --- a/runtime/object_lock.cc +++ b/runtime/object_lock.cc @@ -17,6 +17,7 @@ #include "object_lock.h" #include "mirror/object-inl.h" +#include "mirror/class_ext.h" #include "monitor.h" namespace art { @@ -61,6 +62,7 @@ ObjectTryLock<T>::~ObjectTryLock() { } template class ObjectLock<mirror::Class>; +template class ObjectLock<mirror::ClassExt>; template class ObjectLock<mirror::Object>; template class ObjectTryLock<mirror::Class>; template class ObjectTryLock<mirror::Object>; diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp index b323aef0db..0f9fbb2819 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -24,6 +24,7 @@ cc_defaults { "ti_heap.cc", "ti_method.cc", "ti_stack.cc", + "ti_redefine.cc", "transform.cc"], include_dirs: ["art/runtime"], shared_libs: [ diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 6480843b91..d1c2293cc3 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -40,15 +40,16 @@ #include "base/mutex.h" #include "events-inl.h" #include "jni_env_ext-inl.h" -#include "object_tagging.h" #include "obj_ptr-inl.h" +#include "object_tagging.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" -#include "thread_list.h" #include "thread-inl.h" +#include "thread_list.h" #include "ti_class.h" #include "ti_heap.h" #include "ti_method.h" +#include "ti_redefine.h" #include "ti_stack.h" #include "transform.h" @@ -1148,6 +1149,8 @@ class JvmtiFunctions { if (!IsValidEnv(env)) { return ERR(INVALID_ENVIRONMENT); } + jvmtiError res = OK; + std::string error; for (jclass klass : classes) { JNIEnv* jni_env = nullptr; jobject loader = nullptr; @@ -1183,11 +1186,22 @@ class JvmtiFunctions { /*out*/&new_dex_data); // Check if anything actually changed. if ((new_data_len != 0 || new_dex_data != nullptr) && new_dex_data != dex_data) { - MoveTransformedFileIntoRuntime(klass, std::move(location), new_data_len, new_dex_data); + res = Redefiner::RedefineClass(env, + art::Runtime::Current(), + art::Thread::Current(), + klass, + location, + new_data_len, + new_dex_data, + &error); env->Deallocate(new_dex_data); } // Deallocate the old dex data. env->Deallocate(dex_data); + if (res != OK) { + LOG(ERROR) << "FAILURE TO REDEFINE " << error; + return res; + } } return OK; } diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc new file mode 100644 index 0000000000..69bd88768c --- /dev/null +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -0,0 +1,507 @@ +/* Copyright (C) 2016 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "ti_redefine.h" + +#include <limits> + +#include "art_jvmti.h" +#include "base/logging.h" +#include "events-inl.h" +#include "gc/allocation_listener.h" +#include "instrumentation.h" +#include "jni_env_ext-inl.h" +#include "jvmti_allocator.h" +#include "mirror/class.h" +#include "mirror/class_ext.h" +#include "mirror/object.h" +#include "object_lock.h" +#include "runtime.h" +#include "ScopedLocalRef.h" + +namespace openjdkjvmti { + +// Moves dex data to an anonymous, read-only mmap'd region. +std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location, + jint data_len, + unsigned char* dex_data, + std::string* error_msg) { + std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous( + art::StringPrintf("%s-transformed", original_location.c_str()).c_str(), + nullptr, + data_len, + PROT_READ|PROT_WRITE, + /*low_4gb*/false, + /*reuse*/false, + error_msg)); + if (map == nullptr) { + return map; + } + memcpy(map->Begin(), dex_data, data_len); + // Make the dex files mmap read only. + map->Protect(PROT_READ); + return map; +} + +jvmtiError Redefiner::RedefineClass(ArtJvmTiEnv* env, + art::Runtime* runtime, + art::Thread* self, + jclass klass, + const std::string& original_dex_location, + jint data_len, + unsigned char* dex_data, + std::string* error_msg) { + std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location, + data_len, + dex_data, + error_msg)); + std::ostringstream os; + char* generic_ptr_unused = nullptr; + char* signature_ptr = nullptr; + if (env->GetClassSignature(klass, &signature_ptr, &generic_ptr_unused) != OK) { + signature_ptr = const_cast<char*>("<UNKNOWN CLASS>"); + } + if (map.get() == nullptr) { + os << "Failed to create anonymous mmap for modified dex file of class " << signature_ptr + << "in dex file " << original_dex_location << " because: " << *error_msg; + *error_msg = os.str(); + return ERR(OUT_OF_MEMORY); + } + if (map->Size() < sizeof(art::DexFile::Header)) { + *error_msg = "Could not read dex file header because dex_data was too short"; + return ERR(INVALID_CLASS_FORMAT); + } + uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_; + std::unique_ptr<const art::DexFile> dex_file(art::DexFile::Open(map->GetName(), + checksum, + std::move(map), + /*verify*/true, + /*verify_checksum*/true, + error_msg)); + if (dex_file.get() == nullptr) { + os << "Unable to load modified dex file for " << signature_ptr << ": " << *error_msg; + *error_msg = os.str(); + return ERR(INVALID_CLASS_FORMAT); + } + // Get shared mutator lock. + art::ScopedObjectAccess soa(self); + art::StackHandleScope<1> hs(self); + Redefiner r(runtime, self, klass, signature_ptr, dex_file, error_msg); + // Lock around this class to avoid races. + art::ObjectLock<art::mirror::Class> lock(self, hs.NewHandle(r.GetMirrorClass())); + return r.Run(); +} + +// TODO *MAJOR* This should return the actual source java.lang.DexFile object for the klass. +// TODO Make mirror of DexFile and associated types to make this less hellish. +// TODO Make mirror of BaseDexClassLoader and associated types to make this less hellish. +art::mirror::Object* Redefiner::FindSourceDexFileObject( + art::Handle<art::mirror::ClassLoader> loader) { + const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;"; + const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;"; + const char* dex_file_name = "Ldalvik/system/DexFile;"; + const char* dex_path_list_name = "Ldalvik/system/DexPathList;"; + const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;"; + + CHECK(!self_->IsExceptionPending()); + art::StackHandleScope<11> hs(self_); + art::ClassLinker* class_linker = runtime_->GetClassLinker(); + + art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>( + nullptr)); + art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass( + self_, dex_class_loader_name, null_loader))); + + // Get all the ArtFields so we can look in the BaseDexClassLoader + art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField( + "pathList", dex_path_list_name); + CHECK(path_list_field != nullptr); + + art::ArtField* dex_path_list_element_field = + class_linker->FindClass(self_, dex_path_list_name, null_loader) + ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name); + CHECK(dex_path_list_element_field != nullptr); + + art::ArtField* element_dex_file_field = + class_linker->FindClass(self_, dex_path_list_element_name, null_loader) + ->FindDeclaredInstanceField("dexFile", dex_file_name); + CHECK(element_dex_file_field != nullptr); + + // Check if loader is a BaseDexClassLoader + art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass())); + if (!loader_class->IsSubClass(base_dex_loader_class.Get())) { + LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only " + << "supported class loader type!"; + return nullptr; + } + // Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative) + art::Handle<art::mirror::Object> path_list( + hs.NewHandle(path_list_field->GetObject(loader.Get()))); + CHECK(path_list.Get() != nullptr); + CHECK(!self_->IsExceptionPending()); + art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle( + dex_path_list_element_field->GetObject(path_list.Get())-> + AsObjectArray<art::mirror::Object>())); + CHECK(!self_->IsExceptionPending()); + CHECK(dex_elements_list.Get() != nullptr); + size_t num_elements = dex_elements_list->GetLength(); + art::MutableHandle<art::mirror::Object> current_element( + hs.NewHandle<art::mirror::Object>(nullptr)); + art::MutableHandle<art::mirror::Object> first_dex_file( + hs.NewHandle<art::mirror::Object>(nullptr)); + // Iterate over the DexPathList$Element to find the right one + // TODO Or not ATM just return the first one. + for (size_t i = 0; i < num_elements; i++) { + current_element.Assign(dex_elements_list->Get(i)); + CHECK(current_element.Get() != nullptr); + CHECK(!self_->IsExceptionPending()); + CHECK(dex_elements_list.Get() != nullptr); + CHECK_EQ(current_element->GetClass(), class_linker->FindClass(self_, + dex_path_list_element_name, + null_loader)); + // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class + // comes from but it is more annoying because we would need to find this class. It is not + // necessary for proper function since we just need to be in front of the classes old dex file + // in the path. + first_dex_file.Assign(element_dex_file_field->GetObject(current_element.Get())); + if (first_dex_file.Get() != nullptr) { + return first_dex_file.Get(); + } + } + return nullptr; +} + +art::mirror::Class* Redefiner::GetMirrorClass() { + return self_->DecodeJObject(klass_)->AsClass(); +} + +art::mirror::ClassLoader* Redefiner::GetClassLoader() { + return GetMirrorClass()->GetClassLoader(); +} + +art::mirror::DexCache* Redefiner::CreateNewDexCache(art::Handle<art::mirror::ClassLoader> loader) { + return runtime_->GetClassLinker()->RegisterDexFile(*dex_file_, loader.Get()); +} + +// TODO Really wishing I had that mirror of java.lang.DexFile now. +art::mirror::LongArray* Redefiner::AllocateDexFileCookie( + art::Handle<art::mirror::Object> java_dex_file_obj) { + art::StackHandleScope<2> hs(self_); + // mCookie is nulled out if the DexFile has been closed but mInternalCookie sticks around until + // the object is finalized. Since they always point to the same array if mCookie is not null we + // just use the mInternalCookie field. We will update one or both of these fields later. + // TODO Should I get the class from the classloader or directly? + art::ArtField* internal_cookie_field = java_dex_file_obj->GetClass()->FindDeclaredInstanceField( + "mInternalCookie", "Ljava/lang/Object;"); + // TODO Add check that mCookie is either null or same as mInternalCookie + CHECK(internal_cookie_field != nullptr); + art::Handle<art::mirror::LongArray> cookie( + hs.NewHandle(internal_cookie_field->GetObject(java_dex_file_obj.Get())->AsLongArray())); + // TODO Maybe make these non-fatal. + CHECK(cookie.Get() != nullptr); + CHECK_GE(cookie->GetLength(), 1); + art::Handle<art::mirror::LongArray> new_cookie( + hs.NewHandle(art::mirror::LongArray::Alloc(self_, cookie->GetLength() + 1))); + if (new_cookie.Get() == nullptr) { + self_->AssertPendingOOMException(); + return nullptr; + } + // Copy the oat-dex field at the start. + // TODO Should I clear this field? + // TODO This is a really crappy thing here with the first element being different. + new_cookie->SetWithoutChecks<false>(0, cookie->GetWithoutChecks(0)); + new_cookie->SetWithoutChecks<false>( + 1, static_cast<int64_t>(reinterpret_cast<intptr_t>(dex_file_.get()))); + new_cookie->Memcpy(2, cookie.Get(), 1, cookie->GetLength() - 1); + return new_cookie.Get(); +} + +void Redefiner::RecordFailure(jvmtiError result, const std::string& error_msg) { + *error_msg_ = art::StringPrintf("Unable to perform redefinition of '%s': %s", + class_sig_, + error_msg.c_str()); + result_ = result; +} + +bool Redefiner::FinishRemainingAllocations( + /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader, + /*out*/art::MutableHandle<art::mirror::Object>* java_dex_file_obj, + /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie, + /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache) { + art::StackHandleScope<4> hs(self_); + // This shouldn't allocate + art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader())); + if (loader.Get() == nullptr) { + // TODO Better error msg. + RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); + return false; + } + art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader))); + if (dex_file_obj.Get() == nullptr) { + // TODO Better error msg. + RecordFailure(ERR(INTERNAL), "Unable to find class loader!"); + return false; + } + art::Handle<art::mirror::LongArray> new_cookie(hs.NewHandle(AllocateDexFileCookie(dex_file_obj))); + if (new_cookie.Get() == nullptr) { + self_->AssertPendingOOMException(); + self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader"); + return false; + } + art::Handle<art::mirror::DexCache> dex_cache(hs.NewHandle(CreateNewDexCache(loader))); + if (dex_cache.Get() == nullptr) { + self_->AssertPendingOOMException(); + self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate DexCache"); + return false; + } + source_class_loader->Assign(loader.Get()); + java_dex_file_obj->Assign(dex_file_obj.Get()); + new_dex_file_cookie->Assign(new_cookie.Get()); + new_dex_cache->Assign(dex_cache.Get()); + return true; +} + +jvmtiError Redefiner::Run() { + art::StackHandleScope<5> hs(self_); + // TODO We might want to have a global lock (or one based on the class being redefined at least) + // in order to make cleanup easier. Not a huge deal though. + // + // First we just allocate the ClassExt and its fields that we need. These can be updated + // atomically without any issues (since we allocate the map arrays as empty) so we don't bother + // doing a try loop. The other allocations we need to ensure that nothing has changed in the time + // between allocating them and pausing all threads before we can update them so we need to do a + // try loop. + if (!EnsureRedefinitionIsValid() || !EnsureClassAllocationsFinished()) { + return result_; + } + art::MutableHandle<art::mirror::ClassLoader> source_class_loader( + hs.NewHandle<art::mirror::ClassLoader>(nullptr)); + art::MutableHandle<art::mirror::Object> java_dex_file( + hs.NewHandle<art::mirror::Object>(nullptr)); + art::MutableHandle<art::mirror::LongArray> new_dex_file_cookie( + hs.NewHandle<art::mirror::LongArray>(nullptr)); + art::MutableHandle<art::mirror::DexCache> new_dex_cache( + hs.NewHandle<art::mirror::DexCache>(nullptr)); + if (!FinishRemainingAllocations(&source_class_loader, + &java_dex_file, + &new_dex_file_cookie, + &new_dex_cache)) { + // TODO Null out the ClassExt fields we allocated (if possible, might be racing with another + // redefineclass call which made it even bigger. Leak shouldn't be huge (2x array of size + // declared_methods_.length) but would be good to get rid of. + // new_dex_file_cookie & new_dex_cache should be cleaned up by the GC. + return result_; + } + // Get the mirror class now that we aren't allocating anymore. + art::Handle<art::mirror::Class> art_class(hs.NewHandle(GetMirrorClass())); + // Enable assertion that this thread isn't interrupted during this installation. + // After this we will need to do real cleanup in case of failure. Prior to this we could simply + // return and would let everything get cleaned up or harmlessly leaked. + // Do transition to final suspension + // TODO We might want to give this its own suspended state! + // TODO This isn't right. We need to change state without any chance of suspend ideally! + self_->TransitionFromRunnableToSuspended(art::ThreadState::kNative); + runtime_->GetThreadList()->SuspendAll( + "Final installation of redefined Class!", /*long_suspend*/true); + // TODO Might want to move this into a different type. + // Now we reach the part where we must do active cleanup if something fails. + // TODO We should really Retry if this fails instead of simply aborting. + // Set the new DexFileCookie returns the original so we can fix it back up if redefinition fails + art::ObjPtr<art::mirror::LongArray> original_dex_file_cookie(nullptr); + if (!UpdateJavaDexFile(java_dex_file.Get(), + new_dex_file_cookie.Get(), + &original_dex_file_cookie)) { + // Release suspendAll + runtime_->GetThreadList()->ResumeAll(); + // Get back shared mutator lock as expected for return. + self_->TransitionFromSuspendedToRunnable(); + return result_; + } + if (!UpdateClass(art_class.Get(), new_dex_cache.Get())) { + // TODO Should have some form of scope to do this. + RestoreJavaDexFile(java_dex_file.Get(), original_dex_file_cookie); + // Release suspendAll + runtime_->GetThreadList()->ResumeAll(); + // Get back shared mutator lock as expected for return. + self_->TransitionFromSuspendedToRunnable(); + return result_; + } + // Update the ClassObjects Keep the old DexCache (and other stuff) around so we can restore + // functions/fields. + // Verify the new Class. + // Failure then undo updates to class + // Do stack walks and allocate obsolete methods + // Shrink the obsolete method maps if possible? + // TODO find appropriate class loader. Allocate new dex files array. Pause all java treads. + // Replace dex files array. Do stack scan + allocate obsoletes. Remove array if possible. + // TODO We might want to ensure that all threads are stopped for this! + // AddDexToClassPath(); + // TODO + // Release suspendAll + // TODO Put this into a scoped thing. + runtime_->GetThreadList()->ResumeAll(); + // Get back shared mutator lock as expected for return. + self_->TransitionFromSuspendedToRunnable(); + // TODO Do this at a more reasonable place. + dex_file_.release(); + return OK; +} + +void Redefiner::RestoreJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, + art::ObjPtr<art::mirror::LongArray> orig_cookie) { + art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( + "mInternalCookie", "Ljava/lang/Object;"); + art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( + "mCookie", "Ljava/lang/Object;"); + art::ObjPtr<art::mirror::LongArray> new_cookie( + cookie_field->GetObject(java_dex_file)->AsLongArray()); + internal_cookie_field->SetObject<false>(java_dex_file, orig_cookie); + if (!new_cookie.IsNull()) { + cookie_field->SetObject<false>(java_dex_file, orig_cookie); + } +} + +// Performs updates to class that will allow us to verify it. +bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache) { + art::ClassLinker* linker = runtime_->GetClassLinker(); + art::PointerSize image_pointer_size = linker->GetImagePointerSize(); + const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef( + *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_)); + if (class_def == nullptr) { + RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!"); + return false; + } + const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def->class_idx_); + const art::DexFile& old_dex_file = mclass->GetDexFile(); + for (art::ArtMethod& method : mclass->GetMethods(image_pointer_size)) { + const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(method.GetName()); + art::dex::TypeIndex method_return_idx = + dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(method.GetReturnTypeDescriptor())); + const auto* old_type_list = method.GetParameterTypeList(); + std::vector<art::dex::TypeIndex> new_type_list; + for (uint32_t i = 0; old_type_list != nullptr && i < old_type_list->Size(); i++) { + new_type_list.push_back( + dex_file_->GetIndexForTypeId( + *dex_file_->FindTypeId( + old_dex_file.GetTypeDescriptor( + old_dex_file.GetTypeId( + old_type_list->GetTypeItem(i).type_idx_))))); + } + const art::DexFile::ProtoId* proto_id = dex_file_->FindProtoId(method_return_idx, + new_type_list); + CHECK(proto_id != nullptr || old_type_list == nullptr); + // TODO Return false, cleanup. + const art::DexFile::MethodId* method_id = dex_file_->FindMethodId(declaring_class_id, + *new_name_id, + *proto_id); + CHECK(method_id != nullptr); + // TODO Return false, cleanup. + uint32_t dex_method_idx = dex_file_->GetIndexForMethodId(*method_id); + method.SetDexMethodIndex(dex_method_idx); + linker->SetEntryPointsToInterpreter(&method); + method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(*class_def, dex_method_idx)); + method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size); + method.SetDexCacheResolvedTypes(new_dex_cache->GetResolvedTypes(), image_pointer_size); + } + // Update the class fields. + // Need to update class last since the ArtMethod gets its DexFile from the class (which is needed + // to call GetReturnTypeDescriptor and GetParameterTypeList above). + mclass->SetDexCache(new_dex_cache.Ptr()); + mclass->SetDexCacheStrings(new_dex_cache->GetStrings()); + mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def)); + mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_))); + return true; +} + +bool Redefiner::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, + art::ObjPtr<art::mirror::LongArray> new_cookie, + /*out*/art::ObjPtr<art::mirror::LongArray>* original_cookie) { + art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( + "mInternalCookie", "Ljava/lang/Object;"); + art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField( + "mCookie", "Ljava/lang/Object;"); + CHECK(internal_cookie_field != nullptr); + art::ObjPtr<art::mirror::LongArray> orig_internal_cookie( + internal_cookie_field->GetObject(java_dex_file)->AsLongArray()); + art::ObjPtr<art::mirror::LongArray> orig_cookie( + cookie_field->GetObject(java_dex_file)->AsLongArray()); + internal_cookie_field->SetObject<false>(java_dex_file, new_cookie); + *original_cookie = orig_internal_cookie; + if (!orig_cookie.IsNull()) { + cookie_field->SetObject<false>(java_dex_file, new_cookie); + } + return true; +} + +// This function does all (java) allocations we need to do for the Class being redefined. +// TODO Change this name maybe? +bool Redefiner::EnsureClassAllocationsFinished() { + art::StackHandleScope<2> hs(self_); + art::Handle<art::mirror::Class> klass(hs.NewHandle(self_->DecodeJObject(klass_)->AsClass())); + if (klass.Get() == nullptr) { + RecordFailure(ERR(INVALID_CLASS), "Unable to decode class argument!"); + return false; + } + // Allocate the classExt + art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(self_))); + if (ext.Get() == nullptr) { + // No memory. Clear exception (it's not useful) and return error. + // TODO This doesn't need to be fatal. We could just not support obsolete methods after hitting + // this case. + self_->AssertPendingOOMException(); + self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate ClassExt"); + return false; + } + // Allocate the 2 arrays that make up the obsolete methods map. Since the contents of the arrays + // are only modified when all threads (other than the modifying one) are suspended we don't need + // to worry about missing the unsyncronized writes to the array. We do synchronize when setting it + // however, since that can happen at any time. + // TODO Clear these after we walk the stacks in order to free them in the (likely?) event there + // are no obsolete methods. + { + art::ObjectLock<art::mirror::ClassExt> lock(self_, ext); + if (!ext->ExtendObsoleteArrays( + self_, klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize).size())) { + // OOM. Clear exception and return error. + self_->AssertPendingOOMException(); + self_->ClearException(); + RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate/extend obsolete methods map"); + return false; + } + } + return true; +} + +} // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h new file mode 100644 index 0000000000..f3a583478b --- /dev/null +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -0,0 +1,168 @@ +/* Copyright (C) 2016 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_REDEFINE_H_ +#define ART_RUNTIME_OPENJDKJVMTI_TI_REDEFINE_H_ + +#include <string> + +#include <jni.h> + +#include "art_jvmti.h" +#include "art_method.h" +#include "class_linker.h" +#include "dex_file.h" +#include "gc_root-inl.h" +#include "globals.h" +#include "jni_env_ext-inl.h" +#include "jvmti.h" +#include "linear_alloc.h" +#include "mem_map.h" +#include "mirror/array-inl.h" +#include "mirror/array.h" +#include "mirror/class-inl.h" +#include "mirror/class.h" +#include "mirror/class_loader-inl.h" +#include "mirror/string-inl.h" +#include "oat_file.h" +#include "obj_ptr.h" +#include "scoped_thread_state_change-inl.h" +#include "stack.h" +#include "thread_list.h" +#include "transform.h" +#include "utf.h" +#include "utils/dex_cache_arrays_layout-inl.h" + +namespace openjdkjvmti { + +// Class that can redefine a single class's methods. +class Redefiner { + public: + // Redefine the given class with the given dex data. Note this function does not take ownership of + // the dex_data pointer. It is not used after this call however and may be freed if desired. + // The caller is responsible for freeing it. The runtime makes it's own copy of the data. + static jvmtiError RedefineClass(ArtJvmTiEnv* env, + art::Runtime* runtime, + art::Thread* self, + jclass klass, + const std::string& original_dex_location, + jint data_len, + unsigned char* dex_data, + std::string* error_msg); + + private: + jvmtiError result_; + art::Runtime* runtime_; + art::Thread* self_; + // Kept as a jclass since we have weird run-state changes that make keeping it around as a + // mirror::Class difficult and confusing. + jclass klass_; + std::unique_ptr<const art::DexFile> dex_file_; + std::string* error_msg_; + char* class_sig_; + + // TODO Maybe change jclass to a mirror::Class + Redefiner(art::Runtime* runtime, + art::Thread* self, + jclass klass, + char* class_sig, + std::unique_ptr<const art::DexFile>& redefined_dex_file, + std::string* error_msg) + : result_(ERR(INTERNAL)), + runtime_(runtime), + self_(self), + klass_(klass), + dex_file_(std::move(redefined_dex_file)), + error_msg_(error_msg), + class_sig_(class_sig) { } + + static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location, + jint data_len, + unsigned char* dex_data, + std::string* error_msg); + + // TODO Put on all the lock qualifiers. + jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_); + + bool FinishRemainingAllocations( + /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader, + /*out*/art::MutableHandle<art::mirror::Object>* source_dex_file_obj, + /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie, + /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + // Preallocates all needed allocations in klass so that we can pause execution safely. + // TODO We should be able to free the arrays if they end up not being used. Investigate doing this + // in the future. For now we will just take the memory hit. + bool EnsureClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_); + + art::mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(art::Locks::mutator_lock_); + + // This finds the java.lang.DexFile we will add the native DexFile to as part of the classpath. + // TODO Make sure the DexFile object returned is the one that the klass_ actually comes from. + art::mirror::Object* FindSourceDexFileObject(art::Handle<art::mirror::ClassLoader> loader) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + art::mirror::Class* GetMirrorClass() REQUIRES_SHARED(art::Locks::mutator_lock_); + + // Allocates and fills the new DexFileCookie + art::mirror::LongArray* AllocateDexFileCookie(art::Handle<art::mirror::Object> java_dex_file_obj) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + art::mirror::DexCache* CreateNewDexCache(art::Handle<art::mirror::ClassLoader> loader) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + void RecordFailure(jvmtiError result, const std::string& error_msg); + + // TODO Actually write this. + // This will check that no constraints are violated (more than 1 class in dex file, any changes in + // number/declaration of methods & fields, changes in access flags, etc.) + bool EnsureRedefinitionIsValid() { + return true; + } + + bool UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, + art::ObjPtr<art::mirror::LongArray> new_cookie, + /*out*/art::ObjPtr<art::mirror::LongArray>* original_cookie) + REQUIRES(art::Locks::mutator_lock_); + + void RestoreJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file, + art::ObjPtr<art::mirror::LongArray> original_cookie) + REQUIRES(art::Locks::mutator_lock_); + + bool UpdateClass(art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache) + REQUIRES(art::Locks::mutator_lock_); +}; + +} // namespace openjdkjvmti + +#endif // ART_RUNTIME_OPENJDKJVMTI_TI_REDEFINE_H_ diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc index 7bb5205f05..f7b8b92a75 100644 --- a/runtime/openjdkjvmti/transform.cc +++ b/runtime/openjdkjvmti/transform.cc @@ -29,8 +29,12 @@ * questions. */ +#include <unordered_map> +#include <unordered_set> + #include "transform.h" +#include "art_method.h" #include "class_linker.h" #include "dex_file.h" #include "dex_file_types.h" @@ -46,6 +50,7 @@ #include "mirror/string-inl.h" #include "oat_file.h" #include "scoped_thread_state_change-inl.h" +#include "stack.h" #include "thread_list.h" #include "transform.h" #include "utf.h" @@ -53,196 +58,7 @@ namespace openjdkjvmti { -static bool ReadChecksum(jint data_len, const unsigned char* dex, /*out*/uint32_t* res) { - if (data_len < static_cast<jint>(sizeof(art::DexFile::Header))) { - return false; - } - *res = reinterpret_cast<const art::DexFile::Header*>(dex)->checksum_; - return true; -} - -static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location, - jint data_len, - unsigned char* dex_data) { - std::string error_msg; - std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous( - art::StringPrintf("%s-transformed", original_location.c_str()).c_str(), - nullptr, - data_len, - PROT_READ|PROT_WRITE, - /*low_4gb*/false, - /*reuse*/false, - &error_msg)); - if (map == nullptr) { - return map; - } - memcpy(map->Begin(), dex_data, data_len); - map->Protect(PROT_READ); - return map; -} - -static void InvalidateExistingMethods(art::Thread* self, - art::Handle<art::mirror::Class> klass, - art::Handle<art::mirror::DexCache> cache, - const art::DexFile* dex_file) - REQUIRES_SHARED(art::Locks::mutator_lock_) { - // Create new DexCache with new DexFile. - // reset dex_class_def_idx_ - // for each method reset entry_point_from_quick_compiled_code_ to bridge - // for each method reset dex_code_item_offset_ - // for each method reset dex_method_index_ - // for each method set dex_cache_resolved_methods_ to new DexCache - // for each method set dex_cache_resolved_types_ to new DexCache - auto* runtime = art::Runtime::Current(); - art::ClassLinker* linker = runtime->GetClassLinker(); - art::PointerSize image_pointer_size = linker->GetImagePointerSize(); - std::string descriptor_storage; - const char* descriptor = klass->GetDescriptor(&descriptor_storage); - // Get the new class def - const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef( - *dex_file, descriptor, art::ComputeModifiedUtf8Hash(descriptor)); - CHECK(class_def != nullptr); - const art::DexFile::TypeId& declaring_class_id = dex_file->GetTypeId(class_def->class_idx_); - art::StackHandleScope<6> hs(self); - const art::DexFile& old_dex_file = klass->GetDexFile(); - for (art::ArtMethod& method : klass->GetMethods(image_pointer_size)) { - // Find the code_item for the method then find the dex_method_index and dex_code_item_offset to - // set. - const art::DexFile::StringId* new_name_id = dex_file->FindStringId(method.GetName()); - art::dex::TypeIndex method_return_idx = - dex_file->GetIndexForTypeId(*dex_file->FindTypeId(method.GetReturnTypeDescriptor())); - const auto* old_type_list = method.GetParameterTypeList(); - std::vector<art::dex::TypeIndex> new_type_list; - for (uint32_t i = 0; old_type_list != nullptr && i < old_type_list->Size(); i++) { - new_type_list.push_back( - dex_file->GetIndexForTypeId( - *dex_file->FindTypeId( - old_dex_file.GetTypeDescriptor( - old_dex_file.GetTypeId( - old_type_list->GetTypeItem(i).type_idx_))))); - } - const art::DexFile::ProtoId* proto_id = dex_file->FindProtoId(method_return_idx, - new_type_list); - CHECK(proto_id != nullptr || old_type_list == nullptr); - const art::DexFile::MethodId* method_id = dex_file->FindMethodId(declaring_class_id, - *new_name_id, - *proto_id); - CHECK(method_id != nullptr); - uint32_t dex_method_idx = dex_file->GetIndexForMethodId(*method_id); - method.SetDexMethodIndex(dex_method_idx); - linker->SetEntryPointsToInterpreter(&method); - method.SetCodeItemOffset(dex_file->FindCodeItemOffset(*class_def, dex_method_idx)); - method.SetDexCacheResolvedMethods(cache->GetResolvedMethods(), image_pointer_size); - method.SetDexCacheResolvedTypes(cache->GetResolvedTypes(), image_pointer_size); - } - - // Update the class fields. - // Need to update class last since the ArtMethod gets its DexFile from the class (which is needed - // to call GetReturnTypeDescriptor and GetParameterTypeList above). - klass->SetDexCache(cache.Get()); - klass->SetDexCacheStrings(cache->GetStrings()); - klass->SetDexClassDefIndex(dex_file->GetIndexForClassDef(*class_def)); - klass->SetDexTypeIndex(dex_file->GetIndexForTypeId(*dex_file->FindTypeId(descriptor))); -} - -// Adds the dex file. -static art::mirror::LongArray* InsertDexFileIntoArray(art::Thread* self, - const art::DexFile* dex, - art::Handle<art::mirror::LongArray>& orig) - REQUIRES_SHARED(art::Locks::mutator_lock_) { - art::StackHandleScope<1> hs(self); - CHECK_GE(orig->GetLength(), 1); - art::Handle<art::mirror::LongArray> ret( - hs.NewHandle(art::mirror::LongArray::Alloc(self, orig->GetLength() + 1))); - CHECK(ret.Get() != nullptr); - // Copy the oat-dex. - // TODO Should I clear the oatdex element? - ret->SetWithoutChecks<false>(0, orig->GetWithoutChecks(0)); - ret->SetWithoutChecks<false>(1, static_cast<int64_t>(reinterpret_cast<intptr_t>(dex))); - ret->Memcpy(2, orig.Get(), 1, orig->GetLength() - 1); - return ret.Get(); -} - -// TODO Handle all types of class loaders. -static bool FindDalvikSystemDexFileAndLoaderForClass( - art::Handle<art::mirror::Class> klass, - /*out*/art::mirror::Object** dex_file, - /*out*/art::mirror::ClassLoader** loader) - REQUIRES_SHARED(art::Locks::mutator_lock_) { - const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;"; - const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;"; - const char* dex_file_name = "Ldalvik/system/DexFile;"; - const char* dex_path_list_name = "Ldalvik/system/DexPathList;"; - const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;"; - - art::Thread* self = art::Thread::Current(); - CHECK(!self->IsExceptionPending()); - art::StackHandleScope<11> hs(self); - art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker(); - - art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>( - nullptr)); - art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass( - self, dex_class_loader_name, null_loader))); - - art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField( - "pathList", dex_path_list_name); - CHECK(path_list_field != nullptr); - - art::ArtField* dex_path_list_element_field = - class_linker->FindClass(self, dex_path_list_name, null_loader) - ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name); - CHECK(dex_path_list_element_field != nullptr); - - art::ArtField* element_dex_file_field = - class_linker->FindClass(self, dex_path_list_element_name, null_loader) - ->FindDeclaredInstanceField("dexFile", dex_file_name); - CHECK(element_dex_file_field != nullptr); - - art::Handle<art::mirror::ClassLoader> h_class_loader(hs.NewHandle(klass->GetClassLoader())); - art::Handle<art::mirror::Class> loader_class(hs.NewHandle(h_class_loader->GetClass())); - // Check if loader is a BaseDexClassLoader - if (!loader_class->IsSubClass(base_dex_loader_class.Get())) { - LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only " - << "supported class loader type!"; - return false; - } - art::Handle<art::mirror::Object> path_list( - hs.NewHandle(path_list_field->GetObject(h_class_loader.Get()))); - CHECK(path_list.Get() != nullptr); - CHECK(!self->IsExceptionPending()); - art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle( - dex_path_list_element_field->GetObject(path_list.Get())-> - AsObjectArray<art::mirror::Object>())); - CHECK(!self->IsExceptionPending()); - CHECK(dex_elements_list.Get() != nullptr); - size_t num_elements = dex_elements_list->GetLength(); - art::MutableHandle<art::mirror::Object> current_element( - hs.NewHandle<art::mirror::Object>(nullptr)); - art::MutableHandle<art::mirror::Object> first_dex_file( - hs.NewHandle<art::mirror::Object>(nullptr)); - for (size_t i = 0; i < num_elements; i++) { - current_element.Assign(dex_elements_list->Get(i)); - CHECK(current_element.Get() != nullptr); - CHECK(!self->IsExceptionPending()); - CHECK(dex_elements_list.Get() != nullptr); - CHECK_EQ(current_element->GetClass(), class_linker->FindClass(self, - dex_path_list_element_name, - null_loader)); - // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class - // comes from but it is more annoying because we would need to find this class. It is not - // necessary for proper function since we just need to be in front of the classes old dex file - // in the path. - first_dex_file.Assign(element_dex_file_field->GetObject(current_element.Get())); - if (first_dex_file.Get() != nullptr) { - *dex_file = first_dex_file.Get(); - *loader = h_class_loader.Get(); - return true; - } - } - return false; -} - +// TODO Move this function somewhere more appropriate. // Gets the data surrounding the given class. jvmtiError GetTransformationData(ArtJvmTiEnv* env, jclass klass, @@ -281,83 +97,4 @@ jvmtiError GetTransformationData(ArtJvmTiEnv* env, return OK; } -// Install the new dex file. -// TODO do error checks for bad state (method in a stack, changes to number of methods/fields/etc). -jvmtiError MoveTransformedFileIntoRuntime(jclass jklass, - const std::string& original_location, - jint data_len, - unsigned char* dex_data) { - const char* dex_file_name = "Ldalvik/system/DexFile;"; - art::Thread* self = art::Thread::Current(); - art::Runtime* runtime = art::Runtime::Current(); - art::ThreadList* threads = runtime->GetThreadList(); - art::ClassLinker* class_linker = runtime->GetClassLinker(); - uint32_t checksum = 0; - if (!ReadChecksum(data_len, dex_data, &checksum)) { - return ERR(INVALID_CLASS_FORMAT); - } - - std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_location, data_len, dex_data)); - if (map.get() == nullptr) { - return ERR(INTERNAL); - } - std::string error_msg; - // Load the new dex_data in memory (mmap it, etc) - std::unique_ptr<const art::DexFile> new_dex_file = art::DexFile::Open(map->GetName(), - checksum, - std::move(map), - /*verify*/ true, - /*verify_checksum*/ true, - &error_msg); - CHECK(new_dex_file.get() != nullptr) << "Unable to load dex file! " << error_msg; - - // Get mutator lock. We need the lifetimes of these variables (hs, the classes, etc.) to be longer - // then current lock (since there isn't upgrading of the lock) so we don't use soa. - art::ThreadState old_state = self->TransitionFromSuspendedToRunnable(); - // This scope is needed to make sure that the HandleScope dies with mutator_lock_ since we need to - // upgrade the mutator_lock during the execution. - { - art::StackHandleScope<11> hs(self); - art::Handle<art::mirror::ClassLoader> null_loader( - hs.NewHandle<art::mirror::ClassLoader>(nullptr)); - CHECK(null_loader.Get() == nullptr); - art::ArtField* dex_file_cookie_field = class_linker-> - FindClass(self, dex_file_name, null_loader)-> - FindDeclaredInstanceField("mCookie", "Ljava/lang/Object;"); - art::ArtField* dex_file_internal_cookie_field = - class_linker->FindClass(self, dex_file_name, null_loader) - ->FindDeclaredInstanceField("mInternalCookie", "Ljava/lang/Object;"); - CHECK(dex_file_cookie_field != nullptr); - art::Handle<art::mirror::Class> klass(hs.NewHandle(self->DecodeJObject(jklass)->AsClass())); - art::mirror::Object* dex_file_ptr = nullptr; - art::mirror::ClassLoader* class_loader_ptr = nullptr; - // Find dalvik.system.DexFile that represents the dex file we are changing. - if (!FindDalvikSystemDexFileAndLoaderForClass(klass, &dex_file_ptr, &class_loader_ptr)) { - self->TransitionFromRunnableToSuspended(old_state); - LOG(ERROR) << "Could not find DexFile."; - return ERR(INTERNAL); - } - art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(dex_file_ptr)); - art::Handle<art::mirror::ClassLoader> class_loader(hs.NewHandle(class_loader_ptr)); - art::Handle<art::mirror::LongArray> art_dex_array( - hs.NewHandle<art::mirror::LongArray>( - dex_file_cookie_field->GetObject(dex_file_obj.Get())->AsLongArray())); - art::Handle<art::mirror::LongArray> new_art_dex_array( - hs.NewHandle<art::mirror::LongArray>( - InsertDexFileIntoArray(self, new_dex_file.get(), art_dex_array))); - art::Handle<art::mirror::DexCache> cache( - hs.NewHandle(class_linker->RegisterDexFile(*new_dex_file.get(), class_loader.Get()))); - self->TransitionFromRunnableToSuspended(old_state); - - threads->SuspendAll("moving dex file into runtime", /*long_suspend*/true); - // Change the mCookie field. Old value will be GC'd as normal. - dex_file_cookie_field->SetObject<false>(dex_file_obj.Get(), new_art_dex_array.Get()); - dex_file_internal_cookie_field->SetObject<false>(dex_file_obj.Get(), new_art_dex_array.Get()); - // Invalidate existing methods. - InvalidateExistingMethods(self, klass, cache, new_dex_file.release()); - } - threads->ResumeAll(); - return OK; -} - } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/transform.h b/runtime/openjdkjvmti/transform.h index a76ed939b5..35b990b976 100644 --- a/runtime/openjdkjvmti/transform.h +++ b/runtime/openjdkjvmti/transform.h @@ -52,12 +52,6 @@ jvmtiError GetTransformationData(ArtJvmTiEnv* env, /*out*/jint* data_len, /*out*/unsigned char** dex_data); -// Install the new dex file. -jvmtiError MoveTransformedFileIntoRuntime(jclass jklass, - const std::string& original_location, - jint data_len, - unsigned char* dex_data); - } // namespace openjdkjvmti #endif // ART_RUNTIME_OPENJDKJVMTI_TRANSFORM_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index ee4d669fe0..68b956b17e 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1782,6 +1782,9 @@ void Runtime::DisallowNewSystemWeaks() { intern_table_->ChangeWeakRootState(gc::kWeakRootStateNoReadsOrWrites); java_vm_->DisallowNewWeakGlobals(); heap_->DisallowNewAllocationRecords(); + if (GetJit() != nullptr) { + GetJit()->GetCodeCache()->DisallowInlineCacheAccess(); + } // All other generic system-weak holders. for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) { @@ -1795,6 +1798,9 @@ void Runtime::AllowNewSystemWeaks() { intern_table_->ChangeWeakRootState(gc::kWeakRootStateNormal); // TODO: Do this in the sweeping. java_vm_->AllowNewWeakGlobals(); heap_->AllowNewAllocationRecords(); + if (GetJit() != nullptr) { + GetJit()->GetCodeCache()->AllowInlineCacheAccess(); + } // All other generic system-weak holders. for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) { @@ -1810,6 +1816,9 @@ void Runtime::BroadcastForNewSystemWeaks(bool broadcast_for_checkpoint) { intern_table_->BroadcastForNewInterns(); java_vm_->BroadcastForNewWeakGlobals(); heap_->BroadcastForNewAllocationRecords(); + if (GetJit() != nullptr) { + GetJit()->GetCodeCache()->BroadcastForInlineCacheAccess(); + } // All other generic system-weak holders. for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) { @@ -2023,7 +2032,8 @@ void Runtime::RecordWeakStringRemoval(ObjPtr<mirror::String> s) const { preinitialization_transaction_->RecordWeakStringRemoval(s); } -void Runtime::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) const { +void Runtime::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, + dex::StringIndex string_idx) const { DCHECK(IsAotCompiler()); DCHECK(IsActiveTransaction()); preinitialization_transaction_->RecordResolveString(dex_cache, string_idx); diff --git a/runtime/runtime.h b/runtime/runtime.h index de5a356622..4f31887b72 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -28,6 +28,7 @@ #include "arch/instruction_set.h" #include "base/macros.h" +#include "dex_file_types.h" #include "experimental_flags.h" #include "gc_root.h" #include "instrumentation.h" @@ -520,7 +521,7 @@ class Runtime { REQUIRES(Locks::intern_table_lock_); void RecordWeakStringRemoval(ObjPtr<mirror::String> s) const REQUIRES(Locks::intern_table_lock_); - void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) const + void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx) const REQUIRES_SHARED(Locks::mutator_lock_); void SetFaultMessage(const std::string& message) REQUIRES(!fault_message_lock_); diff --git a/runtime/stack.h b/runtime/stack.h index 992bda5a1e..e879214c75 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -755,10 +755,6 @@ class StackVisitor { return cur_shadow_frame_; } - bool IsCurrentFrameInInterpreter() const { - return cur_shadow_frame_ != nullptr; - } - HandleScope* GetCurrentHandleScope(size_t pointer_size) const { ArtMethod** sp = GetCurrentQuickFrame(); // Skip ArtMethod*; handle scope comes next; diff --git a/runtime/string_reference.h b/runtime/string_reference.h index c75c218cd5..0fc06e6389 100644 --- a/runtime/string_reference.h +++ b/runtime/string_reference.h @@ -21,20 +21,22 @@ #include "base/logging.h" #include "dex_file-inl.h" +#include "dex_file_types.h" #include "utf-inl.h" namespace art { // A string is located by its DexFile and the string_ids_ table index into that DexFile. struct StringReference { - StringReference(const DexFile* file, uint32_t index) : dex_file(file), string_index(index) { } + StringReference(const DexFile* file, dex::StringIndex index) + : dex_file(file), string_index(index) { } const char* GetStringData() const { return dex_file->GetStringData(dex_file->GetStringId(string_index)); } const DexFile* dex_file; - uint32_t string_index; + dex::StringIndex string_index; }; // Compare only the reference and not the string contents. diff --git a/runtime/thread.cc b/runtime/thread.cc index c92e38b6e8..1283cf0e96 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -122,10 +122,7 @@ void Thread::SetIsGcMarkingAndUpdateEntrypoints(bool is_marking) { CHECK(kUseReadBarrier); tls32_.is_gc_marking = is_marking; UpdateReadBarrierEntrypoints(&tlsPtr_.quick_entrypoints, is_marking); - if (kRuntimeISA == kX86_64) { - // Entrypoint switching is only implemented for X86_64. - ResetQuickAllocEntryPointsForThread(is_marking); - } + ResetQuickAllocEntryPointsForThread(is_marking); } void Thread::InitTlsEntryPoints() { @@ -140,8 +137,10 @@ void Thread::InitTlsEntryPoints() { } void Thread::ResetQuickAllocEntryPointsForThread(bool is_marking) { - // Entrypoint switching is currnetly only faster for X86_64 since other archs don't have TLAB - // fast path for non region space entrypoints. + if (kUseReadBarrier && kRuntimeISA != kX86_64) { + // Allocation entrypoint switching is currently only implemented for X86_64. + is_marking = true; + } ResetQuickAllocEntryPoints(&tlsPtr_.quick_entrypoints, is_marking); } diff --git a/runtime/transaction.cc b/runtime/transaction.cc index c5da5d2e7e..25369686fd 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -167,9 +167,10 @@ void Transaction::RecordWriteArray(mirror::Array* array, size_t index, uint64_t array_log.LogValue(index, value); } -void Transaction::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) { +void Transaction::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, + dex::StringIndex string_idx) { DCHECK(dex_cache != nullptr); - DCHECK_LT(string_idx, dex_cache->GetDexFile()->NumStringIds()); + DCHECK_LT(string_idx.index_, dex_cache->GetDexFile()->NumStringIds()); MutexLock mu(Thread::Current(), log_lock_); resolve_string_logs_.push_back(ResolveStringLog(dex_cache, string_idx)); } @@ -510,11 +511,11 @@ void Transaction::ResolveStringLog::Undo() { } Transaction::ResolveStringLog::ResolveStringLog(ObjPtr<mirror::DexCache> dex_cache, - uint32_t string_idx) + dex::StringIndex string_idx) : dex_cache_(dex_cache), string_idx_(string_idx) { DCHECK(dex_cache != nullptr); - DCHECK_LT(string_idx_, dex_cache->GetDexFile()->NumStringIds()); + DCHECK_LT(string_idx_.index_, dex_cache->GetDexFile()->NumStringIds()); } void Transaction::ResolveStringLog::VisitRoots(RootVisitor* visitor) { diff --git a/runtime/transaction.h b/runtime/transaction.h index 2ec2f506fa..1774657d40 100644 --- a/runtime/transaction.h +++ b/runtime/transaction.h @@ -20,6 +20,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "base/value_object.h" +#include "dex_file_types.h" #include "gc_root.h" #include "object_callbacks.h" #include "offsets.h" @@ -97,7 +98,7 @@ class Transaction FINAL { REQUIRES(!log_lock_); // Record resolve string. - void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) + void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!log_lock_); @@ -197,7 +198,7 @@ class Transaction FINAL { class ResolveStringLog : public ValueObject { public: - ResolveStringLog(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx); + ResolveStringLog(ObjPtr<mirror::DexCache> dex_cache, dex::StringIndex string_idx); void Undo() REQUIRES_SHARED(Locks::mutator_lock_); @@ -205,7 +206,7 @@ class Transaction FINAL { private: GcRoot<mirror::DexCache> dex_cache_; - const uint32_t string_idx_; + const dex::StringIndex string_idx_; }; void LogInternedString(const InternStringLog& log) diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index 77c2b76bbb..a43c967092 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -26,8 +26,6 @@ namespace art { -static const size_t kDexNoIndex = DexFile::kDexNoIndex; // Make copy to prevent linking errors. - class TransactionTest : public CommonRuntimeTest { public: // Tests failing class initialization due to native call with transaction rollback. @@ -507,8 +505,8 @@ TEST_F(TransactionTest, ResolveString) { static const char* kResolvedString = "ResolvedString"; const DexFile::StringId* string_id = dex_file->FindStringId(kResolvedString); ASSERT_TRUE(string_id != nullptr); - uint32_t string_idx = dex_file->GetIndexForStringId(*string_id); - ASSERT_NE(string_idx, kDexNoIndex); + dex::StringIndex string_idx = dex_file->GetIndexForStringId(*string_id); + ASSERT_TRUE(string_idx.IsValid()); // String should only get resolved by the initializer. EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache) == nullptr); EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr); diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 01af5ec00a..f9bff23054 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -80,8 +80,8 @@ uint16_t VerifierDeps::GetAccessFlags(T* element) { } } -uint32_t VerifierDeps::GetClassDescriptorStringId(const DexFile& dex_file, - ObjPtr<mirror::Class> klass) { +dex::StringIndex VerifierDeps::GetClassDescriptorStringId(const DexFile& dex_file, + ObjPtr<mirror::Class> klass) { DCHECK(klass != nullptr); ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache(); // Array and proxy classes do not have a dex cache. @@ -104,9 +104,9 @@ uint32_t VerifierDeps::GetClassDescriptorStringId(const DexFile& dex_file, } // Try to find the string descriptor of the class. type_idx is a best guess of a matching string id. -static uint32_t TryGetClassDescriptorStringId(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr<mirror::Class> klass) +static dex::StringIndex TryGetClassDescriptorStringId(const DexFile& dex_file, + dex::TypeIndex type_idx, + ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { if (!klass->IsArrayClass()) { const DexFile::TypeId& type_id = dex_file.GetTypeId(type_idx); @@ -117,21 +117,21 @@ static uint32_t TryGetClassDescriptorStringId(const DexFile& dex_file, return type_id.descriptor_idx_; } } - return DexFile::kDexNoIndex; + return dex::StringIndex::Invalid(); } -uint32_t VerifierDeps::GetMethodDeclaringClassStringId(const DexFile& dex_file, - uint32_t dex_method_index, - ArtMethod* method) { +dex::StringIndex VerifierDeps::GetMethodDeclaringClassStringId(const DexFile& dex_file, + uint32_t dex_method_index, + ArtMethod* method) { static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant"); if (method == nullptr) { - return VerifierDeps::kUnresolvedMarker; + return dex::StringIndex(VerifierDeps::kUnresolvedMarker); } - const uint32_t string_id = TryGetClassDescriptorStringId( + const dex::StringIndex string_id = TryGetClassDescriptorStringId( dex_file, dex_file.GetMethodId(dex_method_index).class_idx_, method->GetDeclaringClass()); - if (string_id != DexFile::kDexNoIndex) { + if (string_id.IsValid()) { // Got lucky using the original dex file, return based on the input dex file. DCHECK_EQ(GetClassDescriptorStringId(dex_file, method->GetDeclaringClass()), string_id); return string_id; @@ -139,18 +139,18 @@ uint32_t VerifierDeps::GetMethodDeclaringClassStringId(const DexFile& dex_file, return GetClassDescriptorStringId(dex_file, method->GetDeclaringClass()); } -uint32_t VerifierDeps::GetFieldDeclaringClassStringId(const DexFile& dex_file, - uint32_t dex_field_idx, - ArtField* field) { +dex::StringIndex VerifierDeps::GetFieldDeclaringClassStringId(const DexFile& dex_file, + uint32_t dex_field_idx, + ArtField* field) { static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant"); if (field == nullptr) { - return VerifierDeps::kUnresolvedMarker; + return dex::StringIndex(VerifierDeps::kUnresolvedMarker); } - const uint32_t string_id = TryGetClassDescriptorStringId( + const dex::StringIndex string_id = TryGetClassDescriptorStringId( dex_file, dex_file.GetFieldId(dex_field_idx).class_idx_, field->GetDeclaringClass()); - if (string_id != DexFile::kDexNoIndex) { + if (string_id.IsValid()) { // Got lucky using the original dex file, return based on the input dex file. DCHECK_EQ(GetClassDescriptorStringId(dex_file, field->GetDeclaringClass()), string_id); return string_id; @@ -190,7 +190,7 @@ static bool FindExistingStringId(const std::vector<std::string>& strings, return false; } -uint32_t VerifierDeps::GetIdFromString(const DexFile& dex_file, const std::string& str) { +dex::StringIndex VerifierDeps::GetIdFromString(const DexFile& dex_file, const std::string& str) { const DexFile::StringId* string_id = dex_file.FindStringId(str.c_str()); if (string_id != nullptr) { // String is in the DEX file. Return its ID. @@ -212,32 +212,33 @@ uint32_t VerifierDeps::GetIdFromString(const DexFile& dex_file, const std::strin { ReaderMutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); if (FindExistingStringId(deps->strings_, str, &found_id)) { - return num_ids_in_dex + found_id; + return dex::StringIndex(num_ids_in_dex + found_id); } } { WriterMutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); if (FindExistingStringId(deps->strings_, str, &found_id)) { - return num_ids_in_dex + found_id; + return dex::StringIndex(num_ids_in_dex + found_id); } deps->strings_.push_back(str); - uint32_t new_id = num_ids_in_dex + deps->strings_.size() - 1; - CHECK_GE(new_id, num_ids_in_dex); // check for overflows + dex::StringIndex new_id(num_ids_in_dex + deps->strings_.size() - 1); + CHECK_GE(new_id.index_, num_ids_in_dex); // check for overflows DCHECK_EQ(str, singleton->GetStringFromId(dex_file, new_id)); return new_id; } } -std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, uint32_t string_id) const { +std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, dex::StringIndex string_id) + const { uint32_t num_ids_in_dex = dex_file.NumStringIds(); - if (string_id < num_ids_in_dex) { + if (string_id.index_ < num_ids_in_dex) { return std::string(dex_file.StringDataByIdx(string_id)); } else { const DexFileDeps* deps = GetDexFileDeps(dex_file); DCHECK(deps != nullptr); - string_id -= num_ids_in_dex; - CHECK_LT(string_id, deps->strings_.size()); - return deps->strings_[string_id]; + string_id.index_ -= num_ids_in_dex; + CHECK_LT(string_id.index_, deps->strings_.size()); + return deps->strings_[string_id.index_]; } } @@ -389,8 +390,8 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file, } // Get string IDs for both descriptors and store in the appropriate set. - uint32_t destination_id = GetClassDescriptorStringId(dex_file, destination); - uint32_t source_id = GetClassDescriptorStringId(dex_file, source); + dex::StringIndex destination_id = GetClassDescriptorStringId(dex_file, destination); + dex::StringIndex source_id = GetClassDescriptorStringId(dex_file, source); if (is_assignable) { dex_deps->assignable_types_.emplace(TypeAssignability(destination_id, source_id)); @@ -471,6 +472,9 @@ template<> inline uint32_t Encode<uint32_t>(uint32_t in) { template<> inline uint32_t Encode<dex::TypeIndex>(dex::TypeIndex in) { return in.index_; } +template<> inline uint32_t Encode<dex::StringIndex>(dex::StringIndex in) { + return in.index_; +} template<typename T> inline T Decode(uint32_t in); @@ -483,6 +487,9 @@ template<> inline uint32_t Decode<uint32_t>(uint32_t in) { template<> inline dex::TypeIndex Decode<dex::TypeIndex>(uint32_t in) { return dex::TypeIndex(in); } +template<> inline dex::StringIndex Decode<dex::StringIndex>(uint32_t in) { + return dex::StringIndex(in); +} template<typename T1, typename T2> static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2>& t) { @@ -508,7 +515,7 @@ template<typename T1, typename T2, typename T3> static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, std::tuple<T1, T2, T3>* t) { T1 v1 = Decode<T1>(DecodeUint32WithOverflowCheck(in, end)); T2 v2 = Decode<T2>(DecodeUint32WithOverflowCheck(in, end)); - T3 v3 = Decode<T2>(DecodeUint32WithOverflowCheck(in, end)); + T3 v3 = Decode<T3>(DecodeUint32WithOverflowCheck(in, end)); *t = std::make_tuple(v1, v2, v3); } diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index a12071b8b6..4b8206f5ce 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -129,41 +129,43 @@ class VerifierDeps { uint16_t GetAccessFlags() const { return std::get<1>(*this); } }; - using FieldResolutionBase = std::tuple<uint32_t, uint16_t, uint32_t>; + using FieldResolutionBase = std::tuple<uint32_t, uint16_t, dex::StringIndex>; struct FieldResolution : public FieldResolutionBase { FieldResolution() = default; FieldResolution(const FieldResolution&) = default; - FieldResolution(uint32_t field_idx, uint16_t access_flags, uint32_t declaring_class_idx) + FieldResolution(uint32_t field_idx, uint16_t access_flags, dex::StringIndex declaring_class_idx) : FieldResolutionBase(field_idx, access_flags, declaring_class_idx) {} bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; } uint32_t GetDexFieldIndex() const { return std::get<0>(*this); } uint16_t GetAccessFlags() const { return std::get<1>(*this); } - uint32_t GetDeclaringClassIndex() const { return std::get<2>(*this); } + dex::StringIndex GetDeclaringClassIndex() const { return std::get<2>(*this); } }; - using MethodResolutionBase = std::tuple<uint32_t, uint16_t, uint32_t>; + using MethodResolutionBase = std::tuple<uint32_t, uint16_t, dex::StringIndex>; struct MethodResolution : public MethodResolutionBase { MethodResolution() = default; MethodResolution(const MethodResolution&) = default; - MethodResolution(uint32_t method_idx, uint16_t access_flags, uint32_t declaring_class_idx) + MethodResolution(uint32_t method_idx, + uint16_t access_flags, + dex::StringIndex declaring_class_idx) : MethodResolutionBase(method_idx, access_flags, declaring_class_idx) {} bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; } uint32_t GetDexMethodIndex() const { return std::get<0>(*this); } uint16_t GetAccessFlags() const { return std::get<1>(*this); } - uint32_t GetDeclaringClassIndex() const { return std::get<2>(*this); } + dex::StringIndex GetDeclaringClassIndex() const { return std::get<2>(*this); } }; - using TypeAssignabilityBase = std::tuple<uint32_t, uint32_t>; + using TypeAssignabilityBase = std::tuple<dex::StringIndex, dex::StringIndex>; struct TypeAssignability : public TypeAssignabilityBase { TypeAssignability() = default; TypeAssignability(const TypeAssignability&) = default; - TypeAssignability(uint32_t destination_idx, uint32_t source_idx) + TypeAssignability(dex::StringIndex destination_idx, dex::StringIndex source_idx) : TypeAssignabilityBase(destination_idx, source_idx) {} - uint32_t GetDestination() const { return std::get<0>(*this); } - uint32_t GetSource() const { return std::get<1>(*this); } + dex::StringIndex GetDestination() const { return std::get<0>(*this); } + dex::StringIndex GetSource() const { return std::get<1>(*this); } }; // Data structure representing dependencies collected during verification of @@ -206,11 +208,11 @@ class VerifierDeps { // string ID. If not, an ID is assigned to the string and cached in `strings_` // of the corresponding DexFileDeps structure (either provided or inferred from // `dex_file`). - uint32_t GetIdFromString(const DexFile& dex_file, const std::string& str) + dex::StringIndex GetIdFromString(const DexFile& dex_file, const std::string& str) REQUIRES(!Locks::verifier_deps_lock_); // Returns the string represented by `id`. - std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id) const; + std::string GetStringFromId(const DexFile& dex_file, dex::StringIndex string_id) const; // Returns the bytecode access flags of `element` (bottom 16 bits), or // `kUnresolvedMarker` if `element` is null. @@ -220,17 +222,17 @@ class VerifierDeps { // Returns a string ID of the descriptor of the declaring class of `element`, // or `kUnresolvedMarker` if `element` is null. - uint32_t GetMethodDeclaringClassStringId(const DexFile& dex_file, - uint32_t dex_method_idx, - ArtMethod* method) + dex::StringIndex GetMethodDeclaringClassStringId(const DexFile& dex_file, + uint32_t dex_method_idx, + ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); - uint32_t GetFieldDeclaringClassStringId(const DexFile& dex_file, - uint32_t dex_field_idx, - ArtField* field) + dex::StringIndex GetFieldDeclaringClassStringId(const DexFile& dex_file, + uint32_t dex_field_idx, + ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_); // Returns a string ID of the descriptor of the class. - uint32_t GetClassDescriptorStringId(const DexFile& dex_file, ObjPtr<mirror::Class> klass) + dex::StringIndex GetClassDescriptorStringId(const DexFile& dex_file, ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); diff --git a/test/454-get-vreg/get_vreg_jni.cc b/test/454-get-vreg/get_vreg_jni.cc index 0360eda022..5fc5464d5f 100644 --- a/test/454-get-vreg/get_vreg_jni.cc +++ b/test/454-get-vreg/get_vreg_jni.cc @@ -46,12 +46,12 @@ class TestVisitor : public StackVisitor { CHECK_EQ(value, 42u); bool success = GetVReg(m, 1, kIntVReg, &value); - if (!IsCurrentFrameInInterpreter() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { + if (!IsShadowFrame() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { CHECK(!success); } success = GetVReg(m, 2, kIntVReg, &value); - if (!IsCurrentFrameInInterpreter() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { + if (!IsShadowFrame() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { CHECK(!success); } @@ -83,12 +83,12 @@ class TestVisitor : public StackVisitor { CHECK_EQ(value, 42u); bool success = GetVRegPair(m, 2, kLongLoVReg, kLongHiVReg, &value); - if (!IsCurrentFrameInInterpreter() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { + if (!IsShadowFrame() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { CHECK(!success); } success = GetVRegPair(m, 4, kLongLoVReg, kLongHiVReg, &value); - if (!IsCurrentFrameInInterpreter() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { + if (!IsShadowFrame() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { CHECK(!success); } diff --git a/test/457-regs/regs_jni.cc b/test/457-regs/regs_jni.cc index f62a77d793..f867bdf17c 100644 --- a/test/457-regs/regs_jni.cc +++ b/test/457-regs/regs_jni.cc @@ -64,7 +64,7 @@ class TestVisitor : public StackVisitor { CHECK_EQ(value, 1u); bool success = GetVReg(m, 2, kIntVReg, &value); - if (!IsCurrentFrameInInterpreter() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { + if (!IsShadowFrame() && GetCurrentOatQuickMethodHeader()->IsOptimized()) { CHECK(!success); } diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java index 6b25747463..0329e63ffd 100644 --- a/test/538-checker-embed-constants/src/Main.java +++ b/test/538-checker-embed-constants/src/Main.java @@ -105,7 +105,7 @@ public class Main { /// CHECK-NOT: and{{(\.w)?}} /// CHECK-NOT: bic{{(\.w)?}} /// CHECK-DAG: and {{r\d+}}, {{r\d+}}, #0xff - /// CHECK-DAG: movs {{r\d+}}, #0 + /// CHECK-DAG: mov{{s?}} {{r\d+}}, #0 /// CHECK-NOT: and{{(\.w)?}} /// CHECK-NOT: bic{{(\.w)?}} @@ -115,7 +115,7 @@ public class Main { /// CHECK-START-ARM: long Main.and511(long) disassembly (after) /// CHECK: mov {{r\d+}}, #511 - /// CHECK-NEXT: movs {{r\d+}}, #0 + /// CHECK-NEXT: mov{{s?}} {{r\d+}}, #0 /// CHECK-NOT: and{{(\.w)?}} /// CHECK-NOT: bic{{(\.w)?}} /// CHECK: and{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}} @@ -167,7 +167,7 @@ public class Main { /// CHECK-START-ARM: long Main.or511(long) disassembly (after) /// CHECK: mov {{r\d+}}, #511 - /// CHECK-NEXT: movs {{r\d+}}, #0 + /// CHECK-NEXT: mov{{s?}} {{r\d+}}, #0 /// CHECK-NOT: orr{{(\.w)?}} /// CHECK-NOT: orn /// CHECK: orr{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}} @@ -218,7 +218,7 @@ public class Main { /// CHECK-START-ARM: long Main.xor511(long) disassembly (after) /// CHECK: mov {{r\d+}}, #511 - /// CHECK-NEXT: movs {{r\d+}}, #0 + /// CHECK-NEXT: mov{{s?}} {{r\d+}}, #0 /// CHECK-NOT: eor{{(\.w)?}} /// CHECK: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}} /// CHECK-NEXT: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}} @@ -242,7 +242,7 @@ public class Main { // Note: No support for partial long constant embedding. /// CHECK-START-ARM: long Main.xor0xfffffff00000000f(long) disassembly (after) - /// CHECK-DAG: movs {{r\d+}}, #15 + /// CHECK-DAG: mov{{s?}} {{r\d+}}, #15 /// CHECK-DAG: mvn {{r\d+}}, #15 /// CHECK-NOT: eor{{(\.w)?}} /// CHECK-DAG: eor{{(\.w)?}} {{r\d+}}, {{r\d+}}, {{r\d+}} @@ -507,7 +507,7 @@ public class Main { /// CHECK: <<Arg:j\d+>> ParameterValue /// CHECK: <<ConstM1:j\d+>> LongConstant -1 /// CHECK: Add [<<Arg>>,<<ConstM1>>] - /// CHECK-NEXT: subs r{{\d+}}, #1 + /// CHECK-NEXT: {{adds|subs}} r{{\d+}}, #{{4294967295|1}} /// CHECK-NEXT: adc r{{\d+}}, r{{\d+}}, #4294967295 /// CHECK: Sub [<<Arg>>,<<ConstM1>>] /// CHECK-NEXT: adds r{{\d+}}, #1 diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc index 50e8382ff6..8eca6b2ccb 100644 --- a/test/570-checker-osr/osr.cc +++ b/test/570-checker-osr/osr.cc @@ -43,7 +43,7 @@ class OsrVisitor : public StackVisitor { Runtime::Current()->GetJit()->GetCodeCache()->LookupOsrMethodHeader(m); if (header != nullptr && header == GetCurrentOatQuickMethodHeader()) { in_osr_method_ = true; - } else if (IsCurrentFrameInInterpreter()) { + } else if (IsShadowFrame()) { in_interpreter_ = true; } return false; diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java index 34e82831a8..d965e3ffce 100644 --- a/test/624-checker-stringops/src/Main.java +++ b/test/624-checker-stringops/src/Main.java @@ -98,9 +98,170 @@ public class Main { return k; } + // + // Allows combining of returned "this". Also ensures that similar looking append() calls + // are not combined somehow through returned result. + // + /// CHECK-START: int Main.bufferLen2() instruction_simplifier (before) + /// CHECK-DAG: <<New:l\d+>> NewInstance + /// CHECK-DAG: <<String1:l\d+>> LoadString + /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBufferAppend + /// CHECK-DAG: <<String2:l\d+>> LoadString + /// CHECK-DAG: <<Null1:l\d+>> NullCheck [<<Append1>>] + /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<Null1>>,<<String2>>] intrinsic:StringBufferAppend + /// CHECK-DAG: <<Null2:l\d+>> NullCheck [<<Append2>>] + /// CHECK-DAG: InvokeVirtual [<<Null2>>] intrinsic:StringBufferLength + // + /// CHECK-START: int Main.bufferLen2() instruction_simplifier (after) + /// CHECK-DAG: <<New:l\d+>> NewInstance + /// CHECK-DAG: <<String1:l\d+>> LoadString + /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBufferAppend + /// CHECK-DAG: <<String2:l\d+>> LoadString + /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBufferAppend + /// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBufferLength + static int bufferLen2() { + StringBuffer s = new StringBuffer(); + return s.append("x").append("x").length(); + } + + // + // Allows combining of returned "this". Also ensures that similar looking append() calls + // are not combined somehow through returned result. + // + /// CHECK-START: int Main.builderLen2() instruction_simplifier (before) + /// CHECK-DAG: <<New:l\d+>> NewInstance + /// CHECK-DAG: <<String1:l\d+>> LoadString + /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBuilderAppend + /// CHECK-DAG: <<String2:l\d+>> LoadString + /// CHECK-DAG: <<Null2:l\d+>> NullCheck [<<Append1>>] + /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<Null2>>,<<String2>>] intrinsic:StringBuilderAppend + /// CHECK-DAG: <<Null3:l\d+>> NullCheck [<<Append2>>] + /// CHECK-DAG: InvokeVirtual [<<Null3>>] intrinsic:StringBuilderLength + // + /// CHECK-START: int Main.builderLen2() instruction_simplifier (after) + /// CHECK-DAG: <<New:l\d+>> NewInstance + /// CHECK-DAG: <<String1:l\d+>> LoadString + /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBuilderAppend + /// CHECK-DAG: <<String2:l\d+>> LoadString + /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBuilderAppend + /// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBuilderLength + static int builderLen2() { + StringBuilder s = new StringBuilder(); + return s.append("x").append("x").length(); + } + + // + // Similar situation in a loop. + // + /// CHECK-START: int Main.bufferLoopAppender() instruction_simplifier (before) + /// CHECK-DAG: <<New:l\d+>> NewInstance loop:none + /// CHECK-DAG: <<String1:l\d+>> LoadString loop:<<Loop:B\d+>> + /// CHECK-DAG: <<Null1:l\d+>> NullCheck [<<New>>] loop:<<Loop>> + /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<Null1>>,<<String1>>] intrinsic:StringBufferAppend loop:<<Loop>> + /// CHECK-DAG: <<String2:l\d+>> LoadString loop:<<Loop>> + /// CHECK-DAG: <<Null2:l\d+>> NullCheck [<<Append1>>] loop:<<Loop>> + /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<Null2>>,<<String2>>] intrinsic:StringBufferAppend loop:<<Loop>> + /// CHECK-DAG: <<String3:l\d+>> LoadString loop:<<Loop>> + /// CHECK-DAG: <<Null3:l\d+>> NullCheck [<<Append2>>] loop:<<Loop>> + /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [<<Null3>>,<<String3>>] intrinsic:StringBufferAppend loop:<<Loop>> + /// CHECK-DAG: <<Null4:l\d+>> NullCheck [<<New>>] loop:none + /// CHECK-DAG: InvokeVirtual [<<Null4>>] intrinsic:StringBufferLength loop:none + // + /// CHECK-START: int Main.bufferLoopAppender() instruction_simplifier (after) + /// CHECK-DAG: <<New:l\d+>> NewInstance loop:none + /// CHECK-DAG: <<String1:l\d+>> LoadString loop:<<Loop:B\d+>> + /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBufferAppend loop:<<Loop>> + /// CHECK-DAG: <<String2:l\d+>> LoadString loop:<<Loop>> + /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBufferAppend loop:<<Loop>> + /// CHECK-DAG: <<String3:l\d+>> LoadString loop:<<Loop>> + /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [<<New>>,<<String3>>] intrinsic:StringBufferAppend loop:<<Loop>> + /// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBufferLength loop:none + static int bufferLoopAppender() { + StringBuffer b = new StringBuffer(); + for (int i = 0; i < 10; i++) { + b.append("x").append("y").append("z"); + } + return b.length(); + } + + // + // Similar situation in a loop. + // + /// CHECK-START: int Main.builderLoopAppender() instruction_simplifier (before) + /// CHECK-DAG: <<New:l\d+>> NewInstance loop:none + /// CHECK-DAG: <<String1:l\d+>> LoadString loop:<<Loop:B\d+>> + /// CHECK-DAG: <<Null1:l\d+>> NullCheck [<<New>>] loop:<<Loop>> + /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<Null1>>,<<String1>>] intrinsic:StringBuilderAppend loop:<<Loop>> + /// CHECK-DAG: <<String2:l\d+>> LoadString loop:<<Loop>> + /// CHECK-DAG: <<Null2:l\d+>> NullCheck [<<Append1>>] loop:<<Loop>> + /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<Null2>>,<<String2>>] intrinsic:StringBuilderAppend loop:<<Loop>> + /// CHECK-DAG: <<String3:l\d+>> LoadString loop:<<Loop>> + /// CHECK-DAG: <<Null3:l\d+>> NullCheck [<<Append2>>] loop:<<Loop>> + /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [<<Null3>>,<<String3>>] intrinsic:StringBuilderAppend loop:<<Loop>> + /// CHECK-DAG: <<Null4:l\d+>> NullCheck [<<New>>] loop:none + /// CHECK-DAG: InvokeVirtual [<<Null4>>] intrinsic:StringBuilderLength loop:none + // + /// CHECK-START: int Main.builderLoopAppender() instruction_simplifier (after) + /// CHECK-DAG: <<New:l\d+>> NewInstance loop:none + /// CHECK-DAG: <<String1:l\d+>> LoadString loop:<<Loop:B\d+>> + /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBuilderAppend loop:<<Loop>> + /// CHECK-DAG: <<String2:l\d+>> LoadString loop:<<Loop>> + /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBuilderAppend loop:<<Loop>> + /// CHECK-DAG: <<String3:l\d+>> LoadString loop:<<Loop>> + /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [<<New>>,<<String3>>] intrinsic:StringBuilderAppend loop:<<Loop>> + /// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBuilderLength loop:none + static int builderLoopAppender() { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < 10; i++) { + b.append("x").append("y").append("z"); + } + return b.length(); + } + + // + // All calls in the loop-body and thus loop can be eliminated. + // + /// CHECK-START: int Main.bufferDeadLoop() instruction_simplifier (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> + /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString loop:<<Loop>> + /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:<<Loop>> + // + /// CHECK-START: int Main.bufferDeadLoop() loop_optimization (after) + /// CHECK-NOT: Phi + /// CHECK-NOT: InvokeVirtual intrinsic:StringBufferToString + /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter + static int bufferDeadLoop() { + StringBuffer b = new StringBuffer(); + for (int i = 0; i < 10; i++) { + int d = b.toString().indexOf("x", 1); + } + return b.length(); + } + + // + // All calls in the loop-body and thus loop can be eliminated. + // + /// CHECK-START: int Main.builderDeadLoop() instruction_simplifier (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> + /// CHECK-DAG: InvokeVirtual intrinsic:StringBuilderToString loop:<<Loop>> + /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:<<Loop>> + // + /// CHECK-START: int Main.builderDeadLoop() loop_optimization (after) + /// CHECK-NOT: Phi + /// CHECK-NOT: InvokeVirtual intrinsic:StringBuilderToString + /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter + static int builderDeadLoop() { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < 10; i++) { + int d = b.toString().indexOf("x", 1); + } + return b.length(); + } + public static void main(String[] args) { expectEquals(1865, liveIndexOf()); expectEquals(29, deadIndexOf()); + try { indexOfExceptions(null, XYZ); throw new Error("Expected: NPE"); @@ -113,6 +274,13 @@ public class Main { } expectEquals(598, indexOfExceptions(ABC, XYZ)); + expectEquals(2, bufferLen2()); + expectEquals(2, builderLen2()); + expectEquals(30, bufferLoopAppender()); + expectEquals(30, builderLoopAppender()); + expectEquals(0, bufferDeadLoop()); + expectEquals(0, builderDeadLoop()); + System.out.println("passed"); } diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc new file mode 100644 index 0000000000..b035896166 --- /dev/null +++ b/test/626-const-class-linking/clear_dex_cache_types.cc @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni.h" +#include "object_lock.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_nativeClearResolvedTypes(JNIEnv*, jclass, jclass cls) { + ScopedObjectAccess soa(Thread::Current()); + mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache(); + for (size_t i = 0, num_types = dex_cache->NumResolvedTypes(); i != num_types; ++i) { + dex_cache->SetResolvedType(dex::TypeIndex(i), ObjPtr<mirror::Class>(nullptr)); + } +} + +extern "C" JNIEXPORT void JNICALL Java_Main_nativeSkipVerification(JNIEnv*, jclass, jclass cls) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::Class> klass = hs.NewHandle(soa.Decode<mirror::Class>(cls)); + mirror::Class::Status status = klass->GetStatus(); + if (status == mirror::Class::kStatusResolved) { + ObjectLock<mirror::Class> lock(soa.Self(), klass); + klass->SetStatus(klass, mirror::Class::kStatusVerified, soa.Self()); + } else { + LOG(ERROR) << klass->PrettyClass() << " has unexpected status: " << status; + } +} + +extern "C" JNIEXPORT void JNICALL Java_Main_nativeDumpClasses(JNIEnv*, jclass, jobjectArray array) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ObjectArray<mirror::Object>> classes = + hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(array)); + CHECK(classes.Get() != nullptr); + for (size_t i = 0, length = classes->GetLength(); i != length; ++i) { + CHECK(classes->Get(i) != nullptr) << i; + CHECK(classes->Get(i)->IsClass()) + << i << " " << classes->Get(i)->GetClass()->PrettyDescriptor(); + mirror::Class* as_class = classes->Get(i)->AsClass(); + mirror::ClassLoader* loader = as_class->GetClassLoader(); + LOG(ERROR) << "Class #" << i << ": " << as_class->PrettyDescriptor() + << " @" << static_cast<const void*>(as_class) + << " status:" << as_class->GetStatus() + << " definingLoader:" << static_cast<const void*>(loader) + << " definingLoaderClass:" + << (loader != nullptr ? loader->GetClass()->PrettyDescriptor() : "N/A"); + } +} + +} // namespace art diff --git a/test/626-const-class-linking/expected.txt b/test/626-const-class-linking/expected.txt new file mode 100644 index 0000000000..de1b8152ee --- /dev/null +++ b/test/626-const-class-linking/expected.txt @@ -0,0 +1,61 @@ +JNI_OnLoad called +first: Helper1 class loader: DelegatingLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: DelegatingLoader +second: Test class loader: DefiningLoader +testClearDexCache done +first: Helper1 class loader: DelegatingLoader +second: Test class loader: DefiningLoader +first: Helper2 class loader: DelegatingLoader +second: Test class loader: DefiningLoader +testMultiDex done +first: Helper1 class loader: RacyLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyLoader +second: Test class loader: DefiningLoader +total: 4 + throwables: 0 + classes: 4 (1 unique) +testRacyLoader done +first: Helper1 class loader: RacyLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyLoader +second: Test class loader: DefiningLoader +first: Helper3 class loader: RacyLoader +second: Test3 class loader: DefiningLoader +first: Helper3 class loader: RacyLoader +second: Test3 class loader: DefiningLoader +total: 4 + throwables: 0 + classes: 4 (2 unique) +testRacyLoader2 done +java.lang.NoClassDefFoundError: Initiating class loader of type MisbehavingLoader returned class Helper2 instead of Test. +testMisbehavingLoader done +first: Helper1 class loader: RacyMisbehavingLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyMisbehavingLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyMisbehavingLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyMisbehavingLoader +second: Test class loader: DefiningLoader +total: 4 + throwables: 0 + classes: 4 (1 unique) +testRacyMisbehavingLoader done +first: Helper1 class loader: RacyMisbehavingLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyMisbehavingLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyMisbehavingLoader +second: Test class loader: DefiningLoader +first: Helper1 class loader: RacyMisbehavingLoader +second: Test class loader: DefiningLoader +total: 4 + throwables: 0 + classes: 4 (1 unique) +testRacyMisbehavingLoader2 done diff --git a/test/626-const-class-linking/info.txt b/test/626-const-class-linking/info.txt new file mode 100644 index 0000000000..9c19a46659 --- /dev/null +++ b/test/626-const-class-linking/info.txt @@ -0,0 +1,3 @@ +Test that once a const-class instruction is linked, it will keep referring +to the same class even in the presence of custom class loaders even after +clearing the dex cache type array. diff --git a/test/626-const-class-linking/multidex.jpp b/test/626-const-class-linking/multidex.jpp new file mode 100644 index 0000000000..c7a66488c0 --- /dev/null +++ b/test/626-const-class-linking/multidex.jpp @@ -0,0 +1,27 @@ +ClassPair: + @@com.android.jack.annotations.ForceInMainDex + class ClassPair +DefiningLoader: + @@com.android.jack.annotations.ForceInMainDex + class DefiningLoader +DelegatingLoader: + @@com.android.jack.annotations.ForceInMainDex + class DelegatingLoader +Helper1: + @@com.android.jack.annotations.ForceInMainDex + class Helper1 +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main +MisbehavingLoader: + @@com.android.jack.annotations.ForceInMainDex + class MisbehavingLoader +RacyLoader: + @@com.android.jack.annotations.ForceInMainDex + class RacyLoader +RacyMisbehavingHelper: + @@com.android.jack.annotations.ForceInMainDex + class RacyMisbehavingHelper +RacyMisbehavingLoader: + @@com.android.jack.annotations.ForceInMainDex + class RacyMisbehavingLoader diff --git a/test/626-const-class-linking/src-multidex/Helper2.java b/test/626-const-class-linking/src-multidex/Helper2.java new file mode 100644 index 0000000000..5bb31eeb17 --- /dev/null +++ b/test/626-const-class-linking/src-multidex/Helper2.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Helper2 { + public static ClassPair get() { + Class<?> helper2_class = Helper2.class; + Class<?> test_class = Test.class; + return new ClassPair(helper2_class, test_class); + } +} diff --git a/test/626-const-class-linking/src-multidex/Helper3.java b/test/626-const-class-linking/src-multidex/Helper3.java new file mode 100644 index 0000000000..af996de2a7 --- /dev/null +++ b/test/626-const-class-linking/src-multidex/Helper3.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Helper3 { + public static ClassPair get() { + Class<?> helper3_class = Helper3.class; + Class<?> test3_class = Test3.class; + return new ClassPair(helper3_class, test3_class); + } +} diff --git a/test/626-const-class-linking/src-multidex/Test.java b/test/626-const-class-linking/src-multidex/Test.java new file mode 100644 index 0000000000..1b0cc2a791 --- /dev/null +++ b/test/626-const-class-linking/src-multidex/Test.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test { +} diff --git a/test/626-const-class-linking/src-multidex/Test3.java b/test/626-const-class-linking/src-multidex/Test3.java new file mode 100644 index 0000000000..c4b134deaa --- /dev/null +++ b/test/626-const-class-linking/src-multidex/Test3.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Test3 { +} diff --git a/test/626-const-class-linking/src/ClassPair.java b/test/626-const-class-linking/src/ClassPair.java new file mode 100644 index 0000000000..b07036c70c --- /dev/null +++ b/test/626-const-class-linking/src/ClassPair.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class ClassPair { + public Class<?> first; + public Class<?> second; + + public ClassPair(Class<?> first, Class<?> second) { + this.first = first; + this.second = second; + } + + public void print() { + String first_loader_name = first.getClassLoader().getClass().getName(); + System.out.println("first: " + first.getName() + " class loader: " + first_loader_name); + String second_loader_name = second.getClassLoader().getClass().getName(); + System.out.println("second: " + second.getName() + " class loader: " + second_loader_name); + } +} diff --git a/test/626-const-class-linking/src/DefiningLoader.java b/test/626-const-class-linking/src/DefiningLoader.java new file mode 100644 index 0000000000..b17ab7755f --- /dev/null +++ b/test/626-const-class-linking/src/DefiningLoader.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +/** + * A class loader with atypical behavior: we try to load a private + * class implementation before asking the system or boot loader. This + * is used to create multiple classes with identical names in a single VM. + * + * If DexFile is available, we use that; if not, we assume we're not in + * Dalvik and instantiate the class with defineClass(). + * + * The location of the DEX files and class data is dependent upon the + * test framework. + */ +public class DefiningLoader extends ClassLoader { + static { + // For JVM, register as parallel capable. + // Android treats all class loaders as parallel capable and makes this a no-op. + registerAsParallelCapable(); + } + + /* this is where the .class files live */ + static final String CLASS_PATH1 = "classes/"; + static final String CLASS_PATH2 = "classes2/"; + + /* this is the DEX/Jar file */ + static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/626-const-class-linking.jar"; + + /* on Dalvik, this is a DexFile; otherwise, it's null */ + private Class<?> mDexClass; + + private Object mDexFile; + + /** + * Construct DefiningLoader, grabbing a reference to the DexFile class + * if we're running under Dalvik. + */ + public DefiningLoader(ClassLoader parent) { + super(parent); + + try { + mDexClass = parent.loadClass("dalvik.system.DexFile"); + } catch (ClassNotFoundException cnfe) { + // ignore -- not running Dalvik + } + } + + /** + * Finds the class with the specified binary name. + * + * We search for a file in CLASS_PATH or pull an entry from DEX_FILE. + * If we don't find a match, we throw an exception. + */ + protected Class<?> findClass(String name) throws ClassNotFoundException + { + if (mDexClass != null) { + return findClassDalvik(name); + } else { + return findClassNonDalvik(name); + } + } + + /** + * Finds the class with the specified binary name, from a DEX file. + */ + private Class<?> findClassDalvik(String name) + throws ClassNotFoundException { + + if (mDexFile == null) { + synchronized (DefiningLoader.class) { + Constructor<?> ctor; + /* + * Construct a DexFile object through reflection. + */ + try { + ctor = mDexClass.getConstructor(String.class); + } catch (NoSuchMethodException nsme) { + throw new ClassNotFoundException("getConstructor failed", + nsme); + } + + try { + mDexFile = ctor.newInstance(DEX_FILE); + } catch (InstantiationException ie) { + throw new ClassNotFoundException("newInstance failed", ie); + } catch (IllegalAccessException iae) { + throw new ClassNotFoundException("newInstance failed", iae); + } catch (InvocationTargetException ite) { + throw new ClassNotFoundException("newInstance failed", ite); + } + } + } + + /* + * Call DexFile.loadClass(String, ClassLoader). + */ + Method meth; + + try { + meth = mDexClass.getMethod("loadClass", String.class, ClassLoader.class); + } catch (NoSuchMethodException nsme) { + throw new ClassNotFoundException("getMethod failed", nsme); + } + + try { + meth.invoke(mDexFile, name, this); + } catch (IllegalAccessException iae) { + throw new ClassNotFoundException("loadClass failed", iae); + } catch (InvocationTargetException ite) { + throw new ClassNotFoundException("loadClass failed", + ite.getCause()); + } + + return null; + } + + /** + * Finds the class with the specified binary name, from .class files. + */ + private Class<?> findClassNonDalvik(String name) + throws ClassNotFoundException { + + String[] pathNames = { CLASS_PATH1 + name + ".class", CLASS_PATH2 + name + ".class" }; + + String pathName = null; + RandomAccessFile raf = null; + + for (String pn : pathNames) { + pathName = pn; + try { + //System.out.println("--- Defining: looking for " + pathName); + raf = new RandomAccessFile(new File(pathName), "r"); + break; + } catch (FileNotFoundException fnfe) { + } + } + if (raf == null) { + throw new ClassNotFoundException("Not found: " + pathNames[0] + ":" + pathNames[1]); + } + + /* read the entire file in */ + byte[] fileData; + try { + fileData = new byte[(int) raf.length()]; + raf.readFully(fileData); + } catch (IOException ioe) { + throw new ClassNotFoundException("Read error: " + pathName); + } finally { + try { + raf.close(); + } catch (IOException ioe) { + // drop + } + } + + /* create the class */ + //System.out.println("--- Defining: defining " + name); + try { + return defineClass(name, fileData, 0, fileData.length); + } catch (Throwable th) { + throw new ClassNotFoundException("defineClass failed", th); + } + } + + /** + * Load a class. + * + * Normally a class loader wouldn't override this, but we want our + * version of the class to take precedence over an already-loaded + * version. + * + * We still want the system classes (e.g. java.lang.Object) from the + * bootstrap class loader. + */ + synchronized protected Class<?> loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + Class<?> res; + + /* + * 1. Invoke findLoadedClass(String) to check if the class has + * already been loaded. + * + * This doesn't change. + */ + res = findLoadedClass(name); + if (res != null) { + // System.out.println("FancyLoader.loadClass: " + name + " already loaded"); + if (resolve) + resolveClass(res); + return res; + } + + /* + * 3. Invoke the findClass(String) method to find the class. + */ + try { + res = findClass(name); + if (resolve) + resolveClass(res); + } + catch (ClassNotFoundException e) { + // we couldn't find it, so eat the exception and keep going + } + + /* + * 2. Invoke the loadClass method on the parent class loader. If + * the parent loader is null the class loader built-in to the + * virtual machine is used, instead. + * + * (Since we're not in java.lang, we can't actually invoke the + * parent's loadClass() method, but we passed our parent to the + * super-class which can take care of it for us.) + */ + res = super.loadClass(name, resolve); // returns class or throws + return res; + } +} diff --git a/test/626-const-class-linking/src/DelegatingLoader.java b/test/626-const-class-linking/src/DelegatingLoader.java new file mode 100644 index 0000000000..49955d4e95 --- /dev/null +++ b/test/626-const-class-linking/src/DelegatingLoader.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class DelegatingLoader extends DefiningLoader { + private DefiningLoader defining_loader; + + public DelegatingLoader(ClassLoader parent, DefiningLoader defining_loader) { + super(parent); + this.defining_loader = defining_loader; + } + + public void resetDefiningLoader(DefiningLoader defining_loader) { + this.defining_loader = defining_loader; + } + + protected Class<?> findClass(String name) throws ClassNotFoundException + { + if (name.equals("Test")) { + throw new Error("Unexpected DelegatingLoader.findClass(\"Test\")"); + } + return super.findClass(name); + } + + protected Class<?> loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + if (name.equals("Test")) { + return defining_loader.loadClass(name, resolve); + } + return super.loadClass(name, resolve); + } +} diff --git a/test/626-const-class-linking/src/Helper1.java b/test/626-const-class-linking/src/Helper1.java new file mode 100644 index 0000000000..ff9cd1a532 --- /dev/null +++ b/test/626-const-class-linking/src/Helper1.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Helper1 { + public static ClassPair get() { + Class<?> helper1_class = Helper1.class; + Class<?> test_class = Test.class; + return new ClassPair(helper1_class, test_class); + } +} diff --git a/test/626-const-class-linking/src/Main.java b/test/626-const-class-linking/src/Main.java new file mode 100644 index 0000000000..0029428d90 --- /dev/null +++ b/test/626-const-class-linking/src/Main.java @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; + +public class Main { + public static void main(String[] args) throws Exception { + try { + System.loadLibrary(args[0]); + } catch (UnsatisfiedLinkError ule) { + usingRI = true; + // Add expected JNI_OnLoad log line to match expected.txt. + System.out.println("JNI_OnLoad called"); + } + + testClearDexCache(); + testMultiDex(); + testRacyLoader(); + testRacyLoader2(); + testMisbehavingLoader(); + testRacyMisbehavingLoader(); + testRacyMisbehavingLoader2(); + } + + private static void testClearDexCache() throws Exception { + DelegatingLoader delegating_loader = createDelegatingLoader(); + Class<?> helper = delegating_loader.loadClass("Helper1"); + + WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper); + changeInner(delegating_loader); + clearResolvedTypes(helper); + Runtime.getRuntime().gc(); + WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper); + Runtime.getRuntime().gc(); + + Class<?> test1 = weak_test1.get(); + if (test1 == null) { + System.out.println("test1 disappeared"); + } + Class<?> test2 = weak_test2.get(); + if (test2 == null) { + System.out.println("test2 disappeared"); + } + if (test1 != test2) { + System.out.println("test1 != test2"); + } + + System.out.println("testClearDexCache done"); + } + + private static void testMultiDex() throws Exception { + DelegatingLoader delegating_loader = createDelegatingLoader(); + + Class<?> helper1 = delegating_loader.loadClass("Helper1"); + WeakReference<Class<?>> weak_test1 = wrapHelperGet(helper1); + + changeInner(delegating_loader); + + Class<?> helper2 = delegating_loader.loadClass("Helper2"); + WeakReference<Class<?>> weak_test2 = wrapHelperGet(helper2); + + Runtime.getRuntime().gc(); + + Class<?> test1 = weak_test1.get(); + if (test1 == null) { + System.out.println("test1 disappeared"); + } + Class<?> test2 = weak_test2.get(); + if (test2 == null) { + System.out.println("test2 disappeared"); + } + if (test1 != test2) { + System.out.println("test1 != test2"); + } + + System.out.println("testMultiDex done"); + } + + private static void testMisbehavingLoader() throws Exception { + ClassLoader system_loader = ClassLoader.getSystemClassLoader(); + DefiningLoader defining_loader = new DefiningLoader(system_loader); + MisbehavingLoader misbehaving_loader = + new MisbehavingLoader(system_loader, defining_loader); + Class<?> helper = misbehaving_loader.loadClass("Helper1"); + + try { + WeakReference<Class<?>> weak_test = wrapHelperGet(helper); + } catch (InvocationTargetException ite) { + String message = ite.getCause().getMessage(); + if (usingRI && "Test".equals(message)) { + // Replace RI message with dalvik message to match expected.txt. + message = "Initiating class loader of type " + + misbehaving_loader.getClass().getName() + + " returned class Helper2 instead of Test."; + } + System.out.println(ite.getCause().getClass().getName() + ": " + message); + } + System.out.println("testMisbehavingLoader done"); + } + + private static void testRacyLoader() throws Exception { + final ClassLoader system_loader = ClassLoader.getSystemClassLoader(); + + final Thread[] threads = new Thread[4]; + final Object[] results = new Object[threads.length]; + + final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length); + final Class<?> helper1 = racy_loader.loadClass("Helper1"); + skipVerification(helper1); // Avoid class loading during verification. + + for (int i = 0; i != threads.length; ++i) { + final int my_index = i; + Thread t = new Thread() { + public void run() { + try { + Method get = helper1.getDeclaredMethod("get"); + results[my_index] = get.invoke(null); + } catch (InvocationTargetException ite) { + results[my_index] = ite.getCause(); + } catch (Throwable t) { + results[my_index] = t; + } + } + }; + t.start(); + threads[i] = t; + } + for (Thread t : threads) { + t.join(); + } + dumpResultStats(results, 1); + System.out.println("testRacyLoader done"); + } + + private static void testRacyLoader2() throws Exception { + final ClassLoader system_loader = ClassLoader.getSystemClassLoader(); + + final Thread[] threads = new Thread[4]; + final Object[] results = new Object[threads.length]; + + final RacyLoader racy_loader = new RacyLoader(system_loader, threads.length); + final Class<?> helper1 = racy_loader.loadClass("Helper1"); + skipVerification(helper1); // Avoid class loading during verification. + final Class<?> helper3 = racy_loader.loadClass("Helper3"); + skipVerification(helper3); // Avoid class loading during verification. + + for (int i = 0; i != threads.length; ++i) { + final int my_index = i; + Thread t = new Thread() { + public void run() { + try { + Class<?> helper = (my_index < threads.length / 2) ? helper1 : helper3; + Method get = helper.getDeclaredMethod("get"); + results[my_index] = get.invoke(null); + } catch (InvocationTargetException ite) { + results[my_index] = ite.getCause(); + } catch (Throwable t) { + results[my_index] = t; + } + } + }; + t.start(); + threads[i] = t; + } + for (Thread t : threads) { + t.join(); + } + dumpResultStats(results, 2); + System.out.println("testRacyLoader2 done"); + } + + private static void testRacyMisbehavingLoader() throws Exception { + final ClassLoader system_loader = ClassLoader.getSystemClassLoader(); + + final Thread[] threads = new Thread[4]; + final Object[] results = new Object[threads.length]; + + final RacyMisbehavingLoader racy_loader = + new RacyMisbehavingLoader(system_loader, threads.length, false); + final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper"); + skipVerification(helper1); // Avoid class loading during verification. + + for (int i = 0; i != threads.length; ++i) { + final int my_index = i; + Thread t = new Thread() { + public void run() { + try { + Method get = helper1.getDeclaredMethod("get"); + results[my_index] = get.invoke(null); + } catch (InvocationTargetException ite) { + results[my_index] = ite.getCause(); + } catch (Throwable t) { + results[my_index] = t; + } + } + }; + t.start(); + threads[i] = t; + } + for (Thread t : threads) { + t.join(); + } + dumpResultStats(results, 1); + System.out.println("testRacyMisbehavingLoader done"); + } + + private static void testRacyMisbehavingLoader2() throws Exception { + final ClassLoader system_loader = ClassLoader.getSystemClassLoader(); + + final Thread[] threads = new Thread[4]; + final Object[] results = new Object[threads.length]; + + final RacyMisbehavingLoader racy_loader = + new RacyMisbehavingLoader(system_loader, threads.length, true); + final Class<?> helper1 = racy_loader.loadClass("RacyMisbehavingHelper"); + skipVerification(helper1); // Avoid class loading during verification. + + for (int i = 0; i != threads.length; ++i) { + final int my_index = i; + Thread t = new Thread() { + public void run() { + try { + Method get = helper1.getDeclaredMethod("get"); + results[my_index] = get.invoke(null); + } catch (InvocationTargetException ite) { + results[my_index] = ite.getCause(); + } catch (Throwable t) { + results[my_index] = t; + } + } + }; + t.start(); + threads[i] = t; + } + for (Thread t : threads) { + t.join(); + } + dumpResultStats(results, 1); + System.out.println("testRacyMisbehavingLoader2 done"); + } + + private static void dumpResultStats(Object[] results, int expected_unique) throws Exception { + int throwables = 0; + int classes = 0; + int unique_classes = 0; + for (int i = 0; i != results.length; ++i) { + Object r = results[i]; + if (r instanceof Throwable) { + ++throwables; + System.out.println(((Throwable) r).getMessage()); + } else if (isClassPair(r)) { + printPair(r); + Object ref = getSecond(r); + ++classes; + ++unique_classes; + for (int j = 0; j != i; ++j) { + Object rj = results[j]; + if (isClassPair(results[j]) && getSecond(results[j]) == ref) { + --unique_classes; + break; + } + } + } + } + System.out.println("total: " + results.length); + System.out.println(" throwables: " + throwables); + System.out.println(" classes: " + classes + + " (" + unique_classes + " unique)"); + if (expected_unique != unique_classes) { + System.out.println("MISMATCH with expected_unique: " + expected_unique); + ArrayList<Class<?>> list = new ArrayList<Class<?>>(); + for (int i = 0; i != results.length; ++i) { + Object r = results[i]; + if (isClassPair(r)) { + list.add(getSecond(r)); + } + } + nativeDumpClasses(list.toArray()); + } + } + + private static DelegatingLoader createDelegatingLoader() { + ClassLoader system_loader = ClassLoader.getSystemClassLoader(); + DefiningLoader defining_loader = new DefiningLoader(system_loader); + return new DelegatingLoader(system_loader, defining_loader); + } + + private static void changeInner(DelegatingLoader delegating_loader) { + ClassLoader system_loader = ClassLoader.getSystemClassLoader(); + DefiningLoader defining_loader = new DefiningLoader(system_loader); + delegating_loader.resetDefiningLoader(defining_loader); + } + + private static WeakReference<Class<?>> wrapHelperGet(Class<?> helper) throws Exception { + Method get = helper.getDeclaredMethod("get"); + Object pair = get.invoke(null); + printPair(pair); + return new WeakReference<Class<?>>(getSecond(pair)); + } + + private static void printPair(Object pair) throws Exception { + Method print = pair.getClass().getDeclaredMethod("print"); + print.invoke(pair); + } + + private static Class<?> getSecond(Object pair) throws Exception { + Field second = pair.getClass().getDeclaredField("second"); + return (Class<?>) second.get(pair); + } + + private static boolean isClassPair(Object r) { + return r != null && r.getClass().getName().equals("ClassPair"); + } + + public static void clearResolvedTypes(Class<?> c) { + if (!usingRI) { + nativeClearResolvedTypes(c); + } + } + + // Skip verification of a class on ART. Verification can cause classes to be loaded + // while holding a lock on the class being verified and holding that lock can interfere + // with the intent of the "racy" tests. In these tests we're waiting in the loadClass() + // for all the tested threads to synchronize and they cannot reach that point if they + // are waiting for the class lock on ClassLinker::InitializeClass(Helper1/Helper3). + public static void skipVerification(Class<?> c) { + if (!usingRI) { + nativeSkipVerification(c); + } + } + + public static native void nativeClearResolvedTypes(Class<?> c); + public static native void nativeSkipVerification(Class<?> c); + public static native void nativeDumpClasses(Object[] array); + + static boolean usingRI = false; +} diff --git a/test/626-const-class-linking/src/MisbehavingLoader.java b/test/626-const-class-linking/src/MisbehavingLoader.java new file mode 100644 index 0000000000..ca9783e4ef --- /dev/null +++ b/test/626-const-class-linking/src/MisbehavingLoader.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Class loader that returns Helper2.class when asked to load "Test". +public class MisbehavingLoader extends DefiningLoader { + private DefiningLoader defining_loader; + + public MisbehavingLoader(ClassLoader parent, DefiningLoader defining_loader) { + super(parent); + this.defining_loader = defining_loader; + } + + protected Class<?> findClass(String name) throws ClassNotFoundException + { + if (name.equals("Helper1") || name.equals("Helper2")) { + return super.findClass(name); + } else if (name.equals("Test")) { + throw new Error("Unexpected MisbehavingLoader.findClass(\"Test\")"); + } + return super.findClass(name); + } + + protected Class<?> loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + if (name.equals("Helper1") || name.equals("Helper2")) { + return super.loadClass(name, resolve); + } else if (name.equals("Test")) { + // Ask for a different class. + return defining_loader.loadClass("Helper2", resolve); + } + return super.loadClass(name, resolve); + } +} diff --git a/test/626-const-class-linking/src/RacyLoader.java b/test/626-const-class-linking/src/RacyLoader.java new file mode 100644 index 0000000000..9c164a3124 --- /dev/null +++ b/test/626-const-class-linking/src/RacyLoader.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class RacyLoader extends DefiningLoader { + static { + // For JVM, register as parallel capable. + // Android treats all class loaders as parallel capable and makes this a no-op. + registerAsParallelCapable(); + } + + private Object lock = new Object(); + private int index = 0; + private int count; + + private DefiningLoader[] defining_loaders; + + public RacyLoader(ClassLoader parent, int count) { + super(parent); + this.count = count; + defining_loaders = new DefiningLoader[2]; + for (int i = 0; i != defining_loaders.length; ++i) { + defining_loaders[i] = new DefiningLoader(parent); + } + } + + protected Class<?> findClass(String name) throws ClassNotFoundException + { + if (name.equals("Test") || name.equals("Test3")) { + throw new Error("Unexpected RacyLoader.findClass(\"" + name + "\")"); + } + return super.findClass(name); + } + + protected Class<?> loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + if (name.equals("Test") || name.equals("Test3")) { + int my_index = syncWithOtherInstances(count); + Class<?> result = defining_loaders[my_index & 1].loadClass(name, resolve); + syncWithOtherInstances(2 * count); + return result; + } + return super.loadClass(name, resolve); + } + + private int syncWithOtherInstances(int limit) { + int my_index; + synchronized (lock) { + my_index = index; + ++index; + if (index != limit) { + do { + try { + lock.wait(); + } catch (InterruptedException ie) { + throw new Error(ie); + } + } while (index < limit); + } else { + lock.notifyAll(); + } + } + return my_index; + } +} diff --git a/test/626-const-class-linking/src/RacyMisbehavingHelper.java b/test/626-const-class-linking/src/RacyMisbehavingHelper.java new file mode 100644 index 0000000000..45252789e4 --- /dev/null +++ b/test/626-const-class-linking/src/RacyMisbehavingHelper.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class RacyMisbehavingHelper { + public static ClassPair get() { + Class<?> helper1_class = Helper1.class; + Class<?> test_class = Test.class; + try { + // After loading the correct class, allow loading the incorrect class. + ClassLoader loader = helper1_class.getClassLoader(); + Method reportAfterLoading = loader.getClass().getDeclaredMethod("reportAfterLoading"); + reportAfterLoading.invoke(loader); + } catch (Throwable t) { + t.printStackTrace(); + } + return new ClassPair(helper1_class, test_class); + } +} diff --git a/test/626-const-class-linking/src/RacyMisbehavingLoader.java b/test/626-const-class-linking/src/RacyMisbehavingLoader.java new file mode 100644 index 0000000000..f5bcb4c412 --- /dev/null +++ b/test/626-const-class-linking/src/RacyMisbehavingLoader.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class RacyMisbehavingLoader extends DefiningLoader { + static { + // For JVM, register as parallel capable. + // Android treats all class loaders as parallel capable and makes this a no-op. + registerAsParallelCapable(); + } + + private Object lock = new Object(); + private int index = 0; + private int count; + private boolean throw_error; + + private DefiningLoader[] defining_loaders; + + public RacyMisbehavingLoader(ClassLoader parent, int count, boolean throw_error) { + super(parent); + this.count = count; + this.throw_error = throw_error; + defining_loaders = new DefiningLoader[2]; + for (int i = 0; i != defining_loaders.length; ++i) { + defining_loaders[i] = new DefiningLoader(parent); + } + } + + public void reportAfterLoading() { + synchronized (lock) { + ++index; + if (index == 2 * count) { + lock.notifyAll(); + } + } + } + + protected Class<?> findClass(String name) throws ClassNotFoundException + { + if (name.equals("Test")) { + throw new Error("Unexpected RacyLoader.findClass(\"" + name + "\")"); + } + return super.findClass(name); + } + + protected Class<?> loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + if (name.equals("Test")) { + int my_index = syncWithOtherInstances(count); + Class<?> result; + if ((my_index & 1) == 0) { + // Do not delay loading the correct class. + result = defining_loaders[my_index & 1].loadClass(name, resolve); + } else { + // Delay loading the wrong class. + syncWithOtherInstances(2 * count); + if (throw_error) { + throw new Error("RacyMisbehavingLoader throw_error=true"); + } + result = defining_loaders[my_index & 1].loadClass("Test3", resolve); + } + return result; + } + return super.loadClass(name, resolve); + } + + private int syncWithOtherInstances(int limit) { + int my_index; + synchronized (lock) { + my_index = index; + ++index; + if (index != limit) { + do { + try { + lock.wait(); + } catch (InterruptedException ie) { + throw new Error(ie); + } + } while (index < limit); + } else { + lock.notifyAll(); + } + } + return my_index; + } +} diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk index 21b31b40b7..5ae961a6f1 100644 --- a/test/Android.arm_vixl.mk +++ b/test/Android.arm_vixl.mk @@ -16,36 +16,6 @@ # Known broken tests for the ARM VIXL backend. TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ - 003-omnibus-opcodes \ - 020-string \ - 021-string2 \ - 042-new-instance \ - 044-proxy \ - 080-oom-throw \ - 082-inline-execute \ - 096-array-copy-concurrent-gc \ - 099-vmdebug \ - 100-reflect2 \ - 103-string-append \ - 114-ParallelGC \ - 122-npe \ - 129-ThreadGetId \ - 137-cfi \ - 144-static-field-sigquit \ - 412-new-array \ - 439-npe \ - 450-checker-types \ 488-checker-inline-recursive-calls \ - 515-dce-dominator \ - 520-equivalent-phi \ - 525-checker-arrays-fields1 \ - 525-checker-arrays-fields2 \ - 527-checker-array-access-split \ - 538-checker-embed-constants \ 552-checker-sharpening \ 562-checker-no-intermediate \ - 570-checker-osr \ - 602-deoptimizeable \ - 700-LoadArgRegs \ - 800-smali \ - diff --git a/test/Android.bp b/test/Android.bp index fe20f29015..39a4059f76 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -314,6 +314,7 @@ cc_defaults { "595-profile-saving/profile-saving.cc", "596-app-images/app_images.cc", "597-deopt-new-string/deopt.cc", + "626-const-class-linking/clear_dex_cache_types.cc", ], shared_libs: [ "libbacktrace", diff --git a/test/etc/default-build b/test/etc/default-build index e6634967d4..408dcfdf9b 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -273,8 +273,10 @@ if [ ${HAS_SRC_EX} = "true" ]; then fi # Create a single jar with two dex files for multidex. -if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then - zip $TEST_NAME.jar classes.dex classes2.dex -elif [ ${NEED_DEX} = "true" ]; then - zip $TEST_NAME.jar classes.dex +if [ ${NEED_DEX} = "true" ]; then + if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then + zip $TEST_NAME.jar classes.dex classes2.dex + else + zip $TEST_NAME.jar classes.dex + fi fi diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index bb3a3ad714..f0abb442bf 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -316,7 +316,8 @@ fi if [ "$USE_JVM" = "y" ]; then export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64 # Xmx is necessary since we don't pass down the ART flags to JVM. - cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes ${FLAGS} $MAIN $@ ${ARGS}" + # We pass the classes2 path whether it's used (src-multidex) or not. + cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes:classes2 ${FLAGS} $MAIN $@ ${ARGS}" if [ "$DEV_MODE" = "y" ]; then echo $cmdline fi @@ -30,8 +30,9 @@ function follow_links() { } function find_libdir() { + # Get the actual file, $DALVIKVM may be a symbolic link. # Use realpath instead of readlink because Android does not have a readlink. - if [ "$(realpath "$ANDROID_ROOT/bin/$DALVIKVM")" = "$(realpath "$ANDROID_ROOT/bin/dalvikvm64")" ]; then + if [[ "$(realpath "$ANDROID_ROOT/bin/$DALVIKVM")" == *dalvikvm64 ]]; then echo "lib64" else echo "lib" |