--- old/src/share/vm/classfile/classFileParser.cpp 2017-09-14 10:45:43.000000000 -0400 +++ new/src/share/vm/classfile/classFileParser.cpp 2017-09-14 10:45:43.000000000 -0400 @@ -3961,6 +3961,7 @@ int static_value_type_count = 0; int nonstatic_value_type_count = 0; + int nonstatic_nonflattened_value_types_count = 0; int* nonstatic_value_type_indexes = NULL; Klass** nonstatic_value_type_klasses = NULL; unsigned int value_type_oop_map_count = 0; @@ -3985,13 +3986,23 @@ _protection_domain, true, CHECK); assert(klass != NULL, "Sanity check"); assert(klass->access_flags().is_value_type(), "Value type expected"); - nonstatic_value_type_indexes[nonstatic_value_type_count] = fs.index(); - nonstatic_value_type_klasses[nonstatic_value_type_count] = klass; - nonstatic_value_type_count++; - - ValueKlass* vklass = ValueKlass::cast(klass); - if (vklass->contains_oops()) { - value_type_oop_map_count += vklass->nonstatic_oop_map_count(); + ValueKlass* vk = ValueKlass::cast(klass); + // Conditions to apply flattening or not should be defined + //in a single place + if (vk->size_helper() <= ValueArrayElemMaxFlatSize) { + nonstatic_value_type_indexes[nonstatic_value_type_count] = fs.index(); + nonstatic_value_type_klasses[nonstatic_value_type_count] = klass; + nonstatic_value_type_count++; + + ValueKlass* vklass = ValueKlass::cast(klass); + if (vklass->contains_oops()) { + value_type_oop_map_count += vklass->nonstatic_oop_map_count(); + } + fs.set_flattening(true); + } else { + nonstatic_nonflattened_value_types_count++; + value_type_oop_map_count++; + fs.set_flattening(false); } } } @@ -4075,7 +4086,7 @@ // Fields order: oops, longs/doubles, ints, shorts/chars, bytes, padded fields next_nonstatic_oop_offset = next_nonstatic_field_offset; next_nonstatic_double_offset = next_nonstatic_oop_offset + - (nonstatic_oop_count * heapOopSize); + ((nonstatic_oop_count + nonstatic_nonflattened_value_types_count) * heapOopSize); } else if( allocation_style == 1 ) { // Fields order: longs/doubles, ints, shorts/chars, bytes, oops, padded fields next_nonstatic_double_offset = next_nonstatic_field_offset; @@ -4086,7 +4097,7 @@ allocation_style = 0; // allocate oops first next_nonstatic_oop_offset = next_nonstatic_field_offset; next_nonstatic_double_offset = next_nonstatic_oop_offset + - (nonstatic_oop_count * heapOopSize); + ((nonstatic_oop_count + nonstatic_nonflattened_value_types_count)* heapOopSize); } } if( allocation_style == 2 ) { @@ -4159,10 +4170,11 @@ // let oops jump before padding with this allocation style if( allocation_style == 1 ) { next_nonstatic_oop_offset = next_nonstatic_padded_offset; - if( nonstatic_oop_count > 0 ) { + if( (nonstatic_oop_count + nonstatic_nonflattened_value_types_count) > 0 ) { next_nonstatic_oop_offset = align_up(next_nonstatic_oop_offset, heapOopSize); } - next_nonstatic_padded_offset = next_nonstatic_oop_offset + (nonstatic_oop_count * heapOopSize); + next_nonstatic_padded_offset = next_nonstatic_oop_offset + + ((nonstatic_oop_count + nonstatic_nonflattened_value_types_count) * heapOopSize); } // Aligning embedded value types @@ -4214,28 +4226,30 @@ next_static_double_offset += BytesPerLong; break; case NONSTATIC_VALUETYPE: - { - Klass* klass = nonstatic_value_type_klasses[next_value_type_index]; - assert(klass != NULL, "Klass should have been loaded and resolved earlier"); - assert(klass->access_flags().is_value_type(),"Must be a value type"); - ValueKlass* vklass = ValueKlass::cast(klass); - real_offset = next_nonstatic_valuetype_offset; - next_nonstatic_valuetype_offset += (vklass->size_helper()) * wordSize - vklass->first_field_offset(); - // aligning next value type on a 64 bits boundary - next_nonstatic_valuetype_offset = align_up(next_nonstatic_valuetype_offset, BytesPerLong); - next_value_type_index += 1; - - if (vklass->contains_oops()) { // add flatten oop maps - int diff = real_offset - vklass->first_field_offset(); - const OopMapBlock* map = vklass->start_of_nonstatic_oop_maps(); - const OopMapBlock* const last_map = map + vklass->nonstatic_oop_map_count(); - while (map < last_map) { - nonstatic_oop_maps->add(map->offset() + diff, map->count()); - map++; + if (fs.is_flatten()) { + Klass* klass = nonstatic_value_type_klasses[next_value_type_index]; + assert(klass != NULL, "Klass should have been loaded and resolved earlier"); + assert(klass->access_flags().is_value_type(),"Must be a value type"); + ValueKlass* vklass = ValueKlass::cast(klass); + real_offset = next_nonstatic_valuetype_offset; + next_nonstatic_valuetype_offset += (vklass->size_helper()) * wordSize - vklass->first_field_offset(); + // aligning next value type on a 64 bits boundary + next_nonstatic_valuetype_offset = align_up(next_nonstatic_valuetype_offset, BytesPerLong); + next_value_type_index += 1; + + if (vklass->contains_oops()) { // add flatten oop maps + int diff = real_offset - vklass->first_field_offset(); + const OopMapBlock* map = vklass->start_of_nonstatic_oop_maps(); + const OopMapBlock* const last_map = map + vklass->nonstatic_oop_map_count(); + while (map < last_map) { + nonstatic_oop_maps->add(map->offset() + diff, map->count()); + map++; + } } + break; + } else { + // Fall through } - } - break; case NONSTATIC_OOP: if( nonstatic_oop_space_count > 0 ) { real_offset = nonstatic_oop_space_offset; @@ -4404,7 +4418,7 @@ } int notaligned_nonstatic_fields_end; - if (nonstatic_value_type_count != 0) { + if ((nonstatic_value_type_count - nonstatic_nonflattened_value_types_count) != 0) { notaligned_nonstatic_fields_end = next_nonstatic_valuetype_offset; } else { notaligned_nonstatic_fields_end = next_nonstatic_padded_offset; --- old/src/share/vm/interpreter/interpreterRuntime.cpp 2017-09-14 10:45:45.000000000 -0400 +++ new/src/share/vm/interpreter/interpreterRuntime.cpp 2017-09-14 10:45:45.000000000 -0400 @@ -266,13 +266,19 @@ assert(aoop == NULL || (oopDesc::is_oop(aoop) && (!aoop->is_value())),"argument must be a reference type"); new_value_h()->obj_field_put(fd.offset(), aoop); } else if (field_type == T_VALUETYPE) { - Klass* field_k = vklass->get_value_field_klass(fd.index()); - ValueKlass* field_vk = ValueKlass::cast(field_k); - oop vt_oop = *(oop*)f.interpreter_frame_expression_stack_at(tos_idx); - assert(vt_oop != NULL && oopDesc::is_oop(vt_oop) && vt_oop->is_value(),"argument must be a value type"); - assert(field_vk == vt_oop->klass(), "Must match"); - field_vk->value_store(field_vk->data_for_oop(vt_oop), - ((char*)(oopDesc*)new_value_h()) + fd.offset(), true, false); + if (fd.is_flatten()) { + Klass* field_k = vklass->get_value_field_klass(fd.index()); + ValueKlass* field_vk = ValueKlass::cast(field_k); + oop vt_oop = *(oop*)f.interpreter_frame_expression_stack_at(tos_idx); + assert(vt_oop != NULL && oopDesc::is_oop(vt_oop) && vt_oop->is_value(),"argument must be a value type"); + assert(field_vk == vt_oop->klass(), "Must match"); + field_vk->value_store(field_vk->data_for_oop(vt_oop), + ((char*)(oopDesc*)new_value_h()) + fd.offset(), true, false); + } else { + oop voop = *(oop*)f.interpreter_frame_expression_stack_at(tos_idx); + assert(voop != NULL || (oopDesc::is_oop(voop) && (voop->is_value())),"argument must be a value type"); + new_value_h()->obj_field_put(fd.offset(), voop); + } } else { intptr_t* addr = f.interpreter_frame_expression_stack_at(tos_idx); copy_primitive_argument(addr, new_value_h, fd.offset(), field_type); @@ -330,9 +336,9 @@ thread->set_vm_result(value); IRT_END -IRT_ENTRY(void, InterpreterRuntime::qgetfield(JavaThread* thread, oopDesc* value, int offset)) - Handle value_h(THREAD, value); - InstanceKlass* klass = InstanceKlass::cast(value->klass()); +IRT_ENTRY(void, InterpreterRuntime::qgetfield(JavaThread* thread, oopDesc* obj, int offset)) + Handle value_h(THREAD, obj); + InstanceKlass* klass = InstanceKlass::cast(obj->klass()); fieldDescriptor fd; klass->find_field_from_offset(offset, false, &fd); @@ -340,14 +346,23 @@ ValueKlass* field_vklass = ValueKlass::cast(field_k); field_vklass->initialize(THREAD); - // allocate instance + instanceOop res; bool in_heap; - instanceOop res = field_vklass->allocate_buffered_or_heap_instance(&in_heap, CHECK); - instanceHandle res_h(THREAD, res); - // copy value - field_vklass->value_store(((char*)(oopDesc*)value_h()) + offset, - field_vklass->data_for_oop(res), in_heap, false); - thread->set_vm_result(res_h()); + if (fd.is_flatten()) { + // allocate instance + res = field_vklass->allocate_buffered_or_heap_instance(&in_heap, CHECK); + instanceHandle res_h(THREAD, res); + // copy value + field_vklass->value_store(((char*)(oopDesc*)value_h()) + offset, + field_vklass->data_for_oop(res), in_heap, false); + thread->set_vm_result(res_h()); + } else { + oop res = value_h()->obj_field_acquire(offset); + if (res == NULL) { + res = field_vklass->allocate_buffered_or_heap_instance(&in_heap, CHECK); + } + thread->set_vm_result(res); + } IRT_END IRT_ENTRY(void, InterpreterRuntime::initialize_static_value_field(JavaThread* thread, oopDesc* mirror, int offset)) @@ -379,9 +394,25 @@ Klass* field_k = klass->get_value_field_klass(fd.index()); ValueKlass* field_vklass = ValueKlass::cast(value->klass()); assert(field_k == field_vklass, "Field descriptor and argument must match"); - // copy value - field_vklass->value_store(field_vklass->data_for_oop(value_h()), - ((char*)(oopDesc*)obj_h()) + offset, true, false); + if (fd.is_flatten()) { + // copy value + field_vklass->value_store(field_vklass->data_for_oop(value_h()), + ((char*)(oopDesc*)obj_h()) + offset, true, false); + } else { + if (Universe::heap()->is_in_reserved(value_h())) { + obj_h()->obj_field_put(offset, value_h()); + } else { + // allocate heap instance + instanceOop val = field_vklass->allocate_instance(CHECK); + instanceHandle res_h(THREAD, val); + // copy value + field_vklass->value_store(field_vklass->data_for_oop(value_h()), + field_vklass->data_for_oop(res_h()), true, false); + + + obj_h()->obj_field_put(offset, res_h()); + } + } IRT_END IRT_ENTRY(void, InterpreterRuntime::qputstatic(JavaThread* thread, oopDesc* value)) @@ -995,6 +1026,7 @@ state, info.access_flags().is_final(), info.access_flags().is_volatile(), + info.is_flatten(), pool->pool_holder() ); } --- old/src/share/vm/oops/cpCache.cpp 2017-09-14 10:45:46.000000000 -0400 +++ new/src/share/vm/oops/cpCache.cpp 2017-09-14 10:45:46.000000000 -0400 @@ -108,6 +108,7 @@ TosState field_type, bool is_final, bool is_volatile, + bool is_flatten, Klass* root_klass) { set_f1(field_holder); set_f2(field_offset); @@ -115,7 +116,8 @@ "field index does not fit in low flag bits"); set_field_flags(field_type, ((is_volatile ? 1 : 0) << is_volatile_shift) | - ((is_final ? 1 : 0) << is_final_shift), + ((is_final ? 1 : 0) << is_final_shift) | + ((is_flatten ? 1 : 0) << is_flatten_field), field_index); set_bytecode_1(get_code); set_bytecode_2(put_code); --- old/src/share/vm/oops/cpCache.hpp 2017-09-14 10:45:48.000000000 -0400 +++ new/src/share/vm/oops/cpCache.hpp 2017-09-14 10:45:47.000000000 -0400 @@ -49,7 +49,7 @@ // _indices [ b2 | b1 | index ] index = constant_pool_index // _f1 [ entry specific ] metadata ptr (method or klass) // _f2 [ entry specific ] vtable or res_ref index, or vfinal method ptr -// _flags [tos|0|F=1|0|0|0|f|v|0 |0000|field_index] (for field entries) +// _flags [tos|0|F=1|0|0|i|f|v|0 |0000|field_index] (for field entries) // bit length [ 4 |1| 1 |1|1|1|1|1|1 |-4--|----16-----] // _flags [tos|0|F=0|M|A|I|f|0|vf|0000|00000|psize] (for method entries) // bit length [ 4 |1| 1 |1|1|1|1|1|1 |-4--|--8--|--8--] @@ -74,6 +74,7 @@ // // The flags after TosState have the following interpretation: // bit 27: 0 for fields, 1 for methods +// i flag true if field is inlined (flatten) // f flag true if field is marked final // v flag true if field is volatile (only for fields) // f2 flag true if f2 contains an oop (e.g., virtual final method) @@ -188,6 +189,7 @@ has_method_type_shift = 25, // (M) does the call site have a MethodType? has_appendix_shift = 24, // (A) does the call site have an appendix argument? is_forced_virtual_shift = 23, // (I) is the interface reference forced to virtual mode? + is_flatten_field = 23, // (i) is the value field flatten? is_final_shift = 22, // (f) is the field or method final? is_volatile_shift = 21, // (v) is the field volatile? is_vfinal_shift = 20, // (vf) did the call resolve to a final method? @@ -226,6 +228,7 @@ TosState field_type, // the (machine) field type bool is_final, // the field is final bool is_volatile, // the field is volatile + bool is_flatten, // the field is flatten (value field) Klass* root_klass // needed by the GC to dirty the klass ); @@ -354,6 +357,7 @@ int parameter_size() const { assert(is_method_entry(), ""); return (_flags & parameter_size_mask); } bool is_volatile() const { return (_flags & (1 << is_volatile_shift)) != 0; } bool is_final() const { return (_flags & (1 << is_final_shift)) != 0; } + bool is_flatten() const { return (_flags & (1 << is_flatten_field)) != 0; } bool is_forced_virtual() const { return (_flags & (1 << is_forced_virtual_shift)) != 0; } bool is_vfinal() const { return (_flags & (1 << is_vfinal_shift)) != 0; } bool has_appendix() const { return (!is_f1_null()) && (_flags & (1 << has_appendix_shift)) != 0; } --- old/src/share/vm/oops/fieldInfo.hpp 2017-09-14 10:45:49.000000000 -0400 +++ new/src/share/vm/oops/fieldInfo.hpp 2017-09-14 10:45:49.000000000 -0400 @@ -45,12 +45,14 @@ // Field info extracted from the class file and stored // as an array of 6 shorts. -#define FIELDINFO_TAG_SIZE 2 +#define FIELDINFO_TAG_SIZE 3 #define FIELDINFO_TAG_BLANK 0 #define FIELDINFO_TAG_OFFSET 1 #define FIELDINFO_TAG_TYPE_PLAIN 2 #define FIELDINFO_TAG_TYPE_CONTENDED 3 -#define FIELDINFO_TAG_MASK 3 +#define FIELDINFO_TAG_TYPE_MASK 3 +#define FIELDINFO_TAG_MASK 7 +#define FIELDINFO_FLATTENING_OFFSET 2 // Packed field has the tag, and can be either of: // hi bits <--------------------------- lo bits @@ -103,7 +105,7 @@ u2 access_flags() const { return _shorts[access_flags_offset]; } u4 offset() const { u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { + switch(lo & FIELDINFO_TAG_TYPE_MASK) { case FIELDINFO_TAG_OFFSET: return build_int_from_shorts(_shorts[low_packed_offset], _shorts[high_packed_offset]) >> FIELDINFO_TAG_SIZE; #ifndef PRODUCT @@ -121,7 +123,7 @@ bool is_contended() const { u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { + switch(lo & FIELDINFO_TAG_TYPE_MASK) { case FIELDINFO_TAG_TYPE_PLAIN: return false; case FIELDINFO_TAG_TYPE_CONTENDED: @@ -139,7 +141,7 @@ u2 contended_group() const { u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { + switch(lo & FIELDINFO_TAG_TYPE_MASK) { case FIELDINFO_TAG_TYPE_PLAIN: return 0; case FIELDINFO_TAG_TYPE_CONTENDED: @@ -157,7 +159,7 @@ u2 allocation_type() const { u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { + switch(lo & FIELDINFO_TAG_TYPE_MASK) { case FIELDINFO_TAG_TYPE_PLAIN: case FIELDINFO_TAG_TYPE_CONTENDED: return (lo >> FIELDINFO_TAG_SIZE); @@ -173,7 +175,7 @@ } bool is_offset_set() const { - return (_shorts[low_packed_offset] & FIELDINFO_TAG_MASK) == FIELDINFO_TAG_OFFSET; + return (_shorts[low_packed_offset] & FIELDINFO_TAG_TYPE_MASK) == FIELDINFO_TAG_OFFSET; } Symbol* name(const constantPoolHandle& cp) const { @@ -195,17 +197,22 @@ void set_access_flags(u2 val) { _shorts[access_flags_offset] = val; } void set_offset(u4 val) { val = val << FIELDINFO_TAG_SIZE; // make room for tag + bool flatten = is_flatten(); _shorts[low_packed_offset] = extract_low_short_from_int(val) | FIELDINFO_TAG_OFFSET; + if (flatten) set_flattening(true); _shorts[high_packed_offset] = extract_high_short_from_int(val); + assert(is_flatten() || !flatten, "just checking"); } void set_allocation_type(int type) { + bool b = is_flatten(); u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { + switch(lo & FIELDINFO_TAG_TYPE_MASK) { case FIELDINFO_TAG_BLANK: - _shorts[low_packed_offset] = ((type << FIELDINFO_TAG_SIZE)) & 0xFFFF; - _shorts[low_packed_offset] &= ~FIELDINFO_TAG_MASK; + _shorts[low_packed_offset] |= ((type << FIELDINFO_TAG_SIZE)) & 0xFFFF; + _shorts[low_packed_offset] &= ~FIELDINFO_TAG_TYPE_MASK; _shorts[low_packed_offset] |= FIELDINFO_TAG_TYPE_PLAIN; + assert(is_flatten() || !b, "Just checking"); return; #ifndef PRODUCT case FIELDINFO_TAG_TYPE_PLAIN: @@ -217,9 +224,21 @@ ShouldNotReachHere(); } + void set_flattening(bool b) { + if (b) { + _shorts[low_packed_offset] |= 1 << FIELDINFO_FLATTENING_OFFSET; + } else { + _shorts[low_packed_offset] &= ~(1 << FIELDINFO_FLATTENING_OFFSET); + } + } + + bool is_flatten() { + return ((_shorts[low_packed_offset] >> FIELDINFO_FLATTENING_OFFSET) & 1) != 0; + } + void set_contended_group(u2 val) { u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { + switch(lo & FIELDINFO_TAG_TYPE_MASK) { case FIELDINFO_TAG_TYPE_PLAIN: _shorts[low_packed_offset] |= FIELDINFO_TAG_TYPE_CONTENDED; _shorts[high_packed_offset] = val; --- old/src/share/vm/oops/fieldStreams.hpp 2017-09-14 10:45:50.000000000 -0400 +++ new/src/share/vm/oops/fieldStreams.hpp 2017-09-14 10:45:50.000000000 -0400 @@ -161,6 +161,14 @@ return field()->allocation_type(); } + bool is_flatten() { + return field()->is_flatten(); + } + + void set_flattening(bool b) { + field()->set_flattening(b); + } + void set_offset(int offset) { field()->set_offset(offset); } --- old/src/share/vm/oops/valueKlass.cpp 2017-09-14 10:45:51.000000000 -0400 +++ new/src/share/vm/oops/valueKlass.cpp 2017-09-14 10:45:51.000000000 -0400 @@ -314,6 +314,7 @@ BasicType bt = fd.field_type(); int offset = base_off + fd.offset() - (base_off > 0 ? first_field_offset() : 0); if (bt == T_VALUETYPE) { + if (fd.is_flatten()) { Symbol* signature = fd.signature(); JavaThread* THREAD = JavaThread::current(); oop loader = class_loader(); @@ -327,6 +328,9 @@ assert(klass != NULL && !HAS_PENDING_EXCEPTION, "lookup shouldn't fail"); const GrowableArray& embedded = ValueKlass::cast(klass)->collect_fields(offset); sig_extended.appendAll(&embedded); + } else { + sig_extended.push(SigEntry(T_OBJECT, offset)); + } } else { sig_extended.push(SigEntry(bt, offset)); if (bt == T_LONG || bt == T_DOUBLE) { --- old/src/share/vm/runtime/fieldDescriptor.hpp 2017-09-14 10:45:52.000000000 -0400 +++ new/src/share/vm/runtime/fieldDescriptor.hpp 2017-09-14 10:45:52.000000000 -0400 @@ -100,6 +100,7 @@ bool is_final() const { return access_flags().is_final(); } bool is_volatile() const { return access_flags().is_volatile(); } bool is_transient() const { return access_flags().is_transient(); } + bool is_flatten() const { return field()->is_flatten(); } bool is_synthetic() const { return access_flags().is_synthetic(); }