--- old/src/share/vm/ci/ciValueKlass.hpp 2017-07-04 18:39:47.056152912 +0200 +++ new/src/share/vm/ci/ciValueKlass.hpp 2017-07-04 18:39:46.982153253 +0200 @@ -26,9 +26,11 @@ #define SHARE_VM_CI_CIVALUEKLASS_HPP #include "ci/ciConstantPoolCache.hpp" +#include "ci/ciEnv.hpp" #include "ci/ciFlags.hpp" #include "ci/ciInstanceKlass.hpp" #include "ci/ciSymbol.hpp" +#include "oops/valueKlass.hpp" // ciValueKlass // @@ -52,6 +54,10 @@ const char* type_string() { return "ciValueKlass"; } int compute_field_index_map(); + ValueKlass* get_valueKlass() const { + return ValueKlass::cast(get_Klass()); + } + public: bool is_valuetype() const { return true; } bool flatten_array() const; @@ -69,6 +75,12 @@ int first_field_offset() const; int value_arg_slots(); + + // Can a value type instance of this type be returned as multiple + // returned values? + bool can_be_returned_as_fields() const { + return this != ciEnv::current()->___Value_klass() && get_valueKlass()->return_regs() != NULL; + } }; #endif // SHARE_VM_CI_CIVALUEKLASS_HPP --- old/src/share/vm/classfile/classFileParser.cpp 2017-07-04 18:39:47.173152373 +0200 +++ new/src/share/vm/classfile/classFileParser.cpp 2017-07-04 18:39:47.093152741 +0200 @@ -5638,6 +5638,7 @@ if (is_value_type()) { ValueKlass* vk = ValueKlass::cast(ik); vk->set_if_bufferable(); + vk->initialize_calling_convention(); } // Valhalla shady value type conversion --- old/src/share/vm/oops/instanceKlass.hpp 2017-07-04 18:39:47.333151635 +0200 +++ new/src/share/vm/oops/instanceKlass.hpp 2017-07-04 18:39:47.250152018 +0200 @@ -1098,7 +1098,8 @@ (is_anonymous ? (int)sizeof(Klass*)/wordSize : 0) + (has_stored_fingerprint ? (int)sizeof(uint64_t*)/wordSize : 0) + (java_fields * (int)sizeof(Klass*)/wordSize) + - (is_value_type ? (int)sizeof(Klass*) : 0)); + (is_value_type ? (int)sizeof(Klass*) : 0) + + (is_value_type ? (int)sizeof(intptr_t)*2 : 0)); } int size() const { return size(vtable_length(), itable_length(), --- old/src/share/vm/oops/valueKlass.cpp 2017-07-04 18:39:47.461151044 +0200 +++ new/src/share/vm/oops/valueKlass.cpp 2017-07-04 18:39:47.386151390 +0200 @@ -26,6 +26,7 @@ #include "gc/shared/gcLocker.inline.hpp" #include "interpreter/interpreter.hpp" #include "logging/log.hpp" +#include "memory/metadataFactory.hpp" #include "oops/oop.inline.hpp" #include "oops/fieldStreams.hpp" #include "oops/method.hpp" @@ -342,37 +343,39 @@ 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"); +void ValueKlass::initialize_calling_convention() { + Thread* THREAD = Thread::current(); + assert(!HAS_PENDING_EXCEPTION, "should have no exception"); + ResourceMark rm; const GrowableArray& sig_vk = collect_fields(); - nb_fields = SigEntry::count_fields(sig_vk)+1; + int nb_fields = SigEntry::count_fields(sig_vk)+1; + Array* extended_sig = MetadataFactory::new_array(class_loader_data(), sig_vk.length(), CHECK_AND_CLEAR); + *((Array**)adr_extended_sig()) = extended_sig; + for (int i = 0; i < sig_vk.length(); i++ ) { + extended_sig->at_put(i, sig_vk.at(i)); + } + 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); + VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, nb_fields); int total = SharedRuntime::java_return_convention(sig_bt, regs, nb_fields); - if (total <= 0) { - regs = NULL; + if (total > 0) { + Array* return_regs = MetadataFactory::new_array(class_loader_data(), nb_fields, CHECK_AND_CLEAR); + *((Array**)adr_return_regs()) = return_regs; + for (int i = 0; i < nb_fields; i++ ) { + return_regs->at_put(i, regs[i]); + } } - - 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); + if (return_regs() != NULL) { + save_oop_fields(reg_map, handles); return true; } } @@ -380,14 +383,17 @@ } // 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; +void ValueKlass::save_oop_fields(const RegisterMap& reg_map, GrowableArray& handles) const { Thread* thread = Thread::current(); - for (int i = 0; i < sig_vk.length(); i++) { - BasicType bt = sig_vk.at(i)._bt; + const Array* sig_vk = extended_sig(); + const Array* regs = return_regs(); + int j = 1; + + 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]; + int off = sig_vk->at(i)._offset; + VMRegPair pair = regs->at(j); address loc = reg_map.location(pair.first()); oop v = *(oop*)loc; assert(v == NULL || v->is_oop(), "not an oop?"); @@ -398,32 +404,28 @@ continue; } if (bt == T_VOID && - sig_vk.at(i-1)._bt != T_LONG && - sig_vk.at(i-1)._bt != T_DOUBLE) { + 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?"); + assert(j == regs->length(), "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); + const Array* sig_vk = extended_sig(); + const Array* regs = return_regs(); 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; + int j = 1; + 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]; + int off = sig_vk->at(i)._offset; + VMRegPair pair = regs->at(j); address loc = reg_map.location(pair.first()); *(oop*)loc = handles.at(k++)(); } @@ -431,37 +433,39 @@ continue; } if (bt == T_VOID && - sig_vk.at(i-1)._bt != T_LONG && - sig_vk.at(i-1)._bt != T_DOUBLE) { + 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?"); + assert(j == regs->length(), ""); } // 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 ValueKlass::realloc_result(const RegisterMap& reg_map, const GrowableArray& handles, TRAPS) { oop new_vt = allocate_instance(CHECK_NULL); - int j = 0; + const Array* sig_vk = extended_sig(); + const Array* regs = return_regs(); + + int j = 1; int k = 0; - for (int i = 0; i < sig_vk.length(); i++) { - BasicType bt = sig_vk.at(i)._bt; + 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) { + 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]; + int off = sig_vk->at(i)._offset; + VMRegPair pair = regs->at(j); address loc = reg_map.location(pair.first()); switch(bt) { case T_BOOLEAN: { @@ -526,7 +530,7 @@ } j++; } - assert(j == nb_fields, "missed a field?"); + assert(j == regs->length(), "missed a field?"); assert(k == handles.length(), "missed an oop?"); return new_vt; } @@ -539,13 +543,11 @@ address loc = map.location(pair.first()); intptr_t ptr = *(intptr_t*)loc; - if (Metaspace::contains((void*)ptr)) { + if ((ptr & 1) != 0) { + ptr = ptr & ~1L; + assert(Metaspace::contains((void*)ptr), "should be klass"); return (ValueKlass*)ptr; } return NULL; -// if (Universe::heap()->is_in_reserved((void*)ptr)) { -// return NULL; -// } -// return (ValueKlass*)ptr; } --- old/src/share/vm/oops/valueKlass.hpp 2017-07-04 18:39:47.580150496 +0200 +++ new/src/share/vm/oops/valueKlass.hpp 2017-07-04 18:39:47.504150846 +0200 @@ -36,7 +36,7 @@ friend class VMStructs; friend class InstanceKlass; -private: + private: // Constructor ValueKlass(const ClassFileParser& parser) @@ -44,7 +44,43 @@ set_has_vcc_klass(); } + address adr_extended_sig() const { + address adr_vcc = adr_vcc_klass(); + if (adr_vcc == NULL) { + address adr_jf = adr_value_fields_klasses(); + if (adr_jf != NULL) { + return adr_jf + this->java_fields_count() * sizeof(Klass*); + } + + address adr_fing = adr_fingerprint(); + if (adr_fing != NULL) { + return adr_fingerprint() + sizeof(u8); + } + + InstanceKlass** adr_host = adr_host_klass(); + if (adr_host != NULL) { + return (address)(adr_host + 1); + } + + Klass** adr_impl = adr_implementor(); + if (adr_impl != NULL) { + return (address)(adr_impl + 1); + } + + return (address)end_of_nonstatic_oop_maps(); + } else { + return adr_vcc + sizeof(Klass*); + } + } + + address adr_return_regs() const { + return adr_extended_sig() + sizeof(intptr_t); + } + // static Klass* array_klass_impl(InstanceKlass* this_k, bool or_null, int n, TRAPS); + + GrowableArray collect_fields(int base_off = 0) const; + protected: // Returns the array class for the n'th dimension Klass* array_klass_impl(bool or_null, int n, TRAPS); @@ -145,12 +181,19 @@ inline void oop_iterate_specialized_bounded(const address oop_addr, OopClosureType* closure, void* lo, void* hi); // calling convention support - GrowableArray collect_fields(int base_off = 0) const; - GrowableArray return_convention(VMRegPair*& regs, int& nb_fields) const; - void save_oop_fields(const GrowableArray& sig_vk, RegisterMap& map, const VMRegPair* regs, GrowableArray& handles, int nb_fields) const; + void initialize_calling_convention(); + const Array* extended_sig() const { + assert(this != SystemDictionary::___Value_klass(), "make no sense for __Value"); + return *((Array**)adr_extended_sig()); + } + const Array* return_regs() const { + assert(this != SystemDictionary::___Value_klass(), "make no sense for __Value"); + return *((Array**)adr_return_regs()); + } + void save_oop_fields(const RegisterMap& map, GrowableArray& handles) const; bool save_oop_results(RegisterMap& map, GrowableArray& handles) const; void restore_oop_results(RegisterMap& map, GrowableArray& handles) const; - oop realloc_result(const GrowableArray& sig_vk, const RegisterMap& reg_map, const VMRegPair* regs, const GrowableArray& handles, int nb_fields, TRAPS); + oop realloc_result(const RegisterMap& reg_map, const GrowableArray& handles, TRAPS); static ValueKlass* returned_value_type(const RegisterMap& reg_map); }; --- old/src/share/vm/opto/type.cpp 2017-07-04 18:39:47.694149970 +0200 +++ new/src/share/vm/opto/type.cpp 2017-07-04 18:39:47.619150316 +0200 @@ -1928,46 +1928,13 @@ } } -// Can a value type instance of this type be returned as multiple -// returned values? -static bool vt_can_be_returned_as_fields(ciValueKlass* vk) { - if (vk == ciEnv::current()->___Value_klass()) { - return false; - } - - ResourceMark rm; - uint args = vk->value_arg_slots() + 1 /* return vk as well */; - - BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, args); - VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, args); - - sig_bt[0] = T_METADATA; - for (uint i = 0, j = 1; i < (uint)vk->nof_nonstatic_fields(); i++) { - BasicType bt = vk->nonstatic_field_at(i)->layout_type(); - assert(i+j < args, "out of bounds access"); - sig_bt[i+j] = bt; - if (bt == T_LONG || bt == T_DOUBLE) { - j++; - assert(i+j < args, "out of bounds access"); - sig_bt[i+j] = T_VOID; - } - } - - if (SharedRuntime::java_return_convention(sig_bt, regs, args) <= 0) { - return false; - } - - return true; -} - - //------------------------------make------------------------------------------- // Make a TypeTuple from the range of a method signature const TypeTuple *TypeTuple::make_range(ciSignature* sig, bool ret_vt_fields) { ciType* return_type = sig->return_type(); uint arg_cnt = 0; if (ret_vt_fields) { - ret_vt_fields = return_type->is_valuetype() && vt_can_be_returned_as_fields((ciValueKlass*)return_type); + ret_vt_fields = return_type->is_valuetype() && ((ciValueKlass*)return_type)->can_be_returned_as_fields(); } if (ret_vt_fields) { ciValueKlass* vk = (ciValueKlass*)return_type; --- old/src/share/vm/opto/valuetypenode.cpp 2017-07-04 18:39:47.839149301 +0200 +++ new/src/share/vm/opto/valuetypenode.cpp 2017-07-04 18:39:47.766149638 +0200 @@ -420,8 +420,10 @@ void ValueTypeNode::pass_klass(Node* n, uint pos, const GraphKit& kit) { ciValueKlass* vk = value_klass(); const TypeKlassPtr* tk = TypeKlassPtr::make(vk); - Node* arg = kit.makecon(tk); - n->init_req(pos, arg); + intptr_t bits = tk->get_con(); + bits |= 1; + Node* klass_tagged = kit.MakeConX(bits); + n->init_req(pos, klass_tagged); } uint ValueTypeNode::pass_fields(Node* n, int base_input, const GraphKit& kit, ciValueKlass* base_vk, int base_offset) { --- old/src/share/vm/runtime/deoptimization.cpp 2017-07-04 18:39:47.953148776 +0200 +++ new/src/share/vm/runtime/deoptimization.cpp 2017-07-04 18:39:47.879149117 +0200 @@ -879,12 +879,7 @@ // reference to a value type instance. Allocate and initialize it from // the register values here. bool Deoptimization::realloc_value_type_result(ValueKlass* vk, const RegisterMap& map, GrowableArray& return_oops, TRAPS) { - VMRegPair* regs; - int nb_fields; - const GrowableArray& sig_vk = vk->return_convention(regs, nb_fields); - regs++; - nb_fields--; - oop new_vt = vk->realloc_result(sig_vk, map, regs, return_oops, nb_fields, THREAD); + oop new_vt = vk->realloc_result(map, return_oops, THREAD); if (new_vt == NULL) { CLEAR_PENDING_EXCEPTION; THROW_OOP_(Universe::out_of_memory_error_realloc_objects(), true); --- old/src/share/vm/runtime/sharedRuntime.cpp 2017-07-04 18:39:48.084148172 +0200 +++ new/src/share/vm/runtime/sharedRuntime.cpp 2017-07-04 18:39:48.006148531 +0200 @@ -2710,8 +2710,8 @@ // fields for the value type. sig_extended.push(SigEntry(T_OBJECT)); } else { - const GrowableArray& sig_vk = vk->collect_fields(); - sig_extended.appendAll(&sig_vk); + const Array* sig_vk = vk->extended_sig(); + sig_extended.appendAll(sig_vk); } } else { sig_extended.push(SigEntry(T_OBJECT)); @@ -2729,8 +2729,8 @@ "should not use __Value for a value type argument"); sig_extended.push(SigEntry(T_OBJECT)); } else { - const GrowableArray& sig_vk = vk->collect_fields(); - sig_extended.appendAll(&sig_vk); + const Array* sig_vk = vk->extended_sig(); + sig_extended.appendAll(sig_vk); } } else { sig_extended.push(SigEntry(ss.type())); @@ -3354,9 +3354,8 @@ ValueKlass* vk = ValueKlass::cast(res->klass()); - VMRegPair* regs; - int nb_fields; - const GrowableArray& sig_vk = vk->return_convention(regs, nb_fields); + const Array* sig_vk = vk->extended_sig() ; + const Array* regs = vk->return_regs(); if (regs == NULL) { // The fields of the value klass don't fit in registers, bail out @@ -3364,20 +3363,20 @@ } int j = 1; - for (int i = 0; i < sig_vk.length(); i++) { - BasicType bt = sig_vk.at(i)._bt; + 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) { + 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]; + int off = sig_vk->at(i)._offset; + VMRegPair pair = regs->at(j); address loc = reg_map.location(pair.first()); switch(bt) { case T_BOOLEAN: @@ -3428,10 +3427,10 @@ } j++; } - assert(j == nb_fields, "missed a field?"); + assert(j == regs->length(), "missed a field?"); #ifdef ASSERT - VMRegPair pair = regs[0]; + VMRegPair pair = regs->at(0); address loc = reg_map.location(pair.first()); assert(*(oopDesc**)loc == res, "overwritten object"); #endif @@ -3452,45 +3451,26 @@ #ifdef ASSERT ValueKlass* verif_vk = ValueKlass::returned_value_type(reg_map); - javaVFrame* vf = javaVFrame::cast(vframe::new_vframe(&callerFrame, ®_map, thread)); - Method* m = vf->method(); - int bci = vf->bci(); - Bytecode_invoke inv(m, bci); - - { - NoSafepointVerifier nsv; - methodHandle callee = inv.static_target(thread); - assert(!thread->has_pending_exception(), "call resolution should work"); - ValueKlass* verif_vk2 = callee->returned_value_type(thread); - assert(verif_vk == NULL || verif_vk == verif_vk2 || - verif_vk2 == SystemDictionary::___Value_klass(), "Bad value klass"); - - } #endif - if (!Metaspace::contains((void*)res)) { + if ((res & 1) == 0) { // We're not returning with value type fields in registers (the // calling convention didn't allow it for this value klass) + assert(!Metaspace::contains((void*)res), "should be oop or pointer in buffer area"); thread->set_vm_result((oopDesc*)res); assert(verif_vk == NULL, "broken calling convention"); return; } + res = res & ~1L; ValueKlass* vk = (ValueKlass*)res; assert(verif_vk == vk, "broken calling convention"); - - VMRegPair* regs; - int nb_fields; - const GrowableArray& sig_vk = vk->return_convention(regs, nb_fields); - assert(regs != NULL, "return convention should allow return as fields"); - - regs++; - nb_fields--; + assert(Metaspace::contains((void*)res), "should be klass"); // Allocate handles for every oop fields so they are safe in case of // a safepoint when allocating GrowableArray handles; - vk->save_oop_fields(sig_vk, reg_map, regs, handles, nb_fields); + vk->save_oop_fields(reg_map, handles); // It's unsafe to safepoint until we are here @@ -3498,8 +3478,20 @@ JRT_BLOCK; { Thread* THREAD = thread; - oop vt = vk->realloc_result(sig_vk, reg_map, regs, handles, nb_fields, CHECK); + oop vt = vk->realloc_result(reg_map, handles, CHECK); new_vt = Handle(thread, vt); + +#ifdef ASSERT + javaVFrame* vf = javaVFrame::cast(vframe::new_vframe(&callerFrame, ®_map, thread)); + Method* m = vf->method(); + int bci = vf->bci(); + Bytecode_invoke inv(m, bci); + + methodHandle callee = inv.static_target(thread); + assert(!thread->has_pending_exception(), "call resolution should work"); + ValueKlass* verif_vk2 = callee->returned_value_type(thread); + assert(verif_vk == verif_vk2 || verif_vk2 == SystemDictionary::___Value_klass(), "Bad value klass"); +#endif } JRT_BLOCK_END; --- old/src/share/vm/utilities/growableArray.hpp 2017-07-04 18:39:48.224147526 +0200 +++ new/src/share/vm/utilities/growableArray.hpp 2017-07-04 18:39:48.150147867 +0200 @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "memory/allocation.inline.hpp" +#include "oops/array.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -390,6 +391,12 @@ } } + void appendAll(const Array* l) { + for (int i = 0; i < l->length(); i++) { + raw_at_put_grow(_len, l->at(i), E()); + } + } + void sort(int f(E*,E*)) { qsort(_data, length(), sizeof(E), (_sort_Fn)f); }