--- old/src/hotspot/share/ci/ciField.hpp 2019-01-21 14:08:25.893756796 +0100 +++ new/src/hotspot/share/ci/ciField.hpp 2019-01-21 14:08:25.710756948 +0100 @@ -105,7 +105,7 @@ ciType* type() { return (_type == NULL) ? compute_type() : _type; } // How is this field actually stored in memory? - BasicType layout_type() { return type2field[(_type == NULL) ? T_OBJECT : _type->basic_type()]; } + BasicType layout_type() { return type2field[type()->basic_type()]; } // How big is this field in memory? int size_in_bytes() { return type2aelembytes(layout_type()); } --- old/src/hotspot/share/ci/ciInstanceKlass.cpp 2019-01-21 14:08:26.145756588 +0100 +++ new/src/hotspot/share/ci/ciInstanceKlass.cpp 2019-01-21 14:08:25.961756740 +0100 @@ -421,6 +421,29 @@ return field; } +ciField* ciInstanceKlass::get_non_flattened_field_by_offset(int field_offset) { + if (super() != NULL && super()->has_nonstatic_fields()) { + ciField* f = super()->get_non_flattened_field_by_offset(field_offset); + if (f != NULL) { + return f; + } + } + + VM_ENTRY_MARK; + InstanceKlass* k = get_instanceKlass(); + Arena* arena = CURRENT_ENV->arena(); + for (JavaFieldStream fs(k); !fs.done(); fs.next()) { + if (fs.access_flags().is_static()) continue; + fieldDescriptor& fd = fs.field_descriptor(); + if (fd.offset() == field_offset) { + ciField* f = new (arena) ciField(&fd); + return f; + } + } + + return NULL; +} + // ------------------------------------------------------------------ // ciInstanceKlass::get_field_by_name ciField* ciInstanceKlass::get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static) { --- old/src/hotspot/share/ci/ciInstanceKlass.hpp 2019-01-21 14:08:26.397756380 +0100 +++ new/src/hotspot/share/ci/ciInstanceKlass.hpp 2019-01-21 14:08:26.213756532 +0100 @@ -192,6 +192,8 @@ ciInstanceKlass* get_canonical_holder(int offset); ciField* get_field_by_offset(int field_offset, bool is_static); ciField* get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static); + // get field descriptor at field_offset ignoring flattening + ciField* get_non_flattened_field_by_offset(int field_offset); // total number of nonstatic fields (including inherited): int nof_nonstatic_fields() { --- old/src/hotspot/share/classfile/vmSymbols.cpp 2019-01-21 14:08:26.643756178 +0100 +++ new/src/hotspot/share/classfile/vmSymbols.cpp 2019-01-21 14:08:26.460756328 +0100 @@ -598,6 +598,8 @@ case vmIntrinsics::_updateByteBufferCRC32: if (!UseCRC32Intrinsics) return true; break; + case vmIntrinsics::_makePrivateBuffer: + case vmIntrinsics::_finishPrivateBuffer: case vmIntrinsics::_getReference: case vmIntrinsics::_getBoolean: case vmIntrinsics::_getByte: @@ -607,6 +609,7 @@ case vmIntrinsics::_getLong: case vmIntrinsics::_getFloat: case vmIntrinsics::_getDouble: + case vmIntrinsics::_getValue: case vmIntrinsics::_putReference: case vmIntrinsics::_putBoolean: case vmIntrinsics::_putByte: @@ -616,6 +619,7 @@ case vmIntrinsics::_putLong: case vmIntrinsics::_putFloat: case vmIntrinsics::_putDouble: + case vmIntrinsics::_putValue: case vmIntrinsics::_getReferenceVolatile: case vmIntrinsics::_getBooleanVolatile: case vmIntrinsics::_getByteVolatile: --- old/src/hotspot/share/classfile/vmSymbols.hpp 2019-01-21 14:08:26.894755970 +0100 +++ new/src/hotspot/share/classfile/vmSymbols.hpp 2019-01-21 14:08:26.714756119 +0100 @@ -1123,6 +1123,8 @@ do_signature(putFloat_signature, "(Ljava/lang/Object;JF)V") \ do_signature(getDouble_signature, "(Ljava/lang/Object;J)D") \ do_signature(putDouble_signature, "(Ljava/lang/Object;JD)V") \ + do_signature(getValue_signature, "(Ljava/lang/Object;JLjava/lang/Class;)Ljava/lang/Object;") \ + do_signature(putValue_signature, "(Ljava/lang/Object;JLjava/lang/Class;Ljava/lang/Object;)V") \ \ do_name(getReference_name,"getReference") do_name(putReference_name,"putReference") \ do_name(getBoolean_name,"getBoolean") do_name(putBoolean_name,"putBoolean") \ @@ -1133,6 +1135,9 @@ do_name(getLong_name,"getLong") do_name(putLong_name,"putLong") \ do_name(getFloat_name,"getFloat") do_name(putFloat_name,"putFloat") \ do_name(getDouble_name,"getDouble") do_name(putDouble_name,"putDouble") \ + do_name(getValue_name,"getValue") do_name(putValue_name,"putValue") \ + do_name(makePrivateBuffer_name,"makePrivateBuffer") \ + do_name(finishPrivateBuffer_name,"finishPrivateBuffer") \ \ do_intrinsic(_getReference, jdk_internal_misc_Unsafe, getReference_name, getReference_signature, F_RN) \ do_intrinsic(_getBoolean, jdk_internal_misc_Unsafe, getBoolean_name, getBoolean_signature, F_RN) \ @@ -1143,6 +1148,7 @@ do_intrinsic(_getLong, jdk_internal_misc_Unsafe, getLong_name, getLong_signature, F_RN) \ do_intrinsic(_getFloat, jdk_internal_misc_Unsafe, getFloat_name, getFloat_signature, F_RN) \ do_intrinsic(_getDouble, jdk_internal_misc_Unsafe, getDouble_name, getDouble_signature, F_RN) \ + do_intrinsic(_getValue, jdk_internal_misc_Unsafe, getValue_name, getValue_signature, F_RN) \ do_intrinsic(_putReference, jdk_internal_misc_Unsafe, putReference_name, putReference_signature, F_RN) \ do_intrinsic(_putBoolean, jdk_internal_misc_Unsafe, putBoolean_name, putBoolean_signature, F_RN) \ do_intrinsic(_putByte, jdk_internal_misc_Unsafe, putByte_name, putByte_signature, F_RN) \ @@ -1152,6 +1158,10 @@ do_intrinsic(_putLong, jdk_internal_misc_Unsafe, putLong_name, putLong_signature, F_RN) \ do_intrinsic(_putFloat, jdk_internal_misc_Unsafe, putFloat_name, putFloat_signature, F_RN) \ do_intrinsic(_putDouble, jdk_internal_misc_Unsafe, putDouble_name, putDouble_signature, F_RN) \ + do_intrinsic(_putValue, jdk_internal_misc_Unsafe, putValue_name, putValue_signature, F_RN) \ + \ + do_intrinsic(_makePrivateBuffer, jdk_internal_misc_Unsafe, makePrivateBuffer_name, object_object_signature, F_RN) \ + do_intrinsic(_finishPrivateBuffer, jdk_internal_misc_Unsafe, finishPrivateBuffer_name, object_object_signature, F_RN) \ \ do_name(getReferenceVolatile_name,"getReferenceVolatile") do_name(putReferenceVolatile_name,"putReferenceVolatile") \ do_name(getBooleanVolatile_name,"getBooleanVolatile") do_name(putBooleanVolatile_name,"putBooleanVolatile") \ --- old/src/hotspot/share/opto/c2compiler.cpp 2019-01-21 14:08:27.174755739 +0100 +++ new/src/hotspot/share/opto/c2compiler.cpp 2019-01-21 14:08:26.988755893 +0100 @@ -479,6 +479,8 @@ case vmIntrinsics::_getCharsStringU: case vmIntrinsics::_getCharStringU: case vmIntrinsics::_putCharStringU: + case vmIntrinsics::_makePrivateBuffer: + case vmIntrinsics::_finishPrivateBuffer: case vmIntrinsics::_getReference: case vmIntrinsics::_getBoolean: case vmIntrinsics::_getByte: @@ -488,6 +490,7 @@ case vmIntrinsics::_getLong: case vmIntrinsics::_getFloat: case vmIntrinsics::_getDouble: + case vmIntrinsics::_getValue: case vmIntrinsics::_putReference: case vmIntrinsics::_putBoolean: case vmIntrinsics::_putByte: @@ -497,6 +500,7 @@ case vmIntrinsics::_putLong: case vmIntrinsics::_putFloat: case vmIntrinsics::_putDouble: + case vmIntrinsics::_putValue: case vmIntrinsics::_getReferenceVolatile: case vmIntrinsics::_getBooleanVolatile: case vmIntrinsics::_getByteVolatile: --- old/src/hotspot/share/opto/callnode.cpp 2019-01-21 14:08:27.425755532 +0100 +++ new/src/hotspot/share/opto/callnode.cpp 2019-01-21 14:08:27.242755683 +0100 @@ -1415,6 +1415,7 @@ _is_scalar_replaceable = false; _is_non_escaping = false; _is_allocation_MemBar_redundant = false; + _larval = false; Node *topnode = C->top(); init_req( TypeFunc::Control , ctrl ); @@ -1485,6 +1486,27 @@ return CallNode::Ideal(phase, can_reshape); } +Node *AllocateNode::make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, Node* mem, Node* klass_node) { + Node* mark_node = NULL; + // For now only enable fast locking for non-array types + if ((EnableValhalla || UseBiasedLocking) && Opcode() == Op_Allocate) { + if (klass_node == NULL) { + Node* k_adr = phase->transform(new AddPNode(obj, obj, phase->MakeConX(oopDesc::klass_offset_in_bytes()))); + klass_node = phase->transform(LoadKlassNode::make(*phase, NULL, phase->C->immutable_memory(), k_adr, phase->type(k_adr)->is_ptr())); + } + Node* proto_adr = phase->transform(new AddPNode(klass_node, klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset())))); + mark_node = LoadNode::make(*phase, control, mem, proto_adr, TypeRawPtr::BOTTOM, TypeX_X, TypeX_X->basic_type(), MemNode::unordered); + } else { + mark_node = phase->MakeConX((intptr_t)markOopDesc::prototype()); + } + if (_larval) { + mark_node = phase->transform(mark_node); + mark_node = new OrXNode(mark_node, phase->MakeConX(markOopDesc::larval_state_pattern)); + } + return mark_node; +} + + //============================================================================= Node* AllocateArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* res = SafePointNode::Ideal(phase, can_reshape); --- old/src/hotspot/share/opto/callnode.hpp 2019-01-21 14:08:27.685755318 +0100 +++ new/src/hotspot/share/opto/callnode.hpp 2019-01-21 14:08:27.505755466 +0100 @@ -901,6 +901,7 @@ bool _is_non_escaping; // True when MemBar for new is redundant with MemBar at initialzer exit bool _is_allocation_MemBar_redundant; + bool _larval; virtual uint size_of() const; // Size is bigger AllocateNode(Compile* C, const TypeFunc *atype, Node *ctrl, Node *mem, Node *abio, @@ -976,6 +977,8 @@ // allocation node. void compute_MemBar_redundancy(ciMethod* initializer); bool is_allocation_MemBar_redundant() { return _is_allocation_MemBar_redundant; } + + Node* make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, Node* mem, Node* klass_node); }; //------------------------------AllocateArray--------------------------------- --- old/src/hotspot/share/opto/escape.cpp 2019-01-21 14:08:27.937755110 +0100 +++ new/src/hotspot/share/opto/escape.cpp 2019-01-21 14:08:27.754755261 +0100 @@ -141,6 +141,16 @@ java_objects_worklist.append(phantom_obj); for( uint next = 0; next < ideal_nodes.size(); ++next ) { Node* n = ideal_nodes.at(next); + if ((n->Opcode() == Op_LoadX || n->Opcode() == Op_StoreX) && + !n->in(MemNode::Address)->is_AddP() && + _igvn->type(n->in(MemNode::Address))->isa_oopptr()) { + // Load/Store at mark work address is at offset 0 so has no AddP which confuses EA + Node* addp = new AddPNode(n->in(MemNode::Address), n->in(MemNode::Address), _igvn->MakeConX(0)); + _igvn->register_new_node_with_optimizer(addp); + _igvn->replace_input_of(n, MemNode::Address, addp); + ideal_nodes.push(addp); + _nodes.at_put_grow(addp->_idx, NULL, NULL); + } // Create PointsTo nodes and add them to Connection Graph. Called // only once per ideal node since ideal_nodes is Unique_Node list. add_node_to_connection_graph(n, &delayed_worklist); --- old/src/hotspot/share/opto/graphKit.cpp 2019-01-21 14:08:28.222754874 +0100 +++ new/src/hotspot/share/opto/graphKit.cpp 2019-01-21 14:08:28.032755031 +0100 @@ -3699,6 +3699,7 @@ hook_memory_on_init(*this, elemidx, minit_in, minit_out); } } else if (oop_type->isa_instptr()) { + set_memory(minit_out, C->get_alias_index(oop_type)); // mark word ciInstanceKlass* ik = oop_type->klass()->as_instance_klass(); for (int i = 0, len = ik->nof_nonstatic_fields(); i < len; i++) { ciField* field = ik->nonstatic_field_at(i); --- old/src/hotspot/share/opto/library_call.cpp 2019-01-21 14:08:28.504754642 +0100 +++ new/src/hotspot/share/opto/library_call.cpp 2019-01-21 14:08:28.320754794 +0100 @@ -269,6 +269,8 @@ bool inline_unsafe_allocate(); bool inline_unsafe_newArray(bool uninitialized); bool inline_unsafe_copyMemory(); + bool inline_unsafe_make_private_buffer(); + bool inline_unsafe_finish_private_buffer(); bool inline_native_currentThread(); bool inline_native_time_funcs(address method, const char* funcName); @@ -605,6 +607,8 @@ case vmIntrinsics::_inflateStringC: case vmIntrinsics::_inflateStringB: return inline_string_copy(!is_compress); + case vmIntrinsics::_makePrivateBuffer: return inline_unsafe_make_private_buffer(); + case vmIntrinsics::_finishPrivateBuffer: return inline_unsafe_finish_private_buffer(); case vmIntrinsics::_getReference: return inline_unsafe_access(!is_store, T_OBJECT, Relaxed, false); case vmIntrinsics::_getBoolean: return inline_unsafe_access(!is_store, T_BOOLEAN, Relaxed, false); case vmIntrinsics::_getByte: return inline_unsafe_access(!is_store, T_BYTE, Relaxed, false); @@ -614,6 +618,7 @@ case vmIntrinsics::_getLong: return inline_unsafe_access(!is_store, T_LONG, Relaxed, false); case vmIntrinsics::_getFloat: return inline_unsafe_access(!is_store, T_FLOAT, Relaxed, false); case vmIntrinsics::_getDouble: return inline_unsafe_access(!is_store, T_DOUBLE, Relaxed, false); + case vmIntrinsics::_getValue: return inline_unsafe_access(!is_store, T_VALUETYPE,Relaxed, false); case vmIntrinsics::_putReference: return inline_unsafe_access( is_store, T_OBJECT, Relaxed, false); case vmIntrinsics::_putBoolean: return inline_unsafe_access( is_store, T_BOOLEAN, Relaxed, false); @@ -624,6 +629,7 @@ case vmIntrinsics::_putLong: return inline_unsafe_access( is_store, T_LONG, Relaxed, false); case vmIntrinsics::_putFloat: return inline_unsafe_access( is_store, T_FLOAT, Relaxed, false); case vmIntrinsics::_putDouble: return inline_unsafe_access( is_store, T_DOUBLE, Relaxed, false); + case vmIntrinsics::_putValue: return inline_unsafe_access( is_store, T_VALUETYPE,Relaxed, false); case vmIntrinsics::_getReferenceVolatile: return inline_unsafe_access(!is_store, T_OBJECT, Volatile, false); case vmIntrinsics::_getBooleanVolatile: return inline_unsafe_access(!is_store, T_BOOLEAN, Volatile, false); @@ -2376,18 +2382,18 @@ if (!is_store) { // Object getReference(Object base, int/long offset), etc. BasicType rtype = sig->return_type()->basic_type(); - assert(rtype == type, "getter must return the expected value"); - assert(sig->count() == 2, "oop getter has 2 arguments"); + assert(rtype == type || (rtype == T_OBJECT && type == T_VALUETYPE), "getter must return the expected value"); + assert(sig->count() == 2 || (type == T_VALUETYPE && sig->count() == 3), "oop getter has 2 or 3 arguments"); assert(sig->type_at(0)->basic_type() == T_OBJECT, "getter base is object"); assert(sig->type_at(1)->basic_type() == T_LONG, "getter offset is correct"); } else { // void putReference(Object base, int/long offset, Object x), etc. assert(sig->return_type()->basic_type() == T_VOID, "putter must not return a value"); - assert(sig->count() == 3, "oop putter has 3 arguments"); + assert(sig->count() == 3 || (type == T_VALUETYPE && sig->count() == 4), "oop putter has 3 arguments"); assert(sig->type_at(0)->basic_type() == T_OBJECT, "putter base is object"); assert(sig->type_at(1)->basic_type() == T_LONG, "putter offset is correct"); BasicType vtype = sig->type_at(sig->count()-1)->basic_type(); - assert(vtype == type, "putter must accept the expected value"); + assert(vtype == type || (type == T_VALUETYPE && vtype == T_OBJECT), "putter must accept the expected value"); } #endif // ASSERT } @@ -2413,30 +2419,59 @@ assert(Unsafe_field_offset_to_byte_offset(11) == 11, "fieldOffset must be byte-scaled"); - if (base->is_ValueType()) { - if (is_store) { + ciValueKlass* value_klass = NULL; + if (type == T_VALUETYPE) { + Node* cls = null_check(argument(4)); + if (stopped()) { + return true; + } + Node* kls = load_klass_from_mirror(cls, false, NULL, 0); + const TypeKlassPtr* kls_t = _gvn.type(kls)->isa_klassptr(); + if (!kls_t->klass_is_exact()) { return false; } + ciKlass* klass = kls_t->klass(); + if (!klass->is_valuetype()) { + return false; + } + value_klass = klass->as_value_klass(); + } + + receiver = null_check(receiver); + if (stopped()) { + return true; + } + if (base->is_ValueType()) { ValueTypeNode* vt = base->as_ValueType(); - if (offset->is_Con()) { - long off = find_long_con(offset, 0); - ciValueKlass* vk = _gvn.type(vt)->is_valuetype()->value_klass(); - if ((long)(int)off != off || !vk->contains_field_offset(off)) { - return false; - } - receiver = null_check(receiver); - if (stopped()) { - return true; + if (is_store) { + if (!vt->is_allocated(&_gvn) || !_gvn.type(vt)->is_valuetype()->larval()) { + return false; } - - set_result(vt->field_value_by_offset((int)off, true)); - return true; + base = vt->get_oop(); } else { - receiver = null_check(receiver); - if (stopped()) { - return true; + if (offset->is_Con()) { + long off = find_long_con(offset, 0); + ciValueKlass* vk = _gvn.type(vt)->is_valuetype()->value_klass(); + if ((long)(int)off != off || !vk->contains_field_offset(off)) { + return false; + } + + ciField* f = vk->get_non_flattened_field_by_offset((int)off); + + if (f != NULL) { + BasicType bt = f->layout_type(); + if (bt == T_ARRAY || bt == T_NARROWOOP) { + bt = T_OBJECT; + } + if (bt == type) { + if (bt != T_VALUETYPE || f->type() == value_klass) { + set_result(vt->field_value_by_offset((int)off, false)); + return true; + } + } + } } vt = vt->allocate(this)->as_ValueType(); base = vt->get_oop(); @@ -2449,7 +2484,7 @@ if (_gvn.type(base)->isa_ptr() != TypePtr::NULL_PTR) { heap_base_oop = base; - } else if (type == T_OBJECT) { + } else if (type == T_OBJECT || (value_klass != NULL && value_klass->has_object_fields())) { return false; // off-heap oop accesses are not supported } @@ -2460,7 +2495,7 @@ decorators |= IN_HEAP; } - val = is_store ? argument(4) : NULL; + val = is_store ? argument(4 + (type == T_VALUETYPE ? 1 : 0)) : NULL; const TypePtr *adr_type = _gvn.type(adr)->isa_ptr(); @@ -2474,7 +2509,31 @@ } bool mismatched = false; - BasicType bt = alias_type->basic_type(); + BasicType bt = T_ILLEGAL; + ciField* field = NULL; + if (adr_type->isa_instptr()) { + const TypeInstPtr* instptr = adr_type->is_instptr(); + ciInstanceKlass* k = instptr->klass()->as_instance_klass(); + int off = instptr->offset(); + if (instptr->const_oop() != NULL && + instptr->klass() == ciEnv::current()->Class_klass() && + instptr->offset() >= (instptr->klass()->as_instance_klass()->size_helper() * wordSize)) { + k = instptr->const_oop()->as_instance()->java_lang_Class_klass()->as_instance_klass(); + field = k->get_field_by_offset(off, true); + } else { + field = k->get_non_flattened_field_by_offset(off); + } + if (field != NULL) { + bt = field->layout_type(); + } + assert(bt == alias_type->basic_type() || bt == T_VALUETYPE, "should match"); + if (field != NULL && bt == T_VALUETYPE && !field->is_flattened()) { + bt = T_OBJECT; + } + } else { + bt = alias_type->basic_type(); + } + if (bt != T_ILLEGAL) { assert(alias_type->adr_type()->is_oopptr(), "should be on-heap access"); if (bt == T_BYTE && adr_type->isa_aryptr()) { @@ -2495,6 +2554,28 @@ mismatched = true; // conservatively mark all "wide" on-heap accesses as mismatched } + if (type == T_VALUETYPE) { + if (adr_type->isa_instptr()) { + if (field == NULL || field->type() != value_klass) { + mismatched = true; + } + } else if (adr_type->isa_aryptr()) { + const Type* elem = adr_type->is_aryptr()->elem(); + if (!elem->isa_valuetype()) { + mismatched = true; + } else if (elem->is_valuetype()->value_klass() != value_klass) { + mismatched = true; + } + } + if (is_store) { + const Type* val_t = _gvn.type(val); + if (!val_t->isa_valuetype() || + val_t->is_valuetype()->value_klass() != value_klass) { + return false; + } + } + } + assert(!mismatched || alias_type->adr_type()->is_oopptr(), "off-heap access can't be mismatched"); if (mismatched) { @@ -2507,17 +2588,17 @@ // Figure out the memory ordering. decorators |= mo_decorator_for_access_kind(kind); - if (!is_store && type == T_OBJECT) { - const TypeOopPtr* tjp = sharpen_unsafe_type(alias_type, adr_type); - if (tjp != NULL) { - value_type = tjp; + if (!is_store) { + if (type == T_OBJECT) { + const TypeOopPtr* tjp = sharpen_unsafe_type(alias_type, adr_type); + if (tjp != NULL) { + value_type = tjp; + } + } else if (type == T_VALUETYPE) { + value_type = NULL; } } - receiver = null_check(receiver); - if (stopped()) { - return true; - } // Heap pointers get a null-check from the interpreter, // as a courtesy. However, this is not guaranteed by Unsafe, // and it is not possible to fully distinguish unintended nulls @@ -2526,14 +2607,24 @@ if (!is_store) { Node* p = NULL; // Try to constant fold a load from a constant field - ciField* field = alias_type->field(); + if (heap_base_oop != top() && field != NULL && field->is_constant() && !mismatched) { // final or stable field p = make_constant_from_field(field, heap_base_oop); } if (p == NULL) { // Could not constant fold the load - p = access_load_at(heap_base_oop, adr, adr_type, value_type, type, decorators); + if (type == T_VALUETYPE) { + if (adr_type->isa_instptr() && !mismatched) { + ciInstanceKlass* holder = adr_type->is_instptr()->klass()->as_instance_klass(); + int offset = adr_type->is_instptr()->offset(); + p = ValueTypeNode::make_from_flattened(this, value_klass, base, base, holder, offset, decorators); + } else { + p = ValueTypeNode::make_from_flattened(this, value_klass, base, adr, NULL, 0, decorators); + } + } else { + p = access_load_at(heap_base_oop, adr, adr_type, value_type, type, decorators); + } // Normalize the value returned by getBoolean in the following cases if (type == T_BOOLEAN && (mismatched || @@ -2560,9 +2651,8 @@ p = gvn().transform(new CastP2XNode(NULL, p)); p = ConvX2UL(p); } - if (field != NULL && field->is_flattenable()) { + if (field != NULL && field->is_flattenable()&& !field->is_flattened()) { // Load a non-flattened but flattenable value type from memory - assert(!field->is_flattened(), "unsafe value type load from flattened field"); if (value_type->value_klass()->is_scalarizable()) { p = ValueTypeNode::make_from_oop(this, p, value_type->value_klass()); } else { @@ -2580,9 +2670,65 @@ val = ConvL2X(val); val = gvn().transform(new CastX2PNode(val)); } - access_store_at(heap_base_oop, adr, adr_type, val, value_type, type, decorators); + if (type == T_VALUETYPE) { + if (adr_type->isa_instptr() && !mismatched) { + ciInstanceKlass* holder = adr_type->is_instptr()->klass()->as_instance_klass(); + int offset = adr_type->is_instptr()->offset(); + val->as_ValueType()->store_flattened(this, base, base, holder, offset, decorators); + } else { + val->as_ValueType()->store_flattened(this, base, adr, NULL, 0, decorators); + } + } else { + access_store_at(heap_base_oop, adr, adr_type, val, value_type, type, decorators); + } + } + + if (argument(1)->is_ValueType() && is_store) { + Node* value = ValueTypeNode::make_from_oop(this, base, _gvn.type(base)->value_klass()); + value = value->as_ValueType()->make_larval(this, false); + replace_in_map(argument(1), value); + } + + return true; +} + +bool LibraryCallKit::inline_unsafe_make_private_buffer() { + Node* receiver = argument(0); + Node* value = argument(1); + + receiver = null_check(receiver); + if (stopped()) { + return true; + } + + if (!value->is_ValueType()) { + return false; + } + + set_result(value->as_ValueType()->make_larval(this, true)); + + return true; +} +bool LibraryCallKit::inline_unsafe_finish_private_buffer() { + Node* receiver = argument(0); + Node* buffer = argument(1); + + receiver = null_check(receiver); + if (stopped()) { + return true; + } + + if (!buffer->is_ValueType()) { + return false; + } + + ValueTypeNode* vt = buffer->as_ValueType(); + if (!vt->is_allocated(&_gvn) || !_gvn.type(vt)->is_valuetype()->larval()) { + return false; } + set_result(vt->finish_larval(this)); + return true; } --- old/src/hotspot/share/opto/macro.cpp 2019-01-21 14:08:28.822754379 +0100 +++ new/src/hotspot/share/opto/macro.cpp 2019-01-21 14:08:28.636754533 +0100 @@ -723,6 +723,8 @@ } } else if (use->is_ValueType() && use->isa_ValueType()->get_oop() == res) { // ok to eliminate + } else if (use->is_Store()) { + // store to mark work } else if (use->Opcode() != Op_CastP2X) { // CastP2X is used by card mark if (use->is_Phi()) { if (use->outcnt() == 1 && use->unique_out()->Opcode() == Op_Return) { @@ -1062,6 +1064,8 @@ assert(use->isa_ValueType()->get_oop() == res, "unexpected value type use"); _igvn.rehash_node_delayed(use); use->isa_ValueType()->set_oop(_igvn.zerocon(T_VALUETYPE)); + } else if (use->is_Store()) { + _igvn.replace_node(use, use->in(MemNode::Memory)); } else { eliminate_gc_barrier(use); } @@ -1704,14 +1708,11 @@ Node* size_in_bytes) { InitializeNode* init = alloc->initialization(); // Store the klass & mark bits - Node* mark_node = NULL; - // For now only enable fast locking for non-array types - if ((EnableValhalla || UseBiasedLocking) && length == NULL) { - mark_node = make_load(control, rawmem, klass_node, in_bytes(Klass::prototype_header_offset()), TypeRawPtr::BOTTOM, T_ADDRESS); - } else { - mark_node = makecon(TypeRawPtr::make((address)markOopDesc::prototype())); + Node* mark_node = alloc->make_ideal_mark(&_igvn, object, control, rawmem, klass_node); + if (!mark_node->is_Con()) { + transform_later(mark_node); } - rawmem = make_store(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, T_ADDRESS); + rawmem = make_store(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, TypeX_X->basic_type()); rawmem = make_store(control, rawmem, object, oopDesc::klass_offset_in_bytes(), klass_node, T_METADATA); int header_size = alloc->minimum_header_size(); // conservatively small --- old/src/hotspot/share/opto/memnode.cpp 2019-01-21 14:08:29.095754154 +0100 +++ new/src/hotspot/share/opto/memnode.cpp 2019-01-21 14:08:28.911754306 +0100 @@ -1677,6 +1677,17 @@ } } + AllocateNode* alloc = AllocateNode::Ideal_allocation(address, phase); + if (alloc != NULL && mem->is_Proj() && + mem->in(0) != NULL && + mem->in(0) == alloc->initialization() && + Opcode() == Op_LoadX && + alloc->initialization()->proj_out_or_null(0) != NULL) { + InitializeNode* init = alloc->initialization(); + Node* control = init->proj_out(0); + return alloc->make_ideal_mark(phase, address, control, mem, NULL); + } + return progress ? this : NULL; } --- old/src/hotspot/share/opto/type.cpp 2019-01-21 14:08:29.375753923 +0100 +++ new/src/hotspot/share/opto/type.cpp 2019-01-21 14:08:29.192754074 +0100 @@ -2403,8 +2403,8 @@ //==============================TypeValueType======================================= //------------------------------make------------------------------------------- -const TypeValueType* TypeValueType::make(ciValueKlass* vk) { - return (TypeValueType*)(new TypeValueType(vk))->hashcons(); +const TypeValueType* TypeValueType::make(ciValueKlass* vk, bool larval) { + return (TypeValueType*)(new TypeValueType(vk, larval))->hashcons(); } //------------------------------meet------------------------------------------- @@ -2452,6 +2452,15 @@ case ValueType: { // All value types inherit from Object + const TypeValueType* other = t->is_valuetype(); + if (_vk == other->_vk) { + if (_larval == other->_larval || + !_larval) { + return this; + } else { + return t; + } + } return TypeInstPtr::NOTNULL; } @@ -2471,7 +2480,7 @@ // Structural equality check for Type representations bool TypeValueType::eq(const Type* t) const { const TypeValueType* vt = t->is_valuetype(); - return (_vk == vt->value_klass()); + return (_vk == vt->value_klass() && _larval == vt->larval()); } //------------------------------hash------------------------------------------- @@ -2501,7 +2510,7 @@ for (int i = 1; i < count; ++i) { st->print(", %s", _vk->declared_nonstatic_field_at(i)->type()->name()); } - st->print("}"); + st->print("}%s", _larval?":larval":""); } #endif --- old/src/hotspot/share/opto/type.hpp 2019-01-21 14:08:29.667753682 +0100 +++ new/src/hotspot/share/opto/type.hpp 2019-01-21 14:08:29.481753835 +0100 @@ -763,13 +763,18 @@ class TypeValueType : public Type { private: ciValueKlass* _vk; + bool _larval; protected: - TypeValueType(ciValueKlass* vk) : Type(ValueType) { _vk = vk; } + TypeValueType(ciValueKlass* vk, bool larval) + : Type(ValueType), + _vk(vk), _larval(larval) { + } public: - static const TypeValueType* make(ciValueKlass* vk); + static const TypeValueType* make(ciValueKlass* vk, bool larval = false); ciValueKlass* value_klass() const { return _vk; } + bool larval() const { return _larval; } virtual bool eq(const Type* t) const; virtual int hash() const; // Type specific hashing @@ -1880,6 +1885,8 @@ #define Op_SubX Op_SubL #define Op_XorX Op_XorL #define Op_URShiftX Op_URShiftL +#define Op_LoadX Op_LoadL +#define Op_StoreX Op_StoreL // conversions #define ConvI2X(x) ConvI2L(x) #define ConvL2X(x) (x) @@ -1928,6 +1935,8 @@ #define Op_SubX Op_SubI #define Op_XorX Op_XorI #define Op_URShiftX Op_URShiftI +#define Op_LoadX Op_LoadI +#define Op_StoreX Op_StoreI // conversions #define ConvI2X(x) (x) #define ConvL2X(x) ConvL2I(x) --- old/src/hotspot/share/opto/valuetypenode.cpp 2019-01-21 14:08:29.934753462 +0100 +++ new/src/hotspot/share/opto/valuetypenode.cpp 2019-01-21 14:08:29.749753614 +0100 @@ -315,11 +315,13 @@ base_input += vk->value_arg_slots(); } -const TypePtr* ValueTypeBaseNode::field_adr_type(Node* base, int offset, ciInstanceKlass* holder, PhaseGVN& gvn) const { +const TypePtr* ValueTypeBaseNode::field_adr_type(Node* base, int offset, ciInstanceKlass* holder, DecoratorSet decorators, PhaseGVN& gvn) const { const TypeAryPtr* ary_type = gvn.type(base)->isa_aryptr(); const TypePtr* adr_type = NULL; bool is_array = ary_type != NULL; - if (is_array) { + if ((decorators & C2_MISMATCHED) != 0) { + adr_type = TypeRawPtr::BOTTOM; + } else if (is_array) { // In the case of a flattened value type array, each field has its own slice adr_type = ary_type->with_field_offset(offset)->add_offset(Type::OffsetBot); } else { @@ -330,7 +332,7 @@ return adr_type; } -void ValueTypeBaseNode::load(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset) { +void ValueTypeBaseNode::load(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset, DecoratorSet decorators) { // Initialize the value type by loading its field values from // memory and adding the values as input edges to the node. for (uint i = 0; i < field_count(); ++i) { @@ -339,7 +341,7 @@ ciType* ft = field_type(i); if (field_is_flattened(i)) { // Recursively load the flattened value type field - value = ValueTypeNode::make_from_flattened(kit, ft->as_value_klass(), base, ptr, holder, offset); + value = ValueTypeNode::make_from_flattened(kit, ft->as_value_klass(), base, ptr, holder, offset, decorators); } else { const TypeOopPtr* oop_ptr = kit->gvn().type(base)->isa_oopptr(); bool is_array = (oop_ptr->isa_aryptr() != NULL); @@ -355,12 +357,11 @@ value = kit->gvn().transform(kit->makecon(con_type)); } else { // Load field value from memory - const TypePtr* adr_type = field_adr_type(base, offset, holder, kit->gvn()); + const TypePtr* adr_type = field_adr_type(base, offset, holder, decorators, kit->gvn()); Node* adr = kit->basic_plus_adr(base, ptr, offset); BasicType bt = type2field[ft->basic_type()]; assert(is_java_primitive(bt) || adr->bottom_type()->is_ptr_to_narrowoop() == UseCompressedOops, "inconsistent"); const Type* val_type = Type::get_const_type(ft); - DecoratorSet decorators = IN_HEAP | MO_UNORDERED; if (is_array) { decorators |= IS_ARRAY; } @@ -379,17 +380,17 @@ } } -void ValueTypeBaseNode::store_flattened(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset) const { +void ValueTypeBaseNode::store_flattened(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset, DecoratorSet decorators) const { // The value type is embedded into the object without an oop header. Subtract the // offset of the first field to account for the missing header when storing the values. if (holder == NULL) { holder = value_klass(); } holder_offset -= value_klass()->first_field_offset(); - store(kit, base, ptr, holder, holder_offset); + store(kit, base, ptr, holder, holder_offset, false, decorators); } -void ValueTypeBaseNode::store(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset, bool deoptimize_on_exception) const { +void ValueTypeBaseNode::store(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset, bool deoptimize_on_exception, DecoratorSet decorators) const { // Write field values to memory for (uint i = 0; i < field_count(); ++i) { int offset = holder_offset + field_offset(i); @@ -401,16 +402,15 @@ assert(!kit->gvn().type(value)->maybe_null(), "should never be null"); value = ValueTypeNode::make_from_oop(kit, value, ft->as_value_klass()); } - value->as_ValueType()->store_flattened(kit, base, ptr, holder, offset); + value->as_ValueType()->store_flattened(kit, base, ptr, holder, offset, decorators); } else { // Store field value to memory - const TypePtr* adr_type = field_adr_type(base, offset, holder, kit->gvn()); + const TypePtr* adr_type = field_adr_type(base, offset, holder, decorators, kit->gvn()); Node* adr = kit->basic_plus_adr(base, ptr, offset); BasicType bt = type2field[ft->basic_type()]; assert(is_java_primitive(bt) || adr->bottom_type()->is_ptr_to_narrowoop() == UseCompressedOops, "inconsistent"); const Type* val_type = Type::get_const_type(ft); const TypeAryPtr* ary_type = kit->gvn().type(base)->isa_aryptr(); - DecoratorSet decorators = IN_HEAP | MO_UNORDERED; if (ary_type != NULL) { decorators |= IS_ARRAY; } @@ -603,14 +603,14 @@ } // GraphKit wrapper for the 'make_from_flattened' method -ValueTypeNode* ValueTypeNode::make_from_flattened(GraphKit* kit, ciValueKlass* vk, Node* obj, Node* ptr, ciInstanceKlass* holder, int holder_offset) { +ValueTypeNode* ValueTypeNode::make_from_flattened(GraphKit* kit, ciValueKlass* vk, Node* obj, Node* ptr, ciInstanceKlass* holder, int holder_offset, DecoratorSet decorators) { // Create and initialize a ValueTypeNode by loading all field values from // a flattened value type field at 'holder_offset' or from a value type array. ValueTypeNode* vt = make_uninitialized(kit->gvn(), vk); // The value type is flattened into the object without an oop header. Subtract the // offset of the first field to account for the missing header when loading the values. holder_offset -= vk->first_field_offset(); - vt->load(kit, obj, ptr, holder, holder_offset); + vt->load(kit, obj, ptr, holder, holder_offset, decorators); assert(vt->is_loaded(&kit->gvn()) != obj, "holder oop should not be used as flattened value type oop"); return kit->gvn().transform(vt)->as_ValueType(); } @@ -621,6 +621,37 @@ return kit->gvn().transform(vt)->as_ValueType(); } +ValueTypeNode* ValueTypeNode::make_larval(GraphKit* kit, bool allocate) const { + ciValueKlass* vk = value_klass(); + ValueTypeNode* res = clone()->as_ValueType(); + if (allocate) { + Node* klass_node = kit->makecon(TypeKlassPtr::make(vk)); + Node* alloc_oop = kit->new_instance(klass_node, NULL, NULL, false); + AllocateNode* alloc = AllocateNode::Ideal_allocation(alloc_oop, &kit->gvn()); + alloc->_larval = true; + + store(kit, alloc_oop, alloc_oop, vk, 0, false); + res->set_oop(alloc_oop); + } + res->set_type(TypeValueType::make(vk, true)); + res = kit->gvn().transform(res)->as_ValueType(); + return res; +} + +ValueTypeNode* ValueTypeNode::finish_larval(GraphKit* kit) const { + Node* obj = get_oop(); + Node* mark_addr = kit->basic_plus_adr(obj, oopDesc::mark_offset_in_bytes()); + Node* mark = kit->make_load(NULL, mark_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered); + mark = kit->gvn().transform(new AndXNode(mark, kit->MakeConX(~markOopDesc::larval_mask_in_place))); + kit->store_to_memory(kit->control(), mark_addr, mark, TypeX_X->basic_type(), kit->gvn().type(mark_addr)->is_ptr(), MemNode::unordered); + + ciValueKlass* vk = value_klass(); + ValueTypeNode* res = clone()->as_ValueType(); + res->set_type(TypeValueType::make(vk, false)); + res = kit->gvn().transform(res)->as_ValueType(); + return res; +} + Node* ValueTypeNode::is_loaded(PhaseGVN* phase, ciValueKlass* vk, Node* base, int holder_offset) { if (vk == NULL) { vk = value_klass(); --- old/src/hotspot/share/opto/valuetypenode.hpp 2019-01-21 14:08:30.189753251 +0100 +++ new/src/hotspot/share/opto/valuetypenode.hpp 2019-01-21 14:08:30.004753404 +0100 @@ -53,7 +53,7 @@ // Initialize the value type fields with the inputs or outputs of a MultiNode void initialize(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, int base_offset, uint& base_input, bool in); - const TypePtr* field_adr_type(Node* base, int offset, ciInstanceKlass* holder, PhaseGVN& gvn) const; + const TypePtr* field_adr_type(Node* base, int offset, ciInstanceKlass* holder, DecoratorSet decorators, PhaseGVN& gvn) const; public: // Support for control flow merges @@ -81,11 +81,11 @@ void make_scalar_in_safepoints(PhaseIterGVN* igvn); // Store the value type as a flattened (headerless) representation - void store_flattened(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder = NULL, int holder_offset = 0) const; + void store_flattened(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder = NULL, int holder_offset = 0, DecoratorSet decorators = IN_HEAP | MO_UNORDERED) const; // Store the field values to memory - void store(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset = 0, bool deoptimize_on_exception = false) const; + void store(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset = 0, bool deoptimize_on_exception = false, DecoratorSet decorators = IN_HEAP | MO_UNORDERED) const; // Initialize the value type by loading its field values from memory - void load(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset = 0); + void load(GraphKit* kit, Node* base, Node* ptr, ciInstanceKlass* holder, int holder_offset = 0, DecoratorSet decorators = IN_HEAP | MO_UNORDERED); // Allocates the value type (if not yet allocated) ValueTypeBaseNode* allocate(GraphKit* kit, bool deoptimize_on_exception = false); @@ -123,9 +123,11 @@ // Create and initialize by loading the field values from an oop static ValueTypeNode* make_from_oop(GraphKit* kit, Node* oop, ciValueKlass* vk); // Create and initialize by loading the field values from a flattened field or array - static ValueTypeNode* make_from_flattened(GraphKit* kit, ciValueKlass* vk, Node* obj, Node* ptr, ciInstanceKlass* holder = NULL, int holder_offset = 0); + static ValueTypeNode* make_from_flattened(GraphKit* kit, ciValueKlass* vk, Node* obj, Node* ptr, ciInstanceKlass* holder = NULL, int holder_offset = 0, DecoratorSet decorators = IN_HEAP | MO_UNORDERED); // Create and initialize with the inputs or outputs of a MultiNode (method entry or call) static ValueTypeNode* make_from_multi(GraphKit* kit, MultiNode* multi, ciValueKlass* vk, uint& base_input, bool in); + ValueTypeNode* make_larval(GraphKit* kit, bool allocate) const; + ValueTypeNode* finish_larval(GraphKit* kit) const; // Returns the constant oop of the default value type allocation static Node* default_oop(PhaseGVN& gvn, ciValueKlass* vk); --- old/src/java.base/share/classes/jdk/internal/misc/Unsafe.java 2019-01-21 14:08:30.444753041 +0100 +++ new/src/java.base/share/classes/jdk/internal/misc/Unsafe.java 2019-01-21 14:08:30.256753196 +0100 @@ -239,6 +239,7 @@ * @throws RuntimeException No defined exceptions are thrown, not even * {@link NullPointerException} */ + @HotSpotIntrinsicCandidate public native V getValue(Object o, long offset, Class vc); /** @@ -258,6 +259,7 @@ * @throws RuntimeException No defined exceptions are thrown, not even * {@link NullPointerException} */ + @HotSpotIntrinsicCandidate public native void putValue(Object o, long offset, Class vc, V v); /** @@ -300,6 +302,7 @@ * @param value a value instance * @param the type of the given value instance */ + @HotSpotIntrinsicCandidate public native V makePrivateBuffer(V value); /** @@ -308,6 +311,7 @@ * @param value a value instance * @param the type of the given value instance */ + @HotSpotIntrinsicCandidate public native V finishPrivateBuffer(V value); /** --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2019-01-21 14:08:30.717752815 +0100 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2019-01-21 14:08:30.536752965 +0100 @@ -334,16 +334,26 @@ private static final Unsafe U = Unsafe.getUnsafe(); private static final long X_OFFSET; + private static final long Y_OFFSET; + private static final long V1_OFFSET; + private static final boolean V1_FLATTENED; static { try { Field xField = MyValue1.class.getDeclaredField("x"); X_OFFSET = U.objectFieldOffset(xField); + Field yField = MyValue1.class.getDeclaredField("y"); + Y_OFFSET = U.objectFieldOffset(yField); + Field v1Field = MyValue1.class.getDeclaredField("v1"); + V1_OFFSET = U.objectFieldOffset(v1Field); + V1_FLATTENED = U.isFlattened(v1Field); } catch (Exception e) { throw new RuntimeException(e); } } - @Test() + protected static final String CALL_Unsafe = START + "CallStaticJava" + MID + "# Static jdk.internal.misc.Unsafe::" + END; + + @Test(failOn=CALL_Unsafe) public int test21(MyValue1 v) { return U.getInt(v, X_OFFSET); } @@ -355,23 +365,23 @@ Asserts.assertEQ(res, v.x); } - @Test() + MyValue1.val test22_vt; + @Test(failOn=CALL_Unsafe + ALLOC) public void test22(MyValue1 v) { - try { - v = U.makePrivateBuffer(v); - U.putInt(v, X_OFFSET, 0); - } finally { - v = U.finishPrivateBuffer(v); - } + v = U.makePrivateBuffer(v); + U.putInt(v, X_OFFSET, rI); + v = U.finishPrivateBuffer(v); + test22_vt = v; } @DontCompile public void test22_verifier(boolean warmup) { MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); - test22(v); + test22(v.setX(v, 0)); + Asserts.assertEQ(test22_vt.hash(), v.hash()); } - @Test() + @Test(failOn=CALL_Unsafe) public int test23(MyValue1 v, long offset) { return U.getInt(v, offset); } @@ -385,7 +395,7 @@ MyValue1.val test24_vt = MyValue1.createWithFieldsInline(rI, rL); - @Test() + @Test(failOn=CALL_Unsafe) public int test24(long offset) { return U.getInt(test24_vt, offset); } @@ -450,9 +460,9 @@ } } - @Test() + @Test(failOn=CALL_Unsafe) public MyValue1 test27() { - return (MyValue1)U.getObject(this, TEST27_OFFSET); + return (MyValue1)U.getReference(this, TEST27_OFFSET); } @DontCompile @@ -460,4 +470,249 @@ MyValue1 res = test27(); Asserts.assertEQ(res.hash(), test24_vt.hash()); } + + // Mismatched type + @Test(failOn=CALL_Unsafe) + public int test28(MyValue1 v) { + return U.getByte(v, X_OFFSET); + } + + @DontCompile + public void test28_verifier(boolean warmup) { + MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); + int res = test28(v); + if (java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.LITTLE_ENDIAN) { + Asserts.assertEQ(res, (int)((byte)v.x)); + } else { + Asserts.assertEQ(res, (int)((byte)Integer.reverseBytes(v.x))); + } + } + + // Wrong alignment + @Test(failOn=CALL_Unsafe) + public long test29(MyValue1 v) { + // Read the field that's guaranteed to not be last in the + // value so we don't read out of the value + if (X_OFFSET < Y_OFFSET) { + return U.getInt(v, X_OFFSET+1); + } + return U.getLong(v, Y_OFFSET+1); + } + + @DontCompile + public void test29_verifier(boolean warmup) { + MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); + long res = test29(v); + if (java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.LITTLE_ENDIAN) { + if (X_OFFSET < Y_OFFSET) { + Asserts.assertEQ(((int)res) << 8, (v.x >> 8) << 8); + } else { + Asserts.assertEQ(res << 8, (v.y >> 8) << 8); + } + } else { + if (X_OFFSET < Y_OFFSET) { + Asserts.assertEQ(((int)res), v.x >>> 8); + } else { + Asserts.assertEQ(res, v.y >>> 8); + } + } + } + + // getValue to retrieve flattened field from value + @Test(failOn=CALL_Unsafe) + public MyValue2 test30(MyValue1 v) { + if (V1_FLATTENED) { + return U.getValue(v, V1_OFFSET, MyValue2.class); + } + return (MyValue2)U.getReference(v, V1_OFFSET); + } + + @DontCompile + public void test30_verifier(boolean warmup) { + MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); + MyValue2 res = test30(v); + Asserts.assertEQ(res.hash(), v.v1.hash()); + } + + MyValue1.val test31_vt; + private static final long TEST31_VT_OFFSET; + private static final boolean TEST31_VT_FLATTENED; + static { + try { + Field test31_vt_Field = TestIntrinsics.class.getDeclaredField("test31_vt"); + TEST31_VT_OFFSET = U.objectFieldOffset(test31_vt_Field); + TEST31_VT_FLATTENED = U.isFlattened(test31_vt_Field); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + // getValue to retrieve flattened field from object + @Test(failOn=CALL_Unsafe) + public MyValue1 test31() { + if (TEST31_VT_FLATTENED) { + return U.getValue(this, TEST31_VT_OFFSET, MyValue1.class); + } + return (MyValue1)U.getReference(this, TEST31_VT_OFFSET); + } + + @DontCompile + public void test31_verifier(boolean warmup) { + test31_vt = MyValue1.createWithFieldsInline(rI, rL); + MyValue1 res = test31(); + Asserts.assertEQ(res.hash(), test31_vt.hash()); + } + + // putValue to set flattened field in object + @Test(failOn=CALL_Unsafe) + public void test32(MyValue1 vt) { + if (TEST31_VT_FLATTENED) { + U.putValue(this, TEST31_VT_OFFSET, MyValue1.class, vt); + } else { + U.putReference(this, TEST31_VT_OFFSET, vt); + } + } + + @DontCompile + public void test32_verifier(boolean warmup) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + test31_vt = MyValue1.createDefaultInline(); + test32(vt); + Asserts.assertEQ(vt.hash(), test31_vt.hash()); + } + + private static final int TEST33_BASE_OFFSET; + private static final int TEST33_INDEX_SCALE; + private static final boolean TEST33_FLATTENED_ARRAY; + static { + try { + TEST33_BASE_OFFSET = U.arrayBaseOffset(MyValue1[].class); + TEST33_INDEX_SCALE = U.arrayIndexScale(MyValue1[].class); + TEST33_FLATTENED_ARRAY = U.isFlattenedArray(MyValue1[].class); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + // getValue to retrieve flattened field from array + @Test(failOn=CALL_Unsafe) + public MyValue1 test33(MyValue1[] arr) { + if (TEST33_FLATTENED_ARRAY) { + return U.getValue(arr, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, MyValue1.class); + } + return (MyValue1)U.getReference(arr, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE); + } + + @DontCompile + public void test33_verifier(boolean warmup) { + MyValue1[] arr = new MyValue1[2]; + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + arr[1] = vt; + MyValue1 res = test33(arr); + Asserts.assertEQ(res.hash(), vt.hash()); + } + + // putValue to set flattened field in array + @Test(failOn=CALL_Unsafe) + public void test34(MyValue1[] arr, MyValue1 vt) { + if (TEST33_FLATTENED_ARRAY) { + U.putValue(arr, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, MyValue1.class, vt); + } else { + U.putReference(arr, TEST33_BASE_OFFSET + TEST33_INDEX_SCALE, vt); + } + } + + @DontCompile + public void test34_verifier(boolean warmup) { + MyValue1[] arr = new MyValue1[2]; + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + test34(arr, vt); + Asserts.assertEQ(arr[1].hash(), vt.hash()); + } + + // getValue to retrieve flattened field from object with unknown + // container type + @Test(failOn=CALL_Unsafe) + public MyValue1 test35(Object o) { + if (TEST31_VT_FLATTENED) { + return U.getValue(o, TEST31_VT_OFFSET, MyValue1.class); + } + return (MyValue1)U.getReference(o, TEST31_VT_OFFSET); + } + + @DontCompile + public void test35_verifier(boolean warmup) { + test31_vt = MyValue1.createWithFieldsInline(rI, rL); + MyValue1 res = test35(this); + Asserts.assertEQ(res.hash(), test31_vt.hash()); + } + + // getValue to retrieve flattened field from object at unknown + // offset + @Test(failOn=CALL_Unsafe) + public MyValue1 test36(long offset) { + if (TEST31_VT_FLATTENED) { + return U.getValue(this, offset, MyValue1.class); + } + return (MyValue1)U.getReference(this, offset); + } + + @DontCompile + public void test36_verifier(boolean warmup) { + test31_vt = MyValue1.createWithFieldsInline(rI, rL); + MyValue1 res = test36(TEST31_VT_OFFSET); + Asserts.assertEQ(res.hash(), test31_vt.hash()); + } + + // putValue to set flattened field in object with unknown + // container + @Test(failOn=CALL_Unsafe) + public void test37(Object o, MyValue1 vt) { + if (TEST31_VT_FLATTENED) { + U.putValue(o, TEST31_VT_OFFSET, MyValue1.class, vt); + } else { + U.putReference(o, TEST31_VT_OFFSET, vt); + } + } + + @DontCompile + public void test37_verifier(boolean warmup) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + test31_vt = MyValue1.createDefaultInline(); + test37(this, vt); + Asserts.assertEQ(vt.hash(), test31_vt.hash()); + } + + // putValue to set flattened field in object, non value argument + // to store + @Test(match = { CALL_Unsafe }, matchCount = { 1 }) + public void test38(Object o) { + if (TEST31_VT_FLATTENED) { + U.putValue(this, TEST31_VT_OFFSET, MyValue1.class, o); + } else { + U.putReference(this, TEST31_VT_OFFSET, o); + } + } + + @DontCompile + public void test38_verifier(boolean warmup) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + test31_vt = MyValue1.createDefaultInline(); + test38(vt); + Asserts.assertEQ(vt.hash(), test31_vt.hash()); + } + + @Test(failOn=CALL_Unsafe) + public MyValue1 test39(MyValue1 v) { + v = U.makePrivateBuffer(v); + U.putInt(v, X_OFFSET, rI); + v = U.finishPrivateBuffer(v); + return v; + } + + @DontCompile + public void test39_verifier(boolean warmup) { + MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); + MyValue1 res = test39(v.setX(v, 0)); + Asserts.assertEQ(res.hash(), v.hash()); + } }