ART: Compress LengthPrefixedArray on 32-bit targets.
Previously, the LengthPrefixedArray<ArtMethod> on 32-bit
targets contained a 64-bit length field followed by the
ArtMethod elements with size only a multiple of 4, not 8.
Consequently, an odd-length array broke the alignment for
the following array which would have the 64-bit length
placed at an unaligned address.
To fix that, we make the length field 32-bit and explicitly
pass the alignment information to the LengthPrefixedArray.
This also makes the 32-bit boot image a bit smaller.
On Nexus 5, AOSP, ToT, the field section is 11528B smaller
and the method section is 21036B smaller. 64-bit targets
should see the same savings for the field section but no
difference for the methods section.
Change-Id: I3e03e7b94129025c8a1c117c27645a34dec516d2
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 85c03ed..8221fe2 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -513,6 +513,13 @@
(sizeof(PtrSizedFields) / sizeof(void*)) * pointer_size;
}
+ // Alignment of an instance of this object.
+ static size_t ObjectAlignment(size_t pointer_size) {
+ // The ArtMethod alignment is the same as image pointer size. This differs from
+ // alignof(ArtMethod) if cross-compiling with image_pointer_size_ != sizeof(void*).
+ return pointer_size;
+ }
+
void CopyFrom(const ArtMethod* src, size_t image_pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h
index 6f45dc8..1da6750 100644
--- a/runtime/base/bit_utils.h
+++ b/runtime/base/bit_utils.h
@@ -158,6 +158,9 @@
#define DCHECK_ALIGNED(value, alignment) \
DCHECK(::art::IsAligned<alignment>(value)) << reinterpret_cast<const void*>(value)
+#define CHECK_ALIGNED_PARAM(value, alignment) \
+ CHECK(::art::IsAlignedParam(value, alignment)) << reinterpret_cast<const void*>(value)
+
#define DCHECK_ALIGNED_PARAM(value, alignment) \
DCHECK(::art::IsAlignedParam(value, alignment)) << reinterpret_cast<const void*>(value)
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f19263d..ef48710 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1213,9 +1213,8 @@
if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
const ImageHeader& header = space->GetImageHeader();
const ImageSection& methods = header.GetMethodsSection();
- const size_t art_method_size = ArtMethod::ObjectSize(image_pointer_size_);
SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_);
- methods.VisitPackedArtMethods(&visitor, space->Begin(), art_method_size);
+ methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
}
// reinit class_roots_
@@ -2294,9 +2293,11 @@
if (length == 0) {
return nullptr;
}
- auto* ret = new(Runtime::Current()->GetLinearAlloc()->Alloc(
- self, LengthPrefixedArray<ArtField>::ComputeSize(length))) LengthPrefixedArray<ArtField>(
- length);
+ // If the ArtField alignment changes, review all uses of LengthPrefixedArray<ArtField>.
+ static_assert(alignof(ArtField) == 4, "ArtField alignment is expected to be 4.");
+ size_t storage_size = LengthPrefixedArray<ArtField>::ComputeSize(length);
+ void* array_storage = Runtime::Current()->GetLinearAlloc()->Alloc(self, storage_size);
+ auto* ret = new(array_storage) LengthPrefixedArray<ArtField>(length);
CHECK(ret != nullptr);
std::uninitialized_fill_n(&ret->At(0), length, ArtField());
return ret;
@@ -2306,13 +2307,15 @@
if (length == 0) {
return nullptr;
}
+ const size_t method_alignment = ArtMethod::ObjectAlignment(image_pointer_size_);
const size_t method_size = ArtMethod::ObjectSize(image_pointer_size_);
- auto* ret = new (Runtime::Current()->GetLinearAlloc()->Alloc(
- self, LengthPrefixedArray<ArtMethod>::ComputeSize(length, method_size)))
- LengthPrefixedArray<ArtMethod>(length);
+ const size_t storage_size =
+ LengthPrefixedArray<ArtMethod>::ComputeSize(length, method_size, method_alignment);
+ void* array_storage = Runtime::Current()->GetLinearAlloc()->Alloc(self, storage_size);
+ auto* ret = new (array_storage) LengthPrefixedArray<ArtMethod>(length);
CHECK(ret != nullptr);
for (size_t i = 0; i < length; ++i) {
- new(reinterpret_cast<void*>(&ret->At(i, method_size))) ArtMethod;
+ new(reinterpret_cast<void*>(&ret->At(i, method_size, method_alignment))) ArtMethod;
}
return ret;
}
@@ -4689,6 +4692,7 @@
const bool have_interfaces = interfaces.Get() != nullptr;
const size_t num_interfaces =
have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
+ const size_t method_alignment = ArtMethod::ObjectAlignment(image_pointer_size_);
const size_t method_size = ArtMethod::ObjectSize(image_pointer_size_);
if (num_interfaces == 0) {
if (super_ifcount == 0) {
@@ -4914,7 +4918,7 @@
// matter which direction we go. We walk it backward anyway.)
for (k = input_array_length - 1; k >= 0; --k) {
ArtMethod* vtable_method = input_virtual_methods != nullptr ?
- &input_virtual_methods->At(k, method_size) :
+ &input_virtual_methods->At(k, method_size, method_alignment) :
input_vtable_array->GetElementPtrSize<ArtMethod*>(k, image_pointer_size_);
ArtMethod* vtable_method_for_name_comparison =
vtable_method->GetInterfaceMethodIfProxy(image_pointer_size_);
@@ -4975,10 +4979,14 @@
// where GCs could attempt to mark stale pointers due to memcpy. And since we overwrite the
// realloced memory with out->CopyFrom, we are guaranteed to have objects in the to space since
// CopyFrom has internal read barriers.
- const size_t old_size = old_virtuals != nullptr ?
- LengthPrefixedArray<ArtMethod>::ComputeSize(old_method_count, method_size) : 0u;
+ const size_t old_size = old_virtuals != nullptr
+ ? LengthPrefixedArray<ArtMethod>::ComputeSize(old_method_count,
+ method_size,
+ method_alignment)
+ : 0u;
const size_t new_size = LengthPrefixedArray<ArtMethod>::ComputeSize(new_method_count,
- method_size);
+ method_size,
+ method_alignment);
auto* virtuals = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
runtime->GetLinearAlloc()->Realloc(self, old_virtuals, old_size, new_size));
if (UNLIKELY(virtuals == nullptr)) {
@@ -4989,7 +4997,7 @@
ScopedArenaUnorderedMap<ArtMethod*, ArtMethod*> move_table(allocator.Adapter());
if (virtuals != old_virtuals) {
// Maps from heap allocated miranda method to linear alloc miranda method.
- StrideIterator<ArtMethod> out = virtuals->Begin(method_size);
+ StrideIterator<ArtMethod> out = virtuals->Begin(method_size, method_alignment);
// Copy over the old methods + miranda methods.
for (auto& m : klass->GetVirtualMethods(image_pointer_size_)) {
move_table.emplace(&m, &*out);
@@ -4999,7 +5007,7 @@
++out;
}
}
- StrideIterator<ArtMethod> out(virtuals->Begin(method_size) + old_method_count);
+ StrideIterator<ArtMethod> out(virtuals->Begin(method_size, method_alignment) + old_method_count);
// Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and
// we want the roots of the miranda methods to get visited.
for (ArtMethod* mir_method : miranda_methods) {
@@ -5022,7 +5030,7 @@
self->AssertPendingOOMException();
return false;
}
- out = StrideIterator<ArtMethod>(virtuals->Begin(method_size) + old_method_count);
+ out = virtuals->Begin(method_size, method_alignment) + old_method_count;
size_t vtable_pos = old_vtable_count;
for (size_t i = old_method_count; i < new_method_count; ++i) {
// Leave the declaring class alone as type indices are relative to it
@@ -5893,8 +5901,10 @@
}
ArtMethod* ClassLinker::CreateRuntimeMethod() {
+ const size_t method_alignment = ArtMethod::ObjectAlignment(image_pointer_size_);
const size_t method_size = ArtMethod::ObjectSize(image_pointer_size_);
- ArtMethod* method = &AllocArtMethodArray(Thread::Current(), 1)->At(0, method_size);
+ LengthPrefixedArray<ArtMethod>* method_array = AllocArtMethodArray(Thread::Current(), 1);
+ ArtMethod* method = &method_array->At(0, method_size, method_alignment);
CHECK(method != nullptr);
method->SetDexMethodIndex(DexFile::kDexNoIndex);
CHECK(method->IsRuntimeMethod());
diff --git a/runtime/image.cc b/runtime/image.cc
index ba1e58b..5890947b 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '1', '8', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '1', '9', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
@@ -153,19 +153,21 @@
for (size_t i = 0; i < array->Length(); ++i) {
visitor->Visit(&array->At(i, sizeof(ArtField)));
}
- pos += array->ComputeSize(array->Length(), sizeof(ArtField));
+ pos += array->ComputeSize(array->Length());
}
}
void ImageSection::VisitPackedArtMethods(ArtMethodVisitor* visitor,
uint8_t* base,
- size_t method_size) const {
+ size_t pointer_size) const {
+ const size_t method_alignment = ArtMethod::ObjectAlignment(pointer_size);
+ const size_t method_size = ArtMethod::ObjectSize(pointer_size);
for (size_t pos = 0; pos < Size(); ) {
auto* array = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(base + Offset() + pos);
for (size_t i = 0; i < array->Length(); ++i) {
- visitor->Visit(&array->At(i, method_size));
+ visitor->Visit(&array->At(i, method_size, method_alignment));
}
- pos += array->ComputeSize(array->Length(), method_size);
+ pos += array->ComputeSize(array->Length(), method_size, method_alignment);
}
}
diff --git a/runtime/image.h b/runtime/image.h
index eb26f7f..1a0d8fd 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -65,7 +65,7 @@
}
// Visit ArtMethods in the section starting at base.
- void VisitPackedArtMethods(ArtMethodVisitor* visitor, uint8_t* base, size_t method_size) const;
+ void VisitPackedArtMethods(ArtMethodVisitor* visitor, uint8_t* base, size_t pointer_size) const;
// Visit ArtMethods in the section starting at base.
void VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const;
diff --git a/runtime/length_prefixed_array.h b/runtime/length_prefixed_array.h
index 2b2e8d3..d9bc656 100644
--- a/runtime/length_prefixed_array.h
+++ b/runtime/length_prefixed_array.h
@@ -21,6 +21,8 @@
#include "linear_alloc.h"
#include "stride_iterator.h"
+#include "base/bit_utils.h"
+#include "base/casts.h"
#include "base/iteration_range.h"
namespace art {
@@ -28,29 +30,35 @@
template<typename T>
class LengthPrefixedArray {
public:
- explicit LengthPrefixedArray(uint64_t length) : length_(length) {}
+ explicit LengthPrefixedArray(size_t length)
+ : length_(dchecked_integral_cast<uint32_t>(length)) {}
- T& At(size_t index, size_t element_size = sizeof(T)) {
+ T& At(size_t index, size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
DCHECK_LT(index, length_);
- return *reinterpret_cast<T*>(&data_[0] + index * element_size);
+ return AtUnchecked(index, element_size, alignment);
}
- StrideIterator<T> Begin(size_t element_size = sizeof(T)) {
- return StrideIterator<T>(reinterpret_cast<T*>(&data_[0]), element_size);
+ StrideIterator<T> Begin(size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
+ return StrideIterator<T>(&AtUnchecked(0, element_size, alignment), element_size);
}
- StrideIterator<T> End(size_t element_size = sizeof(T)) {
- return StrideIterator<T>(reinterpret_cast<T*>(&data_[0] + element_size * length_),
- element_size);
+ StrideIterator<T> End(size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
+ return StrideIterator<T>(&AtUnchecked(length_, element_size, alignment), element_size);
}
- static size_t OffsetOfElement(size_t index, size_t element_size = sizeof(T)) {
- return offsetof(LengthPrefixedArray<T>, data_) + index * element_size;
+ static size_t OffsetOfElement(size_t index,
+ size_t element_size = sizeof(T),
+ size_t alignment = alignof(T)) {
+ DCHECK_ALIGNED_PARAM(element_size, alignment);
+ return RoundUp(offsetof(LengthPrefixedArray<T>, data), alignment) + index * element_size;
}
- // Alignment is the caller's responsibility.
- static size_t ComputeSize(size_t num_elements, size_t element_size = sizeof(T)) {
- return OffsetOfElement(num_elements, element_size);
+ static size_t ComputeSize(size_t num_elements,
+ size_t element_size = sizeof(T),
+ size_t alignment = alignof(T)) {
+ size_t result = OffsetOfElement(num_elements, element_size, alignment);
+ DCHECK_ALIGNED_PARAM(result, alignment);
+ return result;
}
uint64_t Length() const {
@@ -58,21 +66,26 @@
}
// Update the length but does not reallocate storage.
- void SetLength(uint64_t length) {
- length_ = length;
+ void SetLength(size_t length) {
+ length_ = dchecked_integral_cast<uint32_t>(length);
}
private:
- uint64_t length_; // 64 bits for 8 byte alignment of data_.
- uint8_t data_[0];
+ T& AtUnchecked(size_t index, size_t element_size, size_t alignment) {
+ return *reinterpret_cast<T*>(
+ reinterpret_cast<uintptr_t>(this) + OffsetOfElement(index, element_size, alignment));
+ }
+
+ uint32_t length_;
+ uint8_t data[0];
};
// Returns empty iteration range if the array is null.
template<typename T>
IterationRange<StrideIterator<T>> MakeIterationRangeFromLengthPrefixedArray(
- LengthPrefixedArray<T>* arr, size_t element_size) {
+ LengthPrefixedArray<T>* arr, size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
return arr != nullptr ?
- MakeIterationRange(arr->Begin(element_size), arr->End(element_size)) :
+ MakeIterationRange(arr->Begin(element_size, alignment), arr->End(element_size, alignment)) :
MakeEmptyIterationRange(StrideIterator<T>(nullptr, 0));
}
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 887e204..b15747f 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -92,14 +92,18 @@
CheckPointerSize(pointer_size);
auto* methods = GetDirectMethodsPtrUnchecked();
DCHECK(methods != nullptr);
- return &methods->At(i, ArtMethod::ObjectSize(pointer_size));
+ return &methods->At(i,
+ ArtMethod::ObjectSize(pointer_size),
+ ArtMethod::ObjectAlignment(pointer_size));
}
inline ArtMethod* Class::GetDirectMethod(size_t i, size_t pointer_size) {
CheckPointerSize(pointer_size);
auto* methods = GetDirectMethodsPtr();
DCHECK(methods != nullptr);
- return &methods->At(i, ArtMethod::ObjectSize(pointer_size));
+ return &methods->At(i,
+ ArtMethod::ObjectSize(pointer_size),
+ ArtMethod::ObjectAlignment(pointer_size));
}
template<VerifyObjectFlags kVerifyFlags>
@@ -133,7 +137,9 @@
CheckPointerSize(pointer_size);
LengthPrefixedArray<ArtMethod>* methods = GetVirtualMethodsPtrUnchecked();
DCHECK(methods != nullptr);
- return &methods->At(i, ArtMethod::ObjectSize(pointer_size));
+ return &methods->At(i,
+ ArtMethod::ObjectSize(pointer_size),
+ ArtMethod::ObjectAlignment(pointer_size));
}
inline PointerArray* Class::GetVTable() {
@@ -837,29 +843,31 @@
inline IterationRange<StrideIterator<ArtMethod>> Class::GetDirectMethods(size_t pointer_size) {
CheckPointerSize(pointer_size);
return MakeIterationRangeFromLengthPrefixedArray(GetDirectMethodsPtrUnchecked(),
- ArtMethod::ObjectSize(pointer_size));
+ ArtMethod::ObjectSize(pointer_size),
+ ArtMethod::ObjectAlignment(pointer_size));
}
inline IterationRange<StrideIterator<ArtMethod>> Class::GetVirtualMethods(size_t pointer_size) {
CheckPointerSize(pointer_size);
return MakeIterationRangeFromLengthPrefixedArray(GetVirtualMethodsPtrUnchecked(),
- ArtMethod::ObjectSize(pointer_size));
+ ArtMethod::ObjectSize(pointer_size),
+ ArtMethod::ObjectAlignment(pointer_size));
}
inline IterationRange<StrideIterator<ArtField>> Class::GetIFields() {
- return MakeIterationRangeFromLengthPrefixedArray(GetIFieldsPtr(), sizeof(ArtField));
+ return MakeIterationRangeFromLengthPrefixedArray(GetIFieldsPtr());
}
inline IterationRange<StrideIterator<ArtField>> Class::GetSFields() {
- return MakeIterationRangeFromLengthPrefixedArray(GetSFieldsPtr(), sizeof(ArtField));
+ return MakeIterationRangeFromLengthPrefixedArray(GetSFieldsPtr());
}
inline IterationRange<StrideIterator<ArtField>> Class::GetIFieldsUnchecked() {
- return MakeIterationRangeFromLengthPrefixedArray(GetIFieldsPtrUnchecked(), sizeof(ArtField));
+ return MakeIterationRangeFromLengthPrefixedArray(GetIFieldsPtrUnchecked());
}
inline IterationRange<StrideIterator<ArtField>> Class::GetSFieldsUnchecked() {
- return MakeIterationRangeFromLengthPrefixedArray(GetSFieldsPtrUnchecked(), sizeof(ArtField));
+ return MakeIterationRangeFromLengthPrefixedArray(GetSFieldsPtrUnchecked());
}
inline MemberOffset Class::EmbeddedImTableOffset(size_t pointer_size) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 1ca98e5..c337e91 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -208,7 +208,7 @@
}
}
if (kIsDebugBuild) {
- for (ArtField& field : MakeIterationRangeFromLengthPrefixedArray(fields, sizeof(ArtField))) {
+ for (ArtField& field : MakeIterationRangeFromLengthPrefixedArray(fields)) {
CHECK_NE(field.GetName(), name->ToModifiedUtf8());
}
}