diff options
| author | 2018-10-23 12:55:34 -0700 | |
|---|---|---|
| committer | 2018-10-30 16:03:00 +0000 | |
| commit | 1ca718ec2de53fb5ef2a9211bfcc921311775c90 (patch) | |
| tree | f5ce8c3f4edd022765c343cc5a9df8ebd292f0ec | |
| parent | ce2a00daa92670a4fc01ef59fdbc3769a846f69c (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.cc | 8 | ||||
| -rw-r--r-- | compiler/driver/compiler_options_map.def | 2 | ||||
| -rw-r--r-- | dex2oat/dex2oat_test.cc | 38 | ||||
| -rw-r--r-- | dex2oat/linker/image_writer.cc | 83 | ||||
| -rw-r--r-- | dex2oat/linker/image_writer.h | 3 | ||||
| -rw-r--r-- | oatdump/oatdump.cc | 80 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 25 | ||||
| -rw-r--r-- | runtime/class_linker_test.cc | 2 | ||||
| -rw-r--r-- | runtime/gc/accounting/space_bitmap.h | 6 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.cc | 22 | ||||
| -rw-r--r-- | runtime/image.cc | 2 | ||||
| -rw-r--r-- | runtime/image.h | 38 | ||||
| -rw-r--r-- | runtime/mirror/dex_cache-inl.h | 27 | ||||
| -rw-r--r-- | runtime/mirror/dex_cache.cc | 15 | ||||
| -rw-r--r-- | runtime/mirror/dex_cache.h | 73 |
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. |