Merge "ART: Move .bss mappings check before .bss init."
diff --git a/Android.mk b/Android.mk
index 526cd59..1a7ed43 100644
--- a/Android.mk
+++ b/Android.mk
@@ -352,13 +352,18 @@
   # Module with both release and debug variants, as well as
   # additional tools.
   TARGET_RUNTIME_APEX := com.android.runtime.debug
+  APEX_TEST_MODULE := art-check-debug-apex-gen-fakebin
 else
   # Release module (without debug variants nor tools).
   TARGET_RUNTIME_APEX := com.android.runtime.release
+  APEX_TEST_MODULE := art-check-release-apex-gen-fakebin
 endif
 
 LOCAL_MODULE := com.android.runtime
 LOCAL_REQUIRED_MODULES := $(TARGET_RUNTIME_APEX)
+ifneq ($(HOST_OS),darwin)
+  LOCAL_REQUIRED_MODULES += $(APEX_TEST_MODULE)
+endif
 
 # Clear locally used variable.
 art_target_include_debug_build :=
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 4a6637b..e3e5b6e 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -230,3 +230,72 @@
         },
     },
 }
+
+python_binary_host {
+    name: "art-apex-tester",
+    srcs: ["art_apex_test.py"],
+    main: "art_apex_test.py",
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+}
+
+// Genrules so we can run the checker, and empty Java library so that it gets executed.
+
+genrule {
+    name: "art-check-release-apex-gen",
+    srcs: [":com.android.runtime.release"],
+    tools: [
+        "art-apex-tester",
+        "debugfs",
+    ],
+    cmd: "$(location art-apex-tester)"
+              + " --debugfs $(location debugfs)"
+              + " --tmpdir $(genDir)"
+              + " $(in)"
+         + " && touch $(out)",
+    out: ["art-check-release-apex-gen.dummy"],
+}
+cc_prebuilt_binary {
+    name: "art-check-release-apex-gen-fakebin",
+    srcs: [":art-check-release-apex-gen"],
+    host_supported: true,
+    device_supported: false,
+    target: {
+        darwin: {
+            enabled: false,  // No python3.
+        },
+    },
+}
+
+genrule {
+    name: "art-check-debug-apex-gen",
+    srcs: [":com.android.runtime.debug"],
+    tools: [
+        "art-apex-tester",
+        "debugfs",
+    ],
+    cmd: "$(location art-apex-tester)"
+              + " --debugfs $(location debugfs)"
+              + " --tmpdir $(genDir)"
+              + " --debug"
+              + " $(in)"
+         + " && touch $(out)",
+    out: ["art-check-debug-apex-gen.dummy"],
+}
+cc_prebuilt_binary {
+    name: "art-check-debug-apex-gen-fakebin",
+    srcs: [":art-check-debug-apex-gen"],
+    host_supported: true,
+    device_supported: false,
+    target: {
+        darwin: {
+            enabled: false,  // No python3.
+        },
+    },
+}
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 2721cb5..2de0f0c 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -20,6 +20,7 @@
 #include "art_method-inl.h"
 #include "base/utils.h"
 #include "class_linker.h"
+#include "class_root.h"
 #include "dex/invoke_type.h"
 #include "driver/compiler_options.h"
 #include "gc/space/image_space.h"
@@ -362,4 +363,13 @@
   return info;
 }
 
+void IntrinsicVisitor::AssertNonMovableStringClass() {
+  if (kIsDebugBuild) {
+    Thread* const self = Thread::Current();
+    ReaderMutexLock mu(self, *Locks::mutator_lock_);
+    ObjPtr<mirror::Class> string_class = GetClassRoot<art::mirror::String>();
+    CHECK(!art::Runtime::Current()->GetHeap()->IsMovableObject(string_class));
+  }
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 50b13c8..ab68cce 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -142,6 +142,8 @@
  protected:
   IntrinsicVisitor() {}
 
+  static void AssertNonMovableStringClass();
+
  private:
   DISALLOW_COPY_AND_ASSIGN(IntrinsicVisitor);
 };
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index ca790f6..ec5d17a 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1464,8 +1464,16 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // /* HeapReference<Class> */ temp = str->klass_
     __ Ldr(temp, MemOperand(str.X(), class_offset));
+    // /* HeapReference<Class> */ temp1 = arg->klass_
     __ Ldr(temp1, MemOperand(arg.X(), class_offset));
+    // Also, because we use the previously loaded class references only in the
+    // following comparison, we don't need to unpoison them.
     __ Cmp(temp, temp1);
     __ B(&return_false, ne);
   }
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 396ff62..f0aa92e 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -1529,8 +1529,16 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // /* HeapReference<Class> */ temp = str->klass_
     __ Ldr(temp, MemOperand(str, class_offset));
+    // /* HeapReference<Class> */ out = arg->klass_
     __ Ldr(out, MemOperand(arg, class_offset));
+    // Also, because we use the previously loaded class references only in the
+    // following comparison, we don't need to unpoison them.
     __ Cmp(temp, out);
     __ B(ne, &return_false, /* is_far_target= */ false);
   }
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 5b35974..3da0e57 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1575,8 +1575,16 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // /* HeapReference<Class> */ temp1 = str->klass_
     __ Lw(temp1, str, class_offset);
+    // /* HeapReference<Class> */ temp2 = arg->klass_
     __ Lw(temp2, arg, class_offset);
+    // Also, because we use the previously loaded class references only in the
+    // following comparison, we don't need to unpoison them.
     __ Bne(temp1, temp2, &return_false);
   }
 
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index afaa4ca..3e68765 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1429,8 +1429,16 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // /* HeapReference<Class> */ temp1 = str->klass_
     __ Lw(temp1, str, class_offset);
+    // /* HeapReference<Class> */ temp2 = arg->klass_
     __ Lw(temp2, arg, class_offset);
+    // Also, because we use the previously loaded class references only in the
+    // following comparison, we don't need to unpoison them.
     __ Bnec(temp1, temp2, &return_false);
   }
 
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 8747f06..de697f0 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1072,7 +1072,15 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // Also, because we use the loaded class references only to compare them, we
+    // don't need to unpoison them.
+    // /* HeapReference<Class> */ ecx = str->klass_
     __ movl(ecx, Address(str, class_offset));
+    // if (ecx != /* HeapReference<Class> */ arg->klass_) return false
     __ cmpl(ecx, Address(arg, class_offset));
     __ j(kNotEqual, &return_false);
   }
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 167c1d8..e79c0c9 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1275,7 +1275,15 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // Also, because we use the loaded class references only to compare them, we
+    // don't need to unpoison them.
+    // /* HeapReference<Class> */ rcx = str->klass_
     __ movl(rcx, Address(str, class_offset));
+    // if (rcx != /* HeapReference<Class> */ arg->klass_) return false
     __ cmpl(rcx, Address(arg, class_offset));
     __ j(kNotEqual, &return_false);
   }
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
index dee83d1..841ff1c 100644
--- a/compiler/utils/swap_space.cc
+++ b/compiler/utils/swap_space.cc
@@ -115,12 +115,11 @@
       ? free_by_size_.end()
       : free_by_size_.lower_bound(FreeBySizeEntry { size, free_by_start_.begin() });
   if (it != free_by_size_.end()) {
-    auto entry = it->free_by_start_entry;
-    SpaceChunk old_chunk = *entry;
+    SpaceChunk old_chunk = *it->free_by_start_entry;
     if (old_chunk.size == size) {
       RemoveChunk(it);
     } else {
-      // Try to avoid deallocating and allocating the std::set<> nodes.
+      // Avoid deallocating and allocating the std::set<> nodes.
       // This would be much simpler if we could use replace() from Boost.Bimap.
 
       // The free_by_start_ map contains disjoint intervals ordered by the `ptr`.
@@ -128,24 +127,9 @@
       it->free_by_start_entry->ptr += size;
       it->free_by_start_entry->size -= size;
 
-      // The free_by_size_ map is ordered by the `size` and then `free_by_start_entry->ptr`.
-      // Adjusting the `ptr` above does not change that ordering but decreasing `size` can
-      // push the node before the previous node(s).
-      if (it == free_by_size_.begin()) {
-        it->size -= size;
-      } else {
-        auto prev = it;
-        --prev;
-        FreeBySizeEntry new_value(old_chunk.size - size, entry);
-        if (free_by_size_.key_comp()(*prev, new_value)) {
-          it->size -= size;
-        } else {
-          // Changing in place would break the std::set<> ordering, we need to remove and insert.
-          // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
-          free_by_size_.erase(it);
-          free_by_size_.insert(new_value);
-        }
-      }
+      auto node = free_by_size_.extract(it);
+      node.value().size -= size;
+      free_by_size_.insert(std::move(node));
     }
     return old_chunk.ptr;
   } else {
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index 2833750..ba2a7c6 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -796,13 +796,13 @@
     // Shrink the reservation MemMap and update its `gMaps` entry.
     std::lock_guard<std::mutex> mu(*mem_maps_lock_);
     auto it = GetGMapsEntry(*this);
-    // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
-    gMaps->erase(it);
+    auto node = gMaps->extract(it);
     begin_ += byte_count;
     size_ -= byte_count;
     base_begin_ = begin_;
     base_size_ = size_;
-    gMaps->emplace(base_begin_, this);
+    node.key() = base_begin_;
+    gMaps->insert(std::move(node));
   }
 }
 
@@ -1266,9 +1266,9 @@
   std::lock_guard<std::mutex> mu(*mem_maps_lock_);
   if (base_begin < aligned_base_begin) {
     auto it = GetGMapsEntry(*this);
-    // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
-    gMaps->erase(it);
-    gMaps->insert(std::make_pair(aligned_base_begin, this));
+    auto node = gMaps->extract(it);
+    node.key() = aligned_base_begin;
+    gMaps->insert(std::move(node));
   }
   base_begin_ = aligned_base_begin;
   base_size_ = aligned_base_size;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ae6ac9b..2e1f364 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -25,6 +25,7 @@
 #include <memory>
 #include <queue>
 #include <string>
+#include <string_view>
 #include <tuple>
 #include <unordered_map>
 #include <utility>
@@ -7015,11 +7016,13 @@
   return FindSameNameAndSignature(cmp, rest...);
 }
 
+namespace {
+
 // Check that all vtable entries are present in this class's virtuals or are the same as a
 // superclasses vtable entry.
-static void CheckClassOwnsVTableEntries(Thread* self,
-                                        Handle<mirror::Class> klass,
-                                        PointerSize pointer_size)
+void CheckClassOwnsVTableEntries(Thread* self,
+                                 Handle<mirror::Class> klass,
+                                 PointerSize pointer_size)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<2> hs(self);
   Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
@@ -7049,47 +7052,185 @@
 
 // Check to make sure the vtable does not have duplicates. Duplicates could cause problems when a
 // method is overridden in a subclass.
-static void CheckVTableHasNoDuplicates(Thread* self,
-                                       Handle<mirror::Class> klass,
-                                       PointerSize pointer_size)
+template <PointerSize kPointerSize>
+void CheckVTableHasNoDuplicates(Thread* self, Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<1> hs(self);
   Handle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
   int32_t num_entries = vtable->GetLength();
-  for (int32_t i = 0; i < num_entries; i++) {
-    ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
-    // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member maybe).
+
+  // Observations:
+  //   * The older implementation was O(n^2) and got too expensive for apps with larger classes.
+  //   * Many classes do not override Object functions (e.g., equals/hashCode/toString). Thus,
+  //     for many classes outside of libcore a cross-dexfile check has to be run anyways.
+  //   * In the cross-dexfile case, with the O(n^2), in the best case O(n) cross checks would have
+  //     to be done. It is thus OK in a single-pass algorithm to read all data, anyways.
+  //   * The single-pass algorithm will trade memory for speed, but that is OK.
+
+  CHECK_GT(num_entries, 0);
+
+  auto log_fn = [&vtable, &klass](int32_t i, int32_t j) REQUIRES_SHARED(Locks::mutator_lock_) {
+    ArtMethod* m1 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(i);
+    ArtMethod* m2 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(j);
+    LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
+                 << klass->PrettyClass() << " in method " << m1->PrettyMethod()
+                << " (0x" << std::hex << reinterpret_cast<uintptr_t>(m2) << ") and "
+                << m2->PrettyMethod() << "  (0x" << std::hex
+                << reinterpret_cast<uintptr_t>(m2) << ")";
+  };
+  struct BaseHashType {
+    static size_t HashCombine(size_t seed, size_t val) {
+      return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+    }
+  };
+
+  // Check assuming all entries come from the same dex file.
+  {
+    // Find the first interesting method and its dex file.
+    int32_t start = 0;
+    for (; start < num_entries; ++start) {
+      ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start);
+      // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+      // maybe).
+      if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+                                  vtable_entry->GetAccessFlags())) {
+        continue;
+      }
+      break;
+    }
+    if (start == num_entries) {
+      return;
+    }
+    const DexFile* dex_file =
+        vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start)->
+            GetInterfaceMethodIfProxy(kPointerSize)->GetDexFile();
+
+    // Helper function to avoid logging if we have to run the cross-file checks.
+    auto check_fn = [&](bool log_warn) REQUIRES_SHARED(Locks::mutator_lock_) {
+      // Use a map to store seen entries, as the storage space is too large for a bitvector.
+      using PairType = std::pair<uint32_t, uint16_t>;
+      struct PairHash : BaseHashType {
+        size_t operator()(const PairType& key) const {
+          return BaseHashType::HashCombine(BaseHashType::HashCombine(0, key.first), key.second);
+        }
+      };
+      std::unordered_map<PairType, int32_t, PairHash> seen;
+      seen.reserve(2 * num_entries);
+      bool need_slow_path = false;
+      bool found_dup = false;
+      for (int i = start; i < num_entries; ++i) {
+        // Can use Unchecked here as the start loop already ensured that the arrays are correct
+        // wrt/ kPointerSize.
+        ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+        if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+                                    vtable_entry->GetAccessFlags())) {
+          continue;
+        }
+        ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+        if (dex_file != m->GetDexFile()) {
+          need_slow_path = true;
+          break;
+        }
+        const dex::MethodId* m_mid = &dex_file->GetMethodId(m->GetDexMethodIndex());
+        PairType pair = std::make_pair(m_mid->name_idx_.index_, m_mid->proto_idx_.index_);
+        auto it = seen.find(pair);
+        if (it != seen.end()) {
+          found_dup = true;
+          if (log_warn) {
+            log_fn(it->second, i);
+          }
+        } else {
+          seen.emplace(pair, i);
+        }
+      }
+      return std::make_pair(need_slow_path, found_dup);
+    };
+    std::pair<bool, bool> result = check_fn(/* log_warn= */ false);
+    if (!result.first) {
+      if (result.second) {
+        check_fn(/* log_warn= */ true);
+      }
+      return;
+    }
+  }
+
+  // Need to check across dex files.
+  struct Entry {
+    size_t cached_hash = 0;
+    const char* name = nullptr;
+    Signature signature = Signature::NoSignature();
+    uint32_t name_len = 0;
+
+    Entry(const DexFile* dex_file, const dex::MethodId& mid)
+        : name(dex_file->StringDataAndUtf16LengthByIdx(mid.name_idx_, &name_len)),
+          signature(dex_file->GetMethodSignature(mid)) {
+    }
+
+    bool operator==(const Entry& other) const {
+      if (name_len != other.name_len || strcmp(name, other.name) != 0) {
+        return false;
+      }
+      return signature == other.signature;
+    }
+  };
+  struct EntryHash {
+    size_t operator()(const Entry& key) const {
+      return key.cached_hash;
+    }
+  };
+  std::unordered_map<Entry, int32_t, EntryHash> map;
+  for (int32_t i = 0; i < num_entries; ++i) {
+    // Can use Unchecked here as the first loop already ensured that the arrays are correct
+    // wrt/ kPointerSize.
+    ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+    // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+    // maybe).
     if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
                                 vtable_entry->GetAccessFlags())) {
       continue;
     }
-    MethodNameAndSignatureComparator name_comparator(
-        vtable_entry->GetInterfaceMethodIfProxy(pointer_size));
-    for (int32_t j = i + 1; j < num_entries; j++) {
-      ArtMethod* other_entry = vtable->GetElementPtrSize<ArtMethod*>(j, pointer_size);
-      if (!klass->CanAccessMember(other_entry->GetDeclaringClass(),
-                                  other_entry->GetAccessFlags())) {
-        continue;
-      }
-      if (vtable_entry == other_entry ||
-          name_comparator.HasSameNameAndSignature(
-               other_entry->GetInterfaceMethodIfProxy(pointer_size))) {
-        LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
-                     << klass->PrettyClass() << " in method " << vtable_entry->PrettyMethod()
-                     << " (0x" << std::hex << reinterpret_cast<uintptr_t>(vtable_entry) << ") and "
-                     << other_entry->PrettyMethod() << "  (0x" << std::hex
-                     << reinterpret_cast<uintptr_t>(other_entry) << ")";
-      }
+    ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+    const DexFile* dex_file = m->GetDexFile();
+    const dex::MethodId& mid = dex_file->GetMethodId(m->GetDexMethodIndex());
+
+    Entry e(dex_file, mid);
+
+    size_t string_hash = std::hash<std::string_view>()(std::string_view(e.name, e.name_len));
+    size_t sig_hash = std::hash<std::string>()(e.signature.ToString());
+    e.cached_hash = BaseHashType::HashCombine(BaseHashType::HashCombine(0u, string_hash),
+                                              sig_hash);
+
+    auto it = map.find(e);
+    if (it != map.end()) {
+      log_fn(it->second, i);
+    } else {
+      map.emplace(e, i);
     }
   }
 }
 
+void CheckVTableHasNoDuplicates(Thread* self,
+                                Handle<mirror::Class> klass,
+                                PointerSize pointer_size)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  switch (pointer_size) {
+    case PointerSize::k64:
+      CheckVTableHasNoDuplicates<PointerSize::k64>(self, klass);
+      break;
+    case PointerSize::k32:
+      CheckVTableHasNoDuplicates<PointerSize::k32>(self, klass);
+      break;
+  }
+}
+
 static void SanityCheckVTable(Thread* self, Handle<mirror::Class> klass, PointerSize pointer_size)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   CheckClassOwnsVTableEntries(self, klass, pointer_size);
   CheckVTableHasNoDuplicates(self, klass, pointer_size);
 }
 
+}  // namespace
+
 void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass,
                                         ArtMethod* unimplemented_method,
                                         ArtMethod* imt_conflict_method,
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index a6a5ba2..d9d88e1 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -224,15 +224,31 @@
   }
 }
 
-template<typename T, VerifyObjectFlags kVerifyFlags>
-inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx) {
   // C style casts here since we sometimes have T be a pointer, or sometimes an integer
   // (for stack traces).
-  if (ptr_size == PointerSize::k64) {
+  if (kPointerSize == PointerSize::k64) {
     return (T)static_cast<uintptr_t>(AsLongArray<kVerifyFlags>()->GetWithoutChecks(idx));
   }
   return (T)static_cast<uintptr_t>(AsIntArray<kVerifyFlags>()->GetWithoutChecks(idx));
 }
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSizeUnchecked(uint32_t idx) {
+  // C style casts here since we sometimes have T be a pointer, or sometimes an integer
+  // (for stack traces).
+  if (kPointerSize == PointerSize::k64) {
+    return (T)static_cast<uintptr_t>(AsLongArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+  }
+  return (T)static_cast<uintptr_t>(AsIntArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+}
+template<typename T, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+  if (ptr_size == PointerSize::k64) {
+    return GetElementPtrSize<T, PointerSize::k64, kVerifyFlags>(idx);
+  }
+  return GetElementPtrSize<T, PointerSize::k32, kVerifyFlags>(idx);
+}
 
 template<bool kTransactionActive, bool kUnchecked>
 inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, PointerSize ptr_size) {
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 8816c61..2e894d5 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -32,6 +32,8 @@
 
 class MANAGED Array : public Object {
  public:
+  static constexpr size_t kFirstElementOffset = 12u;
+
   // The size of a java.lang.Class representing an array.
   static uint32_t ClassSize(PointerSize pointer_size);
 
@@ -79,6 +81,17 @@
         << "Array data offset isn't aligned with component size";
     return MemberOffset(data_offset);
   }
+  template <size_t kComponentSize>
+  static constexpr MemberOffset DataOffset() {
+    static_assert(IsPowerOfTwo(kComponentSize), "Invalid component size");
+    constexpr size_t data_offset = RoundUp(kFirstElementOffset, kComponentSize);
+    static_assert(RoundUp(data_offset, kComponentSize) == data_offset, "RoundUp fail");
+    return MemberOffset(data_offset);
+  }
+
+  static constexpr size_t FirstElementOffset() {
+    return OFFSETOF_MEMBER(Array, first_element_);
+  }
 
   void* GetRawData(size_t component_size, int32_t index)
       REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -86,12 +99,24 @@
         + (index * component_size);
     return reinterpret_cast<void*>(data);
   }
+  template <size_t kComponentSize>
+  void* GetRawData(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+    intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+        + (index * kComponentSize);
+    return reinterpret_cast<void*>(data);
+  }
 
   const void* GetRawData(size_t component_size, int32_t index) const {
     intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset(component_size).Int32Value() +
         + (index * component_size);
     return reinterpret_cast<void*>(data);
   }
+  template <size_t kComponentSize>
+  const void* GetRawData(int32_t index) const {
+    intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+        + (index * kComponentSize);
+    return reinterpret_cast<void*>(data);
+  }
 
   // Returns true if the index is valid. If not, throws an ArrayIndexOutOfBoundsException and
   // returns false.
@@ -132,11 +157,11 @@
 
 
   const T* GetData() const ALWAYS_INLINE  REQUIRES_SHARED(Locks::mutator_lock_) {
-    return reinterpret_cast<const T*>(GetRawData(sizeof(T), 0));
+    return reinterpret_cast<const T*>(GetRawData<sizeof(T)>(0));
   }
 
   T* GetData() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
-    return reinterpret_cast<T*>(GetRawData(sizeof(T), 0));
+    return reinterpret_cast<T*>(GetRawData<sizeof(T)>(0));
   }
 
   T Get(int32_t i) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
@@ -196,6 +221,15 @@
   template<typename T, VerifyObjectFlags kVerifyFlags = kVerifyNone>
   T GetElementPtrSize(uint32_t idx, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
+  template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+  T GetElementPtrSize(uint32_t idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  // Same as GetElementPtrSize, but uses unchecked version of array conversion. It is thus not
+  // checked whether kPtrSize matches the underlying array. Only use after at least one invocation
+  // of GetElementPtrSize!
+  template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+  T GetElementPtrSizeUnchecked(uint32_t idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kVerifyNone>
   void** ElementAddress(size_t index, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2c2ad9b..005e272 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -253,9 +253,13 @@
 }
 
 template<VerifyObjectFlags kVerifyFlags>
+inline IntArray* Object::AsIntArrayUnchecked() {
+  return down_cast<IntArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
 inline IntArray* Object::AsIntArray() {
   DCHECK((IsIntArray<kVerifyFlags>()));
-  return down_cast<IntArray*>(this);
+  return AsIntArrayUnchecked<kVerifyFlags>();
 }
 
 template<VerifyObjectFlags kVerifyFlags>
@@ -264,9 +268,13 @@
 }
 
 template<VerifyObjectFlags kVerifyFlags>
+inline LongArray* Object::AsLongArrayUnchecked() {
+  return down_cast<LongArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
 inline LongArray* Object::AsLongArray() {
   DCHECK((IsLongArray<kVerifyFlags>()));
-  return down_cast<LongArray*>(this);
+  return AsLongArrayUnchecked<kVerifyFlags>();
 }
 
 template<VerifyObjectFlags kVerifyFlags>
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index ba222f6..ca8867d8 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -223,11 +223,15 @@
   bool IsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   IntArray* AsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  IntArray* AsIntArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   LongArray* AsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  LongArray* AsLongArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsFloatArray() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 26f21b0..8f89f52 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -199,6 +199,7 @@
 };
 
 namespace {
+
 #ifdef __APPLE__
 inline char** GetEnviron() {
   // When Google Test is built as a framework on MacOS X, the environ variable
@@ -212,6 +213,11 @@
 extern "C" char** environ;
 inline char** GetEnviron() { return environ; }
 #endif
+
+void CheckConstants() {
+  CHECK_EQ(mirror::Array::kFirstElementOffset, mirror::Array::FirstElementOffset());
+}
+
 }  // namespace
 
 Runtime::Runtime()
@@ -284,6 +290,7 @@
       verifier_logging_threshold_ms_(100) {
   static_assert(Runtime::kCalleeSaveSize ==
                     static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
+  CheckConstants();
 
   std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
   interpreter::CheckInterpreterAsmConstants();