Skip to content

Commit

Permalink
8315047: [Lilliput/JDK21] Some additions to 8139457: Array bases are …
Browse files Browse the repository at this point in the history
…aligned at HeapWord granularity

Reviewed-by: shade
  • Loading branch information
rkennke committed Sep 12, 2023
1 parent 98e7edd commit 51108cb
Show file tree
Hide file tree
Showing 15 changed files with 366 additions and 50 deletions.
20 changes: 12 additions & 8 deletions src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,6 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register

if (len->is_valid()) {
strw(len, Address(obj, arrayOopDesc::length_offset_in_bytes()));
if (!is_aligned(arrayOopDesc::header_size_in_bytes(), BytesPerWord)) {
assert(is_aligned(arrayOopDesc::header_size_in_bytes(), BytesPerInt), "must be 4-byte aligned");
strw(zr, Address(obj, arrayOopDesc::header_size_in_bytes()));
}
} else if (UseCompressedClassPointers) {
store_klass_gap(obj, zr);
}
Expand Down Expand Up @@ -296,11 +292,19 @@ void C1_MacroAssembler::allocate_array(Register obj, Register len, Register t1,

initialize_header(obj, klass, len, t1, t2);

// 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().
int base_offset = base_offset_in_bytes;
if (!is_aligned(base_offset, BytesPerWord)) {
assert(is_aligned(base_offset, BytesPerInt), "must be 4-byte aligned");
strw(zr, Address(obj, base_offset));
base_offset += BytesPerInt;
}
assert(is_aligned(base_offset, BytesPerWord), "must be word-aligned");

// clear rest of allocated space
// We align-up the header size to word-size, because we clear the
// possible alignment gap in initialize_header().
int hdr_size = align_up(base_offset_in_bytes, BytesPerWord);
initialize_body(obj, arr_size, hdr_size, t1, t2);
initialize_body(obj, arr_size, base_offset, t1, t2);
if (Compilation::current()->bailed_out()) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ using MacroAssembler::null_check;
// header_size: size of object header in words
// f : element scale factor
// slow_case : exit to slow case implementation if fast allocation fails
void allocate_array(Register obj, Register len, Register t, Register t2, int header_size, int f, Register klass, Label& slow_case);
void allocate_array(Register obj, Register len, Register t, Register t2, int base_offset_in_bytes, int f, Register klass, Label& slow_case);

int rsp_offset() const { return _rsp_offset; }
void set_rsp_offset(int n) { _rsp_offset = n; }
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) {
op->tmp1()->as_register(),
op->tmp2()->as_register(),
op->tmp3()->as_register(),
arrayOopDesc::header_size(op->type()),
align_up(arrayOopDesc::header_size_in_bytes(), HeapWordSize) / HeapWordSize,
type2aelembytes(op->type()),
op->klass()->as_register(),
*op->stub()->entry());
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
Register t1, // temp register
Register t2, // temp register
Register t3, // temp register
int hdr_size, // object header size in words
int base_offset_in_bytes, // elements offset in bytes
int elt_size, // element size in bytes
Register klass, // object klass
Label& slow_case // continuation point if fast allocation fails
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ using MacroAssembler::null_check;
// header_size: size of object header in words
// f : element scale factor
// slow_case : exit to slow case implementation if fast allocation fails
void allocate_array(Register obj, Register len, Register tmp1, Register tmp2, int header_size, int f, Register klass, Label& slow_case);
void allocate_array(Register obj, Register len, Register tmp1, Register tmp2, int base_offset_in_bytes, int f, Register klass, Label& slow_case);

int rsp_offset() const { return _rsp_offset; }

Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/cpu/s390/c1_MacroAssembler_s390.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
Register obj, // result: Pointer to object after successful allocation.
Register t1, // temp register
Register t2, // temp register
int hdr_size, // object header size in words
int base_offset_in_bytes, // elements offset in bytes
int obj_size, // object size in words
Register klass, // object klass
Label& slow_case // Continuation point if fast allocation fails.
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/cpu/x86/c1_MacroAssembler_x86.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
// header_size: size of object header in words
// object_size: total size of object in words
// slow_case : exit to slow case implementation if fast allocation fails
void allocate_object(Register obj, Register t1, Register t2, int header_size, int object_size, Register klass, Label& slow_case);
void allocate_object(Register obj, Register t1, Register t2, int base_offset_in_bytes, int object_size, Register klass, Label& slow_case);

enum {
max_array_allocation_length = 0x00FFFFFF
Expand Down
13 changes: 11 additions & 2 deletions src/hotspot/share/gc/shared/collectedHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,15 @@ void CollectedHeap::set_gc_cause(GCCause::Cause v) {
_gc_cause = v;
}

// Should only be called with constants as argument
// (will not constant fold otherwise)
// Returns the header size in words aligned to the requirements of the
// array object type.
static int int_array_header_size() {
size_t typesize_in_bytes = arrayOopDesc::header_size_in_bytes();
return (int)align_up(typesize_in_bytes, HeapWordSize)/HeapWordSize;
}

size_t CollectedHeap::max_tlab_size() const {
// TLABs can't be bigger than we can fill with a int[Integer.MAX_VALUE].
// This restriction could be removed by enabling filling with multiple arrays.
Expand All @@ -409,14 +418,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 = int_array_header_size() +
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(int_array_header_size()); // align to Long
}

size_t CollectedHeap::filler_array_min_size() {
Expand Down
54 changes: 23 additions & 31 deletions src/hotspot/share/oops/arrayOop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,15 @@ class arrayOopDesc : public oopDesc {

// Interpreter/Compiler offsets

public:
// Header size computation.
// The header is considered the oop part of this type plus the length.
// This is not equivalent to sizeof(arrayOopDesc) which should not appear in the code.
static int header_size_in_bytes() {
size_t hs = length_offset_in_bytes() + sizeof(int);
#ifdef ASSERT
// make sure it isn't called before UseCompressedOops is initialized.
static size_t arrayoopdesc_hs = 0;
if (arrayoopdesc_hs == 0) arrayoopdesc_hs = hs;
assert(arrayoopdesc_hs == hs, "header size can't change");
#endif // ASSERT
return (int)hs;
}

private:
// Returns the address of the length "field". See length_offset_in_bytes().
static int* length_addr_impl(void* obj_ptr) {
char* ptr = static_cast<char*>(obj_ptr);
return reinterpret_cast<int*>(ptr + length_offset_in_bytes());
}

// Check whether an element of a typeArrayOop with the given type must be
// aligned 0 mod 8. The typeArrayOop itself must be aligned at least this
// Check whether an element of an arrayOop with the given type must be
// aligned 0 mod 8. The arrayOop itself must be aligned at least this
// strongly.
static bool element_type_should_be_aligned(BasicType type) {
#ifdef _LP64
Expand All @@ -81,6 +66,20 @@ class arrayOopDesc : public oopDesc {
}

public:
// Header size computation.
// The header is considered the oop part of this type plus the length.
// This is not equivalent to sizeof(arrayOopDesc) which should not appear in the code.
static int header_size_in_bytes() {
size_t hs = length_offset_in_bytes() + sizeof(int);
#ifdef ASSERT
// make sure it isn't called before UseCompressedOops is initialized.
static size_t arrayoopdesc_hs = 0;
if (arrayoopdesc_hs == 0) arrayoopdesc_hs = hs;
assert(arrayoopdesc_hs == hs, "header size can't change");
#endif // ASSERT
return (int)hs;
}

// The _length field is not declared in C++. It is allocated after the
// declared nonstatic fields in arrayOopDesc if not compressed, otherwise
// it occupies the second half of the _klass field in oopDesc.
Expand Down Expand Up @@ -129,35 +128,28 @@ class arrayOopDesc : public oopDesc {
*length_addr_impl(mem) = length;
}

// Should only be called with constants as argument
// (will not constant fold otherwise)
// Returns the header size in words aligned to the requirements of the
// array object type.
static int header_size(BasicType type) {
size_t typesize_in_bytes = header_size_in_bytes();
return (int)(element_type_should_be_aligned(type)
? align_object_offset(typesize_in_bytes/HeapWordSize)
: align_up(typesize_in_bytes, HeapWordSize)/HeapWordSize);
}

// Return the maximum length of an array of BasicType. The length can passed
// Return the maximum length of an array of BasicType. The length can be 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
// 32 bit platforms when we convert it to a byte size.
static int32_t max_array_length(BasicType type) {
assert(type >= 0 && type < T_CONFLICT, "wrong type");
assert(type2aelembytes(type) != 0, "wrong type");

size_t hdr_size_in_bytes = header_size_in_bytes();
// This is rounded-up and may overlap with the first array elements.
size_t hdr_size_in_words = align_up(hdr_size_in_bytes, HeapWordSize) / HeapWordSize;

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) {
// It should be ok to return max_jint here, but parts of the code
// (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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/prims/unsafe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@


#define MAX_OBJECT_SIZE \
( arrayOopDesc::header_size(T_DOUBLE) * HeapWordSize \
( arrayOopDesc::base_offset_in_bytes(T_DOUBLE) \
+ ((julong)max_jint * sizeof(double)) )


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ private static void initialize(TypeDataBase db) throws WrongTypeException {
private static long lengthOffsetInBytes=0;
private static long typeSize;

// Check whether an element of a typeArrayOop with the given type must be
// aligned 0 mod 8. The typeArrayOop itself must be aligned at least this
// Check whether an element of an arrayOop with the given type must be
// aligned 0 mod 8. The arrayOop itself must be aligned at least this
// strongly.
private static boolean elementTypeShouldBeAligned(BasicType type) {
if (VM.getVM().isLP64()) {
Expand Down
57 changes: 57 additions & 0 deletions test/hotspot/gtest/oops/test_objArrayOop.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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 ccp; bool coops; int result;
} x[] = {
// ObjAligInB, UseCCP, UseCoops, object size in heap words
#ifdef _LP64
{ 8, false, false, 4 }, // 20 byte header, 8 byte oops
{ 8, false, true, 3 }, // 20 byte header, 4 byte oops
{ 8, true, false, 3 }, // 16 byte header, 8 byte oops
{ 8, true, true, 3 }, // 16 byte header, 4 byte oops
{ 16, false, false, 4 }, // 20 byte header, 8 byte oops, 16-byte align
{ 16, false, true, 4 }, // 20 byte header, 4 byte oops, 16-byte align
{ 16, true, false, 4 }, // 16 byte header, 8 byte oops, 16-byte align
{ 16, true, true, 4 }, // 16 byte header, 4 byte oops, 16-byte align
{ 256, false, false, 32 }, // 20 byte header, 8 byte oops, 256-byte align
{ 256, false, true, 32 }, // 20 byte header, 4 byte oops, 256-byte align
{ 256, true, false, 32 }, // 16 byte header, 8 byte oops, 256-byte align
{ 256, true, true, 32 }, // 16 byte header, 4 byte oops, 256-byte align
#else
{ 8, false, false, 4 }, // 12 byte header, 4 byte oops, wordsize 4
#endif
{ -1, false, false, -1 }
};
for (int i = 0; x[i].result != -1; i++) {
if (x[i].objal == (int)ObjectAlignmentInBytes && x[i].ccp == UseCompressedClassPointers && x[i].coops == UseCompressedOops) {
EXPECT_EQ(objArrayOopDesc::object_size(1), (size_t)x[i].result);
}
}
}
56 changes: 56 additions & 0 deletions test/hotspot/jtreg/gtest/ArrayTests.java
Original file line number Diff line number Diff line change
@@ -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-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=arrayOop -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
*/
/* @test id=with-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:-UseCompressedClassPointers -XX:+UseCompressedOops
*/
/* @test id=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=arrayOop -XX:+UseCompressedClassPointers -XX:-UseCompressedOops
*/
/* @test id=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:-UseCompressedClassPointers -XX:-UseCompressedOops
*/

0 comments on commit 51108cb

Please sign in to comment.