--- old/src/hotspot/cpu/x86/macroAssembler_x86.cpp 2019-03-27 16:10:20.942881205 +0100 +++ new/src/hotspot/cpu/x86/macroAssembler_x86.cpp 2019-03-27 16:10:20.634876108 +0100 @@ -3454,19 +3454,17 @@ jcc(Assembler::notZero, is_flattened); } -void MacroAssembler::test_flat_array_klass(Register klass, Register temp_reg, - Label& is_flat_array) { - movl(temp_reg, Address(klass, Klass::layout_helper_offset())); - sarl(temp_reg, Klass::_lh_array_tag_shift); - cmpl(temp_reg, Klass::_lh_array_tag_vt_value); - jcc(Assembler::equal, is_flat_array); +void MacroAssembler::test_flattened_array_oop(Register oop, Register temp_reg, + Label&is_flattened_array) { + load_storage_props(temp_reg, oop); + testb(temp_reg, ArrayStorageProperties::flattened_value); + jcc(Assembler::notZero, is_flattened_array); } - -void MacroAssembler::test_flat_array_oop(Register oop, Register temp_reg, - Label& is_flat_array) { - load_klass(temp_reg, oop); - test_flat_array_klass(temp_reg, temp_reg, is_flat_array); +void MacroAssembler::test_null_free_array_oop(Register oop, Register temp_reg, Label&is_null_free_array) { + load_storage_props(temp_reg, oop); + testb(temp_reg, ArrayStorageProperties::null_free_value); + jcc(Assembler::notZero, is_null_free_array); } void MacroAssembler::os_breakpoint() { @@ -5088,14 +5086,39 @@ resolve_oop_handle(mirror, tmp); } +void MacroAssembler::load_metadata(Register dst, Register src) { + if (UseCompressedClassPointers) { + movl(dst, Address(src, oopDesc::klass_offset_in_bytes())); + } else { + movptr(dst, Address(src, oopDesc::klass_offset_in_bytes())); + } +} + +void MacroAssembler::load_storage_props(Register dst, Register src) { + load_metadata(dst, src); + if (UseCompressedClassPointers) { + shrl(dst, oopDesc::narrow_storage_props_shift); + } else { + shrq(dst, oopDesc::wide_storage_props_shift); + } +} + void MacroAssembler::load_klass(Register dst, Register src) { + load_metadata(dst, src); #ifdef _LP64 if (UseCompressedClassPointers) { - movl(dst, Address(src, oopDesc::klass_offset_in_bytes())); + andl(dst, oopDesc::compressed_klass_mask()); decode_klass_not_null(dst); } else #endif - movptr(dst, Address(src, oopDesc::klass_offset_in_bytes())); + { +#ifdef _LP64 + shlq(dst, oopDesc::storage_props_nof_bits); + shrq(dst, oopDesc::storage_props_nof_bits); +#else + andl(dst, oopDesc::wide_klass_mask()); +#endif + } } void MacroAssembler::load_prototype_header(Register dst, Register src) { --- old/src/hotspot/cpu/x86/macroAssembler_x86.hpp 2019-03-27 16:10:21.942897751 +0100 +++ new/src/hotspot/cpu/x86/macroAssembler_x86.hpp 2019-03-27 16:10:21.642892787 +0100 @@ -105,9 +105,9 @@ void test_field_is_not_flattenable(Register flags, Register temp_reg, Label& notFlattenable); void test_field_is_flattened(Register flags, Register temp_reg, Label& is_flattened); - // Check klass/oops is flat value type array (oop->_klass->_layout_helper & vt_bit) - void test_flat_array_klass(Register klass, Register temp_reg, Label& is_flat_array); - void test_flat_array_oop(Register oop, Register temp_reg, Label& is_flat_array); + // Check oops array storage properties, i.e. flattened and/or null-free + void test_flattened_array_oop(Register oop, Register temp_reg, Label&is_flattened_array); + void test_null_free_array_oop(Register oop, Register temp_reg, Label&is_null_free_array); // Required platform-specific helpers for Label::patch_instructions. // They _shadow_ the declarations in AbstractAssembler, which are undefined. @@ -327,6 +327,8 @@ void load_mirror(Register mirror, Register method, Register tmp = rscratch2); // oop manipulations + void load_metadata(Register dst, Register src); + void load_storage_props(Register dst, Register src); void load_klass(Register dst, Register src); void store_klass(Register dst, Register src); --- old/src/hotspot/cpu/x86/templateTable_x86.cpp 2019-03-27 16:10:22.810912115 +0100 +++ new/src/hotspot/cpu/x86/templateTable_x86.cpp 2019-03-27 16:10:22.510907150 +0100 @@ -828,7 +828,7 @@ index_check(array, index); // kills rbx if (ValueArrayFlatten) { Label is_flat_array, done; - __ test_flat_array_oop(array, rbx, is_flat_array); + __ test_flattened_array_oop(array, rbx, is_flat_array); do_oop_load(_masm, Address(array, index, UseCompressedOops ? Address::times_4 : Address::times_ptr, @@ -1151,7 +1151,7 @@ // Move array class to rdi __ load_klass(rdi, rdx); if (ValueArrayFlatten) { - __ test_flat_array_klass(rdi, rbx, is_flat_array); + __ test_flattened_array_oop(rdx, rbx, is_flat_array); } // Move subklass into rbx @@ -1185,15 +1185,8 @@ if (EnableValhalla) { Label is_null_into_value_array_npe, store_null; - __ load_klass(rdi, rdx); - // No way to store null in flat array - __ test_flat_array_klass(rdi, rbx, is_null_into_value_array_npe); - - // Use case for storing values in objArray where element_klass is specifically - // a value type because they could not be flattened "for reasons", - // these need to have the same semantics as flat arrays, i.e. NPE - __ movptr(rdi, Address(rdi, ObjArrayKlass::element_klass_offset())); - __ test_klass_is_value(rdi, rdi, is_null_into_value_array_npe); + // No way to store null in null-free array + __ test_null_free_array_oop(rdx, rbx, is_null_into_value_array_npe); __ jmp(store_null); __ bind(is_null_into_value_array_npe); --- old/src/hotspot/cpu/x86/x86_64.ad 2019-03-27 16:10:23.762927867 +0100 +++ new/src/hotspot/cpu/x86/x86_64.ad 2019-03-27 16:10:23.462922903 +0100 @@ -5370,9 +5370,14 @@ match(Set dst (LoadKlass mem)); ins_cost(125); // XXX - format %{ "movq $dst, $mem\t# class" %} - opcode(0x8B); - ins_encode(REX_reg_mem_wide(dst, mem), OpcP, reg_mem(dst, mem)); + format %{ "movq $dst, $mem\t# class\n\t" + "shlq $dst, oopDesc::storage_props_nof_bits\n\t" + "shrq $dst, oopDesc::storage_props_nof_bits" %} + ins_encode %{ + __ movptr($dst$$Register, $mem$$Address); + __ shlq($dst$$Register, oopDesc::storage_props_nof_bits); + __ shrq($dst$$Register, oopDesc::storage_props_nof_bits); + %} ins_pipe(ialu_reg_mem); // XXX %} @@ -5382,9 +5387,11 @@ match(Set dst (LoadNKlass mem)); ins_cost(125); // XXX - format %{ "movl $dst, $mem\t# compressed klass ptr" %} + format %{ "movl $dst, $mem\t# compressed klass ptr\n\t" + "andl $dst, oopDesc::compressed_klass_mask()" %} ins_encode %{ __ movl($dst$$Register, $mem$$Address); + __ andl($dst$$Register, oopDesc::compressed_klass_mask()); %} ins_pipe(ialu_reg_mem); // XXX %} --- old/src/hotspot/share/ci/ciReplay.cpp 2019-03-27 16:10:24.898946665 +0100 +++ new/src/hotspot/share/ci/ciReplay.cpp 2019-03-27 16:10:24.598941701 +0100 @@ -910,7 +910,10 @@ value = oopFactory::new_longArray(length, CHECK_(true)); } else if (field_signature[0] == '[' && field_signature[1] == 'L') { Klass* kelem = resolve_klass(field_signature + 1, CHECK_(true)); - value = oopFactory::new_array(kelem, length, CHECK_(true)); + value = oopFactory::new_objArray(kelem, length, CHECK_(true)); + } else if (field_signature[0] == '[' && field_signature[1] == 'Q') { + Klass* kelem = resolve_klass(field_signature + 1, CHECK_(true)); + value = oopFactory::new_valueArray(kelem, length, CHECK_(true)); } else { report_error("unhandled array staticfield"); } --- old/src/hotspot/share/classfile/classFileParser.cpp 2019-03-27 16:10:25.970964403 +0100 +++ new/src/hotspot/share/classfile/classFileParser.cpp 2019-03-27 16:10:25.666959373 +0100 @@ -498,7 +498,7 @@ Symbol* const name = cp->symbol_at(class_index); const unsigned int name_len = name->utf8_length(); - if (name->is_Q_signature()) { + if (name->is_Q_signature() || name->is_Q_array_signature()) { cp->unresolved_qdescriptor_at_put(index, class_index, num_klasses++); } else { cp->unresolved_klass_at_put(index, class_index, num_klasses++); --- old/src/hotspot/share/classfile/javaClasses.cpp 2019-03-27 16:10:27.126983531 +0100 +++ new/src/hotspot/share/classfile/javaClasses.cpp 2019-03-27 16:10:26.822978501 +0100 @@ -902,12 +902,9 @@ if (k->is_array_klass()) { if (k->is_valueArray_klass()) { Klass* element_klass = (Klass*) ValueArrayKlass::cast(k)->element_klass(); - if (element_klass->is_value()) { - ValueKlass* vk = ValueKlass::cast(InstanceKlass::cast(element_klass)); - comp_mirror = Handle(THREAD, vk->value_mirror()); - } else { - comp_mirror = Handle(THREAD, element_klass->java_mirror()); - } + assert(element_klass->is_value(), "Must be value type component"); + ValueKlass* vk = ValueKlass::cast(InstanceKlass::cast(element_klass)); + comp_mirror = Handle(THREAD, vk->value_mirror()); } else if (k->is_typeArray_klass()) { BasicType type = TypeArrayKlass::cast(k)->element_type(); @@ -916,7 +913,7 @@ assert(k->is_objArray_klass(), "Must be"); Klass* element_klass = ObjArrayKlass::cast(k)->element_klass(); assert(element_klass != NULL, "Must have an element klass"); - if (element_klass->is_value()) { + if (element_klass->is_value() && k->name()->is_Q_array_signature()) { ValueKlass* vk = ValueKlass::cast(InstanceKlass::cast(element_klass)); comp_mirror = Handle(THREAD, vk->value_mirror()); } else { --- old/src/hotspot/share/classfile/javaClasses.hpp 2019-03-27 16:10:28.319003255 +0100 +++ new/src/hotspot/share/classfile/javaClasses.hpp 2019-03-27 16:10:27.982997695 +0100 @@ -322,6 +322,10 @@ static void set_box_mirror(oop java_class, oop mirror); static oop box_mirror(oop java_class); + static bool is_box_type(oop java_class) { // Must match "Class.isBoxType()" + return box_mirror(java_class) == NULL || oopDesc::equals(box_mirror(java_class), java_class); + } + static void set_value_mirror(oop java_class, oop mirror); static oop value_mirror(oop java_class); --- old/src/hotspot/share/classfile/systemDictionary.cpp 2019-03-27 16:10:29.391020993 +0100 +++ new/src/hotspot/share/classfile/systemDictionary.cpp 2019-03-27 16:10:29.091016029 +0100 @@ -297,11 +297,11 @@ protection_domain, CHECK_NULL); if (k != NULL) { - k = k->array_klass(fd.dimension(), CHECK_NULL); + k = k->array_klass(fd.storage_props(), fd.dimension(), CHECK_NULL); } } else { k = Universe::typeArrayKlassObj(t); - k = TypeArrayKlass::cast(k)->array_klass(fd.dimension(), CHECK_NULL); + k = TypeArrayKlass::cast(k)->array_klass(fd.storage_props(), fd.dimension(), CHECK_NULL); } return k; } @@ -1028,7 +1028,7 @@ k = SystemDictionary::find(fd.object_key(), class_loader, protection_domain, THREAD); } if (k != NULL) { - k = k->array_klass_or_null(fd.dimension()); + k = k->array_klass_or_null(fd.storage_props(), fd.dimension()); } } else { k = find(class_name, class_loader, protection_domain, THREAD); @@ -2265,7 +2265,7 @@ } // If element class already loaded, allocate array klass if (klass != NULL) { - klass = klass->array_klass_or_null(fd.dimension()); + klass = klass->array_klass_or_null(fd.storage_props(), fd.dimension()); } } else { MutexLocker mu(SystemDictionary_lock, THREAD); --- old/src/hotspot/share/gc/shared/memAllocator.cpp 2019-03-27 16:10:30.271035555 +0100 +++ new/src/hotspot/share/gc/shared/memAllocator.cpp 2019-03-27 16:10:29.971030591 +0100 @@ -374,9 +374,13 @@ Copy::fill_to_aligned_words(mem + hs, _word_size - hs); } -oop MemAllocator::finish(HeapWord* mem) const { +inline void MemAllocator::finish_mark(HeapWord* mem) const { assert(mem != NULL, "NULL object pointer"); oopDesc::set_mark_raw(mem, Klass::default_prototype_header(_klass)); +} + +oop MemAllocator::finish(HeapWord* mem) const { + finish_mark(mem); // Need a release store to ensure array/class length, mark word, and // object zeroing are visible before setting the klass non-NULL, for // concurrent collectors. @@ -384,6 +388,12 @@ return oop(mem); } +oop MemAllocator::finish_with_properties(HeapWord* mem, ArrayStorageProperties storage_props) const { + finish_mark(mem); + oopDesc::release_set_metadata(mem, storage_props, _klass); + return oop(mem); +} + oop ObjAllocator::initialize(HeapWord* mem) const { mem_clear(mem); return finish(mem); @@ -407,7 +417,7 @@ mem_clear(mem); } arrayOopDesc::set_length(mem, _length); - return finish(mem); + return finish_with_properties(mem, ArrayKlass::cast(_klass)->storage_properties()); } oop ClassAllocator::initialize(HeapWord* mem) const { --- old/src/hotspot/share/gc/shared/memAllocator.hpp 2019-03-27 16:10:30.955046873 +0100 +++ new/src/hotspot/share/gc/shared/memAllocator.hpp 2019-03-27 16:10:30.611041180 +0100 @@ -61,7 +61,10 @@ // This finish constructing an oop by installing the mark word and the Klass* pointer // last. At the point when the Klass pointer is initialized, this is a constructed object // that must be parseable as an oop by concurrent collectors. + inline void finish_mark(HeapWord* mem) const; oop finish(HeapWord* mem) const; + // Encode any extra metadata properties for arrays + oop finish_with_properties(HeapWord* mem, ArrayStorageProperties storage_props) const; // Raw memory allocation. This may or may not use TLAB allocations to satisfy the // allocation. A GC implementation may override this function to satisfy the allocation --- old/src/hotspot/share/interpreter/interpreterRuntime.cpp 2019-03-27 16:10:31.811061037 +0100 +++ new/src/hotspot/share/interpreter/interpreterRuntime.cpp 2019-03-27 16:10:31.511056073 +0100 @@ -437,10 +437,14 @@ IRT_ENTRY(void, InterpreterRuntime::anewarray(JavaThread* thread, ConstantPool* pool, int index, jint size)) Klass* klass = pool->klass_at(index, CHECK); - if (klass->is_value()) { // Logically creates elements, ensure klass init + bool is_qtype_desc = pool->tag_at(index).is_Qdescriptor_klass(); + arrayOop obj; + if ((!klass->is_array_klass()) && is_qtype_desc) { // Logically creates elements, ensure klass init klass->initialize(CHECK); + obj = oopFactory::new_valueArray(klass, size, CHECK); + } else { + obj = oopFactory::new_objArray(klass, size, CHECK); } - arrayOop obj = oopFactory::new_array(klass, size, CHECK); thread->set_vm_result(obj); IRT_END @@ -476,13 +480,14 @@ // We may want to pass in more arguments - could make this slightly faster LastFrameAccessor last_frame(thread); ConstantPool* constants = last_frame.method()->constants(); - int i = last_frame.get_index_u2(Bytecodes::_multianewarray); - Klass* klass = constants->klass_at(i, CHECK); + int i = last_frame.get_index_u2(Bytecodes::_multianewarray); + Klass* klass = constants->klass_at(i, CHECK); + bool is_qtype = constants->tag_at(i).is_Qdescriptor_klass(); int nof_dims = last_frame.number_of_dimensions(); assert(klass->is_klass(), "not a class"); assert(nof_dims >= 1, "multianewarray rank must be nonzero"); - if (klass->is_value()) { // Logically creates elements, ensure klass init + if (is_qtype) { // Logically creates elements, ensure klass init klass->initialize(CHECK); } --- old/src/hotspot/share/memory/metaspace.cpp 2019-03-27 16:10:32.927079503 +0100 +++ new/src/hotspot/share/memory/metaspace.cpp 2019-03-27 16:10:32.623074473 +0100 @@ -952,7 +952,8 @@ higher_address = metaspace_base + compressed_class_space_size(); lower_base = metaspace_base; - uint64_t klass_encoding_max = UnscaledClassSpaceMax << LogKlassAlignmentInBytes; + // Using oopDesc::_metadata high bits so LogKlassAlignmentInBytes shift is no longer possible + uint64_t klass_encoding_max = UnscaledClassSpaceMax; // If compressed class space fits in lower 32G, we don't need a base. if (higher_address <= (address)klass_encoding_max) { lower_base = 0; // Effectively lower base is zero. --- old/src/hotspot/share/memory/oopFactory.cpp 2019-03-27 16:10:33.859094924 +0100 +++ new/src/hotspot/share/memory/oopFactory.cpp 2019-03-27 16:10:33.559089961 +0100 @@ -84,8 +84,6 @@ objArrayOop oopFactory::new_objArray(Klass* klass, int length, TRAPS) { assert(klass->is_klass(), "must be instance class"); - assert(!klass->is_value() || (!ValueKlass::cast(klass)->flatten_array()), - "Did not expect flatten array of value klass"); if (klass->is_array_klass()) { return ArrayKlass::cast(klass)->allocate_arrayArray(1, length, THREAD); } else { @@ -95,32 +93,18 @@ arrayOop oopFactory::new_valueArray(Klass* klass, int length, TRAPS) { assert(klass->is_value(), "Klass must be value type"); - Klass* array_klass = klass->array_klass(CHECK_NULL); // Flat value array or object array ? - assert(array_klass->is_valueArray_klass() || array_klass->is_objArray_klass(), - "Expect an array class here"); + // Request flattened, but we might not actually get it...either way "null-free" are the aaload/aastore semantics + Klass* array_klass = klass->array_klass(ArrayStorageProperties::flattened_and_null_free, 1, CHECK_NULL); + assert(ArrayKlass::cast(array_klass)->storage_properties().is_null_free(), "Expect a null-free array class here"); + arrayOop oop; if (array_klass->is_valueArray_klass()) { - return (arrayOop) ValueArrayKlass::cast(array_klass)->allocate(length, THREAD); - } - - ValueKlass* vklass = ValueKlass::cast(klass); - objArrayOop array = oopFactory::new_objArray(klass, length, CHECK_NULL); - if (length == 0) { - return array; - } - - // Populate default values... - objArrayHandle array_h(THREAD, array); - instanceOop value = (instanceOop)vklass->default_value(); - for (int i = 0; i < length; i++) { - array_h->obj_at_put(i, value); + oop = (arrayOop) ValueArrayKlass::cast(array_klass)->allocate(length, THREAD); + } else { + oop = (arrayOop) ObjArrayKlass::cast(array_klass)->allocate(length, THREAD); } - return array_h(); -} - -arrayOop oopFactory::new_array(Klass* klass, int length, TRAPS) { - return (klass->is_value()) ? new_valueArray(klass, length, THREAD) : - (arrayOop)new_objArray(klass, length, THREAD); + assert(oop->array_storage_properties().is_null_free(), "Bad array storage encoding"); + return oop; } objArrayHandle oopFactory::new_objArray_handle(Klass* klass, int length, TRAPS) { --- old/src/hotspot/share/memory/oopFactory.hpp 2019-03-27 16:10:34.535106110 +0100 +++ new/src/hotspot/share/memory/oopFactory.hpp 2019-03-27 16:10:34.235101146 +0100 @@ -67,12 +67,15 @@ // Regular object arrays static objArrayOop new_objArray(Klass* klass, int length, TRAPS); - // Value arrays + // Value arrays... + // LWorld: + // - Q-type signature allocation should use this path. + // - L-type signature allocation should use new_objArray (even with value type elements) + // + // Method specifically creates ArrayStorageProperties::null_free and possibly flattened if possible + // i.e. valueArrayOop of flattening can be done, else objArrayOop with "null free" storage properties static arrayOop new_valueArray(Klass* klass, int length, TRAPS); - // Object/Value array for klass - static arrayOop new_array(Klass* klass, int length, TRAPS); - // Helpers that return handles static objArrayHandle new_objArray_handle(Klass* klass, int length, TRAPS); static typeArrayHandle new_byteArray_handle(int length, TRAPS); --- old/src/hotspot/share/oops/arrayKlass.cpp 2019-03-27 16:10:35.235117693 +0100 +++ new/src/hotspot/share/oops/arrayKlass.cpp 2019-03-27 16:10:34.935112729 +0100 @@ -98,9 +98,9 @@ JFR_ONLY(INIT_ID(this);) } -Symbol* ArrayKlass::create_element_klass_array_name(Klass* element_klass, TRAPS) { +Symbol* ArrayKlass::create_element_klass_array_name(bool is_qtype, Klass* element_klass, TRAPS) { Symbol* name = NULL; - if (!element_klass->is_instance_klass() || + if (!element_klass->is_instance_klass() || is_qtype || (name = InstanceKlass::cast(element_klass)->array_name()) == NULL) { ResourceMark rm(THREAD); @@ -110,15 +110,7 @@ int idx = 0; new_str[idx++] = '['; if (element_klass->is_instance_klass()) { // it could be an array or simple type - // Temporary hack, for arrays of value types, this code should be removed - // once value types have their own array types - // With Q-descriptors, the code below needs to be reworked. - // It is still correct today because the only kind of value array supported - // is array of null-free values which map to the Q-signature. - // As soon as both arrays of null-free values and arrays of nullable values - // are supported, this code has to be rewritten to consider the kind of the - // array instead of the kind of the elements. - if (element_klass->is_value()) { + if (is_qtype) { new_str[idx++] = 'Q'; } else { new_str[idx++] = 'L'; @@ -131,9 +123,9 @@ } new_str[idx++] = '\0'; name = SymbolTable::new_permanent_symbol(new_str, CHECK_NULL); - if (element_klass->is_instance_klass() || element_klass->is_value()) { + if (element_klass->is_instance_klass() && (!is_qtype)) { InstanceKlass* ik = InstanceKlass::cast(element_klass); - ik->set_array_name(name); + ik->set_array_name(name); // CMH: only cache and deref array_name for L-type...missing for Q-type } } @@ -168,7 +160,7 @@ objArrayOop ArrayKlass::allocate_arrayArray(int n, int length, TRAPS) { check_array_allocation_length(length, arrayOopDesc::max_array_length(T_ARRAY), CHECK_0); int size = objArrayOopDesc::object_size(length); - Klass* k = array_klass(n+dimension(), CHECK_0); + Klass* k = array_klass(FieldType::get_array_storage_properties(name()), n+dimension(), CHECK_0); ArrayKlass* ak = ArrayKlass::cast(k); objArrayOop o = (objArrayOop)Universe::heap()->array_allocate(ak, size, length, /* do_zero */ true, CHECK_0); @@ -176,15 +168,6 @@ return o; } -void ArrayKlass::array_klasses_do(void f(Klass* k, TRAPS), TRAPS) { - Klass* k = this; - // Iterate over this array klass and all higher dimensions - while (k != NULL) { - f(k, CHECK); - k = ArrayKlass::cast(k)->higher_dimension(); - } -} - void ArrayKlass::array_klasses_do(void f(Klass* k)) { Klass* k = this; // Iterate over this array klass and all higher dimensions --- old/src/hotspot/share/oops/arrayKlass.hpp 2019-03-27 16:10:35.915128945 +0100 +++ new/src/hotspot/share/oops/arrayKlass.hpp 2019-03-27 16:10:35.615123980 +0100 @@ -57,7 +57,7 @@ ArrayKlass() { assert(DumpSharedSpaces || UseSharedSpaces, "only for cds"); } // Create array_name for element klass, creates a permanent symbol, returns result - static Symbol* create_element_klass_array_name(Klass* element_klass, TRAPS); + static Symbol* create_element_klass_array_name(bool is_qtype, Klass* element_klass, TRAPS); public: // Instance variables @@ -67,6 +67,9 @@ // Compiler/Interpreter offset static ByteSize element_klass_offset() { return in_ByteSize(offset_of(ArrayKlass, _element_klass)); } + // Presented with an ArrayKlass, which storage_properties should be encoded into arrayOop + virtual ArrayStorageProperties storage_properties() { return ArrayStorageProperties::empty; } + // Testing operation DEBUG_ONLY(bool is_array_klass_slow() const { return true; }) @@ -132,7 +135,6 @@ // Iterators void array_klasses_do(void f(Klass* k)); - void array_klasses_do(void f(Klass* k, TRAPS), TRAPS); // Return a handle. static void complete_create_array_klass(ArrayKlass* k, Klass* super_klass, ModuleEntry* module, TRAPS); --- old/src/hotspot/share/oops/constantPool.cpp 2019-03-27 16:10:36.619140593 +0100 +++ new/src/hotspot/share/oops/constantPool.cpp 2019-03-27 16:10:36.319135629 +0100 @@ -235,7 +235,7 @@ // The interpreter assumes when the tag is stored, the klass is resolved // and the Klass* non-NULL, so we need hardware store ordering here. - jbyte qdesc_bit = name->is_Q_signature() ? (jbyte)JVM_CONSTANT_QDESC_BIT : 0; + jbyte qdesc_bit = (name->is_Q_signature() || name->is_Q_array_signature()) ? (jbyte)JVM_CONSTANT_QDESC_BIT : 0; if (k != NULL) { release_tag_at_put(class_index, JVM_CONSTANT_Class | qdesc_bit); } else { @@ -253,6 +253,7 @@ // The interpreter assumes when the tag is stored, the klass is resolved // and the Klass* non-NULL, so we need hardware store ordering here. + assert(!(k->name()->is_Q_signature() || k->name()->is_Q_array_signature()), "Q-type without JVM_CONSTANT_QDESC_BIT"); release_tag_at_put(class_index, JVM_CONSTANT_Class); } @@ -1987,6 +1988,12 @@ ent_size = 2; break; } + case (JVM_CONSTANT_Class | JVM_CONSTANT_QDESC_BIT): { + idx1 = Bytes::get_Java_u2(bytes); + printf("qclass #%03d", idx1); + ent_size = 2; + break; + } case JVM_CONSTANT_String: { idx1 = Bytes::get_Java_u2(bytes); printf("String #%03d", idx1); @@ -2029,6 +2036,10 @@ printf("UnresolvedClass: %s", WARN_MSG); break; } + case (JVM_CONSTANT_UnresolvedClass | JVM_CONSTANT_QDESC_BIT): { + printf("UnresolvedQClass: %s", WARN_MSG); + break; + } case JVM_CONSTANT_UnresolvedClassInError: { printf("UnresolvedClassInErr: %s", WARN_MSG); break; @@ -2200,6 +2211,7 @@ case JVM_CONSTANT_Class: case JVM_CONSTANT_UnresolvedClass: case JVM_CONSTANT_UnresolvedClassInError: { + assert(!tag_at(idx).is_Qdescriptor_klass(), "Failed to encode QDesc"); *bytes = JVM_CONSTANT_Class; Symbol* sym = klass_name_at(idx); idx1 = tbl->symbol_to_value(sym); --- old/src/hotspot/share/oops/instanceKlass.cpp 2019-03-27 16:10:37.507155287 +0100 +++ new/src/hotspot/share/oops/instanceKlass.cpp 2019-03-27 16:10:37.207150323 +0100 @@ -1398,7 +1398,8 @@ } } -Klass* InstanceKlass::array_klass_impl(bool or_null, int n, TRAPS) { +Klass* InstanceKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS) { + assert(!storage_props.is_flattened(), "Can't be flattened"); // Need load-acquire for lock-free read if (array_klasses_acquire() == NULL) { if (or_null) return NULL; @@ -1411,7 +1412,7 @@ // Check if update has already taken place if (array_klasses() == NULL) { - Klass* k = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), 1, this, CHECK_NULL); + Klass* k = ObjArrayKlass::allocate_objArray_klass(storage_props, 1, this, CHECK_NULL); // use 'release' to pair with lock-free load release_set_array_klasses(k); } @@ -1420,13 +1421,13 @@ // _this will always be set at this point ObjArrayKlass* oak = (ObjArrayKlass*)array_klasses(); if (or_null) { - return oak->array_klass_or_null(n); + return oak->array_klass_or_null(storage_props, n); } - return oak->array_klass(n, THREAD); + return oak->array_klass(storage_props, n, THREAD); } -Klass* InstanceKlass::array_klass_impl(bool or_null, TRAPS) { - return array_klass_impl(or_null, 1, THREAD); +Klass* InstanceKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS) { + return array_klass_impl(storage_props, or_null, 1, THREAD); } static int call_class_initializer_counter = 0; // for debugging @@ -1651,11 +1652,6 @@ } -void InstanceKlass::array_klasses_do(void f(Klass* k, TRAPS), TRAPS) { - if (array_klasses() != NULL) - ArrayKlass::cast(array_klasses())->array_klasses_do(f, THREAD); -} - void InstanceKlass::array_klasses_do(void f(Klass* k)) { if (array_klasses() != NULL) ArrayKlass::cast(array_klasses())->array_klasses_do(f); --- old/src/hotspot/share/oops/instanceKlass.hpp 2019-03-27 16:10:38.771176202 +0100 +++ new/src/hotspot/share/oops/instanceKlass.hpp 2019-03-27 16:10:38.427170509 +0100 @@ -143,6 +143,7 @@ address* _pack_handler; address* _unpack_handler; int* _default_value_offset; + Klass** _value_array_klass; friend class ValueKlass; }; @@ -1096,8 +1097,7 @@ void do_local_static_fields(void f(fieldDescriptor*, Handle, TRAPS), Handle, TRAPS); void methods_do(void f(Method* method)); - void array_klasses_do(void f(Klass* k)); - void array_klasses_do(void f(Klass* k, TRAPS), TRAPS); + virtual void array_klasses_do(void f(Klass* k)); bool super_types_do(SuperTypeClosure* blk); static InstanceKlass* cast(Klass* k) { @@ -1392,10 +1392,10 @@ JNIid* jni_id_for_impl (int offset); protected: // Returns the array class for the n'th dimension - virtual Klass* array_klass_impl(bool or_null, int n, TRAPS); + virtual Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS); // Returns the array class with this class as element type - virtual Klass* array_klass_impl(bool or_null, TRAPS); + virtual Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS); private: --- old/src/hotspot/share/oops/klass.cpp 2019-03-27 16:10:39.687191358 +0100 +++ new/src/hotspot/share/oops/klass.cpp 2019-03-27 16:10:39.379186262 +0100 @@ -631,29 +631,29 @@ } #endif // INCLUDE_CDS_JAVA_HEAP -Klass* Klass::array_klass_or_null(int rank) { +Klass* Klass::array_klass_or_null(ArrayStorageProperties storage_props, int rank) { EXCEPTION_MARK; // No exception can be thrown by array_klass_impl when called with or_null == true. // (In anycase, the execption mark will fail if it do so) - return array_klass_impl(true, rank, THREAD); + return array_klass_impl(storage_props, true, rank, THREAD); } -Klass* Klass::array_klass_or_null() { +Klass* Klass::array_klass_or_null(ArrayStorageProperties storage_props) { EXCEPTION_MARK; // No exception can be thrown by array_klass_impl when called with or_null == true. // (In anycase, the execption mark will fail if it do so) - return array_klass_impl(true, THREAD); + return array_klass_impl(storage_props, true, THREAD); } -Klass* Klass::array_klass_impl(bool or_null, int rank, TRAPS) { +Klass* Klass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int rank, TRAPS) { fatal("array_klass should be dispatched to InstanceKlass, ObjArrayKlass or TypeArrayKlass"); return NULL; } -Klass* Klass::array_klass_impl(bool or_null, TRAPS) { +Klass* Klass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS) { fatal("array_klass should be dispatched to InstanceKlass, ObjArrayKlass or TypeArrayKlass"); return NULL; } @@ -741,6 +741,16 @@ if (WizardMode) { // print header obj->mark()->print_on(st); + ArrayStorageProperties props = obj->array_storage_properties(); + if (props.value() != 0) { + st->print(" - array storage properties: "); + if (props.is_flattened()) { + st->print(" flat"); + } + if (props.is_null_free()) { + st->print(" non nullable"); + } + } } // print class --- old/src/hotspot/share/oops/klass.hpp 2019-03-27 16:10:40.387202940 +0100 +++ new/src/hotspot/share/oops/klass.hpp 2019-03-27 16:10:40.083197911 +0100 @@ -28,6 +28,7 @@ #include "classfile/classLoaderData.hpp" #include "memory/iterator.hpp" #include "memory/memRegion.hpp" +#include "oops/arrayStorageProperties.hpp" #include "oops/markOop.hpp" #include "oops/metadata.hpp" #include "oops/oop.hpp" @@ -483,15 +484,22 @@ } // array class with specific rank - Klass* array_klass(int rank, TRAPS) { return array_klass_impl(false, rank, THREAD); } + Klass* array_klass(int rank, TRAPS) { + return array_klass_impl(ArrayStorageProperties::empty, false, rank, THREAD); + } + Klass* array_klass(ArrayStorageProperties storage_props, int rank, TRAPS) { + return array_klass_impl(storage_props, false, rank, THREAD); + } // array class with this klass as element type - Klass* array_klass(TRAPS) { return array_klass_impl(false, THREAD); } + Klass* array_klass(TRAPS) { + return array_klass_impl(ArrayStorageProperties::empty, false, THREAD); + } // These will return NULL instead of allocating on the heap: // NB: these can block for a mutex, like other functions with TRAPS arg. - Klass* array_klass_or_null(int rank); - Klass* array_klass_or_null(); + Klass* array_klass_or_null(ArrayStorageProperties storage_props, int rank); + Klass* array_klass_or_null(ArrayStorageProperties storage_props); virtual oop protection_domain() const = 0; @@ -504,8 +512,8 @@ oop klass_holder() const { return class_loader_data()->holder_phantom(); } protected: - virtual Klass* array_klass_impl(bool or_null, int rank, TRAPS); - virtual Klass* array_klass_impl(bool or_null, TRAPS); + virtual Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int rank, TRAPS); + virtual Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS); // Error handling when length > max_length or length < 0 static void check_array_allocation_length(int length, int max_length, TRAPS); --- old/src/hotspot/share/oops/objArrayKlass.cpp 2019-03-27 16:10:41.043213795 +0100 +++ new/src/hotspot/share/oops/objArrayKlass.cpp 2019-03-27 16:10:40.743208831 +0100 @@ -55,23 +55,22 @@ return new (loader_data, size, THREAD) ObjArrayKlass(n, k, name); } -Klass* ObjArrayKlass::allocate_objArray_klass(ClassLoaderData* loader_data, - int n, Klass* element_klass, TRAPS) { - +Klass* ObjArrayKlass::allocate_objArray_klass(ArrayStorageProperties storage_props, + int n, Klass* element_klass, TRAPS) { // Eagerly allocate the direct array supertype. Klass* super_klass = NULL; if (!Universe::is_bootstrapping() || SystemDictionary::Object_klass_loaded()) { Klass* element_super = element_klass->super(); if (element_super != NULL) { // The element type has a direct super. E.g., String[] has direct super of Object[]. - super_klass = element_super->array_klass_or_null(); + super_klass = element_super->array_klass_or_null(storage_props); bool supers_exist = super_klass != NULL; // Also, see if the element has secondary supertypes. // We need an array type for each. const Array* element_supers = element_klass->secondary_supers(); for( int i = element_supers->length()-1; i >= 0; i-- ) { Klass* elem_super = element_supers->at(i); - if (elem_super->array_klass_or_null() == NULL) { + if (elem_super->array_klass_or_null(ArrayStorageProperties::empty) == NULL) { supers_exist = false; break; } @@ -87,7 +86,7 @@ elem_super->array_klass(CHECK_0); } // Now retry from the beginning - ek = element_klass->array_klass(n, CHECK_0); + ek = element_klass->array_klass(storage_props, n, CHECK_0); } // re-lock return ek; } @@ -98,9 +97,10 @@ } // Create type name for klass. - Symbol* name = ArrayKlass::create_element_klass_array_name(element_klass, CHECK_NULL); + Symbol* name = ArrayKlass::create_element_klass_array_name(storage_props.is_null_free(), element_klass, CHECK_NULL); // Initialize instance variables + ClassLoaderData* loader_data = element_klass->class_loader_data(); ObjArrayKlass* oak = ObjArrayKlass::allocate(loader_data, n, element_klass, name, CHECK_0); // Add all classes to our internal class loader list here, @@ -151,16 +151,31 @@ objArrayOop ObjArrayKlass::allocate(int length, TRAPS) { check_array_allocation_length(length, arrayOopDesc::max_array_length(T_OBJECT), CHECK_0); int size = objArrayOopDesc::object_size(length); - return (objArrayOop)Universe::heap()->array_allocate(this, size, length, + bool populate_null_free = storage_properties().is_null_free() && (dimension() == 1); + objArrayOop array = (objArrayOop)Universe::heap()->array_allocate(this, size, length, /* do_zero */ true, THREAD); + if (populate_null_free) { + assert(!element_klass()->is_array_klass(), "ArrayKlass unexpected here"); + // Populate default values... + objArrayHandle array_h(THREAD, array); + instanceOop value = (instanceOop) ValueKlass::cast(element_klass())->default_value(); + for (int i = 0; i < length; i++) { + array_h->obj_at_put(i, value); + } + } + return array; } static int multi_alloc_counter = 0; oop ObjArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) { int length = *sizes; - if (rank == 1) { // last dim may be valueArray - return oopFactory::new_array(element_klass(), length, CHECK_NULL); + if (rank == 1) { // last dim may be valueArray, check if we have any special storage requirements + if ((!element_klass()->is_array_klass()) && storage_properties().is_null_free()) { + return oopFactory::new_valueArray(element_klass(), length, CHECK_NULL); + } else { + return oopFactory::new_objArray(element_klass(), length, CHECK_NULL); + } } guarantee(rank > 1, "Rank below 1"); // Call to lower_dimension uses this pointer, so most be called before a @@ -189,6 +204,10 @@ return h_array(); } +ArrayStorageProperties ObjArrayKlass::storage_properties() { + return name()->is_Q_array_signature() ? ArrayStorageProperties::null_free : ArrayStorageProperties::empty; +} + // Either oop or narrowOop depending on UseCompressedOops. void ObjArrayKlass::do_copy(arrayOop s, size_t src_offset, arrayOop d, size_t dst_offset, int length, TRAPS) { @@ -326,8 +345,8 @@ } -Klass* ObjArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) { - +Klass* ObjArrayKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS) { + assert(!storage_props.is_flattened() || n > 1, "Cannot flatten"); assert(dimension() <= n, "check order of chain"); int dim = dimension(); if (dim == n) return this; @@ -347,7 +366,7 @@ // Create multi-dim klass object and link them together Klass* k = - ObjArrayKlass::allocate_objArray_klass(class_loader_data(), dim + 1, this, CHECK_NULL); + ObjArrayKlass::allocate_objArray_klass(storage_props, dim + 1, this, CHECK_NULL); ObjArrayKlass* ak = ObjArrayKlass::cast(k); ak->set_lower_dimension(this); // use 'release' to pair with lock-free load @@ -361,13 +380,13 @@ ObjArrayKlass *ak = ObjArrayKlass::cast(higher_dimension()); if (or_null) { - return ak->array_klass_or_null(n); + return ak->array_klass_or_null(storage_props, n); } - return ak->array_klass(n, THREAD); + return ak->array_klass(storage_props, n, THREAD); } -Klass* ObjArrayKlass::array_klass_impl(bool or_null, TRAPS) { - return array_klass_impl(or_null, dimension() + 1, THREAD); +Klass* ObjArrayKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS) { + return array_klass_impl(storage_props, or_null, dimension() + 1, THREAD); } bool ObjArrayKlass::can_be_primary_super_slow() const { @@ -395,7 +414,7 @@ secondaries->push(SystemDictionary::Serializable_klass()); for (int i = 0; i < num_elem_supers; i++) { Klass* elem_super = elem_supers->at(i); - Klass* array_super = elem_super->array_klass_or_null(); + Klass* array_super = elem_super->array_klass_or_null(ArrayStorageProperties::empty); assert(array_super != NULL, "must already have been created"); secondaries->push(array_super); } --- old/src/hotspot/share/oops/objArrayKlass.hpp 2019-03-27 16:10:41.767225775 +0100 +++ new/src/hotspot/share/oops/objArrayKlass.hpp 2019-03-27 16:10:41.435220281 +0100 @@ -50,6 +50,10 @@ // For dummy objects ObjArrayKlass() {} + // if ObjArrayKlass is used to represent an LWorld "Q-type" value type array, the only thing we can give null-free (i.e. not flattened) + // ArrayStorageProperties::empty meaning nothing special, array of references (possibly even to value types) + ArrayStorageProperties storage_properties(); + Klass* bottom_klass() const { return _bottom_klass; } void set_bottom_klass(Klass* k) { _bottom_klass = k; } Klass** bottom_klass_addr() { return &_bottom_klass; } @@ -65,7 +69,7 @@ int oop_size(oop obj) const; // Allocation - static Klass* allocate_objArray_klass(ClassLoaderData* loader_data, + static Klass* allocate_objArray_klass(ArrayStorageProperties storage_props, int n, Klass* element_klass, TRAPS); objArrayOop allocate(int length, TRAPS); @@ -87,10 +91,10 @@ int length, TRAPS); protected: // Returns the ObjArrayKlass for n'th dimension. - virtual Klass* array_klass_impl(bool or_null, int n, TRAPS); + virtual Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS); // Returns the array class with this class as element type. - virtual Klass* array_klass_impl(bool or_null, TRAPS); + virtual Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS); public: --- old/src/hotspot/share/oops/oop.hpp 2019-03-27 16:10:42.463237291 +0100 +++ new/src/hotspot/share/oops/oop.hpp 2019-03-27 16:10:42.123231665 +0100 @@ -28,6 +28,7 @@ #include "memory/iterator.hpp" #include "memory/memRegion.hpp" #include "oops/access.hpp" +#include "oops/arrayStorageProperties.hpp" #include "oops/metadata.hpp" #include "runtime/atomic.hpp" #include "utilities/macros.hpp" @@ -38,6 +39,42 @@ // (see oopHierarchy for complete oop class hierarchy) // // no virtual functions allowed +// +// oopDesc::_mark - the "oop mark word" encoding to be found separately in markOop.hpp +// +// oopDesc::_metadata - encodes both the object's klass pointer and potentially +// "storage properties" (currently confined to arrays in the form of +// ArrayStorageProperties). Storage properties are peculiar to the +// *instance*, and not necessarily the "type". +// +// The overall size of the _metadata field is dependent on "UseCompressedClassPointers", +// hence the terms "narrow" (32 bits) vs "wide" (64 bits). +// +// "Wide" encoding of _metadata: +// bit number |63 0| +// bit length |--3|-----61-----| +// -------------------------------------- +// _klass [xxx| Klass* ] +// _wide_storage_props [ sp| ] +// -------------------------------------- +// with: +// xxx = klass_mask(), Klass* = Klass pointer to be masked +// sp = storage properties, bit number: wide_storage_props_shift +// +// "Narrow" encoding of _metadata: +// bit number |31 0| +// bit length |--3|-----29-----| +// ---------------------------------------- +// _compressed_klass [xxx| narrowKlass] +// _narrow_storage_props [ sp| ] +// ---------------------------------------- +// with: +// xxx = compressed_klass_mask(), narrowKlass = compressed Klass pointer to be masked +// narrowKlass may be further decoded (Klass::decode_klass()) to produce Klass* +// sp = storage properties, bit number: narrow_storage_props_shift +// +// Storage properties encodings are current confined to arrayStorageProperties.hpp + extern bool always_do_update_barrier; @@ -59,6 +96,8 @@ union _metadata { Klass* _klass; narrowKlass _compressed_klass; + uintptr_t _wide_storage_props; + uint32_t _narrow_storage_props; } _metadata; public: @@ -79,6 +118,17 @@ inline void init_mark(); inline void init_mark_raw(); + enum { + storage_props_nof_bits = LogKlassAlignmentInBytes, // This alignment gives us some "free bits" + narrow_storage_props_shift = (sizeof(narrowKlass) << 3) - storage_props_nof_bits, + wide_storage_props_shift = (sizeof(Klass*) << 3) - storage_props_nof_bits, + }; + + static inline narrowKlass compressed_klass_mask(); + static inline narrowKlass compressed_klass_masked(narrowKlass raw); + static inline uintptr_t klass_mask(); + static inline Klass* klass_masked(uintptr_t raw); + inline Klass* klass() const; inline Klass* klass_or_null() const volatile; inline Klass* klass_or_null_acquire() const volatile; @@ -90,6 +140,13 @@ inline void set_klass(Klass* k); static inline void release_set_klass(HeapWord* mem, Klass* klass); + // Extra container metadata specific to arrays (encoded into high bits of _metadata) + static inline uintptr_t* wide_metadata_addr(HeapWord* mem); + inline ArrayStorageProperties array_storage_properties() const; + inline void set_metadata(ArrayStorageProperties storage_props, Klass* k); + static inline void release_set_metadata(HeapWord* mem, ArrayStorageProperties storage_props, Klass* klass); + + // For klass field compression inline int klass_gap() const; inline void set_klass_gap(int z); --- old/src/hotspot/share/oops/oop.inline.hpp 2019-03-27 16:10:43.203249535 +0100 +++ new/src/hotspot/share/oops/oop.inline.hpp 2019-03-27 16:10:42.883244240 +0100 @@ -86,19 +86,26 @@ set_mark_raw(markOopDesc::prototype_for_object(this)); } +narrowKlass oopDesc::compressed_klass_mask() { return ((narrowKlass) 1 << narrow_storage_props_shift) - 1; } +uintptr_t oopDesc::klass_mask() { return ((uintptr_t) 1 << wide_storage_props_shift) - 1; } + +narrowKlass oopDesc::compressed_klass_masked(narrowKlass raw) { return raw & compressed_klass_mask(); } +Klass* oopDesc::klass_masked(uintptr_t raw) { return reinterpret_cast(raw & klass_mask()); } + + Klass* oopDesc::klass() const { if (UseCompressedClassPointers) { - return Klass::decode_klass_not_null(_metadata._compressed_klass); + return Klass::decode_klass_not_null(compressed_klass_masked(_metadata._compressed_klass)); } else { - return _metadata._klass; + return klass_masked(_metadata._wide_storage_props); } } Klass* oopDesc::klass_or_null() const volatile { if (UseCompressedClassPointers) { - return Klass::decode_klass(_metadata._compressed_klass); + return Klass::decode_klass(compressed_klass_masked(_metadata._compressed_klass)); } else { - return _metadata._klass; + return klass_masked(_metadata._wide_storage_props); } } @@ -107,9 +114,9 @@ // Workaround for non-const load_acquire parameter. const volatile narrowKlass* addr = &_metadata._compressed_klass; volatile narrowKlass* xaddr = const_cast(addr); - return Klass::decode_klass(OrderAccess::load_acquire(xaddr)); + return Klass::decode_klass(compressed_klass_masked(OrderAccess::load_acquire(xaddr))); } else { - return OrderAccess::load_acquire(&_metadata._klass); + return klass_masked(OrderAccess::load_acquire(&_metadata._wide_storage_props)); } } @@ -121,6 +128,12 @@ return (Klass**) (((char*)mem) + in_bytes(offset)); } +uintptr_t* oopDesc::wide_metadata_addr(HeapWord* mem) { + assert(!UseCompressedClassPointers, "only supported with uncompressed klass pointers"); + ByteSize offset = byte_offset_of(oopDesc, _metadata._wide_storage_props); + return (uintptr_t*) (((char*)mem) + in_bytes(offset)); +} + narrowKlass* oopDesc::compressed_klass_addr(HeapWord* mem) { assert(UseCompressedClassPointers, "only called by compressed klass pointers"); ByteSize offset = byte_offset_of(oopDesc, _metadata._compressed_klass); @@ -139,6 +152,8 @@ do { \ assert(Universe::is_bootstrapping() || k != NULL, "NULL Klass"); \ assert(Universe::is_bootstrapping() || k->is_klass(), "not a Klass"); \ + assert(((reinterpret_cast(k) & (~ oopDesc::klass_mask())) == 0), \ + "No room for storage props "); \ } while (0) void oopDesc::set_klass(Klass* k) { @@ -158,10 +173,40 @@ } else { OrderAccess::release_store(klass_addr(mem), klass); } + assert(((oopDesc*)mem)->klass() == klass, "failed oopDesc::klass() encode/decode"); +} + +void oopDesc::set_metadata(ArrayStorageProperties storage_props, Klass* klass) { + CHECK_SET_KLASS(klass); + if (UseCompressedClassPointers) { + *compressed_klass_addr() = (Klass::encode_klass_not_null(klass) | storage_props.encode(narrow_storage_props_shift)); + } else { + *wide_metadata_addr((HeapWord*)this) = (reinterpret_cast(klass) | storage_props.encode(wide_storage_props_shift)); + } } +void oopDesc::release_set_metadata(HeapWord* mem, ArrayStorageProperties storage_props, Klass* klass) { + CHECK_SET_KLASS(klass); + if (UseCompressedClassPointers) { + OrderAccess::release_store(oopDesc::compressed_klass_addr(mem), + Klass::encode_klass_not_null(klass) | storage_props.encode(narrow_storage_props_shift)); + } else { + OrderAccess::release_store(oopDesc::wide_metadata_addr(mem), + (reinterpret_cast(klass) | storage_props.encode(wide_storage_props_shift))); + } +} #undef CHECK_SET_KLASS + +ArrayStorageProperties oopDesc::array_storage_properties() const { + if (UseCompressedClassPointers) { + return ArrayStorageProperties(_metadata._narrow_storage_props >> narrow_storage_props_shift); + } else { + return ArrayStorageProperties(_metadata._wide_storage_props >> wide_storage_props_shift); + } +} + + int oopDesc::klass_gap() const { return *(int*)(((intptr_t)this) + klass_gap_offset_in_bytes()); } --- old/src/hotspot/share/oops/symbol.cpp 2019-03-27 16:10:43.975262309 +0100 +++ new/src/hotspot/share/oops/symbol.cpp 2019-03-27 16:10:43.631256617 +0100 @@ -91,6 +91,23 @@ return utf8_length() > 2 && char_at(0) == 'Q' && char_at(utf8_length() - 1) == ';'; } +bool Symbol::is_Q_array_signature() const { + int l = utf8_length(); + if (l < 2 || char_at(0) != '[' || char_at(l - 1) != ';') { + return false; + } + for (int i = 1; i < (l - 2); i++) { + char c = char_at(i); + if (c == 'Q') { + return true; + } + if (c != '[') { + return false; + } + } + return false; +} + Symbol* Symbol::fundamental_name(TRAPS) { if ((char_at(0) == 'Q' || char_at(0) == 'L') && char_at(utf8_length() - 1) == ';') { return SymbolTable::lookup(this, 1, utf8_length() - 1, CHECK_NULL); --- old/src/hotspot/share/oops/symbol.hpp 2019-03-27 16:10:44.659273627 +0100 +++ new/src/hotspot/share/oops/symbol.hpp 2019-03-27 16:10:44.359268663 +0100 @@ -203,6 +203,7 @@ return starts_with(prefix, (int) strlen(prefix)); } bool is_Q_signature() const; + bool is_Q_array_signature() const; Symbol* fundamental_name(TRAPS); bool is_same_fundamental_type(Symbol*) const; --- old/src/hotspot/share/oops/typeArrayKlass.cpp 2019-03-27 16:10:45.351285077 +0100 +++ new/src/hotspot/share/oops/typeArrayKlass.cpp 2019-03-27 16:10:45.043279981 +0100 @@ -95,7 +95,6 @@ } oop TypeArrayKlass::multi_allocate(int rank, jint* last_size, TRAPS) { - // For typeArrays this is only called for the last dimension assert(rank == 1, "just checking"); int length = *last_size; return allocate(length, THREAD); @@ -171,7 +170,8 @@ } // create a klass of array holding typeArrays -Klass* TypeArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) { +Klass* TypeArrayKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS) { + assert(storage_props.is_empty(), "Didn't expect storage properties"); int dim = dimension(); assert(dim <= n, "check order of chain"); if (dim == n) @@ -189,7 +189,7 @@ if (higher_dimension() == NULL) { Klass* oak = ObjArrayKlass::allocate_objArray_klass( - class_loader_data(), dim + 1, this, CHECK_NULL); + ArrayStorageProperties::empty, dim + 1, this, CHECK_NULL); ObjArrayKlass* h_ak = ObjArrayKlass::cast(oak); h_ak->set_lower_dimension(this); // use 'release' to pair with lock-free load @@ -202,13 +202,13 @@ } ObjArrayKlass* h_ak = ObjArrayKlass::cast(higher_dimension()); if (or_null) { - return h_ak->array_klass_or_null(n); + return h_ak->array_klass_or_null(storage_props, n); } - return h_ak->array_klass(n, THREAD); + return h_ak->array_klass(storage_props, n, THREAD); } -Klass* TypeArrayKlass::array_klass_impl(bool or_null, TRAPS) { - return array_klass_impl(or_null, dimension() + 1, THREAD); +Klass* TypeArrayKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS) { + return array_klass_impl(storage_props, or_null, dimension() + 1, THREAD); } int TypeArrayKlass::oop_size(oop obj) const { --- old/src/hotspot/share/oops/typeArrayKlass.hpp 2019-03-27 16:10:45.999295799 +0100 +++ new/src/hotspot/share/oops/typeArrayKlass.hpp 2019-03-27 16:10:45.699290835 +0100 @@ -94,10 +94,10 @@ protected: // Find n'th dimensional array - virtual Klass* array_klass_impl(bool or_null, int n, TRAPS); + virtual Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS); // Returns the array class with this class as element type - virtual Klass* array_klass_impl(bool or_null, TRAPS); + virtual Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS); public: static TypeArrayKlass* cast(Klass* k) { --- old/src/hotspot/share/oops/valueArrayKlass.cpp 2019-03-27 16:10:46.683307117 +0100 +++ new/src/hotspot/share/oops/valueArrayKlass.cpp 2019-03-27 16:10:46.379302086 +0100 @@ -99,14 +99,14 @@ Klass* element_super = element_klass->super(); if (element_super != NULL) { // The element type has a direct super. E.g., String[] has direct super of Object[]. - super_klass = element_super->array_klass_or_null(); + super_klass = element_super->array_klass_or_null(ArrayStorageProperties::empty); bool supers_exist = super_klass != NULL; // Also, see if the element has secondary supertypes. // We need an array type for each. Array* element_supers = element_klass->secondary_supers(); for( int i = element_supers->length()-1; i >= 0; i-- ) { Klass* elem_super = element_supers->at(i); - if (elem_super->array_klass_or_null() == NULL) { + if (elem_super->array_klass_or_null(ArrayStorageProperties::empty) == NULL) { supers_exist = false; break; } @@ -122,7 +122,7 @@ elem_super->array_klass(CHECK_0); } // Now retry from the beginning - ek = element_klass->array_klass(1, CHECK_0); + ek = element_klass->array_klass(ArrayStorageProperties::flattened_and_null_free, 1, CHECK_0); } // re-lock return ValueArrayKlass::cast(ek); } @@ -146,8 +146,9 @@ return vak; } -ValueArrayKlass* ValueArrayKlass::allocate_klass(Klass* element_klass, TRAPS) { - Symbol* name = ArrayKlass::create_element_klass_array_name(element_klass, CHECK_NULL); +ValueArrayKlass* ValueArrayKlass::allocate_klass(ArrayStorageProperties storage_props, Klass* element_klass, TRAPS) { + assert(storage_props.is_flattened(), "Expected flat storage"); + Symbol* name = ArrayKlass::create_element_klass_array_name(true, element_klass, CHECK_NULL); return allocate_klass(element_klass, name, THREAD); } @@ -321,8 +322,8 @@ } -Klass* ValueArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) { - +Klass* ValueArrayKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS) { + assert(storage_props.is_flattened() || n > 1, "Expected flat storage"); assert(dimension() <= n, "check order of chain"); int dim = dimension(); if (dim == n) return this; @@ -341,7 +342,7 @@ // Create multi-dim klass object and link them together Klass* k = - ObjArrayKlass::allocate_objArray_klass(class_loader_data(), dim + 1, this, CHECK_NULL); + ObjArrayKlass::allocate_objArray_klass(storage_props, dim + 1, this, CHECK_NULL); ObjArrayKlass* ak = ObjArrayKlass::cast(k); ak->set_lower_dimension(this); OrderAccess::storestore(); @@ -355,13 +356,13 @@ ObjArrayKlass *ak = ObjArrayKlass::cast(higher_dimension()); if (or_null) { - return ak->array_klass_or_null(n); + return ak->array_klass_or_null(storage_props, n); } - return ak->array_klass(n, THREAD); + return ak->array_klass(storage_props, n, THREAD); } -Klass* ValueArrayKlass::array_klass_impl(bool or_null, TRAPS) { - return array_klass_impl(or_null, dimension() + 1, THREAD); +Klass* ValueArrayKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS) { + return array_klass_impl(storage_props, or_null, dimension() + 1, THREAD); } ModuleEntry* ValueArrayKlass::module() const { @@ -396,7 +397,7 @@ secondaries->push(SystemDictionary::Serializable_klass()); for (int i = 0; i < num_elem_supers; i++) { Klass* elem_super = (Klass*) elem_supers->at(i); - Klass* array_super = elem_super->array_klass_or_null(); + Klass* array_super = elem_super->array_klass_or_null(ArrayStorageProperties::empty); assert(array_super != NULL, "must already have been created"); secondaries->push(array_super); } --- old/src/hotspot/share/oops/valueArrayKlass.hpp 2019-03-27 16:10:47.455319890 +0100 +++ new/src/hotspot/share/oops/valueArrayKlass.hpp 2019-03-27 16:10:47.111314198 +0100 @@ -46,15 +46,18 @@ static ValueArrayKlass* allocate_klass(Klass* element_klass, Symbol* name, TRAPS); protected: // Returns the ArrayKlass for n'th dimension. - Klass* array_klass_impl(bool or_null, int n, TRAPS); + Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS); // Returns the array class with this class as element type. - Klass* array_klass_impl(bool or_null, TRAPS); + Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS); public: ValueArrayKlass() {} + // Properties of an LWorld (LW2) flattened array + ArrayStorageProperties storage_properties() { return ArrayStorageProperties::flattened_and_null_free; } + virtual ValueKlass* element_klass() const; virtual void set_element_klass(Klass* k); @@ -65,7 +68,7 @@ } // klass allocation - static ValueArrayKlass* allocate_klass(Klass* element_klass, TRAPS); + static ValueArrayKlass* allocate_klass(ArrayStorageProperties storage_props, Klass* element_klass, TRAPS); void initialize(TRAPS); --- old/src/hotspot/share/oops/valueKlass.cpp 2019-03-27 16:10:48.131331075 +0100 +++ new/src/hotspot/share/oops/valueKlass.cpp 2019-03-27 16:10:47.835326177 +0100 @@ -32,7 +32,7 @@ #include "oops/access.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/fieldStreams.hpp" -#include "oops/instanceKlass.hpp" +#include "oops/instanceKlass.inline.hpp" #include "oops/method.hpp" #include "oops/oop.inline.hpp" #include "oops/objArrayKlass.hpp" @@ -143,43 +143,54 @@ } -Klass* ValueKlass::array_klass_impl(bool or_null, int n, TRAPS) { - if (!flatten_array()) { - return InstanceKlass::array_klass_impl(or_null, n, THREAD); +Klass* ValueKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS) { + if (storage_props.is_flattened()) { + return value_array_klass(storage_props, or_null, n, THREAD); + } else { + return InstanceKlass::array_klass_impl(storage_props, or_null, n, THREAD); } +} - // Basically the same as instanceKlass, but using "ValueArrayKlass::allocate_klass" - if (array_klasses() == NULL) { - if (or_null) return NULL; +Klass* ValueKlass::array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS) { + return array_klass_impl(storage_props, or_null, 1, THREAD); +} +Klass* ValueKlass::value_array_klass(ArrayStorageProperties storage_props, bool or_null, int rank, TRAPS) { + Klass* vak = acquire_value_array_klass(); + if (vak == NULL) { + if (or_null) return NULL; ResourceMark rm; JavaThread *jt = (JavaThread *)THREAD; { // Atomic creation of array_klasses MutexLocker ma(MultiArray_lock, THREAD); - - // Check if update has already taken place - if (array_klasses() == NULL) { - Klass* ak; - if (is_atomic() || (!ValueArrayAtomicAccess)) { - ak = ValueArrayKlass::allocate_klass(this, CHECK_NULL); - } else { - ak = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), 1, this, CHECK_NULL); - } - set_array_klasses(ak); + if (get_value_array_klass() == NULL) { + vak = allocate_value_array_klass(CHECK_NULL); + OrderAccess::release_store((Klass**)adr_value_array_klass(), vak); } } } - // _this will always be set at this point - ArrayKlass* ak = ArrayKlass::cast(array_klasses()); + + if (!vak->is_valueArray_klass()) { + storage_props.clear_flattened(); + } if (or_null) { - return ak->array_klass_or_null(n); + return vak->array_klass_or_null(storage_props, rank); + } + return vak->array_klass(storage_props, rank, THREAD); +} + +Klass* ValueKlass::allocate_value_array_klass(TRAPS) { + if (flatten_array() && (is_atomic() || (!ValueArrayAtomicAccess))) { + return ValueArrayKlass::allocate_klass(ArrayStorageProperties::flattened_and_null_free, this, THREAD); } - return ak->array_klass(n, THREAD); + return ObjArrayKlass::allocate_objArray_klass(ArrayStorageProperties::null_free, 1, this, THREAD); } -Klass* ValueKlass::array_klass_impl(bool or_null, TRAPS) { - return array_klass_impl(or_null, 1, THREAD); +void ValueKlass::array_klasses_do(void f(Klass* k)) { + InstanceKlass::array_klasses_do(f); + if (get_value_array_klass() != NULL) + ArrayKlass::cast(get_value_array_klass())->array_klasses_do(f); } void ValueKlass::raw_field_copy(void* src, void* dst, size_t raw_byte_size) { --- old/src/hotspot/share/oops/valueKlass.hpp 2019-03-27 16:10:48.823342526 +0100 +++ new/src/hotspot/share/oops/valueKlass.hpp 2019-03-27 16:10:48.523337561 +0100 @@ -50,6 +50,7 @@ *((address*)adr_unpack_handler()) = NULL; assert(pack_handler() == NULL, "pack handler not null"); *((int*)adr_default_value_offset()) = 0; + *((Klass**)adr_value_array_klass()) = NULL; set_prototype_header(markOopDesc::always_locked_prototype()); } @@ -111,16 +112,34 @@ return ((address)_adr_valueklass_fixed_block) + in_bytes(default_value_offset_offset()); } + address adr_value_array_klass() const { + assert(_adr_valueklass_fixed_block != NULL, "Should have been initialized"); + return ((address)_adr_valueklass_fixed_block) + in_bytes(byte_offset_of(ValueKlassFixedBlock, _value_array_klass)); + } + + Klass* get_value_array_klass() const { + return *(Klass**)adr_value_array_klass(); + } + + Klass* acquire_value_array_klass() const { + return OrderAccess::load_acquire((Klass**)adr_value_array_klass()); + } + + Klass* allocate_value_array_klass(TRAPS); + int collect_fields(GrowableArray* sig, int base_off = 0) const; void cleanup_blobs(); protected: // Returns the array class for the n'th dimension - Klass* array_klass_impl(bool or_null, int n, TRAPS); + Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, int n, TRAPS); // Returns the array class with this class as element type - Klass* array_klass_impl(bool or_null, TRAPS); + Klass* array_klass_impl(ArrayStorageProperties storage_props, bool or_null, TRAPS); + + // Specifically flat array klass + Klass* value_array_klass(ArrayStorageProperties storage_props, bool or_null, int rank, TRAPS); public: // Type testing @@ -143,6 +162,9 @@ return layout_helper_to_size_helper(layout_helper()); } + // Metadata iterators + void array_klasses_do(void f(Klass* k)); + // allocate_instance() allocates a stand alone value in the Java heap instanceOop allocate_instance(TRAPS); @@ -182,7 +204,6 @@ void value_store(void* src, void* dst, size_t raw_byte_size, bool dst_is_heap, bool dst_uninitialized); // GC support... - void iterate_over_inside_oops(OopClosure* f, oop value); // oop iterate raw value type data pointer (where oop_addr may not be an oop, but backing/array-element) --- old/src/hotspot/share/opto/runtime.cpp 2019-03-27 16:10:49.507353843 +0100 +++ new/src/hotspot/share/opto/runtime.cpp 2019-03-27 16:10:49.207348879 +0100 @@ -258,7 +258,7 @@ // that latter value in hand for the fast path. Handle holder(THREAD, array_type->klass_holder()); // keep the array klass alive Klass* elem_type = ObjArrayKlass::cast(array_type)->element_klass(); - result = oopFactory::new_array(elem_type, len, THREAD); + result = ObjArrayKlass::cast(array_type)->allocate(len, THREAD); } // Pass oops back through thread local storage. Our apparent type to Java --- old/src/hotspot/share/prims/jvmtiGetLoadedClasses.cpp 2019-03-27 16:10:50.219365624 +0100 +++ new/src/hotspot/share/prims/jvmtiGetLoadedClasses.cpp 2019-03-27 16:10:49.915360594 +0100 @@ -22,6 +22,7 @@ * */ +#include #include "precompiled.hpp" #include "classfile/classLoaderDataGraph.hpp" #include "classfile/dictionary.hpp" @@ -74,9 +75,10 @@ if (_dictionary_walk) { // Collect array classes this way when walking the dictionary (because array classes are // not in the dictionary). - for (Klass* l = k->array_klass_or_null(); l != NULL; l = l->array_klass_or_null()) { + for (Klass* l = k->array_klass_or_null(ArrayStorageProperties::empty); l != NULL; l = l->array_klass_or_null(ArrayStorageProperties::empty)) { _classStack.push((jclass) _env->jni_reference(Handle(_cur_thread, l->java_mirror()))); } + // CMH flat arrays (ValueKlass) } } --- old/src/hotspot/share/runtime/fieldType.cpp 2019-03-27 16:10:50.911377074 +0100 +++ new/src/hotspot/share/runtime/fieldType.cpp 2019-03-27 16:10:50.611372110 +0100 @@ -85,5 +85,10 @@ } // Pass dimension back to caller fd._dimension = dim; + fd._storage_props = get_array_storage_properties(signature); return element_type; } + +ArrayStorageProperties FieldType::get_array_storage_properties(Symbol* signature) { + return (signature->is_Q_array_signature() || signature->is_Q_signature()) ? ArrayStorageProperties::flattened_and_null_free : ArrayStorageProperties::empty; +} --- old/src/hotspot/share/runtime/fieldType.hpp 2019-03-27 16:10:51.627388921 +0100 +++ new/src/hotspot/share/runtime/fieldType.hpp 2019-03-27 16:10:51.323383891 +0100 @@ -26,6 +26,7 @@ #define SHARE_RUNTIME_FIELDTYPE_HPP #include "memory/allocation.hpp" +#include "oops/arrayStorageProperties.hpp" #include "oops/symbol.hpp" // Note: FieldType should be based on the SignatureIterator (or vice versa). @@ -39,11 +40,13 @@ friend class FieldType; // field type can set these fields. int _dimension; Symbol* _object_key; + ArrayStorageProperties _storage_props; public: int dimension() { return _dimension; } Symbol* object_key() { return _object_key; } + ArrayStorageProperties storage_props() { return _storage_props; } // basic constructor - FieldArrayInfo() : _dimension(0), _object_key(NULL) {} + FieldArrayInfo() : _dimension(0), _object_key(NULL), _storage_props(ArrayStorageProperties::empty) {} // destructor decrements object key's refcount if created ~FieldArrayInfo() { if (_object_key != NULL) _object_key->decrement_refcount(); } }; @@ -70,10 +73,13 @@ static bool is_valuetype(Symbol* signature) { return signature->is_Q_signature(); - } + } // Parse field and extract array information. Works for T_ARRAY only. static BasicType get_array_info(Symbol* signature, FieldArrayInfo& ai, TRAPS); + + // LWorld (LW2) Q-type array descriptors require null-free, and preferably flat if possible + static ArrayStorageProperties get_array_storage_properties(Symbol* signature); }; #endif // SHARE_RUNTIME_FIELDTYPE_HPP --- old/src/hotspot/share/runtime/reflection.cpp 2019-03-27 16:10:52.339400702 +0100 +++ new/src/hotspot/share/runtime/reflection.cpp 2019-03-27 16:10:52.035395672 +0100 @@ -344,7 +344,11 @@ if (k->is_array_klass() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) { THROW_0(vmSymbols::java_lang_IllegalArgumentException()); } - return oopFactory::new_array(k, length, THREAD); + if (java_lang_Class::is_box_type(element_mirror)) { + return oopFactory::new_objArray(k, length, THREAD); + } else { + return oopFactory::new_valueArray(k, length, THREAD); + } } } @@ -385,7 +389,8 @@ dim += k_dim; } } - klass = klass->array_klass(dim, CHECK_NULL); + ArrayStorageProperties storage_props = FieldType::get_array_storage_properties(klass->name()); + klass = klass->array_klass(storage_props, dim, CHECK_NULL); oop obj = ArrayKlass::cast(klass)->multi_allocate(len, dimensions, CHECK_NULL); assert(obj->is_array(), "just checking"); return arrayOop(obj); --- old/src/hotspot/share/services/heapDumper.cpp 2019-03-27 16:10:52.987411424 +0100 +++ new/src/hotspot/share/services/heapDumper.cpp 2019-03-27 16:10:52.691406526 +0100 @@ -1020,7 +1020,7 @@ dump_instance_field_descriptors(writer, k); // array classes - k = k->array_klass_or_null(); + k = k->array_klass_or_null(ArrayStorageProperties::empty); while (k != NULL) { Klass* klass = k; assert(klass->is_objArray_klass(), "not an ObjArrayKlass"); @@ -1046,7 +1046,7 @@ writer->write_u2(0); // instance fields // get the array class for the next rank - k = klass->array_klass_or_null(); + k = klass->array_klass_or_null(ArrayStorageProperties::empty); } } @@ -1078,7 +1078,7 @@ writer->write_u2(0); // instance fields // get the array class for the next rank - k = klass->array_klass_or_null(); + k = klass->array_klass_or_null(ArrayStorageProperties::empty); } } @@ -1633,7 +1633,7 @@ writer()->write_symbolID(name); // write a LOAD_CLASS record for the array type (if it exists) - k = klass->array_klass_or_null(); + k = klass->array_klass_or_null(ArrayStorageProperties::empty); } while (k != NULL); } --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/ValueTypeArray.java 2019-03-27 16:10:53.859425851 +0100 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/ValueTypeArray.java 2019-03-27 16:10:53.563420954 +0100 @@ -58,24 +58,37 @@ testObjectArrayOfValues(); testReflectArray(); - // testUtilArrays(); + testUtilArrays(); } void testClassForName() { String arrayClsName = "[Lruntime.valhalla.valuetypes.Point;"; + String qarrayClsName = "[Qruntime.valhalla.valuetypes.Point;"; try { + // L-type.. Class arrayCls = Class.forName(arrayClsName); assertTrue(arrayCls.isArray(), "Expected an array class"); - // array-of-L-type not supported yet - // the component type of a flattened value array is of the value type - // the component type of a non-flattened array is of the box type - assertTrue(arrayCls.getComponentType().asBoxType() == Point.class, + + assertTrue(arrayCls.getComponentType() == Point.class.asBoxType(), "Expected component type of Point.class got: " + arrayCls.getComponentType()); arrayClsName = "[" + arrayClsName; Class mulArrayCls = Class.forName(arrayClsName); assertTrue(mulArrayCls.isArray()); assertTrue(mulArrayCls.getComponentType() == arrayCls); + + // Q-type... + arrayCls = Class.forName(qarrayClsName); + assertTrue(arrayCls.isArray(), "Expected an array class"); + + assertTrue(arrayCls.getComponentType() == Point.class.asValueType(), + arrayCls + + " Expected component type of Point.class got: " + arrayCls.getComponentType()); + + qarrayClsName = "[" + qarrayClsName; + mulArrayCls = Class.forName(qarrayClsName); + assertTrue(mulArrayCls.isArray()); + assertTrue(mulArrayCls.getComponentType() == arrayCls); } catch (ClassNotFoundException cnfe) { fail("Class.forName(" + arrayClsName + ") failed", cnfe); @@ -187,13 +200,14 @@ assertTrue(array3[0][0] == null, "Expected NULL"); // Now create ObjArrays of ValueArray... - cls = (Class) Point.class; - array = (Point[][]) Array.newInstance(cls, 1, 2); - assertEquals(array.length, 1, "Incorrect length"); - assertEquals(array[0].length, 2, "Incorrect length"); - Point p = array[0][1]; - int x = p.x; - assertEquals(x, 0, "Bad Point Value"); + cls = (Class) Point.class.asBoxType(); + Point.box[][] barray = (Point.box[][]) Array.newInstance(cls, 1, 2); + assertEquals(barray.length, 1, "Incorrect length"); + assertEquals(barray[0].length, 2, "Incorrect length"); + barray[0][1] = Point.createPoint(1, 2); + Point.box pb = barray[0][1]; + int x = pb.getX(); + assertEquals(x, 1, "Bad Point Value"); } static final value class MyInt implements Comparable { @@ -221,6 +235,10 @@ public static final MyInt.box MAX = MyInt.create(Integer.MAX_VALUE); } + static MyInt staticMyInt = MyInt.create(-1); + static MyInt[] staticMyIntArray = new MyInt[] { staticMyInt }; + static MyInt[][] staticMyIntArrayArray = new MyInt[][] { staticMyIntArray, staticMyIntArray }; + static interface SomeSecondaryType { default String hi() { return "Hi"; } } @@ -231,14 +249,13 @@ } void testSanityCheckcasts() { -// TODO Re-enable if value type arrays become covariant with object arrays -/* - MyInt[] myInts = new MyInt[1]; assertTrue(myInts instanceof Object[]); assertTrue(myInts instanceof Comparable[]); - Object arrObj = Array.newInstance(MyInt.class, 1); + Class cls = MyInt.class.asValueType(); + assertTrue(cls.isValue()); + Object arrObj = Array.newInstance(cls, 1); assertTrue(arrObj instanceof Object[], "Not Object array"); assertTrue(arrObj instanceof Comparable[], "Not Comparable array"); assertTrue(arrObj instanceof MyInt[], "Not MyInt array"); @@ -253,13 +270,15 @@ MyOtherInt[][] matrix = new MyOtherInt[1][1]; assertTrue(matrix[0] instanceof MyOtherInt[]); assertTrue(matrix[0] instanceof SomeSecondaryType[]); -*/ + + // Box types vs Value... + MyInt.box[] myValueRefs = new MyInt.box[1]; + assertTrue(myValueRefs instanceof MyInt.box[]); + assertTrue(myValueRefs instanceof Object[]); + assertTrue(myValueRefs instanceof Comparable[]); } -/* - * Comment out this test because value type arrays are not assignable to the array - * parameter types used by the methods in class java.util.Arrays. - * + void testUtilArrays() { // Sanity check j.u.Arrays MyInt[] myInts = new MyInt[] { MyInt.MAX, MyInt.MIN }; @@ -283,16 +302,8 @@ aList.remove(2); aList.add(MyInt.create(5)); - - // Interesting: - //aList.add((MyInt)getNull()); - - // javac currently generating "java/util/Objects.requireNonNull - // should checkcast treat null against Value class as CCE ? - // Then in the end, ArrayList.elementData is Object[], (that's why remove works) - // why can't I write add(null) then ? } -*/ + void testObjectArrayOfValues() { testSanityObjectArrays(); @@ -330,6 +341,10 @@ comparables[0] = null; comparables[1] = null; assertTrue(comparables[0] == null && comparables[1] == null, "Not null ?"); + + MyInt.box[] myIntRefArray = new MyInt.box[1]; + assertTrue(myIntRefArray[0] == null, "Got: " + myIntRefArray[0]); + myIntRefArray[0] = null; } void testMixedLayoutArrays() { @@ -365,6 +380,16 @@ System.arraycopy(objArray, 0, valArray, 0, 3); throw new RuntimeException("Expected ArrayStoreException"); } catch (ArrayStoreException ase) {} + + MyInt.box[] myIntRefArray = new MyInt.box[3]; + System.arraycopy(valArray, 0, myIntRefArray, 0, 3); + checkArrayElementsEqual(valArray, myIntRefArray); + + myIntRefArray[0] = null; + try { + System.arraycopy(myIntRefArray, 0, valArray, 0, 3); + throw new RuntimeException("Expected NullPointerException"); + } catch (NullPointerException npe) {} } static final value class MyPoint { --- /dev/null 2019-03-11 09:54:37.645799180 +0100 +++ new/src/hotspot/share/oops/arrayStorageProperties.cpp 2019-03-27 16:10:54.459435779 +0100 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019, 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. + * + */ + +#include "oops/arrayStorageProperties.hpp" + +#include "oops/oop.hpp" +#include "utilities/debug.hpp" + +STATIC_ASSERT((int)ArrayStorageProperties::nof_oop_properties <= (int)oopDesc::storage_props_nof_bits); + +const ArrayStorageProperties ArrayStorageProperties::empty = ArrayStorageProperties(empty_value); +const ArrayStorageProperties ArrayStorageProperties::flattened = ArrayStorageProperties(flattened_value); +const ArrayStorageProperties ArrayStorageProperties::null_free = ArrayStorageProperties(null_free_value); +const ArrayStorageProperties ArrayStorageProperties::flattened_and_null_free = + ArrayStorageProperties(flattened_value | null_free_value); + --- /dev/null 2019-03-11 09:54:37.645799180 +0100 +++ new/src/hotspot/share/oops/arrayStorageProperties.hpp 2019-03-27 16:10:55.123446766 +0100 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, 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. + * + */ + +#ifndef SHARE_OOPS_ARRAYSTORAGEPROPERTIES_HPP +#define SHARE_OOPS_ARRAYSTORAGEPROPERTIES_HPP + +#include "runtime/globals.hpp" + +class ArrayStorageProperties { + private: + uint8_t _flags; + + void clear_flags_bits(uint8_t value) { _flags &= (~value); } + void set_flags_bits(uint8_t value) { _flags |= value; } + bool test_flags_bit(int idx) const { return (_flags & (1 << idx)) != 0; } + public: + + enum { + empty_value = 0, + flattened_bit = 0, + flattened_value = 1 << flattened_bit, + null_free_bit = flattened_bit + 1, + null_free_value = 1 << null_free_bit, + nof_oop_properties = null_free_bit + 1, + }; + + ArrayStorageProperties() : _flags(empty_value) {}; + ArrayStorageProperties(uint8_t flags): _flags(flags) {}; + + bool is_empty() const { return _flags == empty_value; } + + void clear_flattened() { clear_flags_bits(flattened_value); } + + bool is_flattened() const { return test_flags_bit(flattened_bit); } + void set_flattened() { set_flags_bits(flattened_value); } + + bool is_null_free() const { return test_flags_bit(null_free_bit); } + void set_null_free() { set_flags_bits(null_free_value); } + + uint8_t value() const { return _flags; } + template T encode(int shift) const { return static_cast(_flags) << shift; } + + // Well-known constants... + static const ArrayStorageProperties empty; + static const ArrayStorageProperties flattened; + static const ArrayStorageProperties null_free; + static const ArrayStorageProperties flattened_and_null_free; +}; + + +#endif //SHARE_OOPS_ARRAYSTORAGEPROPERTIES_HPP