diff options
124 files changed, 2453 insertions, 1154 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..fb5560b124 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1451,7 +1451,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); } 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 1b5138f6f2..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); @@ -2776,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) { @@ -3956,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) { @@ -4697,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) { @@ -5156,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>(); } @@ -5200,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; @@ -5238,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) { @@ -5253,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(); @@ -5276,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); @@ -5291,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); @@ -5315,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); @@ -5340,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); @@ -5360,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, @@ -5449,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) { @@ -5466,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); @@ -5491,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. @@ -5505,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. @@ -5523,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); @@ -5530,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. @@ -5541,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` @@ -5559,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 @@ -5570,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); @@ -5862,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); @@ -5879,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) { diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 89fef43e46..bd91127121 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -422,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`: // @@ -437,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/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 73bca037b8..b88515f21f 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1317,6 +1317,7 @@ ENTRY art_quick_check_instance_of RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32 ret .cfi_restore_state // Reset unwind info so following code unwinds. + .cfi_def_cfa_offset 32 // workaround for clang bug: 31975598 .Lthrow_class_cast_exception: // Restore @@ -1484,6 +1485,7 @@ ENTRY art_quick_aput_obj strb w3, [x3, x0] ret .cfi_restore_state // Reset unwind info so following code unwinds. + .cfi_def_cfa_offset 32 // workaround for clang bug: 31975598 .Lthrow_array_store_exception: RESTORE_TWO_REGS x2, xLR, 16 RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32 @@ -1651,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/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 6a442a55bf..5c569232ac 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -71,7 +71,7 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub; // Alloc - ResetQuickAllocEntryPoints(qpoints); + ResetQuickAllocEntryPoints(qpoints, /*is_marking*/ false); // Cast qpoints->pInstanceofNonTrivial = artInstanceOfFromCode; diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index fa86bf4087..db2fdcabea 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -107,7 +107,28 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) .endm +.macro GENERATE_ALLOC_ENTRYPOINTS_FOR_TLAB_ALLOCATOR +// This is to be separately defined for each architecture to allow a hand-written assembly fast path. +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) +.endm + .macro GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_REGION_TLAB_ALLOCATORS +GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS +GENERATE_ALLOC_ENTRYPOINTS_FOR_TLAB_ALLOCATOR +.endm + +.macro GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc, DlMalloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_dlmalloc, DlMalloc) @@ -187,20 +208,6 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, B GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented) -// This is to be separately defined for each architecture to allow a hand-written assembly fast path. -// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) - GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab_instrumented, TLABInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab_instrumented, TLABInstrumented) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index fb405fac0f..c6f4c0346f 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1085,15 +1085,12 @@ MACRO1(ALLOC_OBJECT_TLAB_SLOW_PATH, cxx_name) RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_MACRO -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). May be called +// for CC if the GC is not marking. DEFINE_FUNCTION art_quick_alloc_object_tlab // Fast path tlab allocation. // EAX: uint32_t type_idx/return value, ECX: ArtMethod*. // EBX, EDX: free. -#if defined(USE_READ_BARRIER) - int3 - int3 -#endif PUSH esi PUSH edi movl ART_METHOD_DEX_CACHE_TYPES_OFFSET_32(%ecx), %edx // Load dex cache resolved types array @@ -1151,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 860b77efe3..4c46b08a9e 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -18,6 +18,13 @@ #include "arch/quick_alloc_entrypoints.S" +MACRO0(ASSERT_USE_READ_BARRIER) +#if !defined(USE_READ_BARRIER) + int3 + int3 +#endif +END_MACRO + MACRO0(SETUP_FP_CALLEE_SAVE_FRAME) // Create space for ART FP callee-saved registers subq MACRO_LITERAL(4 * 8), %rsp @@ -972,8 +979,10 @@ MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION) END_MACRO // Generate the allocation entrypoints for each allocator. -GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_REGION_TLAB_ALLOCATORS +GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS + // Comment out allocators that have x86_64 specific asm. +// Region TLAB: // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) // GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) @@ -986,6 +995,19 @@ GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB) +// Normal TLAB: +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB) // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). DEFINE_FUNCTION art_quick_alloc_object_rosalloc @@ -1162,16 +1184,11 @@ MACRO1(ALLOC_ARRAY_TLAB_SLOW_PATH, cxx_name) RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_MACRO -// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). May be +// called with CC if the GC is not active. DEFINE_FUNCTION art_quick_alloc_object_tlab - // Fast path tlab allocation. // RDI: uint32_t type_idx, RSI: ArtMethod* // RDX, RCX, R8, R9: free. RAX: return val. -#if defined(USE_READ_BARRIER) - int3 - int3 -#endif - // Might need a special macro since rsi and edx is 32b/64b mismatched. movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array // Might need to break down into multiple instructions to get the base address in a register. // Load the class @@ -1181,29 +1198,69 @@ DEFINE_FUNCTION art_quick_alloc_object_tlab ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeTLAB END_FUNCTION art_quick_alloc_object_tlab +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB). May be +// called with CC if the GC is not active. +DEFINE_FUNCTION art_quick_alloc_object_resolved_tlab + // RDI: mirror::Class* klass, RSI: ArtMethod* + // RDX, RCX, R8, R9: free. RAX: return val. + movq %rdi, %rdx + ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_tlab_slow_path +.Lart_quick_alloc_object_resolved_tlab_slow_path: + ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedTLAB +END_FUNCTION art_quick_alloc_object_resolved_tlab + +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB). +// May be called with CC if the GC is not active. +DEFINE_FUNCTION art_quick_alloc_object_initialized_tlab + // RDI: mirror::Class* klass, RSI: ArtMethod* + // RDX, RCX, R8, R9: free. RAX: return val. + movq %rdi, %rdx + ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH .Lart_quick_alloc_object_initialized_tlab_slow_path +.Lart_quick_alloc_object_initialized_tlab_slow_path: + ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedTLAB +END_FUNCTION art_quick_alloc_object_initialized_tlab + +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB). +DEFINE_FUNCTION art_quick_alloc_array_tlab + // RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod* + // RCX: klass, R8, R9: free. RAX: return val. + movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rdx), %rcx // Load dex cache resolved types array + movl 0(%rcx, %rdi, COMPRESSED_REFERENCE_SIZE), %ecx // Load the class + testl %ecx, %ecx + jz .Lart_quick_alloc_array_tlab_slow_path + ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_tlab_slow_path +.Lart_quick_alloc_array_tlab_slow_path: + ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeTLAB +END_FUNCTION art_quick_alloc_array_tlab + +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB). +DEFINE_FUNCTION art_quick_alloc_array_resolved_tlab + // RDI: mirror::Class* klass, RSI: int32_t component_count, RDX: ArtMethod* + // RCX: mirror::Class* klass, R8, R9: free. RAX: return val. + movq %rdi, %rcx + // Already resolved, no null check. + ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_resolved_tlab_slow_path +.Lart_quick_alloc_array_resolved_tlab_slow_path: + ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedTLAB +END_FUNCTION art_quick_alloc_array_resolved_tlab + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB). DEFINE_FUNCTION art_quick_alloc_array_region_tlab // Fast path region tlab allocation. // RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod* // RCX: klass, R8, R9: free. RAX: return val. -#if !defined(USE_READ_BARRIER) - int3 - int3 -#endif + ASSERT_USE_READ_BARRIER movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rdx), %rcx // Load dex cache resolved types array movl 0(%rcx, %rdi, COMPRESSED_REFERENCE_SIZE), %ecx // Load the class // Null check so that we can load the lock word. testl %ecx, %ecx jz .Lart_quick_alloc_array_region_tlab_slow_path - - cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET - jne .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_marking + // Since we have allocation entrypoint switching, we know the GC is marking. + // Check the mark bit, if it is 0, do the read barrier mark. + testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx) + jz .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit: ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_region_tlab_slow_path -.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_marking: - // Check the mark bit, if it is 1 return. - testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx) - jnz .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path: // The read barrier slow path. Mark the class. PUSH rdi @@ -1226,33 +1283,11 @@ DEFINE_FUNCTION art_quick_alloc_array_resolved_region_tlab // Fast path region tlab allocation. // RDI: mirror::Class* klass, RSI: int32_t component_count, RDX: ArtMethod* // RCX: mirror::Class* klass, R8, R9: free. RAX: return val. -#if !defined(USE_READ_BARRIER) - int3 - int3 -#endif + ASSERT_USE_READ_BARRIER movq %rdi, %rcx + // Caller is responsible for read barrier. // Already resolved, no null check. - cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET - jne .Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_marking -.Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_slow_path_exit: ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_resolved_region_tlab_slow_path -.Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_marking: - // Check the mark bit, if it is 1 return. - testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx) - jnz .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit -.Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_slow_path: - // The read barrier slow path. Mark the class. - PUSH rdi - PUSH rsi - PUSH rdx - // Outgoing argument set up - movq %rcx, %rdi // Pass the class as the first param. - call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj) - movq %rax, %rcx - POP rdx - POP rsi - POP rdi - jmp .Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_slow_path_exit .Lart_quick_alloc_array_resolved_region_tlab_slow_path: ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedRegionTLAB END_FUNCTION art_quick_alloc_array_resolved_region_tlab @@ -1262,24 +1297,19 @@ DEFINE_FUNCTION art_quick_alloc_object_region_tlab // Fast path region tlab allocation. // RDI: uint32_t type_idx, RSI: ArtMethod* // RDX, RCX, R8, R9: free. RAX: return val. -#if !defined(USE_READ_BARRIER) - int3 - int3 -#endif + ASSERT_USE_READ_BARRIER movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx // Load the class // Null check so that we can load the lock word. testl %edx, %edx jz .Lart_quick_alloc_object_region_tlab_slow_path - // Test if the GC is marking. - cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET - jne .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking -.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit: - ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path -.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking: - // Check the mark bit, if it is 1 avoid the read barrier. + // Since we have allocation entrypoint switching, we know the GC is marking. + // Check the mark bit, if it is 0, do the read barrier mark. testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edx) - jnz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit + jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit: + // Use resolved one since we already did the null check. + ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path: // The read barrier slow path. Mark the class. PUSH rdi @@ -1302,10 +1332,7 @@ DEFINE_FUNCTION art_quick_alloc_object_resolved_region_tlab // Fast path region tlab allocation. // RDI: mirror::Class* klass, RSI: ArtMethod* // RDX, RCX, R8, R9: free. RAX: return val. -#if !defined(USE_READ_BARRIER) - int3 - int3 -#endif + ASSERT_USE_READ_BARRIER // No read barrier since the caller is responsible for that. movq %rdi, %rdx ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_region_tlab_slow_path @@ -1318,10 +1345,7 @@ DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab // Fast path region tlab allocation. // RDI: mirror::Class* klass, RSI: ArtMethod* // RDX, RCX, R8, R9: free. RAX: return val. -#if !defined(USE_READ_BARRIER) - int3 - int3 -#endif + ASSERT_USE_READ_BARRIER movq %rdi, %rdx // No read barrier since the caller is responsible for that. ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH .Lart_quick_alloc_object_initialized_region_tlab_slow_path @@ -1330,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 1e809d5d1b..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> 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..0de647f509 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7485,7 +7485,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 +7501,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 +7510,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); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 95634484fc..60755cdea6 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -237,18 +237,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/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_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc index 397655a895..82bb8e53c6 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -292,7 +292,7 @@ void SetQuickAllocEntryPointsInstrumented(bool instrumented) { entry_points_instrumented = instrumented; } -void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { +void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints, bool is_marking) { #if !defined(__APPLE__) || !defined(__LP64__) switch (entry_points_allocator) { case gc::kAllocatorTypeDlMalloc: { @@ -320,7 +320,12 @@ void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { } case gc::kAllocatorTypeRegionTLAB: { CHECK(kMovingCollector); - SetQuickAllocEntryPoints_region_tlab(qpoints, entry_points_instrumented); + if (is_marking) { + SetQuickAllocEntryPoints_region_tlab(qpoints, entry_points_instrumented); + } else { + // Not marking means we need no read barriers and can just use the normal TLAB case. + SetQuickAllocEntryPoints_tlab(qpoints, entry_points_instrumented); + } return; } default: @@ -328,6 +333,7 @@ void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) { } #else UNUSED(qpoints); + UNUSED(is_marking); #endif UNIMPLEMENTED(FATAL); UNREACHABLE(); diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.h b/runtime/entrypoints/quick/quick_alloc_entrypoints.h index 14a8e0428b..bd1e295e48 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.h +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.h @@ -23,7 +23,9 @@ namespace art { -void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints); +// is_marking is only used for CC, if the GC is marking the allocation entrypoint is the marking +// one. +void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints, bool is_marking); // Runtime shutdown lock is necessary to prevent races in thread initialization. When the thread is // starting it doesn't hold the mutator lock until after it has been added to the thread list. diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index df23f94a31..78dad94dfe 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -31,7 +31,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub; // Alloc - ResetQuickAllocEntryPoints(qpoints); + ResetQuickAllocEntryPoints(qpoints, /* is_marking */ true); // DexCache qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage; 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-inl.h b/runtime/gc/heap-inl.h index 97129e8b19..54f221056a 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -247,7 +247,7 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, if (allocator_type != kAllocatorTypeTLAB && allocator_type != kAllocatorTypeRegionTLAB && allocator_type != kAllocatorTypeRosAlloc && - UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) { + UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type, alloc_size, kGrow))) { return nullptr; } mirror::Object* ret; @@ -267,8 +267,9 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) { // If running on valgrind or asan, we should be using the instrumented path. size_t max_bytes_tl_bulk_allocated = rosalloc_space_->MaxBytesBulkAllocatedFor(alloc_size); - if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, - max_bytes_tl_bulk_allocated))) { + if (UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type, + max_bytes_tl_bulk_allocated, + kGrow))) { return nullptr; } ret = rosalloc_space_->Alloc(self, alloc_size, bytes_allocated, usable_size, @@ -277,14 +278,18 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, DCHECK(!is_running_on_memory_tool_); size_t max_bytes_tl_bulk_allocated = rosalloc_space_->MaxBytesBulkAllocatedForNonvirtual(alloc_size); - if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, - max_bytes_tl_bulk_allocated))) { + if (UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type, + max_bytes_tl_bulk_allocated, + kGrow))) { return nullptr; } if (!kInstrumented) { DCHECK(!rosalloc_space_->CanAllocThreadLocal(self, alloc_size)); } - ret = rosalloc_space_->AllocNonvirtual(self, alloc_size, bytes_allocated, usable_size, + ret = rosalloc_space_->AllocNonvirtual(self, + alloc_size, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated); } break; @@ -292,22 +297,34 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, case kAllocatorTypeDlMalloc: { if (kInstrumented && UNLIKELY(is_running_on_memory_tool_)) { // If running on valgrind, we should be using the instrumented path. - ret = dlmalloc_space_->Alloc(self, alloc_size, bytes_allocated, usable_size, + ret = dlmalloc_space_->Alloc(self, + alloc_size, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated); } else { DCHECK(!is_running_on_memory_tool_); - ret = dlmalloc_space_->AllocNonvirtual(self, alloc_size, bytes_allocated, usable_size, + ret = dlmalloc_space_->AllocNonvirtual(self, + alloc_size, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated); } break; } case kAllocatorTypeNonMoving: { - ret = non_moving_space_->Alloc(self, alloc_size, bytes_allocated, usable_size, + ret = non_moving_space_->Alloc(self, + alloc_size, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated); break; } case kAllocatorTypeLOS: { - ret = large_object_space_->Alloc(self, alloc_size, bytes_allocated, usable_size, + ret = large_object_space_->Alloc(self, + alloc_size, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated); // Note that the bump pointer spaces aren't necessarily next to // the other continuous spaces like the non-moving alloc space or @@ -315,80 +332,38 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, DCHECK(ret == nullptr || large_object_space_->Contains(ret)); break; } - case kAllocatorTypeTLAB: { - DCHECK_ALIGNED(alloc_size, space::BumpPointerSpace::kAlignment); - if (UNLIKELY(self->TlabSize() < alloc_size)) { - const size_t new_tlab_size = alloc_size + kDefaultTLABSize; - if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, new_tlab_size))) { - return nullptr; - } - // Try allocating a new thread local buffer, if the allocaiton fails the space must be - // full so return null. - if (!bump_pointer_space_->AllocNewTlab(self, new_tlab_size)) { - return nullptr; - } - *bytes_tl_bulk_allocated = new_tlab_size; - } else { - *bytes_tl_bulk_allocated = 0; - } - // The allocation can't fail. - ret = self->AllocTlab(alloc_size); - DCHECK(ret != nullptr); - *bytes_allocated = alloc_size; - *usable_size = alloc_size; - break; - } case kAllocatorTypeRegion: { DCHECK(region_space_ != nullptr); alloc_size = RoundUp(alloc_size, space::RegionSpace::kAlignment); - ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size, + ret = region_space_->AllocNonvirtual<false>(alloc_size, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated); break; } + case kAllocatorTypeTLAB: + FALLTHROUGH_INTENDED; case kAllocatorTypeRegionTLAB: { - DCHECK(region_space_ != nullptr); - DCHECK_ALIGNED(alloc_size, space::RegionSpace::kAlignment); + DCHECK_ALIGNED(alloc_size, kObjectAlignment); + static_assert(space::RegionSpace::kAlignment == space::BumpPointerSpace::kAlignment, + "mismatched alignments"); + static_assert(kObjectAlignment == space::BumpPointerSpace::kAlignment, + "mismatched alignments"); if (UNLIKELY(self->TlabSize() < alloc_size)) { - if (space::RegionSpace::kRegionSize >= alloc_size) { - // Non-large. Check OOME for a tlab. - if (LIKELY(!IsOutOfMemoryOnAllocation<kGrow>(allocator_type, space::RegionSpace::kRegionSize))) { - // Try to allocate a tlab. - if (!region_space_->AllocNewTlab(self)) { - // Failed to allocate a tlab. Try non-tlab. - ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size, - bytes_tl_bulk_allocated); - return ret; - } - *bytes_tl_bulk_allocated = space::RegionSpace::kRegionSize; - // Fall-through. - } else { - // Check OOME for a non-tlab allocation. - if (!IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size)) { - ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size, - bytes_tl_bulk_allocated); - return ret; - } else { - // Neither tlab or non-tlab works. Give up. - return nullptr; - } - } - } else { - // Large. Check OOME. - if (LIKELY(!IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) { - ret = region_space_->AllocNonvirtual<false>(alloc_size, bytes_allocated, usable_size, - bytes_tl_bulk_allocated); - return ret; - } else { - return nullptr; - } - } - } else { - *bytes_tl_bulk_allocated = 0; // Allocated in an existing buffer. + // kAllocatorTypeTLAB may be the allocator for region space TLAB if the GC is not marking, + // that is why the allocator is not passed down. + return AllocWithNewTLAB(self, + alloc_size, + kGrow, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated); } // The allocation can't fail. ret = self->AllocTlab(alloc_size); DCHECK(ret != nullptr); *bytes_allocated = alloc_size; + *bytes_tl_bulk_allocated = 0; // Allocated in an existing buffer. *usable_size = alloc_size; break; } @@ -408,15 +383,16 @@ inline bool Heap::ShouldAllocLargeObject(ObjPtr<mirror::Class> c, size_t byte_co return byte_count >= large_object_threshold_ && (c->IsPrimitiveArray() || c->IsStringClass()); } -template <bool kGrow> -inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size) { +inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, + size_t alloc_size, + bool grow) { size_t new_footprint = num_bytes_allocated_.LoadSequentiallyConsistent() + alloc_size; if (UNLIKELY(new_footprint > max_allowed_footprint_)) { if (UNLIKELY(new_footprint > growth_limit_)) { return true; } if (!AllocatorMayHaveConcurrentGC(allocator_type) || !IsGcConcurrent()) { - if (!kGrow) { + if (!grow) { return true; } // TODO: Grow for allocation is racy, fix it. diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 6a97edd336..5c219cc871 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1819,7 +1819,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, break; } // Try to transition the heap if the allocation failure was due to the space being full. - if (!IsOutOfMemoryOnAllocation<false>(allocator, alloc_size)) { + if (!IsOutOfMemoryOnAllocation(allocator, alloc_size, /*grow*/ false)) { // If we aren't out of memory then the OOM was probably from the non moving space being // full. Attempt to disable compaction and turn the main space into a non moving space. DisableMovingGc(); @@ -4219,5 +4219,72 @@ void Heap::RemoveGcPauseListener() { gc_pause_listener_.StoreRelaxed(nullptr); } +mirror::Object* Heap::AllocWithNewTLAB(Thread* self, + size_t alloc_size, + bool grow, + size_t* bytes_allocated, + size_t* usable_size, + size_t* bytes_tl_bulk_allocated) { + const AllocatorType allocator_type = GetCurrentAllocator(); + if (allocator_type == kAllocatorTypeTLAB) { + DCHECK(bump_pointer_space_ != nullptr); + const size_t new_tlab_size = alloc_size + kDefaultTLABSize; + if (UNLIKELY(IsOutOfMemoryOnAllocation(allocator_type, new_tlab_size, grow))) { + return nullptr; + } + // Try allocating a new thread local buffer, if the allocation fails the space must be + // full so return null. + if (!bump_pointer_space_->AllocNewTlab(self, new_tlab_size)) { + return nullptr; + } + *bytes_tl_bulk_allocated = new_tlab_size; + } else { + DCHECK(allocator_type == kAllocatorTypeRegionTLAB); + DCHECK(region_space_ != nullptr); + if (space::RegionSpace::kRegionSize >= alloc_size) { + // Non-large. Check OOME for a tlab. + if (LIKELY(!IsOutOfMemoryOnAllocation(allocator_type, + space::RegionSpace::kRegionSize, + grow))) { + // Try to allocate a tlab. + if (!region_space_->AllocNewTlab(self)) { + // Failed to allocate a tlab. Try non-tlab. + return region_space_->AllocNonvirtual<false>(alloc_size, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated); + } + *bytes_tl_bulk_allocated = space::RegionSpace::kRegionSize; + // Fall-through to using the TLAB below. + } else { + // Check OOME for a non-tlab allocation. + if (!IsOutOfMemoryOnAllocation(allocator_type, alloc_size, grow)) { + return region_space_->AllocNonvirtual<false>(alloc_size, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated); + } + // Neither tlab or non-tlab works. Give up. + return nullptr; + } + } else { + // Large. Check OOME. + if (LIKELY(!IsOutOfMemoryOnAllocation(allocator_type, alloc_size, grow))) { + return region_space_->AllocNonvirtual<false>(alloc_size, + bytes_allocated, + usable_size, + bytes_tl_bulk_allocated); + } + return nullptr; + } + } + // Refilled TLAB, return. + mirror::Object* ret = self->AllocTlab(alloc_size); + DCHECK(ret != nullptr); + *bytes_allocated = alloc_size; + *usable_size = alloc_size; + return ret; +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 0c671d269d..3a8e29b08a 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -854,6 +854,10 @@ class Heap { allocator_type != kAllocatorTypeRegionTLAB; } static ALWAYS_INLINE bool AllocatorMayHaveConcurrentGC(AllocatorType allocator_type) { + if (kUseReadBarrier) { + // Read barrier may have the TLAB allocator but is always concurrent. TODO: clean this up. + return true; + } return allocator_type != kAllocatorTypeBumpPointer && allocator_type != kAllocatorTypeTLAB; @@ -923,11 +927,20 @@ class Heap { size_t* bytes_tl_bulk_allocated) REQUIRES_SHARED(Locks::mutator_lock_); + mirror::Object* AllocWithNewTLAB(Thread* self, + size_t alloc_size, + bool grow, + size_t* bytes_allocated, + size_t* usable_size, + size_t* bytes_tl_bulk_allocated) + REQUIRES_SHARED(Locks::mutator_lock_); + void ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) REQUIRES_SHARED(Locks::mutator_lock_); - template <bool kGrow> - ALWAYS_INLINE bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size); + ALWAYS_INLINE bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, + size_t alloc_size, + bool grow); // Run the finalizers. If timeout is non zero, then we use the VMRuntime version. void RunFinalization(JNIEnv* env, uint64_t timeout); 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/instrumentation.cc b/runtime/instrumentation.cc index d4c322eb84..870d1ae9b5 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -630,7 +630,7 @@ void Instrumentation::ConfigureStubs(const char* key, InstrumentationLevel desir } static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg ATTRIBUTE_UNUSED) { - thread->ResetQuickAllocEntryPointsForThread(); + thread->ResetQuickAllocEntryPointsForThread(kUseReadBarrier && thread->GetIsGcMarking()); } void Instrumentation::SetEntrypointsInstrumented(bool instrumented) { 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/arm64/entry.S b/runtime/interpreter/mterp/arm64/entry.S index 9fbbbd34a4..441c1a1e88 100644 --- a/runtime/interpreter/mterp/arm64/entry.S +++ b/runtime/interpreter/mterp/arm64/entry.S @@ -31,11 +31,11 @@ ExecuteMterpImpl: .cfi_startproc - stp xPROFILE, x27, [sp, #-80]! - stp xIBASE, xREFS, [sp, #16] - stp xSELF, xINST, [sp, #32] - stp xPC, xFP, [sp, #48] - stp fp, lr, [sp, #64] + SAVE_TWO_REGS_INCREASE_FRAME xPROFILE, x27, 80 + SAVE_TWO_REGS xIBASE, xREFS, 16 + SAVE_TWO_REGS xSELF, xINST, 32 + SAVE_TWO_REGS xPC, xFP, 48 + SAVE_TWO_REGS fp, lr, 64 add fp, sp, #64 /* Remember the return register */ diff --git a/runtime/interpreter/mterp/arm64/footer.S b/runtime/interpreter/mterp/arm64/footer.S index ada0326649..6ffbd3f260 100644 --- a/runtime/interpreter/mterp/arm64/footer.S +++ b/runtime/interpreter/mterp/arm64/footer.S @@ -285,12 +285,15 @@ MterpDone: */ cmp wPROFILE, #0 bgt MterpProfileActive // if > 0, we may have some counts to report. - ldp fp, lr, [sp, #64] - ldp xPC, xFP, [sp, #48] - ldp xSELF, xINST, [sp, #32] - ldp xIBASE, xREFS, [sp, #16] - ldp xPROFILE, x27, [sp], #80 + .cfi_remember_state + RESTORE_TWO_REGS fp, lr, 64 + RESTORE_TWO_REGS xPC, xFP, 48 + RESTORE_TWO_REGS xSELF, xINST, 32 + RESTORE_TWO_REGS xIBASE, xREFS, 16 + RESTORE_TWO_REGS_DECREASE_FRAME xPROFILE, x27, 80 ret + .cfi_restore_state // Reset unwind info so following code unwinds. + .cfi_def_cfa_offset 80 // workaround for clang bug: 31975598 MterpProfileActive: mov xINST, x0 // stash return value @@ -301,11 +304,11 @@ MterpProfileActive: strh wPROFILE, [x1, #SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET] bl MterpAddHotnessBatch // (method, shadow_frame, self) mov x0, xINST // restore return value - ldp fp, lr, [sp, #64] - ldp xPC, xFP, [sp, #48] - ldp xSELF, xINST, [sp, #32] - ldp xIBASE, xREFS, [sp, #16] - ldp xPROFILE, x27, [sp], #80 + RESTORE_TWO_REGS fp, lr, 64 + RESTORE_TWO_REGS xPC, xFP, 48 + RESTORE_TWO_REGS xSELF, xINST, 32 + RESTORE_TWO_REGS xIBASE, xREFS, 16 + RESTORE_TWO_REGS_DECREASE_FRAME xPROFILE, x27, 80 ret .cfi_endproc diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index c791eb5ec9..7125d5a74d 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -292,3 +292,41 @@ codes. .macro REFRESH_IBASE ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] .endm + +/* + * Save two registers to the stack. + */ +.macro SAVE_TWO_REGS reg1, reg2, offset + stp \reg1, \reg2, [sp, #(\offset)] + .cfi_rel_offset \reg1, (\offset) + .cfi_rel_offset \reg2, (\offset) + 8 +.endm + +/* + * Restore two registers from the stack. + */ +.macro RESTORE_TWO_REGS reg1, reg2, offset + ldp \reg1, \reg2, [sp, #(\offset)] + .cfi_restore \reg1 + .cfi_restore \reg2 +.endm + +/* + * Increase frame size and save two registers to the bottom of the stack. + */ +.macro SAVE_TWO_REGS_INCREASE_FRAME reg1, reg2, frame_adjustment + stp \reg1, \reg2, [sp, #-(\frame_adjustment)]! + .cfi_adjust_cfa_offset (\frame_adjustment) + .cfi_rel_offset \reg1, 0 + .cfi_rel_offset \reg2, 8 +.endm + +/* + * Restore two registers from the bottom of the stack and decrease frame size. + */ +.macro RESTORE_TWO_REGS_DECREASE_FRAME reg1, reg2, frame_adjustment + ldp \reg1, \reg2, [sp], #(\frame_adjustment) + .cfi_restore \reg1 + .cfi_restore \reg2 + .cfi_adjust_cfa_offset -(\frame_adjustment) +.endm 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/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index 42f8c1b08d..34d99a8126 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -300,6 +300,44 @@ codes. ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] .endm +/* + * Save two registers to the stack. + */ +.macro SAVE_TWO_REGS reg1, reg2, offset + stp \reg1, \reg2, [sp, #(\offset)] + .cfi_rel_offset \reg1, (\offset) + .cfi_rel_offset \reg2, (\offset) + 8 +.endm + +/* + * Restore two registers from the stack. + */ +.macro RESTORE_TWO_REGS reg1, reg2, offset + ldp \reg1, \reg2, [sp, #(\offset)] + .cfi_restore \reg1 + .cfi_restore \reg2 +.endm + +/* + * Increase frame size and save two registers to the bottom of the stack. + */ +.macro SAVE_TWO_REGS_INCREASE_FRAME reg1, reg2, frame_adjustment + stp \reg1, \reg2, [sp, #-(\frame_adjustment)]! + .cfi_adjust_cfa_offset (\frame_adjustment) + .cfi_rel_offset \reg1, 0 + .cfi_rel_offset \reg2, 8 +.endm + +/* + * Restore two registers from the bottom of the stack and decrease frame size. + */ +.macro RESTORE_TWO_REGS_DECREASE_FRAME reg1, reg2, frame_adjustment + ldp \reg1, \reg2, [sp], #(\frame_adjustment) + .cfi_restore \reg1 + .cfi_restore \reg2 + .cfi_adjust_cfa_offset -(\frame_adjustment) +.endm + /* File: arm64/entry.S */ /* * Copyright (C) 2016 The Android Open Source Project @@ -334,11 +372,11 @@ codes. ExecuteMterpImpl: .cfi_startproc - stp xPROFILE, x27, [sp, #-80]! - stp xIBASE, xREFS, [sp, #16] - stp xSELF, xINST, [sp, #32] - stp xPC, xFP, [sp, #48] - stp fp, lr, [sp, #64] + SAVE_TWO_REGS_INCREASE_FRAME xPROFILE, x27, 80 + SAVE_TWO_REGS xIBASE, xREFS, 16 + SAVE_TWO_REGS xSELF, xINST, 32 + SAVE_TWO_REGS xPC, xFP, 48 + SAVE_TWO_REGS fp, lr, 64 add fp, sp, #64 /* Remember the return register */ @@ -7226,12 +7264,15 @@ MterpDone: */ cmp wPROFILE, #0 bgt MterpProfileActive // if > 0, we may have some counts to report. - ldp fp, lr, [sp, #64] - ldp xPC, xFP, [sp, #48] - ldp xSELF, xINST, [sp, #32] - ldp xIBASE, xREFS, [sp, #16] - ldp xPROFILE, x27, [sp], #80 + .cfi_remember_state + RESTORE_TWO_REGS fp, lr, 64 + RESTORE_TWO_REGS xPC, xFP, 48 + RESTORE_TWO_REGS xSELF, xINST, 32 + RESTORE_TWO_REGS xIBASE, xREFS, 16 + RESTORE_TWO_REGS_DECREASE_FRAME xPROFILE, x27, 80 ret + .cfi_restore_state // Reset unwind info so following code unwinds. + .cfi_def_cfa_offset 80 // workaround for clang bug: 31975598 MterpProfileActive: mov xINST, x0 // stash return value @@ -7242,11 +7283,11 @@ MterpProfileActive: strh wPROFILE, [x1, #SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET] bl MterpAddHotnessBatch // (method, shadow_frame, self) mov x0, xINST // restore return value - ldp fp, lr, [sp, #64] - ldp xPC, xFP, [sp, #48] - ldp xSELF, xINST, [sp, #32] - ldp xIBASE, xREFS, [sp, #16] - ldp xPROFILE, x27, [sp], #80 + RESTORE_TWO_REGS fp, lr, 64 + RESTORE_TWO_REGS xPC, xFP, 48 + RESTORE_TWO_REGS xSELF, xINST, 32 + RESTORE_TWO_REGS xIBASE, xREFS, 16 + RESTORE_TWO_REGS_DECREASE_FRAME xPROFILE, x27, 80 ret .cfi_endproc 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_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 8551791cb1..68b956b17e 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -2032,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/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 65c86815b5..1283cf0e96 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -122,21 +122,26 @@ void Thread::SetIsGcMarkingAndUpdateEntrypoints(bool is_marking) { CHECK(kUseReadBarrier); tls32_.is_gc_marking = is_marking; UpdateReadBarrierEntrypoints(&tlsPtr_.quick_entrypoints, is_marking); + ResetQuickAllocEntryPointsForThread(is_marking); } void Thread::InitTlsEntryPoints() { // Insert a placeholder so we can easily tell if we call an unimplemented entry point. uintptr_t* begin = reinterpret_cast<uintptr_t*>(&tlsPtr_.jni_entrypoints); - uintptr_t* end = reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(&tlsPtr_.quick_entrypoints) + - sizeof(tlsPtr_.quick_entrypoints)); + uintptr_t* end = reinterpret_cast<uintptr_t*>( + reinterpret_cast<uint8_t*>(&tlsPtr_.quick_entrypoints) + sizeof(tlsPtr_.quick_entrypoints)); for (uintptr_t* it = begin; it != end; ++it) { *it = reinterpret_cast<uintptr_t>(UnimplementedEntryPoint); } InitEntryPoints(&tlsPtr_.jni_entrypoints, &tlsPtr_.quick_entrypoints); } -void Thread::ResetQuickAllocEntryPointsForThread() { - ResetQuickAllocEntryPoints(&tlsPtr_.quick_entrypoints); +void Thread::ResetQuickAllocEntryPointsForThread(bool is_marking) { + if (kUseReadBarrier && kRuntimeISA != kX86_64) { + // Allocation entrypoint switching is currently only implemented for X86_64. + is_marking = true; + } + ResetQuickAllocEntryPoints(&tlsPtr_.quick_entrypoints, is_marking); } class DeoptimizationContextRecord { diff --git a/runtime/thread.h b/runtime/thread.h index 97093a6350..35226f2230 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1007,7 +1007,7 @@ class Thread { tls32_.state_and_flags.as_atomic_int.FetchAndAndSequentiallyConsistent(-1 ^ flag); } - void ResetQuickAllocEntryPointsForThread(); + void ResetQuickAllocEntryPointsForThread(bool is_marking); // Returns the remaining space in the TLAB. size_t TlabSize() const; 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/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/Android.arm_vixl.mk b/test/Android.arm_vixl.mk index 72616a1414..5ae961a6f1 100644 --- a/test/Android.arm_vixl.mk +++ b/test/Android.arm_vixl.mk @@ -16,9 +16,6 @@ # Known broken tests for the ARM VIXL backend. TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ - 103-string-append \ - 137-cfi \ 488-checker-inline-recursive-calls \ 552-checker-sharpening \ 562-checker-no-intermediate \ - 602-deoptimizeable \ |