/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_RUNTIME_MIRROR_DEX_CACHE_INL_H_ #define ART_RUNTIME_MIRROR_DEX_CACHE_INL_H_ #include "dex_cache.h" #include #include "art_field.h" #include "art_method.h" #include "base/atomic_pair.h" #include "base/casts.h" #include "base/enums.h" #include "class_linker.h" #include "dex/dex_file.h" #include "gc_root-inl.h" #include "linear_alloc-inl.h" #include "mirror/call_site.h" #include "mirror/class.h" #include "mirror/method_type.h" #include "obj_ptr.h" #include "object-inl.h" #include "runtime.h" #include "write_barrier-inl.h" #include namespace art { namespace mirror { template static void InitializeArray(std::atomic* array) { DexCachePair::Initialize(array); } template static void InitializeArray(T*) { // Nothing to do. } template T* DexCache::AllocArray(MemberOffset obj_offset, size_t num, LinearAllocKind kind, bool startup) { Thread* self = Thread::Current(); mirror::DexCache* dex_cache = this; if (gUseReadBarrier && self->GetIsGcMarking()) { // Several code paths use DexCache without read-barrier for performance. // We have to check the "to-space" object here to avoid allocating twice. dex_cache = reinterpret_cast(ReadBarrier::Mark(this)); } // DON'T USE 'this' from now on. Runtime* runtime = Runtime::Current(); // Note: in the 1002-notify-startup test, the startup linear alloc can become null // concurrently, even if the runtime is marked at startup. Therefore we should only // fetch it once here. LinearAlloc* startup_linear_alloc = runtime->GetStartupLinearAlloc(); LinearAlloc* alloc = (startup && startup_linear_alloc != nullptr) ? startup_linear_alloc : runtime->GetClassLinker()->GetOrCreateAllocatorForClassLoader(GetClassLoader()); MutexLock mu(self, *Locks::dex_cache_lock_); // Avoid allocation by multiple threads. T* array = dex_cache->GetFieldPtr64(obj_offset); if (array != nullptr) { DCHECK(alloc->Contains(array)); return array; // Other thread just allocated the array. } array = reinterpret_cast(alloc->AllocAlign16(self, RoundUp(num * sizeof(T), 16), kind)); InitializeArray(array); // Ensure other threads see the array initialized. dex_cache->SetField64Volatile(obj_offset, reinterpret_cast64(array)); return array; } template inline DexCachePair::DexCachePair(ObjPtr object, uint32_t index) : object(object), index(index) {} template inline T* DexCachePair::GetObjectForIndex(uint32_t idx) { if (idx != index) { return nullptr; } DCHECK(!object.IsNull()); return object.Read(); } template inline void DexCachePair::Initialize(std::atomic>* dex_cache) { DexCachePair first_elem; first_elem.object = GcRoot(nullptr); first_elem.index = InvalidIndexForSlot(0); dex_cache[0].store(first_elem, std::memory_order_relaxed); } template inline void NativeDexCachePair::Initialize(std::atomic>* dex_cache) { NativeDexCachePair first_elem; first_elem.object = nullptr; first_elem.index = InvalidIndexForSlot(0); auto* array = reinterpret_cast>*>(dex_cache); AtomicPair v(reinterpret_cast(first_elem.object), first_elem.index); AtomicPairStoreRelease(&array[0], v); } template inline void GcRootArray::Set(uint32_t index, T* value) { GcRoot root(value); entries_[index].store(root, std::memory_order_relaxed); } template inline T* GcRootArray::Get(uint32_t index) { return entries_[index].load(std::memory_order_relaxed).Read(); } inline uint32_t DexCache::ClassSize(PointerSize pointer_size) { const uint32_t vtable_entries = Object::kVTableLength; return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size); } inline String* DexCache::GetResolvedString(dex::StringIndex string_idx) { return GetStringsEntry(string_idx.index_); } inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr resolved) { DCHECK(resolved != nullptr); SetStringsEntry(string_idx.index_, resolved.Ptr()); Runtime* const runtime = Runtime::Current(); if (UNLIKELY(runtime->IsActiveTransaction())) { DCHECK(runtime->IsAotCompiler()); runtime->RecordResolveString(this, string_idx); } // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. WriteBarrier::ForEveryFieldWrite(this); } inline void DexCache::ClearString(dex::StringIndex string_idx) { DCHECK(Runtime::Current()->IsAotCompiler()); auto* array = GetStringsArray(); if (array != nullptr) { array->Set(string_idx.index_, nullptr); } auto* strings = GetStrings(); if (UNLIKELY(strings == nullptr)) { return; } strings->Clear(string_idx.index_); } inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) { return GetResolvedTypesEntry(type_idx.index_); } inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) { DCHECK(Runtime::Current()->IsAotCompiler()); auto* array = GetResolvedTypesArray(); if (array != nullptr) { array->Set(type_idx.index_, nullptr); } auto* resolved_types = GetResolvedTypes(); if (UNLIKELY(resolved_types == nullptr)) { return; } resolved_types->Clear(type_idx.index_); } inline MethodType* DexCache::GetResolvedMethodType(dex::ProtoIndex proto_idx) { return GetResolvedMethodTypesEntry(proto_idx.index_); } inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) { DCHECK(resolved != nullptr); SetResolvedMethodTypesEntry(proto_idx.index_, resolved); Runtime* const runtime = Runtime::Current(); if (UNLIKELY(runtime->IsActiveTransaction())) { DCHECK(runtime->IsAotCompiler()); runtime->RecordResolveMethodType(this, proto_idx); } // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. WriteBarrier::ForEveryFieldWrite(this); } inline void DexCache::ClearMethodType(dex::ProtoIndex proto_idx) { DCHECK(Runtime::Current()->IsAotCompiler()); auto* array = GetResolvedMethodTypesArray(); if (array != nullptr) { array->Set(proto_idx.index_, nullptr); } auto* methods = GetResolvedMethodTypes(); if (methods == nullptr) { return; } methods->Clear(proto_idx.index_); } inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds()); GcRootArray* call_sites = GetResolvedCallSites(); if (UNLIKELY(call_sites == nullptr)) { return nullptr; } Atomic>* target = call_sites->GetGcRoot(call_site_idx); return target->load(std::memory_order_seq_cst).Read(); } inline ObjPtr DexCache::SetResolvedCallSite(uint32_t call_site_idx, ObjPtr call_site) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds()); GcRoot null_call_site(nullptr); GcRoot candidate(call_site); GcRootArray* call_sites = GetResolvedCallSites(); if (UNLIKELY(call_sites == nullptr)) { call_sites = AllocateResolvedCallSites(); } Atomic>* target = call_sites->GetGcRoot(call_site_idx); // The first assignment for a given call site wins. if (target->CompareAndSetStrongSequentiallyConsistent(null_call_site, candidate)) { // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. WriteBarrier::ForEveryFieldWrite(this); return call_site; } else { return target->load(std::memory_order_relaxed).Read(); } } inline ArtField* DexCache::GetResolvedField(uint32_t field_idx) { return GetResolvedFieldsEntry(field_idx); } inline void DexCache::SetResolvedField(uint32_t field_idx, ArtField* field) { SetResolvedFieldsEntry(field_idx, field); } inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx) { return GetResolvedMethodsEntry(method_idx); } inline void DexCache::SetResolvedMethod(uint32_t method_idx, ArtMethod* method) { SetResolvedMethodsEntry(method_idx, method); } template inline void VisitDexCachePairs(T* array, size_t num_pairs, const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { // Check both the data pointer and count since the array might be initialized // concurrently on other thread, and we might observe just one of the values. for (size_t i = 0; array != nullptr && i < num_pairs; ++i) { auto source = array->GetPair(i); // NOTE: We need the "template" keyword here to avoid a compilation // failure. GcRoot is a template argument-dependent type and we need to // tell the compiler to treat "Read" as a template rather than a field or // function. Otherwise, on encountering the "<" token, the compiler would // treat "Read" as a field. auto const before = source.object.template Read(); visitor.VisitRootIfNonNull(source.object.AddressWithoutBarrier()); if (source.object.template Read() != before) { array->SetPair(i, source); } } } template void DexCache::VisitDexCachePairRoots(Visitor& visitor, DexCachePair* pairs_begin, DexCachePair* pairs_end) { for (; pairs_begin < pairs_end; pairs_begin++) { visitor.VisitRootIfNonNull(pairs_begin->object.AddressWithoutBarrier()); } } template inline void DexCache::VisitReferences(ObjPtr klass, const Visitor& visitor) { // Visit instance fields first. VisitInstanceFieldsReferences(klass, visitor); // Visit arrays after. if (kVisitNativeRoots) { VisitNativeRoots(visitor); } } template inline void DexCache::VisitNativeRoots(const Visitor& visitor) { VisitDexCachePairs( GetStrings(), NumStrings(), visitor); VisitDexCachePairs( GetResolvedTypes(), NumResolvedTypes(), visitor); VisitDexCachePairs( GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor); GcRootArray* resolved_call_sites = GetResolvedCallSites(); size_t num_call_sites = NumResolvedCallSites(); for (size_t i = 0; resolved_call_sites != nullptr && i != num_call_sites; ++i) { visitor.VisitRootIfNonNull(resolved_call_sites->GetGcRootAddress(i)->AddressWithoutBarrier()); } // Dex cache arrays can be reset and cleared during app startup. Assert we do not get // suspended to ensure the arrays are not deallocated. ScopedAssertNoThreadSuspension soants("dex caches"); GcRootArray* resolved_types = GetResolvedTypesArray(); size_t num_resolved_types = NumResolvedTypesArray(); for (size_t i = 0; resolved_types != nullptr && i != num_resolved_types; ++i) { visitor.VisitRootIfNonNull(resolved_types->GetGcRootAddress(i)->AddressWithoutBarrier()); } GcRootArray* resolved_strings = GetStringsArray(); size_t num_resolved_strings = NumStringsArray(); for (size_t i = 0; resolved_strings != nullptr && i != num_resolved_strings; ++i) { visitor.VisitRootIfNonNull(resolved_strings->GetGcRootAddress(i)->AddressWithoutBarrier()); } GcRootArray* resolved_method_types = GetResolvedMethodTypesArray(); size_t num_resolved_method_types = NumResolvedMethodTypesArray(); for (size_t i = 0; resolved_method_types != nullptr && i != num_resolved_method_types; ++i) { visitor.VisitRootIfNonNull(resolved_method_types->GetGcRootAddress(i)->AddressWithoutBarrier()); } } template inline ObjPtr DexCache::GetLocation() { return GetFieldObject( OFFSET_OF_OBJECT_MEMBER(DexCache, location_)); } } // namespace mirror } // namespace art #endif // ART_RUNTIME_MIRROR_DEX_CACHE_INL_H_