From 9063e7a3a1a85ac903570b4873e7a5a06009323d Mon Sep 17 00:00:00 2001 From: Liang Mao Date: Tue, 21 Nov 2023 17:37:07 +0800 Subject: [PATCH] Fix array gap --- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 2 +- .../cpu/aarch64/c1_MacroAssembler_aarch64.cpp | 17 ++- src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp | 2 +- src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp | 21 +++- .../share/gc/parallel/mutableNUMASpace.cpp | 3 +- .../share/gc/parallel/psPromotionLAB.cpp | 6 +- src/hotspot/share/gc/shared/collectedHeap.cpp | 4 +- src/hotspot/share/gc/shared/memAllocator.cpp | 4 +- src/hotspot/share/gc/shared/plab.cpp | 4 +- src/hotspot/share/gc/shared/space.cpp | 2 +- .../gc/shared/threadLocalAllocBuffer.hpp | 2 +- src/hotspot/share/oops/arrayOop.hpp | 32 +++++- src/hotspot/share/oops/objArrayOop.hpp | 19 +++- src/hotspot/share/opto/runtime.cpp | 22 +++- src/hotspot/share/opto/type.cpp | 4 +- src/hotspot/share/prims/unsafe.cpp | 3 +- .../sun/jvm/hotspot/memory/Universe.java | 5 + .../classes/sun/jvm/hotspot/oops/Array.java | 17 ++- test/hotspot/gtest/oops/test_arrayOop.cpp | 44 ++++++++ test/hotspot/gtest/oops/test_objArrayOop.cpp | 49 +++++++++ test/hotspot/jtreg/gtest/ArrayTests.java | 56 ++++++++++ test/hotspot/jtreg/gtest/ObjArrayTests.java | 70 ++++++++++++ .../runtime/FieldLayout/ArrayBaseOffsets.java | 103 ++++++++++++++++++ .../runtime/FieldLayout/BaseOffsets.java | 2 +- 24 files changed, 453 insertions(+), 40 deletions(-) create mode 100644 test/hotspot/gtest/oops/test_objArrayOop.cpp create mode 100644 test/hotspot/jtreg/gtest/ArrayTests.java create mode 100644 test/hotspot/jtreg/gtest/ObjArrayTests.java create mode 100644 test/hotspot/jtreg/runtime/FieldLayout/ArrayBaseOffsets.java diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 61e3048a944..a7a9b11e738 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1261,7 +1261,7 @@ void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { len, tmp1, tmp2, - arrayOopDesc::header_size(op->type()), + UseCompactObjectHeaders ? arrayOopDesc::base_offset_in_bytes(op->type()) : arrayOopDesc::header_size(op->type()), array_element_size(op->type()), op->klass()->as_register(), *op->stub()->entry()); diff --git a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp index 083d33af930..f62f4d00d75 100644 --- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp @@ -318,9 +318,12 @@ void C1_MacroAssembler::allocate_array(Register obj, Register len, Register t1, cmp(len, rscratch1); br(Assembler::HS, slow_case); + // header_size is already base offset with UseCompactObjectHeaders + int base_offset = UseCompactObjectHeaders ? header_size : (header_size * BytesPerWord); + const Register arr_size = t2; // okay to be the same // align object end - mov(arr_size, (int32_t)header_size * BytesPerWord + MinObjAlignmentInBytesMask); + mov(arr_size, (int32_t)base_offset + MinObjAlignmentInBytesMask); add(arr_size, arr_size, len, ext::uxtw, f); andr(arr_size, arr_size, ~MinObjAlignmentInBytesMask); @@ -328,9 +331,19 @@ void C1_MacroAssembler::allocate_array(Register obj, Register len, Register t1, initialize_header(obj, klass, len, t1, t2); + assert(is_aligned(base_offset, BytesPerWord) || UseCompactObjectHeaders, "must be aligned or with UseCompactObjectHeaders"); + if (UseCompactObjectHeaders && !is_aligned(base_offset, BytesPerWord)) { + // Clear leading 4 bytes, if necessary. + // TODO: This could perhaps go into initialize_body() and also clear the leading 4 bytes + // for non-array objects, thereby replacing the klass-gap clearing code in initialize_header(). + assert(is_aligned(base_offset, BytesPerInt), "must be 4-byte aligned"); + strw(zr, Address(obj, base_offset)); + base_offset += BytesPerInt; + } + // clear rest of allocated space const Register len_zero = len; - initialize_body(obj, arr_size, header_size * BytesPerWord, len_zero); + initialize_body(obj, arr_size, base_offset, len_zero); membar(StoreStore); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index a336d2a35c1..e0777fd4a28 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -1598,7 +1598,7 @@ void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { len, tmp1, tmp2, - arrayOopDesc::header_size(op->type()), + UseCompactObjectHeaders ? arrayOopDesc::base_offset_in_bytes(op->type()) : arrayOopDesc::header_size(op->type()), array_element_size(op->type()), op->klass()->as_register(), *op->stub()->entry()); diff --git a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp index 88a8e00259c..60b9eef6842 100644 --- a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp @@ -309,9 +309,12 @@ void C1_MacroAssembler::allocate_array(Register obj, Register len, Register t1, cmpptr(len, (int32_t)max_array_allocation_length); jcc(Assembler::above, slow_case); + // header_size is already base offset with UseCompactObjectHeaders + int base_offset = UseCompactObjectHeaders ? header_size : (header_size * BytesPerWord); + const Register arr_size = t2; // okay to be the same // align object end - movptr(arr_size, (int32_t)header_size * BytesPerWord + MinObjAlignmentInBytesMask); + movptr(arr_size, (int32_t)base_offset + MinObjAlignmentInBytesMask); lea(arr_size, Address(arr_size, len, f)); andptr(arr_size, ~MinObjAlignmentInBytesMask); @@ -319,12 +322,20 @@ void C1_MacroAssembler::allocate_array(Register obj, Register len, Register t1, initialize_header(obj, klass, len, t1, t2); + assert(is_aligned(base_offset, BytesPerWord) || UseCompactObjectHeaders, "must be aligned or with UseCompactObjectHeaders"); + if (UseCompactObjectHeaders && !is_aligned(base_offset, BytesPerWord)) { + // Clear leading 4 bytes, if necessary. + // TODO: This could perhaps go into initialize_body() and also clear the leading 4 bytes + // for non-array objects, thereby replacing the klass-gap clearing code in initialize_header(). + assert(is_aligned(base_offset, BytesPerInt), "must be 4-byte aligned"); + movl(Address(obj, base_offset), 0); + base_offset += BytesPerInt; + } + assert(is_aligned(base_offset, BytesPerWord), "must be word aligned"); + // clear rest of allocated space const Register len_zero = len; - if (UseCompactObjectHeaders) { - assert(header_size == 2, "check array header size"); - } - initialize_body(obj, arr_size, header_size * BytesPerWord, len_zero); + initialize_body(obj, arr_size, base_offset, len_zero); if (CURRENT_ENV->dtrace_alloc_probes()) { assert(obj == rax, "must be"); diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index 851ce5e17e1..09a246fd652 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -104,7 +104,8 @@ void MutableNUMASpace::ensure_parsability() { size_t touched_words = words_to_fill; #ifndef ASSERT if (!ZapUnusedHeapArea) { - touched_words = MIN2((size_t)align_object_size(typeArrayOopDesc::header_size(T_INT)), + touched_words = MIN2((size_t)align_object_size(UseCompactObjectHeaders ? + arrayOopDesc::int_array_header_size() : typeArrayOopDesc::header_size(T_INT)), touched_words); } #endif diff --git a/src/hotspot/share/gc/parallel/psPromotionLAB.cpp b/src/hotspot/share/gc/parallel/psPromotionLAB.cpp index 4ecc6bbd9b8..24b2d89bb2c 100644 --- a/src/hotspot/share/gc/parallel/psPromotionLAB.cpp +++ b/src/hotspot/share/gc/parallel/psPromotionLAB.cpp @@ -46,7 +46,7 @@ void PSPromotionLAB::initialize(MemRegion lab) { // Initialize after VM starts up because header_size depends on compressed // oops. - filler_header_size = align_object_size(typeArrayOopDesc::header_size(T_INT)); + filler_header_size = align_object_size(UseCompactObjectHeaders ? arrayOopDesc::int_array_header_size() : typeArrayOopDesc::header_size(T_INT)); // We can be initialized to a zero size! if (free() > 0) { @@ -89,13 +89,13 @@ void PSPromotionLAB::flush() { filler_oop->set_klass(Universe::intArrayKlassObj()); } const size_t array_length = - pointer_delta(tlab_end, top()) - typeArrayOopDesc::header_size(T_INT); + pointer_delta(tlab_end, top()) - (UseCompactObjectHeaders ? arrayOopDesc::int_array_header_size() : typeArrayOopDesc::header_size(T_INT)); assert( (array_length * (HeapWordSize/sizeof(jint))) < (size_t)max_jint, "array too big in PSPromotionLAB"); filler_oop->set_length((int)(array_length * (HeapWordSize/sizeof(jint)))); #ifdef ASSERT // Note that we actually DO NOT want to use the aligned header size! - HeapWord* elt_words = ((HeapWord*)filler_oop) + typeArrayOopDesc::header_size(T_INT); + HeapWord* elt_words = ((HeapWord*)filler_oop) + (UseCompactObjectHeaders ? arrayOopDesc::int_array_header_size() : typeArrayOopDesc::header_size(T_INT)); Copy::fill_to_words(elt_words, array_length, 0xDEAABABE); #endif diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index a3cb2f0bf37..f2681e186cc 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -355,14 +355,14 @@ size_t CollectedHeap::max_tlab_size() const { // We actually lose a little by dividing first, // but that just makes the TLAB somewhat smaller than the biggest array, // which is fine, since we'll be able to fill that. - size_t max_int_size = typeArrayOopDesc::header_size(T_INT) + + size_t max_int_size = (UseCompactObjectHeaders ? arrayOopDesc::int_array_header_size() : typeArrayOopDesc::header_size(T_INT)) + sizeof(jint) * ((juint) max_jint / (size_t) HeapWordSize); return align_down(max_int_size, MinObjAlignment); } size_t CollectedHeap::filler_array_hdr_size() { - return align_object_offset(arrayOopDesc::header_size(T_INT)); // align to Long + return align_object_offset(UseCompactObjectHeaders ? arrayOopDesc::int_array_header_size() : arrayOopDesc::header_size(T_INT)); // align to Long } size_t CollectedHeap::filler_array_min_size() { diff --git a/src/hotspot/share/gc/shared/memAllocator.cpp b/src/hotspot/share/gc/shared/memAllocator.cpp index 90a37116efc..1c3b4199c37 100644 --- a/src/hotspot/share/gc/shared/memAllocator.cpp +++ b/src/hotspot/share/gc/shared/memAllocator.cpp @@ -433,7 +433,9 @@ MemRegion ObjArrayAllocator::obj_memory_range(oop obj) const { return MemAllocator::obj_memory_range(obj); } ArrayKlass* array_klass = ArrayKlass::cast(_klass); - const size_t hs = arrayOopDesc::header_size(array_klass->element_type()); + const size_t hs = UseCompactObjectHeaders ? + align_up(arrayOopDesc::base_offset_in_bytes(array_klass->element_type()), HeapWordSize) / HeapWordSize : + arrayOopDesc::header_size(array_klass->element_type()); return MemRegion(((HeapWord*)obj) + hs, _word_size - hs); } diff --git a/src/hotspot/share/gc/shared/plab.cpp b/src/hotspot/share/gc/shared/plab.cpp index 29c4cc69481..f849787f796 100644 --- a/src/hotspot/share/gc/shared/plab.cpp +++ b/src/hotspot/share/gc/shared/plab.cpp @@ -44,7 +44,9 @@ PLAB::PLAB(size_t desired_plab_sz_) : _end(NULL), _hard_end(NULL), _allocated(0), _wasted(0), _undo_wasted(0) { // ArrayOopDesc::header_size depends on command line initialization. - AlignmentReserve = oopDesc::header_size() > MinObjAlignment ? align_object_size(arrayOopDesc::header_size(T_INT)) : 0; + AlignmentReserve = oopDesc::header_size() > MinObjAlignment ? align_object_size( + UseCompactObjectHeaders ? arrayOopDesc::int_array_header_size() : arrayOopDesc::header_size(T_INT) + ) : 0; assert(min_size() > AlignmentReserve, "Minimum PLAB size " SIZE_FORMAT " must be larger than alignment reserve " SIZE_FORMAT " " "to be able to contain objects", min_size(), AlignmentReserve); diff --git a/src/hotspot/share/gc/shared/space.cpp b/src/hotspot/share/gc/shared/space.cpp index 2b9c40bbba5..5a4c0089e8a 100644 --- a/src/hotspot/share/gc/shared/space.cpp +++ b/src/hotspot/share/gc/shared/space.cpp @@ -645,7 +645,7 @@ void ContiguousSpace::allocate_temporary_filler(int factor) { } size = align_object_size(size); - const size_t array_header_size = typeArrayOopDesc::header_size(T_INT); + const size_t array_header_size = UseCompactObjectHeaders ? arrayOopDesc::int_array_header_size() : typeArrayOopDesc::header_size(T_INT); if (size >= align_object_size(array_header_size)) { size_t length = (size - array_header_size) * (HeapWordSize / sizeof(jint)); // allocate uninitialized int array diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index 908b0854299..3dced66a2fa 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -143,7 +143,7 @@ class ThreadLocalAllocBuffer: public CHeapObj { // Reserve space at the end of TLAB static size_t end_reserve() { - int reserve_size = typeArrayOopDesc::header_size(T_INT); + int reserve_size = UseCompactObjectHeaders ? arrayOopDesc::int_array_header_size() : typeArrayOopDesc::header_size(T_INT); return MAX2(reserve_size, _reserve_for_allocation_prefetch); } static size_t alignment_reserve() { return align_object_size(end_reserve()); } diff --git a/src/hotspot/share/oops/arrayOop.hpp b/src/hotspot/share/oops/arrayOop.hpp index 54551794b22..c2115ef19c8 100644 --- a/src/hotspot/share/oops/arrayOop.hpp +++ b/src/hotspot/share/oops/arrayOop.hpp @@ -51,8 +51,10 @@ class arrayOopDesc : public oopDesc { // Returns the aligned header_size_in_bytes. This is not equivalent to // sizeof(arrayOopDesc) which should not appear in the code. static int header_size_in_bytes() { - size_t hs = align_up(length_offset_in_bytes() + sizeof(int), - HeapWordSize); + size_t hs = UseCompactObjectHeaders? + length_offset_in_bytes() + sizeof(int) : + align_up(length_offset_in_bytes() + sizeof(int), + HeapWordSize); #ifdef ASSERT // make sure it isn't called before UseCompressedOops is initialized. static size_t arrayoopdesc_hs = 0; @@ -66,6 +68,13 @@ class arrayOopDesc : public oopDesc { // aligned 0 mod 8. The typeArrayOop itself must be aligned at least this // strongly. static bool element_type_should_be_aligned(BasicType type) { +#ifdef _LP64 + if (UseCompactObjectHeaders) { + if (type == T_OBJECT || type == T_ARRAY) { + return !UseCompressedOops; + } + } +#endif return type == T_DOUBLE || type == T_LONG; } @@ -81,7 +90,12 @@ class arrayOopDesc : public oopDesc { // Returns the offset of the first element. static int base_offset_in_bytes(BasicType type) { - return header_size(type) * HeapWordSize; + if (UseCompactObjectHeaders) { + size_t hs = header_size_in_bytes(); + return (int)(element_type_should_be_aligned(type) ? align_up(hs, BytesPerLong) : hs); + } else { + return header_size(type) * HeapWordSize; + } } // Returns the address of the first element. The elements in the array will not @@ -121,12 +135,19 @@ class arrayOopDesc : public oopDesc { // Returns the header size in words aligned to the requirements of the // array object type. static int header_size(BasicType type) { + assert(!UseCompactObjectHeaders, "not used with UseCompactObjectHeaders"); size_t typesize_in_bytes = header_size_in_bytes(); return (int)(element_type_should_be_aligned(type) ? align_object_offset(typesize_in_bytes/HeapWordSize) : typesize_in_bytes/HeapWordSize); } + // Used for heap filling with UseCompactObjectHeaders + static int int_array_header_size() { + assert(UseCompactObjectHeaders, "used with UseCompactObjectHeaders"); + return align_up(header_size_in_bytes(), HeapWordSize) / HeapWordSize; + } + // Return the maximum length of an array of BasicType. The length can passed // to typeArrayOop::object_size(scale, length, header_size) without causing an // overflow. We also need to make sure that this will not overflow a size_t on @@ -135,8 +156,9 @@ class arrayOopDesc : public oopDesc { assert(type >= 0 && type < T_CONFLICT, "wrong type"); assert(type2aelembytes(type) != 0, "wrong type"); + int hdr_size_in_words = UseCompactObjectHeaders ? (align_up(base_offset_in_bytes(type), HeapWordSize) / HeapWordSize) : header_size(type); const size_t max_element_words_per_size_t = - align_down((SIZE_MAX/HeapWordSize - header_size(type)), MinObjAlignment); + align_down((SIZE_MAX/HeapWordSize - hdr_size_in_words), MinObjAlignment); const size_t max_elements_per_size_t = HeapWordSize * max_element_words_per_size_t / type2aelembytes(type); if ((size_t)max_jint < max_elements_per_size_t) { @@ -144,7 +166,7 @@ class arrayOopDesc : public oopDesc { // (CollectedHeap, Klass::oop_oop_iterate(), and more) uses an int for // passing around the size (in words) of an object. So, we need to avoid // overflowing an int when we add the header. See CRs 4718400 and 7110613. - return align_down(max_jint - header_size(type), MinObjAlignment); + return align_down(max_jint - hdr_size_in_words, MinObjAlignment); } return (int32_t)max_elements_per_size_t; } diff --git a/src/hotspot/share/oops/objArrayOop.hpp b/src/hotspot/share/oops/objArrayOop.hpp index 15fc9380523..388b51ec547 100644 --- a/src/hotspot/share/oops/objArrayOop.hpp +++ b/src/hotspot/share/oops/objArrayOop.hpp @@ -51,6 +51,7 @@ class objArrayOopDesc : public arrayOopDesc { private: // Give size of objArrayOop in HeapWords minus the header static int array_size(int length) { + assert(!UseCompactObjectHeaders, "sanity"); const uint OopsPerHeapWord = HeapWordSize/heapOopSize; assert(OopsPerHeapWord >= 1 && (HeapWordSize % heapOopSize == 0), "Else the following (new) computation would be in error"); @@ -97,11 +98,19 @@ class objArrayOopDesc : public arrayOopDesc { static int object_size(int length) { // This returns the object size in HeapWords. - uint asz = array_size(length); - uint osz = align_object_size(header_size() + asz); - assert(osz >= asz, "no overflow"); - assert((int)osz > 0, "no overflow"); - return (int)osz; + if (UseCompactObjectHeaders) { + size_t asz = (size_t)length * heapOopSize; + size_t size_words = heap_word_size(base_offset_in_bytes() + asz); + size_t osz = align_object_size(size_words); + assert(osz < max_jint, "no overflow"); + return (int)osz; + } else { + uint asz = array_size(length); + uint osz = align_object_size(header_size() + asz); + assert(osz >= asz, "no overflow"); + assert((int)osz > 0, "no overflow"); + return (int)osz; + } } Klass* element_klass(); diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index af697956c37..6fe629383fa 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -314,12 +314,24 @@ JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_nozero_C(Klass* array_type, int len // Zero array here if the caller is deoptimized. int size = ((typeArrayOop)result)->object_size(); BasicType elem_type = TypeArrayKlass::cast(array_type)->element_type(); - const size_t hs = arrayOopDesc::header_size(elem_type); - // Align to next 8 bytes to avoid trashing arrays's length. - const size_t aligned_hs = align_object_offset(hs); HeapWord* obj = (HeapWord*)result; - if (aligned_hs > hs) { - Copy::zero_to_words(obj+hs, aligned_hs-hs); + size_t aligned_hs = 0; + if (UseCompactObjectHeaders) { + size_t hs_bytes = arrayOopDesc::base_offset_in_bytes(elem_type); + assert(is_aligned(hs_bytes, BytesPerInt), "must be 4 byte aligned"); + if (!is_aligned(hs_bytes, BytesPerLong)) { + *reinterpret_cast(reinterpret_cast(obj) + hs_bytes) = 0; + hs_bytes += BytesPerInt; + } + assert(is_aligned(hs_bytes, BytesPerLong), "must be 8-byte aligned"); + aligned_hs = hs_bytes / BytesPerLong; + } else { + const size_t hs = arrayOopDesc::header_size(elem_type); + // Align to next 8 bytes to avoid trashing arrays's length. + aligned_hs = align_object_offset(hs); + if (aligned_hs > hs) { + Copy::zero_to_words(obj+hs, aligned_hs-hs); + } } // Optimized zeroing. Copy::fill_to_aligned_words(obj+aligned_hs, size-aligned_hs); diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index f9bfe80de41..1c96dbb5b43 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -4542,7 +4542,9 @@ void TypeAryPtr::dump2( Dict &d, uint depth, outputStream *st ) const { } if( _offset != 0 ) { - int header_size = objArrayOopDesc::header_size() * wordSize; + int header_size = UseCompactObjectHeaders ? + arrayOopDesc::base_offset_in_bytes(elem()->basic_type()) : + objArrayOopDesc::header_size() * wordSize; if( _offset == OffsetTop ) st->print("+undefined"); else if( _offset == OffsetBot ) st->print("+any"); else if( _offset < header_size ) st->print("+%d", _offset); diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index b408754fad1..9b99fd6cfde 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -59,7 +59,8 @@ #define MAX_OBJECT_SIZE \ - ( arrayOopDesc::header_size(T_DOUBLE) * HeapWordSize \ + ( (UseCompactObjectHeaders ? arrayOopDesc::base_offset_in_bytes(T_DOUBLE) \ + : (arrayOopDesc::header_size(T_DOUBLE) * HeapWordSize)) \ + ((julong)max_jint * sizeof(double)) ) diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java index 9a0f1170826..4027c20e360 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/Universe.java @@ -207,6 +207,11 @@ public void printOn(PrintStream tty) { // aligned 0 mod 8. The typeArrayOop itself must be aligned at least this // strongly. public static boolean elementTypeShouldBeAligned(BasicType type) { + if (VM.getVM().isLP64() && VM.getVM().isCompactObjectHeadersEnabled()) { + if (type == BasicType.T_OBJECT || type == BasicType.T_ARRAY) { + return !VM.getVM().isCompressedOopsEnabled(); + } + } return type == BasicType.T_DOUBLE || type == BasicType.T_LONG; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java index 8d1e1f401ec..9775b4f993d 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java @@ -59,7 +59,9 @@ private static long headerSizeInBytes() { if (headerSize != 0) { return headerSize; } - if (VM.getVM().isCompressedKlassPointersEnabled()) { + if (VM.getVM().isCompactObjectHeadersEnabled()) { + headerSize = lengthOffsetInBytes() + VM.getVM().getIntSize(); + } else if (VM.getVM().isCompressedKlassPointersEnabled()) { headerSize = typeSize; } else { headerSize = VM.getVM().alignUp(typeSize + VM.getVM().getIntSize(), @@ -76,7 +78,7 @@ private static long headerSize(BasicType type) { } } - private long lengthOffsetInBytes() { + private static long lengthOffsetInBytes() { if (lengthOffsetInBytes != 0) { return lengthOffsetInBytes; } @@ -108,7 +110,16 @@ public long getObjectSize() { } public static long baseOffsetInBytes(BasicType type) { - return headerSize(type) * VM.getVM().getHeapWordSize(); + if (VM.getVM().isCompactObjectHeadersEnabled()) { + long typeSizeInBytes = headerSizeInBytes(); + if (Universe.elementTypeShouldBeAligned(type)) { + return VM.getVM().alignUp(typeSizeInBytes, VM.getVM().getHeapWordSize()); + } else { + return typeSizeInBytes; + } + } else { + return headerSize(type) * VM.getVM().getHeapWordSize(); + } } public boolean isArray() { return true; } diff --git a/test/hotspot/gtest/oops/test_arrayOop.cpp b/test/hotspot/gtest/oops/test_arrayOop.cpp index 84063813be3..5e97287dba0 100644 --- a/test/hotspot/gtest/oops/test_arrayOop.cpp +++ b/test/hotspot/gtest/oops/test_arrayOop.cpp @@ -86,4 +86,48 @@ TEST_VM(arrayOopDesc, array) { TEST_VM(arrayOopDesc, narrowOop) { ASSERT_PRED1(check_max_length_overflow, T_NARROWOOP); } + +TEST_VM(arrayOopDesc, base_offset) { +#ifdef _LP64 + if (UseCompactObjectHeaders) { + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BOOLEAN), 12); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BYTE), 12); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_SHORT), 12); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_CHAR), 12); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_INT), 12); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_FLOAT), 12); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_LONG), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_DOUBLE), 16); + if (UseCompressedOops) { + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_OBJECT), 12); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_ARRAY), 12); + } else { + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_OBJECT), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_ARRAY), 16); + } + } else if (UseCompressedClassPointers) { + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BOOLEAN), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BYTE), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_SHORT), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_CHAR), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_INT), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_FLOAT), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_LONG), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_DOUBLE), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_OBJECT), 16); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_ARRAY), 16); + } else { + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BOOLEAN), 24); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BYTE), 24); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_SHORT), 24); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_CHAR), 24); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_INT), 24); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_FLOAT), 24); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_LONG), 24); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_DOUBLE), 24); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_OBJECT), 24); + EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_ARRAY), 24); + } +#endif +} // T_VOID and T_ADDRESS are not supported by max_array_length() diff --git a/test/hotspot/gtest/oops/test_objArrayOop.cpp b/test/hotspot/gtest/oops/test_objArrayOop.cpp new file mode 100644 index 00000000000..231db30f80c --- /dev/null +++ b/test/hotspot/gtest/oops/test_objArrayOop.cpp @@ -0,0 +1,49 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "precompiled.hpp" +#include "oops/objArrayOop.hpp" +#include "unittest.hpp" +#include "utilities/globalDefinitions.hpp" + +TEST_VM(objArrayOop, osize) { + static const struct { + int objal; bool coh; bool ccp; bool coops; int result; + } x[] = { +// ObjAligInB, UseCOH, UseCCP, UseCoops, object size in heap words +#ifdef _LP64 + { 8, true, true, false, 3 }, // 16 byte header, 8 byte oops + { 8, true, true, true, 2 }, // 12 byte header, 4 byte oops + { 8, false, false, false, 4 }, // 24 byte header, 8 byte oops + { 8, false, false, true, 4 }, // 24 byte header, 4 byte oops + { 8, false, true, false, 3 }, // 16 byte header, 8 byte oops + { 8, false, true, true, 3 }, // 16 byte header, 4 byte oops +#endif + { -1, false, false, -1 } + }; + for (int i = 0; x[i].result != -1; i++) { + if (x[i].objal == (int)ObjectAlignmentInBytes && x[i].coh == UseCompactObjectHeaders && x[i].ccp == UseCompressedClassPointers && x[i].coops == UseCompressedOops) { + EXPECT_EQ(objArrayOopDesc::object_size(1), x[i].result); + } + } +} diff --git a/test/hotspot/jtreg/gtest/ArrayTests.java b/test/hotspot/jtreg/gtest/ArrayTests.java new file mode 100644 index 00000000000..40e64bc07b2 --- /dev/null +++ b/test/hotspot/jtreg/gtest/ArrayTests.java @@ -0,0 +1,56 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * This tests object array sizes by running gtests with different settings. + */ + +/* @test id=with-coh-with-coops + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders -XX:+UseCompressedOops + */ +/* @test id=with-coh-no-coops + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders -XX:-UseCompressedOops + */ +/* @test id=no-coh-with-ccp + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:+UnlockExperimentalVMOptions -XX:-UseCompactObjectHeaders -XX:+UseCompressedClassPointers -XX:-UseCompressedOops + */ +/* @test id=no-coh-no-coops-no-ccp + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:+UnlockExperimentalVMOptions -XX:-UseCompactObjectHeaders -XX:-UseCompressedClassPointers -XX:-UseCompressedOops + */ diff --git a/test/hotspot/jtreg/gtest/ObjArrayTests.java b/test/hotspot/jtreg/gtest/ObjArrayTests.java new file mode 100644 index 00000000000..62adc197335 --- /dev/null +++ b/test/hotspot/jtreg/gtest/ObjArrayTests.java @@ -0,0 +1,70 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * This tests object array sizes by running gtests with different settings. + */ + +/* @test id=with-coh-with-coops + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders -XX:+UseCompressedOops + */ +/* @test id=with-coh-no-coops + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders -XX:-UseCompressedOops + */ +/* @test id=no-coh-no-coops-with-ccp + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UnlockExperimentalVMOptions -XX:-UseCompactObjectHeaders -XX:+UseCompressedClassPointers -XX:-UseCompressedOops + */ +/* @test id=no-coh-no-coops-no-ccp + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UnlockExperimentalVMOptions -XX:-UseCompactObjectHeaders -XX:-UseCompressedClassPointers -XX:-UseCompressedOops + */ +/* @test id=no-coh-coops-with-ccp + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UnlockExperimentalVMOptions -XX:-UseCompactObjectHeaders -XX:+UseCompressedClassPointers -XX:+UseCompressedOops + */ +/* @test id=no-coh-coops-no-ccp + * @summary Run object array size tests with compressed oops and compressed class pointers + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.xml + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UnlockExperimentalVMOptions -XX:-UseCompactObjectHeaders -XX:-UseCompressedClassPointers -XX:+UseCompressedOops + */ diff --git a/test/hotspot/jtreg/runtime/FieldLayout/ArrayBaseOffsets.java b/test/hotspot/jtreg/runtime/FieldLayout/ArrayBaseOffsets.java new file mode 100644 index 00000000000..fbb2a55cb0d --- /dev/null +++ b/test/hotspot/jtreg/runtime/FieldLayout/ArrayBaseOffsets.java @@ -0,0 +1,103 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=with-coh-with-coops + * @library /test/lib + * @requires vm.bits == "64" + * @requires vm.opt.UseCompressedClassPointers != false + * @modules java.base/jdk.internal.misc + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders -XX:+UseCompressedOops ArrayBaseOffsets + */ +/* + * @test id=with-coh-no-coops + * @library /test/lib + * @requires vm.bits == "64" + * @requires vm.opt.UseCompressedClassPointers != false + * @modules java.base/jdk.internal.misc + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders -XX:-UseCompressedOops ArrayBaseOffsets + */ +/* + * @test id=no-coh-with-ccp + * @library /test/lib + * @requires vm.bits == "64" + * @requires vm.opt.UseCompressedClassPointers != false + * @modules java.base/jdk.internal.misc + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseCompactObjectHeaders -XX:+UseCompressedClassPointers ArrayBaseOffsets + */ +/* + * @test id=no-coh-no-ccp + * @library /test/lib + * @requires vm.bits == "64" + * @modules java.base/jdk.internal.misc + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseCompactObjectHeaders -XX:-UseCompressedClassPointers ArrayBaseOffsets + */ + +import jdk.internal.misc.Unsafe; + +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.List; + +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; + +public class ArrayBaseOffsets { + + private static final boolean COOP; + private static final boolean CCP; + private static final boolean COH; + + static { + RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); + List vmargs = runtime.getInputArguments(); + CCP = !vmargs.contains("-XX:-UseCompressedClassPointers"); + COOP = System.getProperty("java.vm.compressedOopsMode") != null; + COH = vmargs.contains("-XX:+UseCompactObjectHeaders"); + } + + static public void main(String[] args) { + Unsafe unsafe = Unsafe.getUnsafe(); + int intOffset = 0, longOffset = 0; + if (COH) { + intOffset = 12; + longOffset = 16; + } else if (CCP) { + intOffset = 16; + longOffset = 16; + } else { + intOffset = 24; + longOffset = 24; + } + Asserts.assertEquals(unsafe.arrayBaseOffset(boolean[].class), intOffset, "Misplaced boolean array base"); + Asserts.assertEquals(unsafe.arrayBaseOffset(byte[].class), intOffset, "Misplaced byte array base"); + Asserts.assertEquals(unsafe.arrayBaseOffset(char[].class), intOffset, "Misplaced char array base"); + Asserts.assertEquals(unsafe.arrayBaseOffset(short[].class), intOffset, "Misplaced short array base"); + Asserts.assertEquals(unsafe.arrayBaseOffset(int[].class), intOffset, "Misplaced int array base"); + Asserts.assertEquals(unsafe.arrayBaseOffset(long[].class), longOffset, "Misplaced long array base"); + Asserts.assertEquals(unsafe.arrayBaseOffset(float[].class), intOffset, "Misplaced float array base"); + Asserts.assertEquals(unsafe.arrayBaseOffset(double[].class), longOffset, "Misplaced double array base"); + int expectedObjArrayOffset = (COOP && COH) ? intOffset : longOffset; + Asserts.assertEquals(unsafe.arrayBaseOffset(Object[].class), expectedObjArrayOffset, "Misplaced object array base"); + } +} diff --git a/test/hotspot/jtreg/runtime/FieldLayout/BaseOffsets.java b/test/hotspot/jtreg/runtime/FieldLayout/BaseOffsets.java index 1bb7fe15de2..3b463b46584 100644 --- a/test/hotspot/jtreg/runtime/FieldLayout/BaseOffsets.java +++ b/test/hotspot/jtreg/runtime/FieldLayout/BaseOffsets.java @@ -88,7 +88,7 @@ static class LIClass { LONG_ARRAY_OFFSET = 16; } else if (WB.getBooleanVMFlag("UseCompactObjectHeaders")) { INT_OFFSET = 8; - INT_ARRAY_OFFSET = 16; // Should be 12 once JDK-8139457 lands. + INT_ARRAY_OFFSET = 12; LONG_ARRAY_OFFSET = 16; } else if (WB.getBooleanVMFlag("UseCompressedClassPointers")) { INT_OFFSET = 12;