diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 363d84f3d2a..c18f97122f4 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -5490,7 +5490,8 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, vk->set_non_atomic_size_in_bytes(_layout_info->_non_atomic_size_in_bytes); vk->set_non_atomic_alignment(_layout_info->_non_atomic_alignment); vk->set_atomic_size_in_bytes(_layout_info->_atomic_layout_size_in_bytes); - vk->set_nullable_size_in_bytes(_layout_info->_nullable_layout_size_in_bytes); + vk->set_nullable_atomic_size_in_bytes(_layout_info->_nullable_atomic_layout_size_in_bytes); + vk->set_nullable_non_atomic_size_in_bytes(_layout_info->_nullable_non_atomic_layout_size_in_bytes); vk->set_null_marker_offset(_layout_info->_null_marker_offset); vk->set_default_value_offset(_layout_info->_default_value_offset); vk->set_null_reset_value_offset(_layout_info->_null_reset_value_offset); diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index 68044583e6e..0e520cd7977 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -80,7 +80,8 @@ class FieldLayoutInfo : public ResourceObj { int _non_atomic_size_in_bytes; int _non_atomic_alignment; int _atomic_layout_size_in_bytes; - int _nullable_layout_size_in_bytes; + int _nullable_atomic_layout_size_in_bytes; + int _nullable_non_atomic_layout_size_in_bytes; int _null_marker_offset; int _default_value_offset; int _null_reset_value_offset; diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp index 66cf1c6bf35..19fcf0833cd 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp @@ -38,7 +38,12 @@ #include "utilities/powerOfTwo.hpp" static LayoutKind field_layout_selection(FieldInfo field_info, Array* inline_layout_info_array, - bool use_atomic_flat) { + bool can_use_atomic_flat) { + + // The can_use_atomic_flat argument indicates if an atomic flat layout can be used for this field. + // This argument will be false if the container is a loosely consistent value class. Using an atomic layout + // in a container that has no atomicity guarantee creates a risk to see this field's value be subject to + // tearing even if the field's class was declared atomic (non loosely consistent). if (!UseFieldFlattening) { return LayoutKind::REFERENCE; @@ -66,13 +71,18 @@ static LayoutKind field_layout_selection(FieldInfo field_info, Arrayis_implicitly_constructible(), "null-free fields must be implicitly constructible"); if (vk->must_be_atomic() || AlwaysAtomicAccesses) { if (vk->is_naturally_atomic() && vk->has_non_atomic_layout()) return LayoutKind::NON_ATOMIC_FLAT; - return (vk->has_atomic_layout() && use_atomic_flat) ? LayoutKind::ATOMIC_FLAT : LayoutKind::REFERENCE; + return (vk->has_atomic_layout() && can_use_atomic_flat) ? LayoutKind::ATOMIC_FLAT : LayoutKind::REFERENCE; } else { return vk->has_non_atomic_layout() ? LayoutKind::NON_ATOMIC_FLAT : LayoutKind::REFERENCE; } } else { + // To preserve the consistency between the null-marker and the field content, the NULLABLE_NON_ATOMIC_FLAT + // can only be used in containers that have atomicity quarantees (can_use_atomic_flat argument set to true) + if (field_info.access_flags().is_strict() && field_info.access_flags().is_final() && can_use_atomic_flat) { + if (vk->has_nullable_non_atomic_layout()) return LayoutKind::NULLABLE_NON_ATOMIC_FLAT; + } if (UseNullableValueFlattening && vk->has_nullable_atomic_layout()) { - return use_atomic_flat ? LayoutKind::NULLABLE_ATOMIC_FLAT : LayoutKind::REFERENCE; + return can_use_atomic_flat ? LayoutKind::NULLABLE_ATOMIC_FLAT : LayoutKind::REFERENCE; } else { return LayoutKind::REFERENCE; } @@ -92,7 +102,11 @@ static void get_size_and_alignment(InlineKlass* vk, LayoutKind kind, int* size, case LayoutKind::NULLABLE_ATOMIC_FLAT: *size = vk->nullable_atomic_size_in_bytes(); *alignment = *size; - break; + break; + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: + *size = vk->nullable_non_atomic_size_in_bytes(); + *alignment = vk->non_atomic_alignment(); + break; default: ShouldNotReachHere(); } @@ -405,7 +419,8 @@ LayoutRawBlock* FieldLayout::insert_field_block(LayoutRawBlock* slot, LayoutRawB _null_reset_value_offset = block->offset(); } } - if (block->block_kind() == LayoutRawBlock::FLAT && block->layout_kind() == LayoutKind::NULLABLE_ATOMIC_FLAT) { + if (block->block_kind() == LayoutRawBlock::FLAT + && (block->layout_kind() == LayoutKind::NULLABLE_ATOMIC_FLAT || block->layout_kind() == LayoutKind::NULLABLE_NON_ATOMIC_FLAT)) { int nm_offset = block->inline_klass()->null_marker_offset() - block->inline_klass()->payload_offset() + block->offset(); _field_info->adr_at(block->field_index())->set_null_marker_offset(nm_offset); _inline_layout_info_array->adr_at(block->field_index())->set_null_marker_offset(nm_offset); @@ -567,7 +582,8 @@ void FieldLayout::shift_fields(int shift) { b->set_offset(b->offset() + shift); if (b->block_kind() == LayoutRawBlock::REGULAR || b->block_kind() == LayoutRawBlock::FLAT) { _field_info->adr_at(b->field_index())->set_offset(b->offset()); - if (b->layout_kind() == LayoutKind::NULLABLE_ATOMIC_FLAT) { + if (b->block_kind() == LayoutRawBlock::FLAT && + (b->layout_kind() == LayoutKind::NULLABLE_ATOMIC_FLAT || b->layout_kind() == LayoutKind::NULLABLE_NON_ATOMIC_FLAT)) { int new_nm_offset = _field_info->adr_at(b->field_index())->null_marker_offset() + shift; _field_info->adr_at(b->field_index())->set_null_marker_offset(new_nm_offset); _inline_layout_info_array->adr_at(b->field_index())->set_null_marker_offset(new_nm_offset); @@ -619,6 +635,8 @@ static const char* layout_kind_to_string(LayoutKind lk) { return "ATOMIC_FLAT"; case LayoutKind::NULLABLE_ATOMIC_FLAT: return "NULLABLE_ATOMIC_FLAT"; + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: + return "NULLABLE_NON_ATOMIC_FLAT"; case LayoutKind::UNKNOWN: return "UNKNOWN"; default: @@ -737,7 +755,8 @@ FieldLayoutBuilder::FieldLayoutBuilder(const Symbol* classname, ClassLoaderData* _non_atomic_layout_size_in_bytes(-1), _non_atomic_layout_alignment(-1), _atomic_layout_size_in_bytes(-1), - _nullable_layout_size_in_bytes(-1), + _nullable_atomic_layout_size_in_bytes(-1), + _nullable_non_atomic_layout_size_in_bytes(-1), _fields_size_sum(0), _declared_non_static_fields_count(0), _has_non_naturally_atomic_fields(false), @@ -1128,10 +1147,9 @@ void FieldLayoutBuilder::compute_inline_class_layout() { } } - // Next step is the nullable layout: the layout must include a null marker and must also be atomic - if (UseNullableValueFlattening) { + // Next step is the nullable layouts: they must include a null marker + if (UseNullableValueFlattening || UseNullableNonAtomicValueFlattening) { // Looking if there's an empty slot inside the layout that could be used to store a null marker - // FIXME: could it be possible to re-use the .empty field as a null marker for empty values? LayoutRawBlock* b = _layout->first_field_block(); assert(b != nullptr, "A concrete value class must have at least one (possible dummy) field"); int null_marker_offset = -1; @@ -1160,20 +1178,28 @@ void FieldLayoutBuilder::compute_inline_class_layout() { null_marker_offset = marker->offset(); } } - - // Now that the null marker is there, the size of the nullable layout must computed (remember, must be atomic too) + assert(null_marker_offset != -1, "Sanity check"); + // Now that the null marker is there, the size of the nullable layout must computed int new_raw_size = _layout->last_block()->offset() - _layout->first_field_block()->offset(); - int nullable_size = round_up_power_of_2(new_raw_size); - if (nullable_size <= (int)MAX_ATOMIC_OP_SIZE) { - _nullable_layout_size_in_bytes = nullable_size; + if (UseNullableNonAtomicValueFlattening) { + _nullable_non_atomic_layout_size_in_bytes = new_raw_size; _null_marker_offset = null_marker_offset; - } else { + _non_atomic_layout_alignment = _payload_alignment; + } + if (UseNullableValueFlattening) { + // For the nullable atomic layout, the size mut be compatible with the platform capabilities + int nullable_atomic_size = round_up_power_of_2(new_raw_size); + if (nullable_atomic_size <= (int)MAX_ATOMIC_OP_SIZE) { + _nullable_atomic_layout_size_in_bytes = nullable_atomic_size; + _null_marker_offset = null_marker_offset; + } + } + if (_null_marker_offset == -1) { // No nullable layout has been accepted // If the nullable layout is rejected, the NULL_MARKER block should be removed // from the layout, otherwise it will appear anyway if the layout is printer if (!_is_empty_inline_class) { // empty values don't have a dedicated NULL_MARKER block _layout->remove_null_marker(); } - _null_marker_offset = -1; } } // If the inline class has an atomic or nullable (which is also atomic) layout, @@ -1203,11 +1229,11 @@ void FieldLayoutBuilder::compute_inline_class_layout() { _payload_alignment = required_alignment; } else { _atomic_layout_size_in_bytes = -1; - if (has_nullable_atomic_layout() && !_is_empty_inline_class) { // empty values don't have a dedicated NULL_MARKER block + if (has_nullable_atomic_layout() && !has_nullable_non_atomic_layout() && !_is_empty_inline_class) { // empty values don't have a dedicated NULL_MARKER block _layout->remove_null_marker(); + _null_marker_offset = -1; } - _nullable_layout_size_in_bytes = -1; - _null_marker_offset = -1; + _nullable_atomic_layout_size_in_bytes = -1; } } else { _payload_alignment = required_alignment; @@ -1308,7 +1334,8 @@ void FieldLayoutBuilder::epilogue() { _info->_non_atomic_size_in_bytes = _non_atomic_layout_size_in_bytes; _info->_non_atomic_alignment = _non_atomic_layout_alignment; _info->_atomic_layout_size_in_bytes = _atomic_layout_size_in_bytes; - _info->_nullable_layout_size_in_bytes = _nullable_layout_size_in_bytes; + _info->_nullable_atomic_layout_size_in_bytes = _nullable_atomic_layout_size_in_bytes; + _info->_nullable_non_atomic_layout_size_in_bytes = _nullable_non_atomic_layout_size_in_bytes; _info->_null_marker_offset = _null_marker_offset; _info->_default_value_offset = _static_layout->default_value_offset(); _info->_null_reset_value_offset = _static_layout->null_reset_value_offset(); @@ -1381,9 +1408,14 @@ void FieldLayoutBuilder::epilogue() { st.print_cr("Atomic flat layout: -/-"); } if (has_nullable_atomic_layout()) { - st.print_cr("Nullable flat layout: %d/%d", _nullable_layout_size_in_bytes, _nullable_layout_size_in_bytes); + st.print_cr("Nullable atomic flat layout: %d/%d", _nullable_atomic_layout_size_in_bytes, _nullable_atomic_layout_size_in_bytes); + } else { + st.print_cr("Nullable atomic flat layout: -/-"); + } + if (has_nullable_non_atomic_layout()) { + st.print_cr("Nullable non-atomic flat layout: %d/%d", _nullable_non_atomic_layout_size_in_bytes, _payload_alignment); } else { - st.print_cr("Nullable flat layout: -/-"); + st.print_cr("Nullable non-atomic flat layout: -/-"); } if (_null_marker_offset != -1) { st.print_cr("Null marker offset = %d", _null_marker_offset); diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp index aa26bd8a0a1..1d9b5627044 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp @@ -297,7 +297,8 @@ class FieldLayoutBuilder : public ResourceObj { int _non_atomic_layout_size_in_bytes; int _non_atomic_layout_alignment; int _atomic_layout_size_in_bytes; - int _nullable_layout_size_in_bytes; + int _nullable_atomic_layout_size_in_bytes; + int _nullable_non_atomic_layout_size_in_bytes; int _fields_size_sum; int _declared_non_static_fields_count; bool _has_non_naturally_atomic_fields; @@ -318,7 +319,7 @@ class FieldLayoutBuilder : public ResourceObj { GrowableArray* field_info, bool is_contended, bool is_inline_type, bool is_abstract_value, bool must_be_atomic, FieldLayoutInfo* info, Array* inline_layout_info_array); - int payload_offset() const { assert(_payload_offset != -1, "Uninitialized"); return _payload_offset; } + int payload_offset() const { assert(_payload_offset != -1, "Uninitialized"); return _payload_offset; } int payload_layout_size_in_bytes() const { return _payload_size_in_bytes; } int payload_layout_alignment() const { assert(_payload_alignment != -1, "Uninitialized"); return _payload_alignment; } bool has_non_atomic_flat_layout() const { return _non_atomic_layout_size_in_bytes != -1; } @@ -326,8 +327,10 @@ class FieldLayoutBuilder : public ResourceObj { int non_atomic_layout_alignment() const { return _non_atomic_layout_alignment; } bool has_atomic_layout() const { return _atomic_layout_size_in_bytes != -1; } int atomic_layout_size_in_bytes() const { return _atomic_layout_size_in_bytes; } - bool has_nullable_atomic_layout() const { return _nullable_layout_size_in_bytes != -1; } - int nullable_layout_size_in_bytes() const { return _nullable_layout_size_in_bytes; } + bool has_nullable_atomic_layout() const { return _nullable_atomic_layout_size_in_bytes != -1; } + int nullable_layout_size_in_bytes() const { return _nullable_atomic_layout_size_in_bytes; } + bool has_nullable_non_atomic_layout() const { return _nullable_non_atomic_layout_size_in_bytes != -1; } + int nullable_non_atomic_layout_size_in_bytes() const { return _nullable_non_atomic_layout_size_in_bytes; } int null_marker_offset() const { return _null_marker_offset; } bool is_empty_inline_class() const { return _is_empty_inline_class; } diff --git a/src/hotspot/share/oops/flatArrayKlass.cpp b/src/hotspot/share/oops/flatArrayKlass.cpp index 0f19c32936b..f0a5e4ca6cc 100644 --- a/src/hotspot/share/oops/flatArrayKlass.cpp +++ b/src/hotspot/share/oops/flatArrayKlass.cpp @@ -56,6 +56,7 @@ FlatArrayKlass::FlatArrayKlass(Klass* element_klass, Symbol* name, LayoutKind lk) : ArrayKlass(name, Kind, markWord::flat_array_prototype(lk)) { assert(element_klass->is_inline_klass(), "Expected Inline"); + assert(lk != LayoutKind::NULLABLE_NON_ATOMIC_FLAT, "Layout not supported by arrays yet (needs frozen arrays)"); assert(lk == LayoutKind::NON_ATOMIC_FLAT || lk == LayoutKind::ATOMIC_FLAT || lk == LayoutKind::NULLABLE_ATOMIC_FLAT, "Must be a flat layout"); set_element_klass(InlineKlass::cast(element_klass)); @@ -81,6 +82,8 @@ FlatArrayKlass::FlatArrayKlass(Klass* element_klass, Symbol* name, LayoutKind lk assert(!layout_helper_is_null_free(layout_helper()), "Must be"); assert(!prototype_header().is_null_free_array(), "Must be"); break; + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: + ShouldNotReachHere(); default: ShouldNotReachHere(); break; diff --git a/src/hotspot/share/oops/inlineKlass.cpp b/src/hotspot/share/oops/inlineKlass.cpp index db0aeaf744b..315216fbef5 100644 --- a/src/hotspot/share/oops/inlineKlass.cpp +++ b/src/hotspot/share/oops/inlineKlass.cpp @@ -82,7 +82,7 @@ void InlineKlass::init_fixed_block() { set_non_atomic_size_in_bytes(-1); set_non_atomic_alignment(-1); set_atomic_size_in_bytes(-1); - set_nullable_size_in_bytes(-1); + set_nullable_atomic_size_in_bytes(-1); set_null_marker_offset(-1); } @@ -144,6 +144,10 @@ int InlineKlass::layout_size_in_bytes(LayoutKind kind) const { assert(has_nullable_atomic_layout(), "Layout not available"); return nullable_atomic_size_in_bytes(); break; + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: + assert(has_nullable_non_atomic_layout(), "Layout not available"); + return nullable_non_atomic_size_in_bytes(); + break; case LayoutKind::BUFFERED: return payload_size_in_bytes(); break; @@ -166,6 +170,10 @@ int InlineKlass::layout_alignment(LayoutKind kind) const { assert(has_nullable_atomic_layout(), "Layout not available"); return nullable_atomic_size_in_bytes(); break; + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: + assert(has_nullable_non_atomic_layout(), "Layout not available"); + return non_atomic_alignment(); + break; case LayoutKind::BUFFERED: return payload_alignment(); break; @@ -185,6 +193,9 @@ bool InlineKlass::is_layout_supported(LayoutKind lk) { case LayoutKind::NULLABLE_ATOMIC_FLAT: return has_nullable_atomic_layout(); break; + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: + return has_nullable_non_atomic_layout(); + break; case LayoutKind::BUFFERED: return true; break; @@ -197,8 +208,9 @@ void InlineKlass::copy_payload_to_addr(void* src, void* dst, LayoutKind lk, bool assert(is_layout_supported(lk), "Unsupported layout"); assert(lk != LayoutKind::REFERENCE && lk != LayoutKind::UNKNOWN, "Sanity check"); switch(lk) { + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: case LayoutKind::NULLABLE_ATOMIC_FLAT: { - if (is_payload_marked_as_null((address)src)) { + if (is_payload_marked_as_null((address)src)) { if (!contains_oops()) { mark_payload_as_null((address)dst); return; @@ -240,6 +252,7 @@ oop InlineKlass::read_payload_from_addr(oop src, int offset, LayoutKind lk, TRAP assert(src != nullptr, "Must be"); assert(is_layout_supported(lk), "Unsupported layout"); switch(lk) { + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: case LayoutKind::NULLABLE_ATOMIC_FLAT: { if (is_payload_marked_as_null((address)((char*)(oopDesc*)src + offset))) { return nullptr; @@ -254,7 +267,7 @@ oop InlineKlass::read_payload_from_addr(oop src, int offset, LayoutKind lk, TRAP Handle obj_h(THREAD, src); oop res = allocate_instance_buffer(CHECK_NULL); copy_payload_to_addr((void*)((char*)(oopDesc*)obj_h() + offset), payload_addr(res), lk, false); - if (lk == LayoutKind::NULLABLE_ATOMIC_FLAT) { + if (lk == LayoutKind::NULLABLE_ATOMIC_FLAT || lk == LayoutKind::NULLABLE_NON_ATOMIC_FLAT) { // Should not happen for NULLABLE_NON_ATOMIC_FLAT but let's play it safe if(is_payload_marked_as_null(payload_addr(res))) { return nullptr; } @@ -270,7 +283,7 @@ oop InlineKlass::read_payload_from_addr(oop src, int offset, LayoutKind lk, TRAP void InlineKlass::write_value_to_addr(oop src, void* dst, LayoutKind lk, bool dest_is_initialized, TRAPS) { void* src_addr = nullptr; if (src == nullptr) { - if (lk != LayoutKind::NULLABLE_ATOMIC_FLAT) { + if (lk != LayoutKind::NULLABLE_ATOMIC_FLAT && lk != LayoutKind::NULLABLE_NON_ATOMIC_FLAT) { THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Value is null"); } // Writing null to a nullable flat field/element is usually done by writing @@ -287,7 +300,7 @@ void InlineKlass::write_value_to_addr(oop src, void* dst, LayoutKind lk, bool de src_addr = payload_addr(null_reset_value()); } else { src_addr = payload_addr(src); - if (lk == LayoutKind::NULLABLE_ATOMIC_FLAT) { + if (lk == LayoutKind::NULLABLE_ATOMIC_FLAT || lk == LayoutKind::NULLABLE_NON_ATOMIC_FLAT) { mark_payload_as_non_null((address)src_addr); } } @@ -352,6 +365,8 @@ FlatArrayKlass* InlineKlass::flat_array_klass(LayoutKind lk, TRAPS) { assert(has_nullable_atomic_layout(), "Must be"); adr_flat_array_klass = adr_nullable_atomic_flat_array_klass(); break; + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: + ShouldNotReachHere(); default: ShouldNotReachHere(); } @@ -383,6 +398,8 @@ FlatArrayKlass* InlineKlass::flat_array_klass_or_null(LayoutKind lk) { assert(has_nullable_atomic_layout(), "Must be"); adr_flat_array_klass = adr_nullable_atomic_flat_array_klass(); break; + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: + ShouldNotReachHere(); default: ShouldNotReachHere(); } @@ -447,6 +464,7 @@ int InlineKlass::collect_fields(GrowableArray* sig, float& max_offset, int offset = base_off + size_helper()*HeapWordSize - (base_off > 0 ? payload_offset() : 0); // Null markers are no real fields, add them manually at the end (C2 relies on this) of the flat fields if (null_marker_offset != -1) { + assert(null_marker_offset != 0, "Must be"); max_offset += 0.1f; // We add the markers "in-between" because they are no real fields SigEntry::add_entry(sig, T_BOOLEAN, name(), null_marker_offset, max_offset); count++; diff --git a/src/hotspot/share/oops/inlineKlass.hpp b/src/hotspot/share/oops/inlineKlass.hpp index 1e605d78a91..f2eb8261944 100644 --- a/src/hotspot/share/oops/inlineKlass.hpp +++ b/src/hotspot/share/oops/inlineKlass.hpp @@ -153,7 +153,12 @@ class InlineKlass: public InstanceKlass { address adr_nullable_atomic_size_in_bytes() const { assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized"); - return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _nullable_size_in_bytes)); + return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _nullable_atomic_size_in_bytes)); + } + + address adr_nullable_non_atomic_size_in_bytes() const { + assert(_adr_inlineklass_fixed_block != nullptr, "Should have been initialized"); + return ((address)_adr_inlineklass_fixed_block) + in_bytes(byte_offset_of(InlineKlassFixedBlock, _nullable_non_atomic_size_in_bytes)); } address adr_null_marker_offset() const { @@ -192,23 +197,26 @@ class InlineKlass: public InstanceKlass { bool has_nullable_atomic_layout() const { return nullable_atomic_size_in_bytes() != -1; } int nullable_atomic_size_in_bytes() const { return *(int*)adr_nullable_atomic_size_in_bytes(); } - void set_nullable_size_in_bytes(int size) { *(int*)adr_nullable_atomic_size_in_bytes() = size; } + void set_nullable_atomic_size_in_bytes(int size) { *(int*)adr_nullable_atomic_size_in_bytes() = size; } + bool has_nullable_non_atomic_layout() const { return nullable_non_atomic_size_in_bytes() != -1; } + int nullable_non_atomic_size_in_bytes() const { return *(int*)adr_nullable_non_atomic_size_in_bytes(); } + void set_nullable_non_atomic_size_in_bytes(int size) { *(int*)adr_nullable_non_atomic_size_in_bytes() = size; } int null_marker_offset() const { return *(int*)adr_null_marker_offset(); } int null_marker_offset_in_payload() const { return null_marker_offset() - payload_offset(); } void set_null_marker_offset(int offset) { *(int*)adr_null_marker_offset() = offset; } bool is_payload_marked_as_null(address payload) { - assert(has_nullable_atomic_layout(), " Must have"); + assert(has_nullable_atomic_layout() || has_nullable_non_atomic_layout(), " Must have"); return *((jbyte*)payload + null_marker_offset_in_payload()) == 0; } void mark_payload_as_non_null(address payload) { - assert(has_nullable_atomic_layout(), " Must have"); + assert(has_nullable_atomic_layout() || has_nullable_non_atomic_layout(), " Must have"); *((jbyte*)payload + null_marker_offset_in_payload()) = 1; } void mark_payload_as_null(address payload) { - assert(has_nullable_atomic_layout(), " Must have"); + assert(has_nullable_atomic_layout() || has_nullable_non_atomic_layout(), " Must have"); *((jbyte*)payload + null_marker_offset_in_payload()) = 0; } diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 4227ee0e996..56149f94f08 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -157,7 +157,8 @@ class InlineKlassFixedBlock { int _non_atomic_size_in_bytes; // size of null-free non-atomic flat layout int _non_atomic_alignment; // alignment requirement for null-free non-atomic layout int _atomic_size_in_bytes; // size and alignment requirement for a null-free atomic layout, -1 if no atomic flat layout is possible - int _nullable_size_in_bytes; // size and alignment requirement for a nullable layout (always atomic), -1 if no nullable flat layout is possible + int _nullable_atomic_size_in_bytes; // size and alignment requirement for a nullable atomic layout, -1 if not available + int _nullable_non_atomic_size_in_bytes; // size and alignment requirement for a nullable non-atomic layout, -1 if not available int _null_marker_offset; // expressed as an offset from the beginning of the object for a heap buffered value // payload_offset must be subtracted to get the offset from the beginning of the payload diff --git a/src/hotspot/share/oops/layoutKind.hpp b/src/hotspot/share/oops/layoutKind.hpp index 16da43624bb..e05c3ec762c 100644 --- a/src/hotspot/share/oops/layoutKind.hpp +++ b/src/hotspot/share/oops/layoutKind.hpp @@ -63,6 +63,17 @@ // null marker). The reset value instance is needed because the VM needs an instance guaranteed to // always be filled with zeros, and the default value could have its null marker set to non-zero if // it is used as a source to update a NULLABLE_ATOMIC_FLAT field. +// NULLABLE_NON_ATOMIC_FLAT: this is a special layout, only used for strict final non-static fields. Because strict +// final non-static fields cannot be updated after the call to the super constructor, there's no +// concurrency issue on those fields, so they can be flattened even if they are nullable. During the +// construction of the instance, the uninitializedThis reference cannot escape before the call to +// the super's constructor, so no concurrent reads are possible when the field is initialized. After +// the call to the super's constructor, no update is possible because the field is strict and final, +// so no write possible during a read. This field has a null marker similar to the one of the +// NULLABLE_ATOMIC_FLAT layout. However, there's no requirement to read the null marker and the +// rest of the value atomically. If the null marker indicates a non-null value, the fields of the +// field's value can be read independently. Same rules for a putfield, no atomicity requirement, +// as long as all fields and the null marker are up to date at the end of the putfield. // BUFFERED: this layout is only used in heap buffered instances of a value class. It is computed to be compatible // to be compatible in size and alignment with all other flat layouts supported by the value class. // @@ -71,12 +82,13 @@ // of the lava.lang.invoke.MemberName class relies on this property. enum class LayoutKind : uint32_t { - REFERENCE = 0, // indirection to a heap allocated instance - BUFFERED = 1, // layout used in heap allocated standalone instances - NON_ATOMIC_FLAT = 2, // flat, no guarantee of atomic updates, no null marker - ATOMIC_FLAT = 3, // flat, size compatible with atomic updates, alignment requirement is equal to the size - NULLABLE_ATOMIC_FLAT = 4, // flat, include a null marker, plus same properties as ATOMIC layout - UNKNOWN = 5 // used for uninitialized fields of type LayoutKind + REFERENCE = 0, // indirection to a heap allocated instance + BUFFERED = 1, // layout used in heap allocated standalone instances + NON_ATOMIC_FLAT = 2, // flat, no guarantee of atomic updates, no null marker + ATOMIC_FLAT = 3, // flat, size compatible with atomic updates, alignment requirement is equal to the size + NULLABLE_ATOMIC_FLAT = 4, // flat, include a null marker, plus same properties as ATOMIC layout + NULLABLE_NON_ATOMIC_FLAT = 5, // flat, include a null marker, non-atomic, only used for strict final non-static fields + UNKNOWN = 6 // used for uninitialized fields of type LayoutKind }; #endif // SHARE_OOPS_LAYOUTKIND_HPP diff --git a/src/hotspot/share/oops/markWord.cpp b/src/hotspot/share/oops/markWord.cpp index f4b883100db..df81a6d2a5e 100644 --- a/src/hotspot/share/oops/markWord.cpp +++ b/src/hotspot/share/oops/markWord.cpp @@ -109,6 +109,8 @@ markWord markWord::flat_array_prototype(LayoutKind lk) { case LayoutKind::NULLABLE_ATOMIC_FLAT: return markWord(nullable_flat_array_pattern); break; + case LayoutKind::NULLABLE_NON_ATOMIC_FLAT: + ShouldNotReachHere(); default: ShouldNotReachHere(); } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 4933d445e7a..b72bbeed459 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -827,6 +827,9 @@ const int ObjectAlignmentInBytes = 8; product(bool, UseAtomicValueFlattening, false, \ "Allow the JVM to flatten some atomic values") \ \ + product(bool, UseNullableNonAtomicValueFlattening, false, \ + "Allow the JVM to flatten some strict final non-static fields") \ + \ product(intx, FlatArrayElementMaxOops, 4, \ "Max nof embedded object references in an inline type to flatten, <0 no limit") \ \ diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldLayoutAnalyzer.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldLayoutAnalyzer.java index 5b37933a8b2..5f07c329bd8 100644 --- a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldLayoutAnalyzer.java +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/FieldLayoutAnalyzer.java @@ -80,14 +80,16 @@ static enum LayoutKind { NON_FLAT, NON_ATOMIC_FLAT, ATOMIC_FLAT, - NULLABLE_FLAT; + NULLABLE_ATOMIC_FLAT, + NULLABLE_NON_ATOMIC_FLAT; static LayoutKind parseLayoutKind(String s) { switch(s) { case "" : return NON_FLAT; case "NON_ATOMIC_FLAT" : return NON_ATOMIC_FLAT; case "ATOMIC_FLAT" : return ATOMIC_FLAT; - case "NULLABLE_ATOMIC_FLAT" : return NULLABLE_FLAT; + case "NULLABLE_ATOMIC_FLAT" : return NULLABLE_ATOMIC_FLAT; + case "NULLABLE_NON_ATOMIC_FLAT" : return NULLABLE_NON_ATOMIC_FLAT; default: throw new RuntimeException("Unknown layout kind: " + s); } @@ -125,10 +127,6 @@ void print(PrintStream out) { static FieldBlock parseField(String line) { String[] fieldLine = line.split("\\s+"); - // for(String s : fieldLine) { - // System.out.print("["+s+"]"); // debugging statement to be removed - // } - // System.out.println(); int offset = Integer.parseInt(fieldLine[1].substring(1, fieldLine[1].length())); BlockType type = BlockType.parseType(fieldLine[2]); String[] size_align = fieldLine[3].split("/"); @@ -185,8 +183,10 @@ static class ClassLayout { int nonAtomicLayoutAlignment; // -1 if no non-nullable layout int atomicLayoutSize; // -1 if no atomic layout int atomicLayoutAlignment; // -1 if no atomic layout - int nullableLayoutSize; // -1 if no nullable layout - int nullableLayoutAlignment; // -1 if no nullable layout + int nullableAtomicLayoutSize; // -1 if no nullable layout + int nullableAtomicLayoutAlignment; // -1 if no nullable layout + int nullableNonAtomicLayoutSize; + int nullableNonAtomicLayoutAlignment; int nullMarkerOffset; // -1 if no nullable layout String[] lines; ArrayList staticFields; @@ -199,7 +199,8 @@ private ClassLayout() { boolean hasNonAtomicLayout() { return nonAtomicLayoutSize != -1; } boolean hasAtomicLayout() { return atomicLayoutSize != -1; } - boolean hasNullableLayout() { return nullableLayoutSize != -1; } + boolean hasNullableAtomicLayout() { return nullableAtomicLayoutSize != -1; } + boolean hasNullableNonAtomicLayout() { return nullableNonAtomicLayoutSize != -1; } boolean hasNullMarker() {return nullMarkerOffset != -1; } int getSize(LayoutKind layoutKind) { @@ -212,9 +213,12 @@ int getSize(LayoutKind layoutKind) { case ATOMIC_FLAT: Asserts.assertTrue(atomicLayoutSize != -1); return atomicLayoutSize; - case NULLABLE_FLAT: - Asserts.assertTrue(nullableLayoutSize != -1); - return nullableLayoutSize; + case NULLABLE_ATOMIC_FLAT: + Asserts.assertTrue(nullableAtomicLayoutSize != -1); + return nullableAtomicLayoutSize; + case NULLABLE_NON_ATOMIC_FLAT: + Asserts.assertTrue(nullableNonAtomicLayoutSize != -1); + return nullableNonAtomicLayoutSize; default: throw new RuntimeException("Unknown LayoutKind " + layoutKind); } @@ -230,9 +234,12 @@ int getAlignment(LayoutKind layoutKind) { case ATOMIC_FLAT: Asserts.assertTrue(atomicLayoutSize != -1); return atomicLayoutAlignment; - case NULLABLE_FLAT: - Asserts.assertTrue(nullableLayoutSize != -1); - return nullableLayoutAlignment; + case NULLABLE_ATOMIC_FLAT: + Asserts.assertTrue(nullableAtomicLayoutSize != -1); + return nullableAtomicLayoutAlignment; + case NULLABLE_NON_ATOMIC_FLAT: + Asserts.assertTrue(nullableNonAtomicLayoutSize != -1); + return nullableNonAtomicLayoutAlignment; default: throw new RuntimeException("Unknown LayoutKind " + layoutKind); } @@ -320,21 +327,34 @@ static ClassLayout parseClassLayout(LogOutput lo) { cl.atomicLayoutAlignment = Integer.parseInt(size_align[1]); } lo.moveToNextLine(); - // Nullable flat layout: x/y - Asserts.assertTrue(lo.getCurrentLine().startsWith("Nullable flat layout")); - String[] nullableLayoutLine = lo.getCurrentLine().split("\\s+"); - size_align = nullableLayoutLine[3].split("/"); + // Nullable atomic flat layout: x/y + Asserts.assertTrue(lo.getCurrentLine().startsWith("Nullable atomic flat layout")); + String[] nullableAtomicLayoutLine = lo.getCurrentLine().split("\\s+"); + size_align = nullableAtomicLayoutLine[4].split("/"); if (size_align[0].contentEquals("-")) { Asserts.assertTrue(size_align[1].contentEquals("-"), "Size/Alignment mismatch"); - cl.nullableLayoutSize = -1; - cl.nullableLayoutAlignment = -1; + cl.nullableAtomicLayoutSize = -1; + cl.nullableAtomicLayoutAlignment = -1; } else { - cl.nullableLayoutSize = Integer.parseInt(size_align[0]); - cl.nullableLayoutAlignment = Integer.parseInt(size_align[1]); + cl.nullableAtomicLayoutSize = Integer.parseInt(size_align[0]); + cl.nullableAtomicLayoutAlignment = Integer.parseInt(size_align[1]); + } + lo.moveToNextLine(); + // Nullable non-atomic flat layout: x/y + Asserts.assertTrue(lo.getCurrentLine().startsWith("Nullable non-atomic flat layout")); + String[] nullableNonAtomicLayoutLine = lo.getCurrentLine().split("\\s+"); + size_align = nullableNonAtomicLayoutLine[4].split("/"); + if (size_align[0].contentEquals("-")) { + Asserts.assertTrue(size_align[1].contentEquals("-"), "Size/Alignment mismatch"); + cl.nullableNonAtomicLayoutSize = -1; + cl.nullableNonAtomicLayoutAlignment = -1; + } else { + cl.nullableNonAtomicLayoutSize = Integer.parseInt(size_align[0]); + cl.nullableNonAtomicLayoutAlignment = Integer.parseInt(size_align[1]); } lo.moveToNextLine(); // Null marker offset = 15 (if class has a nullable flat layout) - if (cl.nullableLayoutSize != -1) { + if (cl.nullableAtomicLayoutSize != -1 || cl.nullableNonAtomicLayoutSize != -1) { Asserts.assertTrue(lo.getCurrentLine().startsWith("Null marker offset")); String[] nullMarkerLine = lo.getCurrentLine().split("\\s+"); cl.nullMarkerOffset = Integer.parseInt(nullMarkerLine[4]); @@ -345,7 +365,6 @@ static ClassLayout parseClassLayout(LogOutput lo) { } else { cl.isValue = false; } - Asserts.assertTrue(lo.getCurrentLine().startsWith("---"), lo.getCurrentLine()); lo.moveToNextLine(); return cl; @@ -647,7 +666,7 @@ void checkNullMarkers() { last_type = block.type; if (block.type() == BlockType.NULL_MARKER) { Asserts.assertTrue(layout.hasNullMarker()); - Asserts.assertTrue(layout.hasNullableLayout()); + Asserts.assertTrue(layout.hasNullableAtomicLayout() || layout.hasNullableNonAtomicLayout()); Asserts.assertEQ(block.offset(), layout.nullMarkerOffset); } if (block.type() == BlockType.EMPTY) has_empty_slot = true; diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/StrictFinalTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/StrictFinalTest.java new file mode 100644 index 00000000000..26edc759687 --- /dev/null +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/StrictFinalTest.java @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2025, Oracle and/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 + * @library /test/lib + * @requires vm.flagless + * @modules java.base/jdk.internal.vm.annotation + * @enablePreview + * @compile FieldLayoutAnalyzer.java StrictFinalTest.java + * @run main/othervm -XX:+UseNullableNonAtomicValueFlattening StrictFinalTest + */ + + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jdk.internal.vm.annotation.ImplicitlyConstructible; +import jdk.internal.vm.annotation.LooselyConsistentValue; +import jdk.internal.vm.annotation.NullRestricted; +import jdk.internal.vm.annotation.Strict; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class StrictFinalTest { + + static class TestRunner { + public static void main(String[] args) throws Exception { + Class testClass = Class.forName("StrictFinalTest"); + Asserts.assertNotNull(testClass); + Method[] testMethods = testClass.getMethods(); + for (Method test : testMethods) { + if (test.getName().startsWith("test_")) { + Asserts.assertTrue(Modifier.isStatic(test.getModifiers())); + Asserts.assertTrue(test.getReturnType().equals(Void.TYPE)); + System.out.println("Running " + test.getName()); + test.invoke(null); + } + } + } + } + + @LooselyConsistentValue + static value class Value0 { + // Just big enough to be bigger than 64 bits with the null marker + int i = 0; + int j = 0; + } + + static value class Container0 { + Value0 val0 = new Value0(); + } + + static public void test_0() { + Container0 c = new Container0(); + } + + static public void check_0(FieldLayoutAnalyzer fla) { + FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("StrictFinalTest$Container0"); + FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val0", false); + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_NON_ATOMIC_FLAT, f.layoutKind()); + } + + static value class Value1 { + // Just big enough to be bigger than 64 bits with the null marker + int i = 0; + int j = 0; + } + + static value class Container1 { + Value1 val0 = new Value1(); + } + + static public void test_1() { + Container1 c = new Container1(); + } + + static public void check_1(FieldLayoutAnalyzer fla) { + FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("StrictFinalTest$Container1"); + FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val0", false); + // Value classes' fields are always strict and final, must be flattened + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_NON_ATOMIC_FLAT, f.layoutKind()); + } + + static class Container2 { + Value1 val0 = new Value1(); + } + + + static public void test_2() { + Container2 c = new Container2(); + } + + static public void check_2(FieldLayoutAnalyzer fla) { + FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("StrictFinalTest$Container2"); + FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val0", false); + // Not strict nor final, must not be flattened + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f.layoutKind()); + } + + // Test temporarily disabled, to be be re-enabled when strict non-final fields are supported + // + // static class Container3 { + // @Strict + // Value1 val0 = new Value1(); + // } + + + // static public void test_3() { + // Container3 c = new Container3(); + // } + + // static public void check_3(FieldLayoutAnalyzer fla) { + // FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("StrictFinalTest$Container3"); + // FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val0", false); + // // Not final, must not be flattened + // Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f.layoutKind()); + // } + + static class Container4 { + final Value1 val0 = new Value1(); + } + + + static public void test_4() { + Container4 c = new Container4(); + } + + static public void check_4(FieldLayoutAnalyzer fla) { + FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("StrictFinalTest$Container4"); + FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val0", false); + // Not strict, must not be flattened + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f.layoutKind()); + } + + static class Container5 { + @Strict + final Value1 val0 = new Value1(); + } + + static public void test_5() { + Container5 c = new Container5(); + Asserts.assertNotNull(c.val0); + } + + static public void check_5(FieldLayoutAnalyzer fla) { + FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("StrictFinalTest$Container5"); + FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val0", false); + // Strict and final, must be flattened + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_NON_ATOMIC_FLAT, f.layoutKind()); + } + + static class Container6 { + @Strict + final Value1 val0 = null; + } + + static public void test_6() { + Container6 c = new Container6(); + Asserts.assertNull(c.val0); + } + + static public void check_6(FieldLayoutAnalyzer fla) { + FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("StrictFinalTest$Container6"); + FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val0", false); + // Strict and final, must be flattened + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_NON_ATOMIC_FLAT, f.layoutKind()); + } + + @LooselyConsistentValue + static value class Container7 { + Value1 val0 = new Value1(); + } + + static public void test_7() { + Container7 c = new Container7(); + Asserts.assertNotNull(c.val0); + } + + static public void check_7(FieldLayoutAnalyzer fla) { + FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("StrictFinalTest$Container7"); + FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val0", false); + // Container is not atomic, must not flattened + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f.layoutKind()); + } + + static ProcessBuilder exec(String... args) throws Exception { + List argsList = new ArrayList<>(); + Collections.addAll(argsList, "--enable-preview"); + Collections.addAll(argsList, "-Xint"); + Collections.addAll(argsList, "-XX:+UnlockDiagnosticVMOptions"); + Collections.addAll(argsList, "-XX:+PrintFieldLayout"); + Collections.addAll(argsList, "-Xshare:off"); + Collections.addAll(argsList, "-Xmx256m"); + Collections.addAll(argsList, "-XX:+UseNullableNonAtomicValueFlattening"); + Collections.addAll(argsList, "-cp", System.getProperty("java.class.path") + System.getProperty("path.separator") + "."); + Collections.addAll(argsList, args); + return ProcessTools.createTestJavaProcessBuilder(argsList); + } + + public static void main(String[] args) throws Exception { + + // Generate test classes + StrictFinalTest sft = new StrictFinalTest(); + + // Execute the test runner in charge of loading all test classes + ProcessBuilder pb = exec("StrictFinalTest$TestRunner"); + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + + if (out.getExitValue() != 0) { + System.out.print(out.getOutput()); + } + Asserts.assertEquals(out.getExitValue(), 0, "Something went wrong while running the tests"); + + // To help during test development + System.out.print(out.getOutput()); + + // Get and parse the test output + FieldLayoutAnalyzer.LogOutput lo = new FieldLayoutAnalyzer.LogOutput(out.asLines()); + FieldLayoutAnalyzer fla = FieldLayoutAnalyzer.createFieldLayoutAnalyzer(lo); + + // Running tests verification method (check that tests produced the right configuration) + Class testClass = StrictFinalTest.class; + Method[] testMethods = testClass.getMethods(); + for (Method test : testMethods) { + if (test.getName().startsWith("check_")) { + Asserts.assertTrue(Modifier.isStatic(test.getModifiers())); + Asserts.assertTrue(test.getReturnType().equals(Void.TYPE)); + test.invoke(null, fla); + } + } + + // Verify that all layouts are correct + fla.check(); + } +} diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/TestLayoutFlags.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/TestLayoutFlags.java index 015ec68d6d0..4ef95b3b562 100644 --- a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/TestLayoutFlags.java +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/TestLayoutFlags.java @@ -146,7 +146,7 @@ static public void check_0(FieldLayoutAnalyzer fla) { FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("TestLayoutFlags$Container0"); FieldLayoutAnalyzer.FieldBlock f0 = cl.getFieldFromName("val0", false); if (useNullableAtomicFlat) { - Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_FLAT, f0.layoutKind()); + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_ATOMIC_FLAT, f0.layoutKind()); } else { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f0.layoutKind()); } diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/ValueCompositionTest.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/ValueCompositionTest.java index 47177185dd7..a1bbde3d415 100644 --- a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/ValueCompositionTest.java +++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/field_layout/ValueCompositionTest.java @@ -117,7 +117,7 @@ static public void check_0(FieldLayoutAnalyzer fla) { } FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false); if (useNullableAtomicFlat) { - Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_FLAT, f1.layoutKind()); + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_ATOMIC_FLAT, f1.layoutKind()); } else { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f1.layoutKind()); } @@ -144,7 +144,7 @@ static public void check_1(FieldLayoutAnalyzer fla) { } FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false); if (useNullableAtomicFlat) { - Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_FLAT, f1.layoutKind()); + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_ATOMIC_FLAT, f1.layoutKind()); } else { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f1.layoutKind()); } @@ -194,7 +194,7 @@ static public void check_3(FieldLayoutAnalyzer fla) { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_ATOMIC_FLAT, f0.layoutKind()); FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false); if (useNullableAtomicFlat) { - Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_FLAT, f1.layoutKind()); + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_ATOMIC_FLAT, f1.layoutKind()); } else { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f1.layoutKind()); } @@ -217,7 +217,7 @@ static public void check_4(FieldLayoutAnalyzer fla) { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_ATOMIC_FLAT, f0.layoutKind()); FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false); if (useNullableAtomicFlat) { - Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_FLAT, f1.layoutKind()); + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_ATOMIC_FLAT, f1.layoutKind()); } else { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f1.layoutKind()); } @@ -264,7 +264,7 @@ static public void check_6(FieldLayoutAnalyzer fla) { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_ATOMIC_FLAT, f0.layoutKind()); FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false); if (useNullableAtomicFlat) { - Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_FLAT, f1.layoutKind()); + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_ATOMIC_FLAT, f1.layoutKind()); } else { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f1.layoutKind()); } @@ -287,7 +287,7 @@ static public void check_7(FieldLayoutAnalyzer fla) { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_ATOMIC_FLAT, f0.layoutKind()); FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false); if (useNullableAtomicFlat) { - Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_FLAT, f1.layoutKind()); + Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NULLABLE_ATOMIC_FLAT, f1.layoutKind()); } else { Asserts.assertEquals(FieldLayoutAnalyzer.LayoutKind.NON_FLAT, f1.layoutKind()); }