Merge "Add feature flag for loading app image startup cache"
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2e1f364..148fdba 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1449,6 +1449,7 @@
 
   static void UpdateInternStrings(
       gc::space::ImageSpace* space,
+      bool use_preresolved_strings,
       const SafeMap<mirror::String*, mirror::String*>& intern_remap)
       REQUIRES_SHARED(Locks::mutator_lock_);
 };
@@ -1464,8 +1465,10 @@
   ScopedTrace app_image_timing("AppImage:Updating");
 
   Thread* const self = Thread::Current();
-  gc::Heap* const heap = Runtime::Current()->GetHeap();
+  Runtime* const runtime = Runtime::Current();
+  gc::Heap* const heap = runtime->GetHeap();
   const ImageHeader& header = space->GetImageHeader();
+  bool load_app_image_startup_cache = runtime->LoadAppImageStartupCache();
   {
     // Register dex caches with the class loader.
     WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -1479,6 +1482,10 @@
         class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get());
       }
 
+      if (!load_app_image_startup_cache) {
+        dex_cache->ClearPreResolvedStrings();
+      }
+
       if (kIsDebugBuild) {
         CHECK(new_class_set != nullptr);
         mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
@@ -1545,11 +1552,13 @@
 
 void AppImageLoadingHelper::UpdateInternStrings(
     gc::space::ImageSpace* space,
+    bool use_preresolved_strings,
     const SafeMap<mirror::String*, mirror::String*>& intern_remap) {
   const uint8_t* target_base = space->Begin();
   const ImageSection& sro_section =
       space->GetImageHeader().GetImageStringReferenceOffsetsSection();
   const size_t num_string_offsets = sro_section.Size() / sizeof(AppImageReferenceOffsetInfo);
+  InternTable* const intern_table = Runtime::Current()->GetInternTable();
 
   VLOG(image)
       << "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = "
@@ -1582,24 +1591,29 @@
         WriteBarrier::ForEveryFieldWrite(dex_cache);
         dex_cache->GetStrings()[string_index].store(
             mirror::StringDexCachePair(it->second, source.index));
+      } else if (!use_preresolved_strings) {
+        dex_cache->GetStrings()[string_index].store(
+            mirror::StringDexCachePair(intern_table->InternStrong(referred_string), source.index));
       }
     } else if (HasDexCachePreResolvedStringNativeRefTag(base_offset)) {
-      base_offset = ClearDexCacheNativeRefTags(base_offset);
-      DCHECK_ALIGNED(base_offset, 2);
+      if (use_preresolved_strings) {
+        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::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);
+        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);
+        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;
@@ -1622,6 +1636,13 @@
                                 /* kCheckTransaction= */ false,
                                 kVerifyNone,
                                 /* kIsVolatile= */ false>(member_offset, it->second);
+      } else if (!use_preresolved_strings) {
+        obj_ptr->SetFieldObject</* kTransactionActive= */ false,
+                                /* kCheckTransaction= */ false,
+                                kVerifyNone,
+                                /* kIsVolatile= */ false>(
+            member_offset,
+            intern_table->InternStrong(referred_string));
       }
     }
   }
@@ -1632,13 +1653,16 @@
   // the strings they point to.
   ScopedTrace timing("AppImage:InternString");
 
-  InternTable* const intern_table = Runtime::Current()->GetInternTable();
+  Runtime* const runtime = Runtime::Current();
+  InternTable* const intern_table = runtime->GetInternTable();
+
+  const bool load_startup_cache = runtime->LoadAppImageStartupCache();
 
   // Add the intern table, removing any conflicts. For conflicts, store the new address in a map
   // for faster lookup.
   // TODO: Optimize with a bitmap or bloom filter
   SafeMap<mirror::String*, mirror::String*> intern_remap;
-  intern_table->AddImageStringsToTable(space, [&](InternTable::UnorderedSet& interns)
+  auto func = [&](InternTable::UnorderedSet& interns)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(Locks::intern_table_lock_) {
     const size_t non_boot_image_strings = intern_table->CountInterns(
@@ -1681,14 +1705,23 @@
         CHECK(intern_table->LookupStrongLocked(string) == nullptr) << string->ToModifiedUtf8();
       }
     }
-  });
+  };
 
-  VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size();
+  bool update_intern_strings;
+  if (load_startup_cache) {
+    // Only add the intern table if we are using the startup cache. Otherwise,
+    // UpdateInternStrings adds the strings to the intern table.
+    intern_table->AddImageStringsToTable(space, func);
+    update_intern_strings = kIsDebugBuild || !intern_remap.empty();
+    VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size();
+  } else {
+    update_intern_strings = true;
+  }
 
   // For debug builds, always run the code below to get coverage.
-  if (kIsDebugBuild || !intern_remap.empty()) {
+  if (update_intern_strings) {
     // Slow path case is when there are conflicting intern strings to fix up.
-    UpdateInternStrings(space, intern_remap);
+    UpdateInternStrings(space, /*use_preresolved_strings=*/ load_startup_cache, intern_remap);
   }
 }
 
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 47b621a..f0ad931 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -110,8 +110,7 @@
   WriteBarrier::ForEveryFieldWrite(this);
 }
 
-inline void DexCache::SetPreResolvedString(dex::StringIndex string_idx,
-                                           ObjPtr<String> resolved) {
+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);
@@ -122,6 +121,17 @@
   WriteBarrier::ForEveryFieldWrite(this);
 }
 
+inline void DexCache::ClearPreResolvedStrings() {
+  SetFieldPtr64</*kTransactionActive=*/false,
+                /*kCheckTransaction=*/false,
+                kVerifyNone,
+                GcRoot<mirror::String>*>(PreResolvedStringsOffset(), nullptr);
+  SetField32</*kTransactionActive=*/false,
+             /*bool kCheckTransaction=*/false,
+             kVerifyNone,
+             /*kIsVolatile=*/false>(NumPreResolvedStringsOffset(), 0);
+}
+
 inline void DexCache::ClearString(dex::StringIndex string_idx) {
   DCHECK(Runtime::Current()->IsAotCompiler());
   uint32_t slot_idx = StringSlotIndex(string_idx);
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index c742928..b5619f8 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -283,6 +283,11 @@
                             ObjPtr<mirror::String> resolved)
       ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Clear the preresolved string cache to prevent further usage. Not thread safe, so should only
+  // be called when the string cache is guaranteed to not be accessed.
+  void ClearPreResolvedStrings()
+      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_);
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 26fc5e9..891ecef 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -149,6 +149,7 @@
   HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
                                      | (1 << 13),
   PROFILE_SYSTEM_SERVER              = 1 << 14,
+  USE_APP_IMAGE_STARTUP_CACHE        = 1 << 16,
 
   // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value
   // corresponding to hiddenapi::EnforcementPolicy
@@ -298,6 +299,10 @@
   bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER;
   runtime_flags &= ~PROFILE_SYSTEM_SERVER;
 
+  Runtime::Current()->SetLoadAppImageStartupCacheEnabled(
+      (runtime_flags & USE_APP_IMAGE_STARTUP_CACHE) != 0u);
+  runtime_flags &= ~USE_APP_IMAGE_STARTUP_CACHE;
+
   if (runtime_flags != 0) {
     LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags);
   }
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ee2c514..81c17a5 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -823,6 +823,14 @@
     ThreadPool* const thread_pool_;
   };
 
+  bool LoadAppImageStartupCache() const {
+    return load_app_image_startup_cache_;
+  }
+
+  void SetLoadAppImageStartupCacheEnabled(bool enabled) {
+    load_app_image_startup_cache_ = enabled;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -1145,6 +1153,8 @@
 
   uint32_t verifier_logging_threshold_ms_;
 
+  bool load_app_image_startup_cache_ = false;
+
   // Note: See comments on GetFaultMessage.
   friend std::string GetFaultMessageForAbortLogging();
   friend class ScopedThreadPoolUsage;