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();