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/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 898940a..88ce324 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -2079,8 +2079,8 @@
ScratchFile profile_file;
std::vector<uint16_t> methods;
std::vector<dex::TypeIndex> classes;
+ std::unique_ptr<const DexFile> dex(OpenTestDexFile("StringLiterals"));
{
- std::unique_ptr<const DexFile> dex(OpenTestDexFile("StringLiterals"));
for (ClassAccessor accessor : dex->GetClasses()) {
if (accessor.GetDescriptor() == std::string("LStringLiterals$StartupClass;")) {
classes.push_back(accessor.GetClassIdx());
@@ -2140,15 +2140,43 @@
seen.insert(str.Read()->ToModifiedUtf8());
}
});
+ // Ensure that the dex cache has a preresolved string array.
+ std::set<std::string> preresolved_seen;
+ bool saw_dexcache = false;
+ space->GetLiveBitmap()->VisitAllMarked(
+ [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (obj->IsDexCache<kVerifyNone>()) {
+ ObjPtr<mirror::DexCache> dex_cache = obj->AsDexCache();
+ GcRoot<mirror::String>* preresolved_strings = dex_cache->GetPreResolvedStrings();
+ ASSERT_EQ(dex->NumStringIds(), dex_cache->NumPreResolvedStrings());
+ for (size_t i = 0; i < dex_cache->NumPreResolvedStrings(); ++i) {
+ ObjPtr<mirror::String> string = preresolved_strings[i].Read<kWithoutReadBarrier>();
+ if (string != nullptr) {
+ preresolved_seen.insert(string->ToModifiedUtf8());
+ }
+ }
+ saw_dexcache = true;
+ }
+ });
+ ASSERT_TRUE(saw_dexcache);
+ // Everything in the preresolved array should also be in the intern table.
+ for (const std::string& str : preresolved_seen) {
+ EXPECT_TRUE(seen.find(str) != seen.end());
+ }
// Normal methods
- EXPECT_TRUE(seen.find("Loading ") != seen.end());
- EXPECT_TRUE(seen.find("Starting up") != seen.end());
- EXPECT_TRUE(seen.find("abcd.apk") != seen.end());
+ EXPECT_TRUE(preresolved_seen.find("Loading ") != preresolved_seen.end());
+ EXPECT_TRUE(preresolved_seen.find("Starting up") != preresolved_seen.end());
+ EXPECT_TRUE(preresolved_seen.find("abcd.apk") != preresolved_seen.end());
EXPECT_TRUE(seen.find("Unexpected error") == seen.end());
EXPECT_TRUE(seen.find("Shutting down!") == seen.end());
+ EXPECT_TRUE(preresolved_seen.find("Unexpected error") == preresolved_seen.end());
+ EXPECT_TRUE(preresolved_seen.find("Shutting down!") == preresolved_seen.end());
// Classes initializers
- EXPECT_TRUE(seen.find("Startup init") != seen.end());
+ EXPECT_TRUE(preresolved_seen.find("Startup init") != preresolved_seen.end());
EXPECT_TRUE(seen.find("Other class init") == seen.end());
+ EXPECT_TRUE(preresolved_seen.find("Other class init") == preresolved_seen.end());
+ // Expect the sets match.
+ EXPECT_GE(seen.size(), preresolved_seen.size());
}
}
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index be62058..33af833 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -288,11 +288,16 @@
for (const HeapReferencePointerInfo& ref_info : string_ref_info) {
uint32_t base_offset;
- if (HasDexCacheNativeRefTag(ref_info.first)) {
+ if (HasDexCacheStringNativeRefTag(ref_info.first)) {
++native_string_refs;
- auto* obj_ptr = reinterpret_cast<mirror::Object*>(ClearDexCacheNativeRefTag(
+ auto* obj_ptr = reinterpret_cast<mirror::Object*>(ClearDexCacheNativeRefTags(
ref_info.first));
- base_offset = SetDexCacheNativeRefTag(GetImageOffset(obj_ptr));
+ base_offset = SetDexCacheStringNativeRefTag(GetImageOffset(obj_ptr));
+ } else if (HasDexCachePreResolvedStringNativeRefTag(ref_info.first)) {
+ ++native_string_refs;
+ auto* obj_ptr = reinterpret_cast<mirror::Object*>(ClearDexCacheNativeRefTags(
+ ref_info.first));
+ base_offset = SetDexCachePreResolvedStringNativeRefTag(GetImageOffset(obj_ptr));
} else {
++managed_string_refs;
base_offset = GetImageOffset(reinterpret_cast<mirror::Object*>(ref_info.first));
@@ -447,7 +452,19 @@
if (IsValidAppImageStringReference(referred_string)) {
++string_info_collected;
visitor.AddStringRefInfo(
- SetDexCacheNativeRefTag(reinterpret_cast<uintptr_t>(object.Ptr())), index);
+ SetDexCacheStringNativeRefTag(reinterpret_cast<uintptr_t>(object.Ptr())), index);
+ }
+ }
+
+ // Visit all of the preinitialized strings.
+ GcRoot<mirror::String>* preresolved_strings = dex_cache->GetPreResolvedStrings();
+ for (size_t index = 0; index < dex_cache->NumPreResolvedStrings(); ++index) {
+ ObjPtr<mirror::String> referred_string = preresolved_strings[index].Read();
+ if (IsValidAppImageStringReference(referred_string)) {
+ ++string_info_collected;
+ visitor.AddStringRefInfo(SetDexCachePreResolvedStringNativeRefTag(
+ reinterpret_cast<uintptr_t>(object.Ptr())),
+ index);
}
}
@@ -852,15 +869,27 @@
DCHECK_EQ(dex_file->NumStringIds() != 0u, dex_cache->GetStrings() != nullptr);
AddDexCacheArrayRelocation(dex_cache->GetStrings(), start + layout.StringsOffset(), oat_index);
- if (dex_cache->GetResolvedMethodTypes() != nullptr) {
- AddDexCacheArrayRelocation(dex_cache->GetResolvedMethodTypes(),
- start + layout.MethodTypesOffset(),
- oat_index);
- }
- if (dex_cache->GetResolvedCallSites() != nullptr) {
- AddDexCacheArrayRelocation(dex_cache->GetResolvedCallSites(),
- start + layout.CallSitesOffset(),
- oat_index);
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedMethodTypes(),
+ start + layout.MethodTypesOffset(),
+ oat_index);
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedCallSites(),
+ start + layout.CallSitesOffset(),
+ oat_index);
+
+ // Preresolved strings aren't part of the special layout.
+ GcRoot<mirror::String>* preresolved_strings = dex_cache->GetPreResolvedStrings();
+ if (preresolved_strings != nullptr) {
+ DCHECK(!IsInBootImage(preresolved_strings));
+ // Add the array to the metadata section.
+ const size_t count = dex_cache->NumPreResolvedStrings();
+ auto bin = BinTypeForNativeRelocationType(NativeObjectRelocationType::kGcRootPointer);
+ for (size_t i = 0; i < count; ++i) {
+ native_object_relocations_.emplace(&preresolved_strings[i],
+ NativeObjectRelocation { oat_index,
+ image_info.GetBinSlotSize(bin),
+ NativeObjectRelocationType::kGcRootPointer });
+ image_info.IncrementBinSlotSize(bin, sizeof(GcRoot<mirror::Object>));
+ }
}
}
}
@@ -2340,9 +2369,21 @@
sizeof(typename decltype(string_reference_offsets_)::value_type) *
num_string_references_);
+ /*
+ * Metadata section.
+ */
+
+ // Round up to the alignment of the offsets we are going to store.
+ cur_pos = RoundUp(string_reference_offsets.End(),
+ mirror::DexCache::PreResolvedStringsAlignment());
+
+ const ImageSection& metadata_section =
+ sections[ImageHeader::kSectionMetadata] =
+ ImageSection(cur_pos, GetBinSlotSize(Bin::kMetadata));
+
// Return the number of bytes described by these sections, and the sections
// themselves.
- return make_pair(string_reference_offsets.End(), std::move(sections));
+ return make_pair(metadata_section.End(), std::move(sections));
}
void ImageWriter::CreateHeader(size_t oat_index) {
@@ -2546,6 +2587,12 @@
new(dest)ImtConflictTable(orig_table->NumEntries(target_ptr_size_), target_ptr_size_));
break;
}
+ case NativeObjectRelocationType::kGcRootPointer: {
+ auto* orig_pointer = reinterpret_cast<GcRoot<mirror::Object>*>(pair.first);
+ auto* dest_pointer = reinterpret_cast<GcRoot<mirror::Object>*>(dest);
+ CopyAndFixupReference(dest_pointer->AddressWithoutBarrier(), orig_pointer->Read());
+ break;
+ }
}
}
// Fixup the image method roots.
@@ -2966,6 +3013,12 @@
copy_dex_cache,
DexCache::ResolvedCallSitesOffset(),
orig_dex_cache->NumResolvedCallSites());
+ if (orig_dex_cache->GetPreResolvedStrings() != nullptr) {
+ CopyAndFixupPointer(copy_dex_cache,
+ DexCache::PreResolvedStringsOffset(),
+ orig_dex_cache->GetPreResolvedStrings(),
+ PointerSize::k64);
+ }
// Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving
// compiler pointers in here will make the output non-deterministic.
@@ -3179,6 +3232,8 @@
return Bin::kImTable;
case NativeObjectRelocationType::kIMTConflictTable:
return Bin::kIMTConflictTable;
+ case NativeObjectRelocationType::kGcRootPointer:
+ return Bin::kMetadata;
}
UNREACHABLE();
}
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index e019a50..06c694c 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -209,6 +209,8 @@
kIMTConflictTable,
// Runtime methods (always clean, do not have a length prefix array).
kRuntimeMethod,
+ // Metadata bin for data that is temporary during image lifetime.
+ kMetadata,
// Dex cache arrays have a special slot for PC-relative addressing. Since they are
// huge, and as such their dirtiness is not important for the clean/dirty separation,
// we arbitrarily keep them at the end of the native data.
@@ -226,6 +228,7 @@
kArtMethodArrayClean,
kArtMethodDirty,
kArtMethodArrayDirty,
+ kGcRootPointer,
kRuntimeMethod,
kIMTable,
kIMTConflictTable,