Merge "Remove core-simple from the boot classpath"
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 47729c1..aa199d3 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -34,14 +34,17 @@
     "dexoptanalyzerd",
     "profmand",
 ]
+// - Debug variants (libraries).
 art_runtime_debug_native_shared_libs = [
     "libartd",
     "libartd-compiler",
-    "libopenjdkd",
     "libopenjdkjvmd",
     "libopenjdkjvmtid",
     "libadbconnectiond",
 ]
+libcore_debug_native_shared_libs = [
+    "libopenjdkd",
+]
 
 // Data files associated with bionic / managed core library APIs.
 art_runtime_data_file_prebuilts = [
@@ -77,6 +80,11 @@
 art_tools_device_binaries = art_tools_common_binaries + art_tools_device_only_binaries
 art_tools_host_binaries = art_tools_common_binaries + art_tools_host_only_binaries
 
+// (Some) Libcore native libraries.
+libcore_native_shared_libs = [
+    "libopenjdk",
+]
+
 apex_key {
     name: "com.android.runtime.key",
     public_key: "com.android.runtime.avbpubkey",
@@ -101,7 +109,8 @@
     compile_multilib: "both",
     manifest: "manifest.json",
     native_shared_libs: art_runtime_base_native_shared_libs
-        + bionic_native_shared_libs,
+        + bionic_native_shared_libs
+        + libcore_native_shared_libs,
     multilib: {
         both: {
             // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
@@ -130,7 +139,9 @@
     manifest: "manifest.json",
     native_shared_libs: art_runtime_base_native_shared_libs
         + art_runtime_debug_native_shared_libs
-        + bionic_native_shared_libs,
+        + bionic_native_shared_libs
+        + libcore_native_shared_libs
+        + libcore_debug_native_shared_libs,
     multilib: {
         both: {
             // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
@@ -164,7 +175,9 @@
     device_supported: false,
     manifest: "manifest.json",
     native_shared_libs: art_runtime_base_native_shared_libs
-        + art_runtime_debug_native_shared_libs,
+        + art_runtime_debug_native_shared_libs
+        + libcore_native_shared_libs
+        + libcore_debug_native_shared_libs,
     multilib: {
         both: {
             // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index 9db325a..b2aa4df 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -157,6 +157,7 @@
   check_library libprofile.so
   # Check that the mounted image contains Android Core libraries.
   check_library libjavacrypto.so
+  check_library libopenjdk.so
   # Check that the mounted image contains additional required libraries.
   check_library libadbconnection.so
 
@@ -193,11 +194,12 @@
   check_library libartd-dexlayout.so
   check_library libartd.so
   check_library libdexfiled.so
-  check_library libopenjdkd.so
   check_library libopenjdkjvmd.so
   check_library libopenjdkjvmtid.so
   check_library libprofiled.so
-  # Check that the mounted image contains additional required libraries.
+  # Check that the mounted image contains Android Core debug libraries.
+  check_library libopenjdkd.so
+  # Check that the mounted image contains additional required debug libraries.
   check_library libadbconnectiond.so
 }
 
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 341f16a..52c9386 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -441,6 +441,10 @@
     return process_cpu_start_time_ns_;
   }
 
+  uint64_t GetPostGCLastProcessCpuTime() const {
+    return post_gc_last_process_cpu_time_ns_;
+  }
+
   // Set target ideal heap utilization ratio, implements
   // dalvik.system.VMRuntime.setTargetHeapUtilization.
   void SetTargetHeapUtilization(float target);
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 5ad5f52..4a2dbf5 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -342,6 +342,10 @@
     return address - source_ < length_;
   }
 
+  bool InDest(const void* dest) const {
+    return InDest(reinterpret_cast<uintptr_t>(dest));
+  }
+
   bool InDest(uintptr_t address) const {
     return address - dest_ < length_;
   }
@@ -382,11 +386,11 @@
             << reinterpret_cast<const void*>(reloc.Dest() + reloc.Length()) << ")";
 }
 
-template <PointerSize kPointerSize, typename ReferenceVisitor>
+template <PointerSize kPointerSize, typename HeapVisitor, typename NativeVisitor>
 class ImageSpace::PatchObjectVisitor final {
  public:
-  explicit PatchObjectVisitor(ReferenceVisitor reference_visitor)
-      : reference_visitor_(reference_visitor) {}
+  explicit PatchObjectVisitor(HeapVisitor heap_visitor, NativeVisitor native_visitor)
+      : heap_visitor_(heap_visitor), native_visitor_(native_visitor) {}
 
   void VisitClass(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_) {
     // A mirror::Class object consists of
@@ -446,12 +450,8 @@
   }
 
   template <typename T>
-  T* operator()(T* ptr, void** dest_addr ATTRIBUTE_UNUSED) const
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (ptr != nullptr) {
-      ptr = reference_visitor_(ptr);
-    }
-    return ptr;
+  T* operator()(T* ptr, void** dest_addr ATTRIBUTE_UNUSED) const {
+    return (ptr != nullptr) ? native_visitor_(ptr) : nullptr;
   }
 
   void VisitPointerArray(mirror::PointerArray* pointer_array)
@@ -527,7 +527,7 @@
     T* old_value = root->template Read<kWithoutReadBarrier>();
     DCHECK(kMayBeNull || old_value != nullptr);
     if (!kMayBeNull || old_value != nullptr) {
-      *root = GcRoot<T>(reference_visitor_(old_value));
+      *root = GcRoot<T>(heap_visitor_(old_value));
     }
   }
 
@@ -538,7 +538,7 @@
       T* old_value = reinterpret_cast64<T*>(*raw_entry);
       DCHECK(kMayBeNull || old_value != nullptr);
       if (!kMayBeNull || old_value != nullptr) {
-        T* new_value = reference_visitor_(old_value);
+        T* new_value = native_visitor_(old_value);
         *raw_entry = reinterpret_cast64<uint64_t>(new_value);
       }
     } else {
@@ -546,7 +546,7 @@
       T* old_value = reinterpret_cast32<T*>(*raw_entry);
       DCHECK(kMayBeNull || old_value != nullptr);
       if (!kMayBeNull || old_value != nullptr) {
-        T* new_value = reference_visitor_(old_value);
+        T* new_value = native_visitor_(old_value);
         *raw_entry = reinterpret_cast32<uint32_t>(new_value);
       }
     }
@@ -559,7 +559,7 @@
         object->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset);
     DCHECK(kMayBeNull || old_value != nullptr);
     if (!kMayBeNull || old_value != nullptr) {
-      mirror::Object* new_value = reference_visitor_(old_value);
+      mirror::Object* new_value = heap_visitor_(old_value);
       object->SetFieldObjectWithoutWriteBarrier</*kTransactionActive=*/ false,
                                                 /*kCheckTransaction=*/ true,
                                                 kVerifyNone>(offset, new_value);
@@ -583,7 +583,7 @@
     mirror::NativeDexCachePair<T> pair =
         mirror::DexCache::GetNativePairPtrSize(array, index, kPointerSize);
     if (pair.object != nullptr) {
-      pair.object = reference_visitor_(pair.object);
+      pair.object = native_visitor_(pair.object);
       mirror::DexCache::SetNativePairPtrSize(array, index, pair, kPointerSize);
     }
   }
@@ -606,7 +606,7 @@
         reinterpret_cast64<EntryType*>(dex_cache->GetField64<kVerifyNone>(array_offset));
     DCHECK_EQ(old_array != nullptr, size != 0u);
     if (old_array != nullptr) {
-      EntryType* new_array = reference_visitor_(old_array);
+      EntryType* new_array = native_visitor_(old_array);
       dex_cache->SetField64<kVerifyNone>(array_offset, reinterpret_cast64<uint64_t>(new_array));
       for (uint32_t i = 0; i != size; ++i) {
         FixupDexCacheArrayEntry(new_array, i);
@@ -615,7 +615,11 @@
   }
 
  private:
-  ReferenceVisitor reference_visitor_;
+  // Heap objects visitor.
+  HeapVisitor heap_visitor_;
+
+  // Native objects visitor.
+  NativeVisitor native_visitor_;
 };
 
 template <typename ObjectVisitor>
@@ -829,7 +833,7 @@
 
     // GetImageBegin is the preferred address to map the image. If we manage to map the
     // image at the image begin, the amount of fixup work required is minimized.
-    // If it is pic we will retry with error_msg for the failure case. Pass a null error_msg to
+    // If it is pic we will retry with error_msg for the2 failure case. Pass a null error_msg to
     // avoid reading proc maps for a mapping failure and slowing everything down.
     // For the boot image, we have already reserved the memory and we load the image
     // into the `image_reservation`.
@@ -982,12 +986,11 @@
         auto function = [&](Thread*) {
           const uint64_t start2 = NanoTime();
           ScopedTrace trace("LZ4 decompress block");
-          if (!block.Decompress(/*out_ptr=*/map.Begin(),
-                                /*in_ptr=*/temp_map.Begin(),
-                                error_msg)) {
-            if (error_msg != nullptr) {
-              *error_msg = "Failed to decompress image block " + *error_msg;
-            }
+          bool result = block.Decompress(/*out_ptr=*/map.Begin(),
+                                         /*in_ptr=*/temp_map.Begin(),
+                                         error_msg);
+          if (!result && error_msg != nullptr) {
+            *error_msg = "Failed to decompress image block " + *error_msg;
           }
           VLOG(image) << "Decompress block " << block.GetDataSize() << " -> "
                       << block.GetImageSize() << " in " << PrettyDuration(NanoTime() - start2);
@@ -1012,70 +1015,51 @@
     return map;
   }
 
-  class FixupVisitor : public ValueObject {
+  class EmptyRange {
    public:
-    FixupVisitor(const RelocationRange& boot_image,
-                 const RelocationRange& app_image,
-                 const RelocationRange& app_oat)
-        : boot_image_(boot_image),
-          app_image_(app_image),
-          app_oat_(app_oat) {}
+    ALWAYS_INLINE bool InSource(uintptr_t) const { return false; }
+    ALWAYS_INLINE bool InDest(uintptr_t) const { return false; }
+    ALWAYS_INLINE uintptr_t ToDest(uintptr_t) const { UNREACHABLE(); }
+  };
+
+  template <typename Range0, typename Range1 = EmptyRange, typename Range2 = EmptyRange>
+  class ForwardAddress {
+   public:
+    ForwardAddress(const Range0& range0 = Range0(),
+                   const Range1& range1 = Range1(),
+                   const Range2& range2 = Range2())
+        : range0_(range0), range1_(range1), range2_(range2) {}
 
     // Return the relocated address of a heap object.
     // Null checks must be performed in the caller (for performance reasons).
     template <typename T>
-    ALWAYS_INLINE T* ForwardObject(T* src) const {
+    ALWAYS_INLINE T* operator()(T* src) const {
       DCHECK(src != nullptr);
       const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src);
-      if (boot_image_.InSource(uint_src)) {
-        return reinterpret_cast<T*>(boot_image_.ToDest(uint_src));
+      if (range2_.InSource(uint_src)) {
+        return reinterpret_cast<T*>(range2_.ToDest(uint_src));
       }
-      // Since we are fixing up the app image, there should only be pointers to the app image and
-      // boot image.
-      DCHECK(app_image_.InSource(uint_src)) << reinterpret_cast<const void*>(src);
-      return reinterpret_cast<T*>(app_image_.ToDest(uint_src));
-    }
-
-    // Return the relocated address of a code pointer (contained by an oat file).
-    // Null checks must be performed in the caller (for performance reasons).
-    ALWAYS_INLINE const void* ForwardCode(const void* src) const {
-      DCHECK(src != nullptr);
-      const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src);
-      if (boot_image_.InSource(uint_src)) {
-        return reinterpret_cast<const void*>(boot_image_.ToDest(uint_src));
+      if (range1_.InSource(uint_src)) {
+        return reinterpret_cast<T*>(range1_.ToDest(uint_src));
       }
-      DCHECK(app_oat_.InSource(uint_src)) << src;
-      return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src));
+      CHECK(range0_.InSource(uint_src))
+          << reinterpret_cast<const void*>(src) << " not in "
+          << reinterpret_cast<const void*>(range0_.Source()) << "-"
+          << reinterpret_cast<const void*>(range0_.Source() + range0_.Length());
+      return reinterpret_cast<T*>(range0_.ToDest(uint_src));
     }
 
-    // Must be called on pointers that already have been relocated to the destination relocation.
-    ALWAYS_INLINE bool IsInAppImage(mirror::Object* object) const {
-      return app_image_.InDest(reinterpret_cast<uintptr_t>(object));
-    }
-
-   protected:
-    // Source section.
-    const RelocationRange boot_image_;
-    const RelocationRange app_image_;
-    const RelocationRange app_oat_;
+   private:
+    const Range0 range0_;
+    const Range1 range1_;
+    const Range2 range2_;
   };
 
-  // Adapt for mirror::Class::FixupNativePointers.
-  class FixupObjectAdapter : public FixupVisitor {
+  template <typename Forward>
+  class FixupRootVisitor {
    public:
     template<typename... Args>
-    explicit FixupObjectAdapter(Args... args) : FixupVisitor(args...) {}
-
-    template <typename T>
-    T* operator()(T* obj, void** dest_addr ATTRIBUTE_UNUSED = nullptr) const {
-      return ForwardObject(obj);
-    }
-  };
-
-  class FixupRootVisitor : public FixupVisitor {
-   public:
-    template<typename... Args>
-    explicit FixupRootVisitor(Args... args) : FixupVisitor(args...) {}
+    explicit FixupRootVisitor(Args... args) : forward_(args...) {}
 
     ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
         REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1087,19 +1071,22 @@
     ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
         REQUIRES_SHARED(Locks::mutator_lock_) {
       mirror::Object* ref = root->AsMirrorPtr();
-      mirror::Object* new_ref = ForwardObject(ref);
+      mirror::Object* new_ref = forward_(ref);
       if (ref != new_ref) {
         root->Assign(new_ref);
       }
     }
+
+   private:
+    Forward forward_;
   };
 
-  class FixupObjectVisitor : public FixupVisitor {
+  template <typename Forward>
+  class FixupObjectVisitor {
    public:
-    template<typename... Args>
-    explicit FixupObjectVisitor(gc::accounting::ContinuousSpaceBitmap* visited, Args... args)
-        : FixupVisitor(args...),
-          visited_(visited) {}
+    explicit FixupObjectVisitor(gc::accounting::ContinuousSpaceBitmap* visited,
+                                const Forward& forward)
+        : visited_(visited), forward_(forward) {}
 
     // Fix up separately since we also need to fix up method entrypoints.
     ALWAYS_INLINE void VisitRootIfNonNull(
@@ -1118,8 +1105,7 @@
       if (ref != nullptr) {
         // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
         // image.
-        obj->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
-            offset, ForwardObject(ref));
+        obj->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(offset, forward_(ref));
       }
     }
 
@@ -1131,7 +1117,7 @@
       if (obj != nullptr) {
         ref->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
             mirror::Reference::ReferentOffset(),
-            ForwardObject(obj));
+            forward_(obj));
       }
     }
 
@@ -1148,79 +1134,61 @@
 
    private:
     gc::accounting::ContinuousSpaceBitmap* const visited_;
+    Forward forward_;
   };
 
-  class ForwardObjectAdapter {
-   public:
-    ALWAYS_INLINE explicit ForwardObjectAdapter(const FixupVisitor* visitor) : visitor_(visitor) {}
-
-    template <typename T>
-    ALWAYS_INLINE T* operator()(T* src) const {
-      return visitor_->ForwardObject(src);
-    }
-
-   private:
-    const FixupVisitor* const visitor_;
-  };
-
-  class ForwardCodeAdapter {
-   public:
-    ALWAYS_INLINE explicit ForwardCodeAdapter(const FixupVisitor* visitor)
-        : visitor_(visitor) {}
-
-    template <typename T>
-    ALWAYS_INLINE T* operator()(T* src) const {
-      return visitor_->ForwardCode(src);
-    }
-
-   private:
-    const FixupVisitor* const visitor_;
-  };
-
-  class FixupArtMethodVisitor : public FixupVisitor, public ArtMethodVisitor {
+  template <typename ForwardObject, typename ForwardNative, typename ForwardCode>
+  class FixupArtMethodVisitor : public ArtMethodVisitor {
    public:
     template<typename... Args>
-    explicit FixupArtMethodVisitor(bool fixup_heap_objects, PointerSize pointer_size, Args... args)
-        : FixupVisitor(args...),
-          fixup_heap_objects_(fixup_heap_objects),
-          pointer_size_(pointer_size) {}
+    explicit FixupArtMethodVisitor(PointerSize pointer_size,
+                                   const ForwardObject& forward_object,
+                                   const ForwardNative& forward_native,
+                                   const ForwardCode& forward_code)
+        : pointer_size_(pointer_size),
+          forward_object_(forward_object),
+          forward_native_(forward_native),
+          forward_code_(forward_code) {}
 
     void Visit(ArtMethod* method) override NO_THREAD_SAFETY_ANALYSIS {
       // TODO: Separate visitor for runtime vs normal methods.
       if (UNLIKELY(method->IsRuntimeMethod())) {
         ImtConflictTable* table = method->GetImtConflictTable(pointer_size_);
         if (table != nullptr) {
-          ImtConflictTable* new_table = ForwardObject(table);
+          ImtConflictTable* new_table = forward_native_(table);
           if (table != new_table) {
             method->SetImtConflictTable(new_table, pointer_size_);
           }
         }
         const void* old_code = method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
-        const void* new_code = ForwardCode(old_code);
+        const void* new_code = forward_code_(old_code);
         if (old_code != new_code) {
           method->SetEntryPointFromQuickCompiledCodePtrSize(new_code, pointer_size_);
         }
       } else {
-        if (fixup_heap_objects_) {
-          method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this));
-        }
-        method->UpdateEntrypoints(ForwardCodeAdapter(this), pointer_size_);
+        method->UpdateObjectsForImageRelocation(forward_object_);
+        method->UpdateEntrypoints(forward_code_, pointer_size_);
       }
     }
 
    private:
-    const bool fixup_heap_objects_;
     const PointerSize pointer_size_;
+    const ForwardObject forward_object_;
+    const ForwardNative forward_native_;
+    const ForwardCode forward_code_;
   };
 
-  class FixupArtFieldVisitor : public FixupVisitor, public ArtFieldVisitor {
+  template <typename Forward>
+  class FixupArtFieldVisitor : public ArtFieldVisitor {
    public:
-    template<typename... Args>
-    explicit FixupArtFieldVisitor(Args... args) : FixupVisitor(args...) {}
+    explicit FixupArtFieldVisitor(Forward forward) : forward_(forward) {}
 
     void Visit(ArtField* field) override NO_THREAD_SAFETY_ANALYSIS {
-      field->UpdateObjects(ForwardObjectAdapter(this));
+      field->UpdateObjects(forward_);
     }
+
+   private:
+    Forward forward_;
   };
 
   // Relocate an image space mapped at target_base which possibly used to be at a different base
@@ -1257,30 +1225,49 @@
                                 static_cast<uint64_t>(image_header_boot_image_size));
       return false;
     }
+    const ImageSection& objects_section = image_header.GetObjectsSection();
+    // Where the app image objects are mapped to.
+    uint8_t* objects_location = target_base + objects_section.Offset();
     TimingLogger logger(__FUNCTION__, true, false);
     RelocationRange boot_image(image_header.GetBootImageBegin(),
                                boot_image_begin,
                                boot_image_size);
-    RelocationRange app_image(reinterpret_cast<uintptr_t>(image_header.GetImageBegin()),
-                              reinterpret_cast<uintptr_t>(target_base),
-                              image_header.GetImageSize());
+    // Metadata is everything after the objects section, use exclusion to be safe.
+    RelocationRange app_image_metadata(
+        reinterpret_cast<uintptr_t>(image_header.GetImageBegin()) + objects_section.End(),
+        reinterpret_cast<uintptr_t>(target_base) + objects_section.End(),
+        image_header.GetImageSize() - objects_section.End());
+    // App image heap objects, may be mapped in the heap.
+    RelocationRange app_image_objects(
+        reinterpret_cast<uintptr_t>(image_header.GetImageBegin()) + objects_section.Offset(),
+        reinterpret_cast<uintptr_t>(objects_location),
+        objects_section.Size());
     // Use the oat data section since this is where the OatFile::Begin is.
     RelocationRange app_oat(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()),
                             // Not necessarily in low 4GB.
                             reinterpret_cast<uintptr_t>(app_oat_file->Begin()),
                             image_header.GetOatDataEnd() - image_header.GetOatDataBegin());
-    VLOG(image) << "App image " << app_image;
+    VLOG(image) << "App image metadata " << app_image_metadata;
+    VLOG(image) << "App image objects " << app_image_objects;
     VLOG(image) << "App oat " << app_oat;
     VLOG(image) << "Boot image " << boot_image;
     // True if we need to fixup any heap pointers.
-    const bool fixup_image = boot_image.Delta() != 0 || app_image.Delta() != 0;
+    const bool fixup_image = boot_image.Delta() != 0 || app_image_metadata.Delta() != 0 ||
+        app_image_objects.Delta() != 0;
     if (!fixup_image) {
       // Nothing to fix up.
       return true;
     }
     ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
-    FixupObjectAdapter fixup_adapter(boot_image, app_image, app_oat);
-    PatchObjectVisitor<kPointerSize, FixupObjectAdapter> patch_object_visitor(fixup_adapter);
+
+    using ForwardObject = ForwardAddress<RelocationRange, RelocationRange>;
+    ForwardObject forward_object(boot_image, app_image_objects);
+    ForwardObject forward_metadata(boot_image, app_image_metadata);
+    using ForwardCode = ForwardAddress<RelocationRange, RelocationRange>;
+    ForwardCode forward_code(boot_image, app_oat);
+    PatchObjectVisitor<kPointerSize, ForwardObject, ForwardCode> patch_object_visitor(
+        forward_object,
+        forward_metadata);
     if (fixup_image) {
       // Two pass approach, fix up all classes first, then fix up non class-objects.
       // The visited bitmap is used to ensure that pointer arrays are not forwarded twice.
@@ -1288,13 +1275,12 @@
           gc::accounting::ContinuousSpaceBitmap::Create("Relocate bitmap",
                                                         target_base,
                                                         image_header.GetImageSize()));
-      FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(), boot_image, app_image, app_oat);
       {
         TimingLogger::ScopedTiming timing("Fixup classes", &logger);
         const auto& class_table_section = image_header.GetClassTableSection();
         if (class_table_section.Size() > 0u) {
           ScopedObjectAccess soa(Thread::Current());
-          ClassTableVisitor class_table_visitor(fixup_adapter);
+          ClassTableVisitor class_table_visitor(forward_object);
           size_t read_count = 0u;
           const uint8_t* data = target_base + class_table_section.Offset();
           // We avoid making a copy of the data since we want modifications to be propagated to the
@@ -1303,7 +1289,7 @@
           for (ClassTable::TableSlot& slot : temp_set) {
             slot.VisitRoot(class_table_visitor);
             mirror::Class* klass = slot.Read<kWithoutReadBarrier>();
-            if (!fixup_adapter.IsInAppImage(klass)) {
+            if (!app_image_objects.InDest(klass)) {
               continue;
             }
             const bool already_marked = visited_bitmap->Set(klass);
@@ -1312,12 +1298,12 @@
             // Then patch the non-embedded vtable and iftable.
             mirror::PointerArray* vtable = klass->GetVTable<kVerifyNone, kWithoutReadBarrier>();
             if (vtable != nullptr &&
-                fixup_object_visitor.IsInAppImage(vtable) &&
+                app_image_objects.InDest(vtable) &&
                 !visited_bitmap->Set(vtable)) {
               patch_object_visitor.VisitPointerArray(vtable);
             }
             auto* iftable = klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>();
-            if (iftable != nullptr && fixup_object_visitor.IsInAppImage(iftable)) {
+            if (iftable != nullptr && app_image_objects.InDest(iftable)) {
               // Avoid processing the fields of iftable since we will process them later anyways
               // below.
               int32_t ifcount = klass->GetIfTableCount<kVerifyNone>();
@@ -1326,9 +1312,8 @@
                     iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i);
                 if (unpatched_ifarray != nullptr) {
                   // The iftable has not been patched, so we need to explicitly adjust the pointer.
-                  mirror::PointerArray* ifarray = fixup_adapter(unpatched_ifarray);
-                  if (fixup_object_visitor.IsInAppImage(ifarray) &&
-                      !visited_bitmap->Set(ifarray)) {
+                  mirror::PointerArray* ifarray = forward_object(unpatched_ifarray);
+                  if (app_image_objects.InDest(ifarray) && !visited_bitmap->Set(ifarray)) {
                     patch_object_visitor.VisitPointerArray(ifarray);
                   }
                 }
@@ -1343,14 +1328,14 @@
       TimingLogger::ScopedTiming timing("Fixup objects", &logger);
       ScopedObjectAccess soa(Thread::Current());
       // Need to update the image to be at the target base.
-      const ImageSection& objects_section = image_header.GetObjectsSection();
       uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
       uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+      FixupObjectVisitor<ForwardObject> fixup_object_visitor(visited_bitmap.get(), forward_object);
       bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor);
       // Fixup image roots.
-      CHECK(app_image.InSource(reinterpret_cast<uintptr_t>(
+      CHECK(app_image_objects.InSource(reinterpret_cast<uintptr_t>(
           image_header.GetImageRoots<kWithoutReadBarrier>().Ptr())));
-      image_header.RelocateImageObjects(app_image.Delta());
+      image_header.RelocateImageObjects(app_image_objects.Delta());
       CHECK_EQ(image_header.GetImageBegin(), target_base);
       // Fix up dex cache DexFile pointers.
       auto* dex_caches = image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches)->
@@ -1364,27 +1349,26 @@
     {
       // Only touches objects in the app image, no need for mutator lock.
       TimingLogger::ScopedTiming timing("Fixup methods", &logger);
-      FixupArtMethodVisitor method_visitor(fixup_image,
-                                           kPointerSize,
-                                           boot_image,
-                                           app_image,
-                                           app_oat);
+      FixupArtMethodVisitor method_visitor(kPointerSize,
+                                           forward_object,
+                                           forward_metadata,
+                                           forward_code);
       image_header.VisitPackedArtMethods(&method_visitor, target_base, kPointerSize);
     }
     if (fixup_image) {
       {
         // Only touches objects in the app image, no need for mutator lock.
         TimingLogger::ScopedTiming timing("Fixup fields", &logger);
-        FixupArtFieldVisitor field_visitor(boot_image, app_image, app_oat);
+        FixupArtFieldVisitor field_visitor(forward_object);
         image_header.VisitPackedArtFields(&field_visitor, target_base);
       }
       {
         TimingLogger::ScopedTiming timing("Fixup imt", &logger);
-        image_header.VisitPackedImTables(fixup_adapter, target_base, kPointerSize);
+        image_header.VisitPackedImTables(forward_metadata, target_base, kPointerSize);
       }
       {
         TimingLogger::ScopedTiming timing("Fixup conflict tables", &logger);
-        image_header.VisitPackedImtConflictTables(fixup_adapter, target_base, kPointerSize);
+        image_header.VisitPackedImtConflictTables(forward_metadata, target_base, kPointerSize);
       }
       // In the app image case, the image methods are actually in the boot image.
       image_header.RelocateImageMethods(boot_image.Delta());
@@ -1397,12 +1381,11 @@
         InternTable temp_intern_table;
         // Note that we require that ReadFromMemory does not make an internal copy of the elements
         // so that the VisitRoots() will update the memory directly rather than the copies.
-        FixupRootVisitor root_visitor(boot_image, app_image, app_oat);
         temp_intern_table.AddTableFromMemory(target_base + intern_table_section.Offset(),
                                              [&](InternTable::UnorderedSet& strings)
             REQUIRES_SHARED(Locks::mutator_lock_) {
           for (GcRoot<mirror::String>& root : strings) {
-            root = GcRoot<mirror::String>(fixup_adapter(root.Read<kWithoutReadBarrier>()));
+            root = GcRoot<mirror::String>(forward_object(root.Read<kWithoutReadBarrier>()));
           }
         }, /*is_boot_image=*/ false);
       }
@@ -1619,9 +1602,9 @@
             "Marked objects",
             spaces.front()->Begin(),
             spaces.back()->End() - spaces.front()->Begin()));
-    using PatchRelocateVisitor = PatchObjectVisitor<kPointerSize, RelocateVisitor>;
+    using PatchRelocateVisitor = PatchObjectVisitor<kPointerSize, RelocateVisitor, RelocateVisitor>;
     RelocateVisitor relocate_visitor(diff);
-    PatchRelocateVisitor patch_object_visitor(relocate_visitor);
+    PatchRelocateVisitor patch_object_visitor(relocate_visitor, relocate_visitor);
 
     mirror::Class* dcheck_class_class = nullptr;  // Used only for a DCHECK().
     for (size_t s = 0u, size = spaces.size(); s != size; ++s) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 42ac3e2..bb19097 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -216,7 +216,7 @@
   class PatchArtFieldVisitor;
   template <PointerSize kPointerSize, typename PatchObjectVisitor, typename PatchCodeVisitor>
   class PatchArtMethodVisitor;
-  template <PointerSize kPointerSize, typename ReferenceVisitor>
+  template <PointerSize kPointerSize, typename HeapVisitor, typename NativeVisitor>
   class PatchObjectVisitor;
 
   DISALLOW_COPY_AND_ASSIGN(ImageSpace);
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index dae8e29..ec1d166 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -259,20 +259,17 @@
   void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
-  static constexpr size_t kLocalScopeSize = 64u;
-  static constexpr size_t kSizeOfReferencesPerScope =
-      kLocalScopeSize
-          - /* BaseHandleScope::link_ */ sizeof(BaseHandleScope*)
-          - /* BaseHandleScope::number_of_references_ */ sizeof(int32_t)
-          - /* FixedSizeHandleScope<>::pos_ */ sizeof(uint32_t);
-  static constexpr size_t kNumReferencesPerScope =
-      kSizeOfReferencesPerScope / sizeof(StackReference<mirror::Object>);
+  static constexpr size_t kMaxLocalScopeSize = 64u;
+  // In order to have consistent compilation with both 32bit and 64bit dex2oat
+  // binaries we need this to be an actual constant. We picked this because it
+  // will ensure that we use <64bit internal scopes.
+  static constexpr size_t kNumReferencesPerScope = 12u;
 
   Thread* const self_;
 
   // Linked list of fixed size handle scopes.
   using LocalScopeType = FixedSizeHandleScope<kNumReferencesPerScope>;
-  static_assert(sizeof(LocalScopeType) == kLocalScopeSize, "Unexpected size of LocalScopeType");
+  static_assert(sizeof(LocalScopeType) <= kMaxLocalScopeSize, "Unexpected size of LocalScopeType");
   LocalScopeType* current_scope_;
 
   DISALLOW_COPY_AND_ASSIGN(VariableSizedHandleScope);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index eea7c67..d79793b 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -324,7 +324,6 @@
 
   if (dump_gc_performance_on_shutdown_) {
     heap_->CalculatePreGcWeightedAllocatedBytes();
-    heap_->CalculatePostGcWeightedAllocatedBytes();
     uint64_t process_cpu_end_time = ProcessCpuNanoTime();
     ScopedLogSeverity sls(LogSeverity::INFO);
     // This can't be called from the Heap destructor below because it
@@ -341,8 +340,12 @@
         << "\n";
     double pre_gc_weighted_allocated_bytes =
         heap_->GetPreGcWeightedAllocatedBytes() / process_cpu_time;
+    // Here we don't use process_cpu_time for normalization, because VM shutdown is not a real
+    // GC. Both numerator and denominator take into account until the end of the last GC,
+    // instead of the whole process life time like pre_gc_weighted_allocated_bytes.
     double post_gc_weighted_allocated_bytes =
-        heap_->GetPostGcWeightedAllocatedBytes() / process_cpu_time;
+        heap_->GetPostGcWeightedAllocatedBytes() /
+          (heap_->GetPostGCLastProcessCpuTime() - heap_->GetProcessCpuStartTime());
 
     LOG_STREAM(INFO) << "Average bytes allocated at GC start, weighted by CPU time between GCs: "
         << static_cast<uint64_t>(pre_gc_weighted_allocated_bytes)
diff --git a/tools/art b/tools/art
index 518bc77..58a8150 100644
--- a/tools/art
+++ b/tools/art
@@ -400,34 +400,39 @@
 
 PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
 ANDROID_ROOT="$(cd $PROG_DIR/..; pwd -P)"
-# This script is used on host and target (device). However, the (expected)
-# default value `ANDROID_RUNTIME_ROOT` is not the same on host and target:
-# - on host, `ANDROID_RUNTIME_ROOT` is expected to be "$ANDROID_ROOT/com.android.apex";
-# - on target, `ANDROID_RUNTIME_ROOT` is expected to be "$ANDROID_ROOT/../apex/com.android.apex".
-#
-# We use the presence/absence of the `$ANDROID_ROOT/../apex` directory to
-# determine whether we are on target or host (this is brittle, but simple).
-if [ -d "$ANDROID_ROOT/../apex" ]; then
-  # Target case.
+
+# If ANDROID_RUNTIME_ROOT is not set, try to detect whether we are running on
+# target or host and set that environment variable to the usual default value.
+if [ -z "$ANDROID_RUNTIME_ROOT" ]; then
+  # This script is used on host and target (device). However, the (expected)
+  # default value `ANDROID_RUNTIME_ROOT` is not the same on host and target:
+  # - on host, `ANDROID_RUNTIME_ROOT` is expected to be "$ANDROID_ROOT/com.android.apex";
+  # - on target, `ANDROID_RUNTIME_ROOT` is expected to be "$ANDROID_ROOT/../apex/com.android.apex".
   #
-  # We should be setting `ANDROID_RUNTIME_ROOT` to
-  # "$ANDROID_ROOT/../apex/com.android.runtime" here. However, the Runtime APEX
-  # is not (yet) supported by the ART Buildbot setup (see b/121117762); and yet
-  # ICU code depends on `ANDROID_RUNTIME_ROOT` to find ICU .dat files.
-  #
-  # As a temporary workaround, we:
-  # - make the ART Buildbot build script (art/tools/buildbot-build.sh) also
-  #   generate the ICU .dat files in `/system/etc/icu` on device (these files
-  #   are normally only put in the Runtime APEX on device);
-  # - set `ANDROID_RUNTIME_ROOT` to `$ANDROID_ROOT` (i.e. "/system") here.
-  #
-  # TODO(b/121117762): Set `ANDROID_RUNTIME_ROOT` to
-  # "$ANDROID_ROOT/../apex/com.android.runtime" when the Runtime APEX is fully
-  # supported on the ART Buildbot and Golem.
-  ANDROID_RUNTIME_ROOT=$ANDROID_ROOT
-else
-  # Host case.
-  ANDROID_RUNTIME_ROOT="$ANDROID_ROOT/com.android.runtime"
+  # We use the presence/absence of the `$ANDROID_ROOT/../apex` directory to
+  # determine whether we are on target or host (this is brittle, but simple).
+  if [ -d "$ANDROID_ROOT/../apex" ]; then
+    # Target case.
+    #
+    # We should be setting `ANDROID_RUNTIME_ROOT` to
+    # "$ANDROID_ROOT/../apex/com.android.runtime" here. However, the Runtime APEX
+    # is not (yet) supported by the ART Buildbot setup (see b/121117762); and yet
+    # ICU code depends on `ANDROID_RUNTIME_ROOT` to find ICU .dat files.
+    #
+    # As a temporary workaround, we:
+    # - make the ART Buildbot build script (art/tools/buildbot-build.sh) also
+    #   generate the ICU .dat files in `/system/etc/icu` on device (these files
+    #   are normally only put in the Runtime APEX on device);
+    # - set `ANDROID_RUNTIME_ROOT` to `$ANDROID_ROOT` (i.e. "/system") here.
+    #
+    # TODO(b/121117762): Set `ANDROID_RUNTIME_ROOT` to
+    # "$ANDROID_ROOT/../apex/com.android.runtime" when the Runtime APEX is fully
+    # supported on the ART Buildbot and Golem.
+    ANDROID_RUNTIME_ROOT=$ANDROID_ROOT
+  else
+    # Host case.
+    ANDROID_RUNTIME_ROOT="$ANDROID_ROOT/com.android.runtime"
+  fi
 fi
 
 ART_BINARY_PATH=$ANDROID_ROOT/bin/$ART_BINARY
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index a2777e8..7ef20bd 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -17,13 +17,13 @@
 {
   description: "Differences between vogar and cts in user directory",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   name: "libcore.java.lang.SystemTest#testSystemProperties_mutable"
 },
 {
   description: "Differences between vogar and cts. Passes with --mode activity",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["libcore.java.lang.OldSystemTest#test_getProperties"]
 },
 {
@@ -32,7 +32,7 @@
                 (--invoke-with \"su root\"). Does not pass after setting chmod
                 777 all directories on path to socket (on device without su).",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["libcore.libcore.io.OsTest#testUnixDomainSockets_in_file_system"]
 },
 {
@@ -47,7 +47,7 @@
 {
   description: "Issue with incorrect device time (1970)",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["libcore.java.util.TimeZoneTest#testDisplayNames",
           "libcore.java.util.TimeZoneTest#test_useDaylightTime_Taiwan",
           "org.apache.harmony.tests.java.util.TimeZoneTest#test_hasSameRules_Ljava_util_TimeZone"],
@@ -57,13 +57,13 @@
   description: "Issue with incorrect device time (1970). Test assumes that DateTime.now()
                 is greater then a date in 1998.",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["org.apache.harmony.tests.java.util.DateTest#test_Constructor"]
 },
 {
   description: "Failing due to a locale problem on hammerhead.",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["libcore.icu.DateIntervalFormatTest#test10089890",
           "libcore.icu.DateIntervalFormatTest#test10209343_when_not_this_year",
           "libcore.icu.DateIntervalFormatTest#test10560853_for_single_day_events",
@@ -80,7 +80,7 @@
 {
   description: "Failing due to missing localhost on hammerhead and volantis.",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["libcore.javax.crypto.CipherTest#testCipherInitWithCertificate",
           "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithFtpURLConnection",
           "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarFtpURLConnection",
@@ -95,13 +95,13 @@
 {
   description: "Test timeouts",
   result: EXEC_TIMEOUT,
-  modes: [device],
+  modes: [device_testdex],
   names: ["org.apache.harmony.tests.java.util.ScannerTest#testPerformance"]
 },
 {
   description: "Needs the newest cat version on the device",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getErrorStream"]
 },
 {
@@ -117,7 +117,7 @@
 },
 {
   description: "Linker issues in chrooted environment",
-  modes: [device],
+  modes: [device_testdex],
   result: EXEC_FAILED,
   names: ["org.apache.harmony.tests.java.lang.ProcessManagerTest#testEnvironment"]
 },
@@ -130,14 +130,14 @@
 {
   description: "test_xattr fails on arm64 on the buildbots only: needs investigation",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["libcore.libcore.io.OsTest#test_xattr"],
   bug: 22258911
 },
 {
   description: "fails on L builds: needs investigation",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["org.apache.harmony.tests.java.lang.ClassTest#test_forNameLjava_lang_String"]
 },
 {
@@ -163,7 +163,7 @@
   description: "Flaky test",
   result: EXEC_FAILED,
   bug: 30107038,
-  modes: [device],
+  modes: [device_testdex],
   names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_destroyForcibly"]
 },
 {
@@ -171,7 +171,7 @@
                 Unclear if this relates to the tests running sh as a child process.",
   result: EXEC_FAILED,
   bug: 30657148,
-  modes: [device],
+  modes: [device_testdex],
   names: ["libcore.java.lang.ProcessBuilderTest#testRedirectInherit",
           "libcore.java.lang.ProcessBuilderTest#testRedirect_nullStreams"]
 },
@@ -195,7 +195,7 @@
 {
   description: "Timeout on heap-poisoning target builds",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   bug: 116446372,
   names: ["libcore.libcore.io.FdsanTest#testSocket"]
 },
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 23533af..25a4c82 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -7,7 +7,7 @@
 {
   description: "Timeouts on target with gcstress and debug.",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
           "jsr166.CompletableFutureTest#testDelayedExecutor",
           "jsr166.ExecutorsTest#testTimedCallable",
diff --git a/tools/libcore_gcstress_failures.txt b/tools/libcore_gcstress_failures.txt
index eec45fa..34ede69 100644
--- a/tools/libcore_gcstress_failures.txt
+++ b/tools/libcore_gcstress_failures.txt
@@ -7,7 +7,7 @@
 {
   description: "Timeouts on target with gcstress.",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["libcore.javax.crypto.CipherBasicsTest#testGcmEncryption"]
 },
 {
@@ -26,7 +26,7 @@
 {
   description: "Timeouts.",
   result: EXEC_FAILED,
-  modes: [device],
+  modes: [device_testdex],
   names: ["jsr166.TimeUnitTest#testConvert",
           "libcore.java.lang.StringTest#testFastPathString_wellFormedUtf8Sequence",
           "libcore.java.text.DecimalFormatTest#testCurrencySymbolSpacing",