--- old/src/share/vm/oops/valueKlass.cpp 2017-05-29 18:07:43.652026371 +0200 +++ new/src/share/vm/oops/valueKlass.cpp 2017-05-29 18:07:43.578026451 +0200 @@ -238,3 +238,264 @@ value_store(data_for_oop(src()), data_for_oop(value), true, true); return value; } + +// Value type arguments are not passed by reference, instead each +// field of the value type is passed as an argument. This helper +// function collects the fields of the value types (including embedded +// value type's fields) in a list. Included with the field's type is +// the offset of each field in the value type: i2c and c2i adapters +// need that to load or store fields. Finally, the list of fields is +// sorted in order of increasing offsets: the adapters and the +// compiled code need and agreed upon order of fields. +// +// The list of basic types that is returned starts with a T_VALUETYPE +// and ends with an extra T_VOID. T_VALUETYPE/T_VOID are used as +// delimiters. Every entry between the two is a field of the value +// type. If there's an embedded value type in the list, it also starts +// with a T_VALUETYPE and ends with a T_VOID. This is so we can +// generate a unique fingerprint for the method's adapters and we can +// generate the list of basic types from the interpreter point of view +// (value types passed as reference: iterate on the list until a +// T_VALUETYPE, drop everything until and including the closing +// T_VOID) or the compiler point of view (each field of the value +// types is an argument: drop all T_VALUETYPE/T_VOID from the list). +GrowableArray ValueKlass::collect_fields(int base_off) const { + GrowableArray sig_extended; + sig_extended.push(SigEntry(T_VALUETYPE, base_off)); + for (JavaFieldStream fs(this); !fs.done(); fs.next()) { + if (fs.access_flags().is_static()) continue; + fieldDescriptor& fd = fs.field_descriptor(); + BasicType bt = fd.field_type(); + int offset = base_off + fd.offset() - (base_off > 0 ? first_field_offset() : 0); + if (bt == T_VALUETYPE) { + Symbol* signature = fd.signature(); + JavaThread* THREAD = JavaThread::current(); + oop loader = class_loader(); + oop domain = protection_domain(); + ResetNoHandleMark rnhm; + HandleMark hm; + NoSafepointVerifier nsv; + Klass* klass = SystemDictionary::resolve_or_null(signature, + Handle(THREAD, loader), Handle(THREAD, domain), + THREAD); + 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(bt, offset)); + if (bt == T_LONG || bt == T_DOUBLE) { + sig_extended.push(SigEntry(T_VOID, offset)); + } + } + } + int offset = base_off + size_helper()*HeapWordSize - (base_off > 0 ? first_field_offset() : 0); + sig_extended.push(SigEntry(T_VOID, offset)); // hack: use T_VOID to mark end of value type fields + if (base_off == 0) { + sig_extended.sort(SigEntry::compare); + } + assert(sig_extended.at(0)._bt == T_VALUETYPE && sig_extended.at(sig_extended.length()-1)._bt == T_VOID, "broken structure"); + return sig_extended; +} + +// Returns the basic types and registers for fields to return an +// instance of this value type in registers if possible. +GrowableArray ValueKlass::return_convention(VMRegPair*& regs, int& nb_fields) const { + assert(ValueTypeReturnedAsFields, "inconsistent"); + const GrowableArray& sig_vk = collect_fields(); + nb_fields = SigEntry::count_fields(sig_vk)+1; + BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, nb_fields); + sig_bt[0] = T_METADATA; + SigEntry::fill_sig_bt(sig_vk, sig_bt+1, nb_fields-1, true); + regs = NEW_RESOURCE_ARRAY(VMRegPair, nb_fields); + int total = SharedRuntime::java_return_convention(sig_bt, regs, nb_fields); + + if (total <= 0) { + regs = NULL; + } + + return sig_vk; +} + +// Create handles for all oop fields returned in registers that are +// going to be live across a safepoint. +bool ValueKlass::save_oop_results(RegisterMap& reg_map, GrowableArray& handles) const { + if (ValueTypeReturnedAsFields) { + int nb_fields; + VMRegPair* regs; + const GrowableArray& sig_vk = return_convention(regs, nb_fields); + + if (regs != NULL) { + regs++; + nb_fields--; + save_oop_fields(sig_vk, reg_map, regs, handles, nb_fields); + return true; + } + } + return false; +} + +// Same as above but with pre-computed return convention +void ValueKlass::save_oop_fields(const GrowableArray& sig_vk, RegisterMap& reg_map, const VMRegPair* regs, GrowableArray& handles, int nb_fields) const { + int j = 0; + Thread* thread = Thread::current(); + for (int i = 0; i < sig_vk.length(); i++) { + BasicType bt = sig_vk.at(i)._bt; + if (bt == T_OBJECT || bt == T_ARRAY) { + int off = sig_vk.at(i)._offset; + VMRegPair pair = regs[j]; + address loc = reg_map.location(pair.first()); + oop v = *(oop*)loc; + assert(v == NULL || v->is_oop(), "not an oop?"); + assert(Universe::heap()->is_in_or_null(v), "must be heap pointer"); + handles.push(Handle(thread, v)); + } + if (bt == T_VALUETYPE) { + continue; + } + if (bt == T_VOID && + sig_vk.at(i-1)._bt != T_LONG && + sig_vk.at(i-1)._bt != T_DOUBLE) { + continue; + } + j++; + } + assert(j == nb_fields, "missed a field?"); +} + +// Update oop fields in registers from handles after a safepoint +void ValueKlass::restore_oop_results(RegisterMap& reg_map, GrowableArray& handles) const { + assert(ValueTypeReturnedAsFields, "inconsistent"); + int nb_fields; + VMRegPair* regs; + const GrowableArray& sig_vk = return_convention(regs, nb_fields); + assert(regs != NULL, "inconsistent"); + + regs++; + nb_fields--; + + int j = 0; + for (int i = 0, k = 0; i < sig_vk.length(); i++) { + BasicType bt = sig_vk.at(i)._bt; + if (bt == T_OBJECT || bt == T_ARRAY) { + int off = sig_vk.at(i)._offset; + VMRegPair pair = regs[j]; + address loc = reg_map.location(pair.first()); + *(oop*)loc = handles.at(k++)(); + } + if (bt == T_VALUETYPE) { + continue; + } + if (bt == T_VOID && + sig_vk.at(i-1)._bt != T_LONG && + sig_vk.at(i-1)._bt != T_DOUBLE) { + continue; + } + j++; + } + assert(j == nb_fields, "missed a field?"); +} + +// Fields are in registers. Create an instance of the value type and +// initialize it with the values of the fields. +oop ValueKlass::realloc_result(const GrowableArray& sig_vk, const RegisterMap& reg_map, const VMRegPair* regs, + const GrowableArray& handles, int nb_fields, TRAPS) { + oop new_vt = allocate_instance(CHECK_NULL); + + int j = 0; + int k = 0; + for (int i = 0; i < sig_vk.length(); i++) { + BasicType bt = sig_vk.at(i)._bt; + if (bt == T_VALUETYPE) { + continue; + } + if (bt == T_VOID) { + if (sig_vk.at(i-1)._bt == T_LONG || + sig_vk.at(i-1)._bt == T_DOUBLE) { + j++; + } + continue; + } + int off = sig_vk.at(i)._offset; + VMRegPair pair = regs[j]; + address loc = reg_map.location(pair.first()); + switch(bt) { + case T_BOOLEAN: { + jboolean v = *(intptr_t*)loc; + *(jboolean*)((address)new_vt + off) = v; + break; + } + case T_CHAR: { + jchar v = *(intptr_t*)loc; + *(jchar*)((address)new_vt + off) = v; + break; + } + case T_BYTE: { + jbyte v = *(intptr_t*)loc; + *(jbyte*)((address)new_vt + off) = v; + break; + } + case T_SHORT: { + jshort v = *(intptr_t*)loc; + *(jshort*)((address)new_vt + off) = v; + break; + } + case T_INT: { + jint v = *(intptr_t*)loc; + *(jint*)((address)new_vt + off) = v; + break; + } + case T_LONG: { +#ifdef _LP64 + jlong v = *(intptr_t*)loc; + *(jlong*)((address)new_vt + off) = v; +#else + Unimplemented(); +#endif + break; + } + case T_OBJECT: + case T_ARRAY: { + Handle handle = handles.at(k++); + oop v = handle(); + if (!UseCompressedOops) { + oop* p = (oop*)((address)new_vt + off); + oopDesc::store_heap_oop(p, v); + } else { + narrowOop* p = (narrowOop*)((address)new_vt + off); + oopDesc::encode_store_heap_oop(p, v); + } + break; + } + case T_FLOAT: { + jfloat v = *(jfloat*)loc; + *(jfloat*)((address)new_vt + off) = v; + break; + } + case T_DOUBLE: { + jdouble v = *(jdouble*)loc; + *(jdouble*)((address)new_vt + off) = v; + break; + } + default: + ShouldNotReachHere(); + } + j++; + } + assert(j == nb_fields, "missed a field?"); + assert(k == handles.length(), "missed an oop?"); + return new_vt; +} + +ValueKlass* ValueKlass::returned_value_type(const RegisterMap& map) { + BasicType bt = T_METADATA; + VMRegPair pair; + int nb = SharedRuntime::java_return_convention(&bt, &pair, 1); + assert(nb == 1, "broken"); + + address loc = map.location(pair.first()); + intptr_t ptr = *(intptr_t*)loc; + if (Universe::heap()->is_in_reserved((void*)ptr)) { + return NULL; + } + return (ValueKlass*)ptr; +}