ART: Optimize array accesses

Optimize computation of the data offset of arrays by adding
a constant for the array payload field offset, and templatized
versions of the computation. Add a correctness check on runtime
creation.

Templatize CheckVTableHasNoDuplicates.

Decreases dex2oatd preopting of a big app from 165s to 151s.

Bug: 123888325
Test: m test-art-host
Change-Id: I8db9df545dc807a307aef8af7dad7a15757670b1
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d29a6b7..cb1fbfe 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -7074,31 +7074,30 @@
 
 // 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>
+static 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);
+    ArtMethod* vtable_entry = vtable->GetElementPtrSize<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));
+        vtable_entry->GetInterfaceMethodIfProxy(kPointerSize));
     for (int32_t j = i + 1; j < num_entries; j++) {
-      ArtMethod* other_entry = vtable->GetElementPtrSize<ArtMethod*>(j, pointer_size);
+      ArtMethod* other_entry = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(j);
       if (!klass->CanAccessMember(other_entry->GetDeclaringClass(),
                                   other_entry->GetAccessFlags())) {
         continue;
       }
       if (vtable_entry == other_entry ||
           name_comparator.HasSameNameAndSignature(
-               other_entry->GetInterfaceMethodIfProxy(pointer_size))) {
+               other_entry->GetInterfaceMethodIfProxy(kPointerSize))) {
         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 "
@@ -7108,6 +7107,19 @@
     }
   }
 }
+static 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_) {
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index a6a5ba2..40507e7 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -224,14 +224,23 @@
   }
 }
 
+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 (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, VerifyObjectFlags kVerifyFlags>
 inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
   // C style casts here since we sometimes have T be a pointer, or sometimes an integer
   // (for stack traces).
   if (ptr_size == PointerSize::k64) {
-    return (T)static_cast<uintptr_t>(AsLongArray<kVerifyFlags>()->GetWithoutChecks(idx));
+    return GetElementPtrSize<T, PointerSize::k64, kVerifyFlags>(idx);
   }
-  return (T)static_cast<uintptr_t>(AsIntArray<kVerifyFlags>()->GetWithoutChecks(idx));
+  return GetElementPtrSize<T, PointerSize::k32, kVerifyFlags>(idx);
 }
 
 template<bool kTransactionActive, bool kUnchecked>
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 8816c61..ce64272 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,9 @@
   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_);
 
   template<VerifyObjectFlags kVerifyFlags = kVerifyNone>
   void** ElementAddress(size_t index, PointerSize ptr_size) 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();