Add PreResolved strings dex cache array
For app images, this dex cache may be prepopulated to speed up
application startup. This new dex cache array is created when
--resolve-startup-const-strings=true.
Test: test-art-host
Bug: 116059983
Bug: 118385560
Change-Id: I379dc15174281665d7f4ceb106f7fbf51f89b921
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index e240167..9b45b48 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1415,9 +1415,9 @@
for (size_t offset_index = 0; offset_index < num_string_offsets; ++offset_index) {
uint32_t base_offset = sro_base[offset_index].first;
- if (HasDexCacheNativeRefTag(base_offset)) {
- base_offset = ClearDexCacheNativeRefTag(base_offset);
- DCHECK_ALIGNED(base_offset, 2);
+ if (HasDexCacheStringNativeRefTag(base_offset)) {
+ base_offset = ClearDexCacheNativeRefTags(base_offset);
+ DCHECK_ALIGNED(base_offset, 2);
ObjPtr<mirror::DexCache> dex_cache =
reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset);
@@ -1437,10 +1437,27 @@
dex_cache->GetStrings()[string_index].store(
mirror::StringDexCachePair(it->second, source.index));
}
+ } else if (HasDexCachePreResolvedStringNativeRefTag(base_offset)) {
+ base_offset = ClearDexCacheNativeRefTags(base_offset);
+ DCHECK_ALIGNED(base_offset, 2);
+ ObjPtr<mirror::DexCache> dex_cache =
+ reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset);
+ uint32_t string_index = sro_base[offset_index].second;
+
+ ObjPtr<mirror::String> referred_string =
+ dex_cache->GetPreResolvedStrings()[string_index].Read();
+ DCHECK(referred_string != nullptr);
+
+ auto it = intern_remap.find(referred_string.Ptr());
+ if (it != intern_remap.end()) {
+ // Because we are not using a helper function we need to mark the GC card manually.
+ WriteBarrier::ForEveryFieldWrite(dex_cache);
+ dex_cache->GetPreResolvedStrings()[string_index] = GcRoot<mirror::String>(it->second);
+ }
} else {
uint32_t raw_member_offset = sro_base[offset_index].second;
- DCHECK_ALIGNED(base_offset, 2);
+ DCHECK_ALIGNED(base_offset, 2);
DCHECK_ALIGNED(raw_member_offset, 2);
ObjPtr<mirror::Object> obj_ptr =
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 91636dc..56fdd06 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -662,12 +662,14 @@
DexCacheOffsets() : CheckOffsets<mirror::DexCache>(false, "Ljava/lang/DexCache;") {
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_preresolved_strings_), "numPreResolvedStrings");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_call_sites_), "numResolvedCallSites");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, preresolved_strings_), "preResolvedStrings");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_call_sites_), "resolvedCallSites");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes");
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index 6a3faef..fcc3007 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -151,6 +151,12 @@
void VisitMarkedRange(uintptr_t visit_begin, uintptr_t visit_end, Visitor&& visitor) const
NO_THREAD_SAFETY_ANALYSIS;
+ // Visit all of the set bits in HeapBegin(), HeapLimit().
+ template <typename Visitor>
+ void VisitAllMarked(Visitor&& visitor) const {
+ VisitMarkedRange(HeapBegin(), HeapLimit(), visitor);
+ }
+
// Visits set bits in address order. The callback is not permitted to change the bitmap bits or
// max during the traversal.
template <typename Visitor>
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 9e67957..db4a48c 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1172,6 +1172,19 @@
}
dex_cache->FixupResolvedCallSites<kWithoutReadBarrier>(new_call_sites, fixup_adapter);
}
+
+ GcRoot<mirror::String>* preresolved_strings = dex_cache->GetPreResolvedStrings();
+ if (preresolved_strings != nullptr) {
+ GcRoot<mirror::String>* new_array = fixup_adapter.ForwardObject(preresolved_strings);
+ if (preresolved_strings != new_array) {
+ dex_cache->SetPreResolvedStrings(new_array);
+ }
+ const size_t num_preresolved_strings = dex_cache->NumPreResolvedStrings();
+ for (size_t j = 0; j < num_preresolved_strings; ++j) {
+ new_array[j] = GcRoot<mirror::String>(
+ fixup_adapter(new_array[j].Read<kWithoutReadBarrier>()));
+ }
+ }
}
}
{
@@ -1731,6 +1744,10 @@
dex_cache,
mirror::DexCache::ResolvedCallSitesOffset(),
dex_cache->NumResolvedCallSites<kVerifyNone>());
+ FixupDexCacheArray<GcRoot<mirror::String>>(
+ dex_cache,
+ mirror::DexCache::PreResolvedStringsOffset(),
+ dex_cache->NumPreResolvedStrings<kVerifyNone>());
}
private:
@@ -1775,6 +1792,11 @@
PatchGcRoot(diff_, &array[index]);
}
+ void FixupDexCacheArrayEntry(GcRoot<mirror::String>* array, uint32_t index)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ PatchGcRoot(diff_, &array[index]);
+ }
+
template <typename EntryType>
void FixupDexCacheArray(mirror::DexCache* dex_cache,
MemberOffset array_offset,
diff --git a/runtime/image.cc b/runtime/image.cc
index e7f4486..376742a 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '5', '\0' }; // Remove relocation section.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '6', '\0' }; // Add metadata section.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index 0dec5f7..d925956 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -236,6 +236,7 @@
kSectionInternedStrings,
kSectionClassTable,
kSectionStringReferenceOffsets,
+ kSectionMetadata,
kSectionImageBitmap,
kSectionCount, // Number of elements in enum.
};
@@ -293,6 +294,10 @@
return GetImageSection(kSectionStringReferenceOffsets);
}
+ const ImageSection& GetMetadataSection() const {
+ return GetImageSection(kSectionMetadata);
+ }
+
const ImageSection& GetImageBitmapSection() const {
return GetImageSection(kSectionImageBitmap);
}
@@ -462,22 +467,45 @@
* to managed objects and pointers to native reference arrays.
*/
template<typename T>
-T SetDexCacheNativeRefTag(T val) {
+T SetDexCacheStringNativeRefTag(T val) {
static_assert(std::is_integral<T>::value, "Expected integral type.");
return val | 1u;
}
/*
+ * Tags the second last bit. Used by AppImage logic to differentiate between pointers
+ * to managed objects and pointers to native reference arrays.
+ */
+template<typename T>
+T SetDexCachePreResolvedStringNativeRefTag(T val) {
+ static_assert(std::is_integral<T>::value, "Expected integral type.");
+
+ return val | 2u;
+}
+
+/*
* Retrieves the value of the last bit. Used by AppImage logic to
* differentiate between pointers to managed objects and pointers to native
* reference arrays.
*/
template<typename T>
-bool HasDexCacheNativeRefTag(T val) {
+bool HasDexCacheStringNativeRefTag(T val) {
static_assert(std::is_integral<T>::value, "Expected integral type.");
- return (val & 1u) == 1u;
+ return (val & 1u) != 0u;
+}
+
+/*
+ * Retrieves the value of the second last bit. Used by AppImage logic to
+ * differentiate between pointers to managed objects and pointers to native
+ * reference arrays.
+ */
+template<typename T>
+bool HasDexCachePreResolvedStringNativeRefTag(T val) {
+ static_assert(std::is_integral<T>::value, "Expected integral type.");
+
+ return (val & 2u) != 0u;
}
/*
@@ -486,10 +514,10 @@
* reference arrays.
*/
template<typename T>
-T ClearDexCacheNativeRefTag(T val) {
+T ClearDexCacheNativeRefTags(T val) {
static_assert(std::is_integral<T>::value, "Expected integral type.");
- return val & ~1u;
+ return val & ~3u;
}
std::ostream& operator<<(std::ostream& os, const ImageHeader::ImageMethod& policy);
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 13eaf3d..47b621a 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -84,6 +84,15 @@
}
inline String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
+ const uint32_t num_preresolved_strings = NumPreResolvedStrings();
+ if (num_preresolved_strings != 0u) {
+ DCHECK_LT(string_idx.index_, num_preresolved_strings);
+ DCHECK_EQ(num_preresolved_strings, GetDexFile()->NumStringIds());
+ mirror::String* string = GetPreResolvedStrings()[string_idx.index_].Read();
+ if (LIKELY(string != nullptr)) {
+ return string;
+ }
+ }
return GetStrings()[StringSlotIndex(string_idx)].load(
std::memory_order_relaxed).GetObjectForIndex(string_idx.index_);
}
@@ -101,6 +110,18 @@
WriteBarrier::ForEveryFieldWrite(this);
}
+inline void DexCache::SetPreResolvedString(dex::StringIndex string_idx,
+ ObjPtr<String> resolved) {
+ DCHECK(resolved != nullptr);
+ DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
+ GetPreResolvedStrings()[string_idx.index_] = GcRoot<mirror::String>(resolved);
+ Runtime* const runtime = Runtime::Current();
+ CHECK(runtime->IsAotCompiler());
+ CHECK(!runtime->IsActiveTransaction());
+ // 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());
uint32_t slot_idx = StringSlotIndex(string_idx);
@@ -344,6 +365,12 @@
for (size_t i = 0; i != num_call_sites; ++i) {
visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier());
}
+
+ GcRoot<mirror::String>* const preresolved_strings = GetPreResolvedStrings();
+ const size_t num_preresolved_strings = NumPreResolvedStrings();
+ for (size_t i = 0; i != num_preresolved_strings; ++i) {
+ visitor.VisitRootIfNonNull(preresolved_strings[i].AddressWithoutBarrier());
+ }
}
}
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 661f954..8d2b838 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -172,6 +172,21 @@
dex_file->NumCallSiteIds());
}
+void DexCache::AddPreResolvedStringsArray() {
+ DCHECK_EQ(NumPreResolvedStrings(), 0u);
+ Thread* const self = Thread::Current();
+ LinearAlloc* linear_alloc = Runtime::Current()->GetLinearAlloc();
+ const size_t num_strings = GetDexFile()->NumStringIds();
+ SetField32<false>(NumPreResolvedStringsOffset(), num_strings);
+ GcRoot<mirror::String>* strings =
+ linear_alloc->AllocArray<GcRoot<mirror::String>>(self, num_strings);
+ CHECK(strings != nullptr);
+ SetPreResolvedStrings(strings);
+ for (size_t i = 0; i < GetDexFile()->NumStringIds(); ++i) {
+ CHECK(GetPreResolvedStrings()[i].Read() == nullptr);
+ }
+}
+
void DexCache::Init(const DexFile* dex_file,
ObjPtr<String> location,
StringDexCacheType* strings,
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 6149f9c..58b199d 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -217,6 +217,10 @@
return OFFSET_OF_OBJECT_MEMBER(DexCache, strings_);
}
+ static constexpr MemberOffset PreResolvedStringsOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, preresolved_strings_);
+ }
+
static constexpr MemberOffset ResolvedTypesOffset() {
return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_);
}
@@ -241,6 +245,10 @@
return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
}
+ static constexpr MemberOffset NumPreResolvedStringsOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, num_preresolved_strings_);
+ }
+
static constexpr MemberOffset NumResolvedTypesOffset() {
return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_types_);
}
@@ -261,12 +269,20 @@
return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_);
}
+ static constexpr size_t PreResolvedStringsAlignment() {
+ return alignof(GcRoot<mirror::String>);
+ }
+
String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_);
void SetResolvedString(dex::StringIndex string_idx, ObjPtr<mirror::String> resolved) ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetPreResolvedString(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(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -318,10 +334,21 @@
return GetFieldPtr64<StringDexCacheType*, kVerifyFlags>(StringsOffset());
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ GcRoot<mirror::String>* GetPreResolvedStrings() ALWAYS_INLINE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldPtr64<GcRoot<mirror::String>*, kVerifyFlags>(PreResolvedStringsOffset());
+ }
+
void SetStrings(StringDexCacheType* strings) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
SetFieldPtr<false>(StringsOffset(), strings);
}
+ void SetPreResolvedStrings(GcRoot<mirror::String>* strings)
+ ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+ SetFieldPtr<false>(PreResolvedStringsOffset(), strings);
+ }
+
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldPtr<TypeDexCacheType*, kVerifyFlags>(ResolvedTypesOffset());
@@ -384,6 +411,11 @@
}
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ size_t NumPreResolvedStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetField32<kVerifyFlags>(NumPreResolvedStringsOffset());
+ }
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
size_t NumResolvedTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetField32<kVerifyFlags>(NumResolvedTypesOffset());
}
@@ -429,12 +461,18 @@
NativeDexCachePair<T> pair,
PointerSize ptr_size);
+ static size_t PreResolvedStringsSize(size_t num_strings) {
+ return sizeof(GcRoot<mirror::String>) * num_strings;
+ }
+
uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_);
uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+ void AddPreResolvedStringsArray() REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
void Init(const DexFile* dex_file,
ObjPtr<String> location,
@@ -516,22 +554,25 @@
#endif
HeapReference<String> location_;
- // Number of elements in the call_sites_ array. Note that this appears here
- // because of our packing logic for 32 bit fields.
- uint32_t num_resolved_call_sites_;
+ // Number of elements in the preresolved_strings_ array. Note that this appears here because of
+ // our packing logic for 32 bit fields.
+ uint32_t num_preresolved_strings_;
- uint64_t dex_file_; // const DexFile*
- uint64_t resolved_call_sites_; // GcRoot<CallSite>* array with num_resolved_call_sites_
- // elements.
- uint64_t resolved_fields_; // std::atomic<FieldDexCachePair>*, array with
- // num_resolved_fields_ elements.
- uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with
- // num_resolved_method_types_ elements.
- uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements.
- uint64_t resolved_types_; // TypeDexCacheType*, array with num_resolved_types_ elements.
- uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_
- // elements.
+ uint64_t dex_file_; // const DexFile*
+ uint64_t preresolved_strings_; // GcRoot<mirror::String*> array with num_preresolved_strings
+ // elements.
+ uint64_t resolved_call_sites_; // GcRoot<CallSite>* array with num_resolved_call_sites_
+ // elements.
+ uint64_t resolved_fields_; // std::atomic<FieldDexCachePair>*, array with
+ // num_resolved_fields_ elements.
+ uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with
+ // num_resolved_method_types_ elements.
+ uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements.
+ uint64_t resolved_types_; // TypeDexCacheType*, array with num_resolved_types_ elements.
+ uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_
+ // elements.
+ uint32_t num_resolved_call_sites_; // Number of elements in the call_sites_ array.
uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array.
uint32_t num_resolved_method_types_; // Number of elements in the resolved_method_types_ array.
uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array.