summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mathieu Chartier <mathieuc@google.com> 2018-10-23 12:55:34 -0700
committer Mathieu Chartier <mathieuc@google.com> 2018-10-30 16:03:00 +0000
commit1ca718ec2de53fb5ef2a9211bfcc921311775c90 (patch)
treef5ce8c3f4edd022765c343cc5a9df8ebd292f0ec
parentce2a00daa92670a4fc01ef59fdbc3769a846f69c (diff)
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
-rw-r--r--compiler/driver/compiler_driver.cc8
-rw-r--r--compiler/driver/compiler_options_map.def2
-rw-r--r--dex2oat/dex2oat_test.cc38
-rw-r--r--dex2oat/linker/image_writer.cc83
-rw-r--r--dex2oat/linker/image_writer.h3
-rw-r--r--oatdump/oatdump.cc80
-rw-r--r--runtime/class_linker.cc25
-rw-r--r--runtime/class_linker_test.cc2
-rw-r--r--runtime/gc/accounting/space_bitmap.h6
-rw-r--r--runtime/gc/space/image_space.cc22
-rw-r--r--runtime/image.cc2
-rw-r--r--runtime/image.h38
-rw-r--r--runtime/mirror/dex_cache-inl.h27
-rw-r--r--runtime/mirror/dex_cache.cc15
-rw-r--r--runtime/mirror/dex_cache.h73
15 files changed, 334 insertions, 90 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 864b215a90..df6e8a83e1 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -719,6 +719,10 @@ void CompilerDriver::ResolveConstStrings(const std::vector<const DexFile*>& dex_
for (const DexFile* dex_file : dex_files) {
dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file));
+ if (only_startup_strings) {
+ // When resolving startup strings, create the preresolved strings array.
+ dex_cache->AddPreResolvedStringsArray();
+ }
TimingLogger::ScopedTiming t("Resolve const-string Strings", timings);
for (ClassAccessor accessor : dex_file->GetClasses()) {
@@ -757,6 +761,10 @@ void CompilerDriver::ResolveConstStrings(const std::vector<const DexFile*>& dex_
: inst->VRegB_31c());
ObjPtr<mirror::String> string = class_linker->ResolveString(string_index, dex_cache);
CHECK(string != nullptr) << "Could not allocate a string when forcing determinism";
+ if (only_startup_strings) {
+ dex_cache->GetPreResolvedStrings()[string_index.index_] =
+ GcRoot<mirror::String>(string);
+ }
++num_instructions;
break;
}
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index a593240365..1ec34ec73a 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -52,7 +52,7 @@ COMPILER_OPTIONS_KEY (Unit, Baseline)
COMPILER_OPTIONS_KEY (double, TopKProfileThreshold)
COMPILER_OPTIONS_KEY (bool, AbortOnHardVerifierFailure)
COMPILER_OPTIONS_KEY (bool, AbortOnSoftVerifierFailure)
-COMPILER_OPTIONS_KEY (bool, ResolveStartupConstStrings, false)
+COMPILER_OPTIONS_KEY (bool, ResolveStartupConstStrings, kIsDebugBuild)
COMPILER_OPTIONS_KEY (std::string, DumpInitFailures)
COMPILER_OPTIONS_KEY (std::string, DumpCFG)
COMPILER_OPTIONS_KEY (Unit, DumpCFGAppend)
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 898940a948..88ce324273 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -2079,8 +2079,8 @@ TEST_F(Dex2oatTest, AppImageResolveStrings) {
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 @@ TEST_F(Dex2oatTest, AppImageResolveStrings) {
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 be620587b0..33af83379e 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -288,11 +288,16 @@ bool ImageWriter::PrepareImageAddressSpace(TimingLogger* timings) {
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 @@ std::vector<ImageWriter::HeapReferencePointerInfo> ImageWriter::CollectStringRef
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 @@ void ImageWriter::PrepareDexCacheArraySlots() {
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 @@ std::pair<size_t, std::vector<ImageSection>> ImageWriter::ImageInfo::CreateImage
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 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) {
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 @@ void ImageWriter::FixupDexCache(DexCache* orig_dex_cache, DexCache* copy_dex_cac
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 @@ ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocat
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 e019a501a2..06c694c793 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -209,6 +209,8 @@ class ImageWriter final {
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 @@ class ImageWriter final {
kArtMethodArrayClean,
kArtMethodDirty,
kArtMethodArrayDirty,
+ kGcRootPointer,
kRuntimeMethod,
kIMTable,
kIMTConflictTable,
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 86a36f292b..0610b971b2 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1953,6 +1953,8 @@ class ImageDumper {
const auto& dex_cache_arrays_section = image_header_.GetDexCacheArraysSection();
const auto& intern_section = image_header_.GetInternedStringsSection();
const auto& class_table_section = image_header_.GetClassTableSection();
+ const auto& sro_section = image_header_.GetImageStringReferenceOffsetsSection();
+ const auto& metadata_section = image_header_.GetMetadataSection();
const auto& bitmap_section = image_header_.GetImageBitmapSection();
stats_.header_bytes = header_bytes;
@@ -1999,6 +2001,9 @@ class ImageDumper {
stats_.dex_cache_arrays_bytes += dex_cache_arrays_section.Size();
stats_.interned_strings_bytes += intern_section.Size();
stats_.class_table_bytes += class_table_section.Size();
+ stats_.sro_offset_bytes += sro_section.Size();
+ stats_.metadata_bytes += metadata_section.Size();
+
stats_.Dump(os, indent_os);
os << "\n";
@@ -2415,55 +2420,38 @@ class ImageDumper {
public:
struct Stats {
- size_t oat_file_bytes;
- size_t file_bytes;
-
- size_t header_bytes;
- size_t object_bytes;
- size_t art_field_bytes;
- size_t art_method_bytes;
- size_t dex_cache_arrays_bytes;
- size_t interned_strings_bytes;
- size_t class_table_bytes;
- size_t bitmap_bytes;
- size_t alignment_bytes;
-
- size_t managed_code_bytes;
- size_t managed_code_bytes_ignoring_deduplication;
- size_t native_to_managed_code_bytes;
- size_t class_initializer_code_bytes;
- size_t large_initializer_code_bytes;
- size_t large_method_code_bytes;
-
- size_t vmap_table_bytes;
-
- size_t dex_instruction_bytes;
+ size_t oat_file_bytes = 0u;
+ size_t file_bytes = 0u;
+
+ size_t header_bytes = 0u;
+ size_t object_bytes = 0u;
+ size_t art_field_bytes = 0u;
+ size_t art_method_bytes = 0u;
+ size_t dex_cache_arrays_bytes = 0u;
+ size_t interned_strings_bytes = 0u;
+ size_t class_table_bytes = 0u;
+ size_t sro_offset_bytes = 0u;
+ size_t metadata_bytes = 0u;
+ size_t bitmap_bytes = 0u;
+ size_t alignment_bytes = 0u;
+
+ size_t managed_code_bytes = 0u;
+ size_t managed_code_bytes_ignoring_deduplication = 0u;
+ size_t native_to_managed_code_bytes = 0u;
+ size_t class_initializer_code_bytes = 0u;
+ size_t large_initializer_code_bytes = 0u;
+ size_t large_method_code_bytes = 0u;
+
+ size_t vmap_table_bytes = 0u;
+
+ size_t dex_instruction_bytes = 0u;
std::vector<ArtMethod*> method_outlier;
std::vector<size_t> method_outlier_size;
std::vector<double> method_outlier_expansion;
std::vector<std::pair<std::string, size_t>> oat_dex_file_sizes;
- Stats()
- : oat_file_bytes(0),
- file_bytes(0),
- header_bytes(0),
- object_bytes(0),
- art_field_bytes(0),
- art_method_bytes(0),
- dex_cache_arrays_bytes(0),
- interned_strings_bytes(0),
- class_table_bytes(0),
- bitmap_bytes(0),
- alignment_bytes(0),
- managed_code_bytes(0),
- managed_code_bytes_ignoring_deduplication(0),
- native_to_managed_code_bytes(0),
- class_initializer_code_bytes(0),
- large_initializer_code_bytes(0),
- large_method_code_bytes(0),
- vmap_table_bytes(0),
- dex_instruction_bytes(0) {}
+ Stats() {}
struct SizeAndCount {
SizeAndCount(size_t bytes_in, size_t count_in) : bytes(bytes_in), count(count_in) {}
@@ -2617,6 +2605,8 @@ class ImageDumper {
"dex_cache_arrays_bytes = %8zd (%2.0f%% of art file bytes)\n"
"interned_string_bytes = %8zd (%2.0f%% of art file bytes)\n"
"class_table_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "sro_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "metadata_bytes = %8zd (%2.0f%% of art file bytes)\n"
"bitmap_bytes = %8zd (%2.0f%% of art file bytes)\n"
"alignment_bytes = %8zd (%2.0f%% of art file bytes)\n\n",
header_bytes, PercentOfFileBytes(header_bytes),
@@ -2628,13 +2618,15 @@ class ImageDumper {
interned_strings_bytes,
PercentOfFileBytes(interned_strings_bytes),
class_table_bytes, PercentOfFileBytes(class_table_bytes),
+ sro_offset_bytes, PercentOfFileBytes(sro_offset_bytes),
+ metadata_bytes, PercentOfFileBytes(metadata_bytes),
bitmap_bytes, PercentOfFileBytes(bitmap_bytes),
alignment_bytes, PercentOfFileBytes(alignment_bytes))
<< std::flush;
CHECK_EQ(file_bytes,
header_bytes + object_bytes + art_field_bytes + art_method_bytes +
dex_cache_arrays_bytes + interned_strings_bytes + class_table_bytes +
- bitmap_bytes + alignment_bytes);
+ sro_offset_bytes + metadata_bytes + bitmap_bytes + alignment_bytes);
}
os << "object_bytes breakdown:\n";
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index e240167ba8..9b45b48fe2 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1415,9 +1415,9 @@ void AppImageLoadingHelper::UpdateInternStrings(
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 @@ void AppImageLoadingHelper::UpdateInternStrings(
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 91636dc7e9..56fdd06ff2 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -662,12 +662,14 @@ struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> {
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 6a3faefe08..fcc3007acd 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -151,6 +151,12 @@ class SpaceBitmap {
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 9e679573bd..db4a48c7d7 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1172,6 +1172,19 @@ class ImageSpace::Loader {
}
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 @@ class ImageSpace::BootImageLoader {
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 @@ class ImageSpace::BootImageLoader {
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 e7f44864b2..376742afbc 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 0dec5f71ab..d925956f7d 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -236,6 +236,7 @@ class PACKED(4) ImageHeader {
kSectionInternedStrings,
kSectionClassTable,
kSectionStringReferenceOffsets,
+ kSectionMetadata,
kSectionImageBitmap,
kSectionCount, // Number of elements in enum.
};
@@ -293,6 +294,10 @@ class PACKED(4) ImageHeader {
return GetImageSection(kSectionStringReferenceOffsets);
}
+ const ImageSection& GetMetadataSection() const {
+ return GetImageSection(kSectionMetadata);
+ }
+
const ImageSection& GetImageBitmapSection() const {
return GetImageSection(kSectionImageBitmap);
}
@@ -462,22 +467,45 @@ typedef std::pair<uint32_t, uint32_t> AppImageReferenceOffsetInfo;
* 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) != 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 & 1u) == 1u;
+ return (val & 2u) != 0u;
}
/*
@@ -486,10 +514,10 @@ bool HasDexCacheNativeRefTag(T val) {
* 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 13eaf3da45..47b621ad1a 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -84,6 +84,15 @@ inline uint32_t DexCache::StringSlotIndex(dex::StringIndex string_idx) {
}
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 @@ inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr<Stri
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 @@ inline void DexCache::VisitReferences(ObjPtr<Class> klass, const Visitor& visito
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 661f954ef5..8d2b838cb2 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -172,6 +172,21 @@ void DexCache::InitializeDexCache(Thread* self,
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 6149f9c0be..58b199d639 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -217,6 +217,10 @@ class MANAGED DexCache final : public Object {
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 @@ class MANAGED DexCache final : public Object {
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 @@ class MANAGED DexCache final : public Object {
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 @@ class MANAGED DexCache final : public Object {
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 @@ class MANAGED DexCache final : public Object {
}
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 @@ class MANAGED DexCache final : public Object {
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 @@ class MANAGED DexCache final : public Object {
#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_;
-
- 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.
-
+ // 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 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.