--- old/src/hotspot/share/oops/markOop.hpp 2018-12-11 10:43:00.000000000 -0800 +++ new/src/hotspot/share/oops/markOop.hpp 2018-12-11 10:42:58.000000000 -0800 @@ -106,7 +106,9 @@ // reference doesn't use the lowest bit ("2 << thread_shift"), we can use // this illegal thread pointer alignment to denote "always locked" pattern. // -// [ |1| epoch | age | 1 | 01] permanently locked +// [ | larval |1| epoch | age | 1 | 01] permanently locked +// +// A private buffered value is always locked and can be in a larval state. // class BasicLock; @@ -126,7 +128,9 @@ max_hash_bits = BitsPerWord - age_bits - lock_bits - biased_lock_bits, hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits, cms_bits = LP64_ONLY(1) NOT_LP64(0), - epoch_bits = 2 + epoch_bits = 2, + always_locked_bits = 1, + larval_bits = 1 }; // The biased locking code currently requires that the age bits be @@ -137,7 +141,8 @@ cms_shift = age_shift + age_bits, hash_shift = cms_shift + cms_bits, epoch_shift = hash_shift, - thread_shift = epoch_shift + epoch_bits + thread_shift = epoch_shift + epoch_bits, + larval_shift = thread_shift + always_locked_bits }; enum { lock_mask = right_n_bits(lock_bits), @@ -150,11 +155,13 @@ epoch_mask = right_n_bits(epoch_bits), epoch_mask_in_place = epoch_mask << epoch_shift, cms_mask = right_n_bits(cms_bits), - cms_mask_in_place = cms_mask << cms_shift + cms_mask_in_place = cms_mask << cms_shift, #ifndef _WIN64 - ,hash_mask = right_n_bits(hash_bits), - hash_mask_in_place = (address_word)hash_mask << hash_shift + hash_mask = right_n_bits(hash_bits), + hash_mask_in_place = (address_word)hash_mask << hash_shift, #endif + larval_mask = right_n_bits(larval_bits), + larval_mask_in_place = larval_mask << larval_shift }; // Alignment of JavaThread pointers encoded in object header required by biased locking @@ -186,6 +193,8 @@ enum { max_bias_epoch = epoch_mask }; + enum { larval_state_pattern = (1 << larval_shift) }; + static markOop always_locked_prototype() { return markOop(always_locked_pattern); } @@ -371,6 +380,17 @@ return hash() == no_hash; } + // private buffered value operations + markOop enter_larval_state() const { + return markOop((value() & ~larval_mask_in_place) | larval_state_pattern); + } + markOop exit_larval_state() const { + return markOop(value() & ~larval_mask_in_place); + } + bool is_larval_state() const { + return (value() & larval_mask_in_place) == larval_state_pattern; + } + // Prototype mark for initialization static markOop prototype() { return markOop( no_hash_in_place | no_lock_in_place ); --- old/src/hotspot/share/prims/unsafe.cpp 2018-12-11 10:43:05.000000000 -0800 +++ new/src/hotspot/share/prims/unsafe.cpp 2018-12-11 10:43:04.000000000 -0800 @@ -31,6 +31,7 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "logging/log.hpp" +#include "logging/logStream.hpp" #include "oops/access.inline.hpp" #include "oops/fieldStreams.hpp" #include "oops/objArrayOop.inline.hpp" @@ -148,7 +149,6 @@ return byte_offset; } - ///// Data read/writes on the Java heap and in native (off-heap) memory /** @@ -233,11 +233,11 @@ GuardUnsafeAccess guard(_thread); RawAccess<>::store(addr(), normalize_for_write(x)); } else { + assert(!_obj->is_value() || _obj->mark()->is_larval_state(), "must be an object instance or a larval value"); HeapAccess<>::store_at(_obj, _offset, normalize_for_write(x)); } } - T get_volatile() { if (_obj == NULL) { GuardUnsafeAccess guard(_thread); @@ -281,16 +281,22 @@ } #endif // ASSERT -static void assert_and_log_unsafe_value_type_access(oop p, jlong offset, ValueKlass* vk) { +static void assert_and_log_unsafe_value_access(oop p, jlong offset, ValueKlass* vk) { Klass* k = p->klass(); - #ifdef ASSERT if (k->is_instance_klass()) { assert_field_offset_sane(p, offset); fieldDescriptor fd; bool found = get_field_descriptor(p, offset, &fd); - assert(found, "value field not found"); - assert(fd.is_flattened(), "field not flat"); + if (found) { + assert(found, "value field not found"); + assert(fd.is_flattened(), "field not flat"); + } else { + if (log_is_enabled(Trace, valuetypes)) { + log_trace(valuetypes)("not a field in %s at offset " SIZE_FORMAT_HEX, + p->klass()->external_name(), offset); + } + } } else if (k->is_valueArray_klass()) { ValueArrayKlass* vak = ValueArrayKlass::cast(k); int index = (offset - vak->array_header_in_bytes()) / vak->element_byte_size(); @@ -300,17 +306,17 @@ ShouldNotReachHere(); } #endif // ASSERT - if (log_is_enabled(Trace, valuetypes)) { if (k->is_valueArray_klass()) { ValueArrayKlass* vak = ValueArrayKlass::cast(k); int index = (offset - vak->array_header_in_bytes()) / vak->element_byte_size(); address dest = (address)((valueArrayOop)p)->value_at_addr(index, vak->layout_helper()); - log_trace(valuetypes)("array type %s index %d element size %d offset " SIZE_FORMAT_HEX " at " INTPTR_FORMAT, - vak->external_name(), index, vak->element_byte_size(), offset, p2i(dest)); + log_trace(valuetypes)("%s array type %s index %d element size %d offset " SIZE_FORMAT_HEX " at " INTPTR_FORMAT, + p->klass()->external_name(), vak->external_name(), + index, vak->element_byte_size(), offset, p2i(dest)); } else { - log_trace(valuetypes)("field type %s at offset " SIZE_FORMAT_HEX, - vk->external_name(), offset); + log_trace(valuetypes)("%s field type %s at offset " SIZE_FORMAT_HEX, + p->klass()->external_name(), vk->external_name(), offset); } } } @@ -329,36 +335,64 @@ oop x = JNIHandles::resolve(x_h); oop p = JNIHandles::resolve(obj); assert_field_offset_sane(p, offset); + assert(!p->is_value() || p->mark()->is_larval_state(), "must be an object instance or a larval value"); HeapAccess::oop_store_at(p, offset, x); } UNSAFE_END +UNSAFE_ENTRY(jlong, Unsafe_ValueHeaderSize(JNIEnv *env, jobject unsafe, jclass c)) { + Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(c)); + ValueKlass* vk = ValueKlass::cast(k); + return vk->first_field_offset(); +} UNSAFE_END + UNSAFE_ENTRY(jboolean, Unsafe_IsFlattenedArray(JNIEnv *env, jobject unsafe, jclass c)) { Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(c)); return k->is_valueArray_klass(); } UNSAFE_END -UNSAFE_ENTRY(jobject, Unsafe_GetValue(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jclass c)) { - oop p = JNIHandles::resolve(obj); - Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(c)); +UNSAFE_ENTRY(jobject, Unsafe_GetValue(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jclass vc)) { + oop base = JNIHandles::resolve(obj); + Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(vc)); ValueKlass* vk = ValueKlass::cast(k); - assert_and_log_unsafe_value_type_access(p, offset, vk); - Handle p_h(THREAD, p); + assert_and_log_unsafe_value_access(base, offset, vk); + Handle base_h(THREAD, base); oop v = vk->allocate_instance(CHECK_NULL); // allocate instance vk->initialize(CHECK_NULL); // If field is a default value, value class might not be initialized yet - vk->value_store(((char*)(oopDesc*)p_h()) + offset, + vk->value_store(((address)(oopDesc*)base_h()) + offset, vk->data_for_oop(v), true, true); return JNIHandles::make_local(env, v); } UNSAFE_END -UNSAFE_ENTRY(void, Unsafe_PutValue(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jclass c, jobject value)) { - oop v = JNIHandles::resolve(value); - oop p = JNIHandles::resolve(obj); - Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(c)); +UNSAFE_ENTRY(void, Unsafe_PutValue(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jclass vc, jobject value)) { + oop base = JNIHandles::resolve(obj); + Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(vc)); ValueKlass* vk = ValueKlass::cast(k); - assert_and_log_unsafe_value_type_access(p, offset, vk); + assert(!base->is_value() || base->mark()->is_larval_state(), "must be an object instance or a larval value"); + assert_and_log_unsafe_value_access(base, offset, vk); + oop v = JNIHandles::resolve(value); vk->value_store(vk->data_for_oop(v), - ((char*)(oopDesc*)p) + offset, true, true); + ((address)(oopDesc*)base) + offset, true, true); +} UNSAFE_END + +UNSAFE_ENTRY(jobject, Unsafe_MakePrivateBuffer(JNIEnv *env, jobject unsafe, jobject value)) { + oop v = JNIHandles::resolve_non_null(value); + assert(v->is_value(), "must be a value instance"); + Handle vh(THREAD, v); + ValueKlass* vk = ValueKlass::cast(v->klass()); + instanceOop new_value = vk->allocate_instance(CHECK_NULL); + vk->value_store(vk->data_for_oop(vh()), vk->data_for_oop(new_value), true, false); + markOop mark = new_value->mark(); + new_value->set_mark(mark->enter_larval_state()); + return JNIHandles::make_local(env, new_value); +} UNSAFE_END + +UNSAFE_ENTRY(jobject, Unsafe_FinishPrivateBuffer(JNIEnv *env, jobject unsafe, jobject value)) { + oop v = JNIHandles::resolve(value); + assert(v->mark()->is_larval_state(), "must be a larval value"); + markOop mark = v->mark(); + v->set_mark(mark->exit_larval_state()); + return JNIHandles::make_local(env, v); } UNSAFE_END UNSAFE_ENTRY(jobject, Unsafe_GetReferenceVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { @@ -1122,10 +1156,10 @@ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) #define DECLARE_GETPUTOOP(Type, Desc) \ - {CC "get" #Type, CC "(" OBJ "J)" #Desc, FN_PTR(Unsafe_Get##Type)}, \ - {CC "put" #Type, CC "(" OBJ "J" #Desc ")V", FN_PTR(Unsafe_Put##Type)}, \ - {CC "get" #Type "Volatile", CC "(" OBJ "J)" #Desc, FN_PTR(Unsafe_Get##Type##Volatile)}, \ - {CC "put" #Type "Volatile", CC "(" OBJ "J" #Desc ")V", FN_PTR(Unsafe_Put##Type##Volatile)} + {CC "get" #Type, CC "(" OBJ "J)" #Desc, FN_PTR(Unsafe_Get##Type)}, \ + {CC "put" #Type, CC "(" OBJ "J" #Desc ")V", FN_PTR(Unsafe_Put##Type)}, \ + {CC "get" #Type "Volatile", CC "(" OBJ "J)" #Desc, FN_PTR(Unsafe_Get##Type##Volatile)}, \ + {CC "put" #Type "Volatile", CC "(" OBJ "J" #Desc ")V", FN_PTR(Unsafe_Put##Type##Volatile)} static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = { @@ -1134,9 +1168,12 @@ {CC "getReferenceVolatile", CC "(" OBJ "J)" OBJ, FN_PTR(Unsafe_GetReferenceVolatile)}, {CC "putReferenceVolatile", CC "(" OBJ "J" OBJ ")V", FN_PTR(Unsafe_PutReferenceVolatile)}, - {CC "isFlattenedArray", CC "(" CLS ")Z", FN_PTR(Unsafe_IsFlattenedArray)}, - {CC "getValue", CC "(" OBJ "J" CLS ")" OBJ "", FN_PTR(Unsafe_GetValue)}, - {CC "putValue", CC "(" OBJ "J" CLS OBJ ")V", FN_PTR(Unsafe_PutValue)}, + {CC "isFlattenedArray", CC "(" CLS ")Z", FN_PTR(Unsafe_IsFlattenedArray)}, + {CC "getValue", CC "(" OBJ "J" CLS ")" OBJ, FN_PTR(Unsafe_GetValue)}, + {CC "putValue", CC "(" OBJ "J" CLS OBJ ")V", FN_PTR(Unsafe_PutValue)}, + {CC "makePrivateBuffer", CC "(" OBJ ")" OBJ, FN_PTR(Unsafe_MakePrivateBuffer)}, + {CC "finishPrivateBuffer", CC "(" OBJ ")" OBJ, FN_PTR(Unsafe_FinishPrivateBuffer)}, + {CC "valueHeaderSize", CC "(" CLS ")J", FN_PTR(Unsafe_ValueHeaderSize)}, {CC "getUncompressedObject", CC "(" ADR ")" OBJ, FN_PTR(Unsafe_GetUncompressedObject)}, --- old/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java 2018-12-11 10:43:11.000000000 -0800 +++ new/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java 2018-12-11 10:43:10.000000000 -0800 @@ -658,7 +658,7 @@ private static LambdaForm preparedFieldLambdaForm(MemberName m) { Class ftype = m.getFieldType(); boolean isVolatile = m.isVolatile(); - boolean isFlatValue = m.isFlatValue(); + boolean isFlatValue = m.isFlattened(); boolean canBeNull = m.canBeNull(); byte formOp; switch (m.getReferenceKind()) { --- old/src/java.base/share/classes/java/lang/invoke/MemberName.java 2018-12-11 10:43:17.000000000 -0800 +++ new/src/java.base/share/classes/java/lang/invoke/MemberName.java 2018-12-11 10:43:15.000000000 -0800 @@ -455,7 +455,6 @@ static final int SYNTHETIC = 0x00001000; static final int ANNOTATION = 0x00002000; static final int ENUM = 0x00004000; - static final int FLATTENABLE = 0x00000100; static final int FLATTENED = 0x00008000; /** Utility method to query the modifier flags of this member; returns false if the member is not a method. */ @@ -471,19 +470,17 @@ return testAllFlags(SYNTHETIC); } - /* - * Query whether this member is a flattenable field. - * - * A flattenable field whose type must be of value class with ACC_FLATTENABLE flag set. - * A field of value type may or may not be flattenable. - */ - public boolean isFlattenable() { return (flags & FLATTENABLE) == FLATTENABLE; } - - /** Query whether this member is a flat value field */ - public boolean isFlatValue() { return (flags & FLATTENED) == FLATTENED; } + /** Query whether this member is a flattened field */ + public boolean isFlattened() { return (flags & FLATTENED) == FLATTENED; } /** Query whether this member can be assigned to null. */ - public boolean canBeNull() { return !isFlattenable(); } + public boolean canBeNull() { + if (isField()) { + Class type = getFieldType(); + return type == type.asBoxType(); + } + return false; + } static final String CONSTRUCTOR_NAME = ""; // the ever-popular --- old/src/java.base/share/classes/java/lang/invoke/VarHandles.java 2018-12-11 10:43:21.000000000 -0800 +++ new/src/java.base/share/classes/java/lang/invoke/VarHandles.java 2018-12-11 10:43:20.000000000 -0800 @@ -33,7 +33,7 @@ if (!f.isStatic()) { long foffset = MethodHandleNatives.objectFieldOffset(f); if (!type.isPrimitive()) { - if (f.isFlatValue()) { + if (f.isFlattened()) { return f.isFinal() && !isWriteAllowedOnFinalFields ? new VarHandleReferences.FlatValueFieldInstanceReadOnly(refc, foffset, type) : new VarHandleReferences.FlatValueFieldInstanceReadWrite(refc, foffset, type); @@ -100,7 +100,7 @@ Object base = MethodHandleNatives.staticFieldBase(f); long foffset = MethodHandleNatives.staticFieldOffset(f); if (!type.isPrimitive()) { - assert(!f.isFlatValue()); // static field is not flattened + assert(!f.isFlattened()); // static field is not flattened return f.isFinal() && !isWriteAllowedOnFinalFields ? new VarHandleReferences.FieldStaticReadOnly(base, foffset, type) : new VarHandleReferences.FieldStaticReadWrite(base, foffset, type, f.canBeNull()); --- old/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java 2018-12-11 10:43:28.000000000 -0800 +++ new/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java 2018-12-11 10:43:26.000000000 -0800 @@ -165,19 +165,4 @@ { return ctor.newInstanceWithCaller(args, true, caller); } - /* - * With a Field object for C.f of type T, T should have been resolved - * and T must be present in ValueType attribute. - */ - public boolean isFlattenable(Field f) { - return (f.getModifiers() & Modifier.FLATTENABLE) == Modifier.FLATTENABLE; - } - - public boolean isFlatValue(Field f) { - return (f.getModifiers() & Modifier.FLATTENED) == Modifier.FLATTENED; - } - - public boolean canBeNull(Field f) { - return !isFlattenable(f); - } } --- old/src/java.base/share/classes/jdk/internal/misc/Unsafe.java 2018-12-11 10:43:34.000000000 -0800 +++ new/src/java.base/share/classes/jdk/internal/misc/Unsafe.java 2018-12-11 10:43:32.000000000 -0800 @@ -183,6 +183,15 @@ return c.isValue() && c == c.asValueType(); } + private static final int JVM_ACC_FLATTENED = 0x00008000; // HotSpot-specific bit + + /** + * Returns true if the given field is flattened. + */ + public boolean isFlattened(Field f) { + return (f.getModifiers() & JVM_ACC_FLATTENED) == JVM_ACC_FLATTENED; + } + /** * Returns true if the given class is a flattened array. */ @@ -236,8 +245,7 @@ * Stores the given value into a given Java variable. * * Unless the reference {@code o} being stored is either null - * or matches the field type and not in a value container, - * the results are undefined. + * or matches the field type, the results are undefined. * * @param o Java heap object in which the variable resides, if any, else * null @@ -252,6 +260,33 @@ */ public native void putValue(Object o, long offset, Class vc, V v); + /** + * Returns an object instance with a private buffered value whose layout + * and contents is exactly the given value instance. The return object + * is in the larval state that can be updated using the unsafe put operation. + * + * @param value a value instance + * @param the type of the given value instance + */ + public native V makePrivateBuffer(V value); + + /** + * Exits the larval state and returns a value instance. + * + * @param value a value instance + * @param the type of the given value instance + */ + public native V finishPrivateBuffer(V value); + + /** + * Returns the header size of the given value class + * + * @param vc Value class + * @param value clas + * @return the header size of the value class + */ + public native long valueHeaderSize(Class vc); + /** @see #getInt(Object, long) */ @HotSpotIntrinsicCandidate public native boolean getBoolean(Object o, long offset); --- old/src/java.base/share/classes/jdk/internal/reflect/LangReflectAccess.java 2018-12-11 10:43:40.000000000 -0800 +++ new/src/java.base/share/classes/jdk/internal/reflect/LangReflectAccess.java 2018-12-11 10:43:39.000000000 -0800 @@ -122,15 +122,4 @@ /** Returns a new instance created by the given constructor with access check */ public T newInstance(Constructor ctor, Object[] args, Class caller) throws IllegalAccessException, InstantiationException, InvocationTargetException; - /** - * A field is flattenable if ACC_FLATTENABLE is set in its modifier - * and the field type is present in ValueType attribute. - */ - public boolean isFlattenable(Field f); - - /** A field is flattenable and is flattened. */ - public boolean isFlatValue(Field f); - - /** A field can be set to null */ - public boolean canBeNull(Field f); } --- old/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java 2018-12-11 10:43:45.000000000 -0800 +++ new/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java 2018-12-11 10:43:44.000000000 -0800 @@ -38,13 +38,10 @@ abstract class UnsafeFieldAccessorImpl extends FieldAccessorImpl { static final Unsafe unsafe = Unsafe.getUnsafe(); - private static final int FLAT_VALUE = 0x01; - private static final int CAN_BE_NULL = 0x10; protected final Field field; protected final long fieldOffset; protected final boolean isFinal; - protected final int flags; UnsafeFieldAccessorImpl(Field field) { this.field = field; @@ -53,13 +50,6 @@ else this.fieldOffset = unsafe.objectFieldOffset(field); this.isFinal = Modifier.isFinal(field.getModifiers()); - - int flags = 0; - if (ReflectionFactory.langReflectAccess().isFlatValue(field)) - flags |= FLAT_VALUE; - if (ReflectionFactory.langReflectAccess().canBeNull(field)) - flags |= CAN_BE_NULL; - this.flags = flags; } protected void ensureObj(Object o) { @@ -69,12 +59,12 @@ } } - protected boolean isFlatValue() { - return (flags & FLAT_VALUE) == FLAT_VALUE; + protected boolean isFlattened() { + return unsafe.isFlattened(field); } protected boolean canBeNull() { - return (flags & CAN_BE_NULL) == CAN_BE_NULL; + return field.getType() == field.getType().asBoxType(); } protected Object checkValue(Object value) { --- old/src/java.base/share/classes/jdk/internal/reflect/UnsafeObjectFieldAccessorImpl.java 2018-12-11 10:43:49.000000000 -0800 +++ new/src/java.base/share/classes/jdk/internal/reflect/UnsafeObjectFieldAccessorImpl.java 2018-12-11 10:43:48.000000000 -0800 @@ -34,7 +34,7 @@ public Object get(Object obj) throws IllegalArgumentException { ensureObj(obj); - return isFlatValue() ? unsafe.getValue(obj, fieldOffset, field.getType()) + return isFlattened() ? unsafe.getValue(obj, fieldOffset, field.getType()) : unsafe.getReference(obj, fieldOffset); } @@ -78,7 +78,7 @@ throwFinalFieldIllegalAccessException(value); } checkValue(value); - if (isFlatValue()) { + if (isFlattened()) { unsafe.putValue(obj, fieldOffset, field.getType(), value); } else { unsafe.putReference(obj, fieldOffset, value); --- old/src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedObjectFieldAccessorImpl.java 2018-12-11 10:43:55.000000000 -0800 +++ new/src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedObjectFieldAccessorImpl.java 2018-12-11 10:43:53.000000000 -0800 @@ -36,7 +36,7 @@ public Object get(Object obj) throws IllegalArgumentException { ensureObj(obj); - return isFlatValue() ? unsafe.getValue(obj, fieldOffset, field.getType()) + return isFlattened() ? unsafe.getValue(obj, fieldOffset, field.getType()) : unsafe.getReferenceVolatile(obj, fieldOffset); } @@ -80,7 +80,7 @@ throwFinalFieldIllegalAccessException(value); } checkValue(value); - if (isFlatValue()) { + if (isFlattened()) { unsafe.putValue(obj, fieldOffset, field.getType(), value); } else { unsafe.putReferenceVolatile(obj, fieldOffset, value); --- old/src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl.java 2018-12-11 10:44:00.000000000 -0800 +++ new/src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl.java 2018-12-11 10:43:59.000000000 -0800 @@ -35,7 +35,7 @@ } public Object get(Object obj) throws IllegalArgumentException { - return isFlatValue() ? unsafe.getValue(obj, fieldOffset, field.getType()) + return isFlattened() ? unsafe.getValue(obj, fieldOffset, field.getType()) : unsafe.getReferenceVolatile(base, fieldOffset); } @@ -78,7 +78,7 @@ throwFinalFieldIllegalAccessException(value); } checkValue(value); - if (isFlatValue()) { + if (isFlattened()) { unsafe.putValue(base, fieldOffset, field.getType(), value); } else { unsafe.putReferenceVolatile(base, fieldOffset, value); --- old/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticObjectFieldAccessorImpl.java 2018-12-11 10:44:05.000000000 -0800 +++ new/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticObjectFieldAccessorImpl.java 2018-12-11 10:44:04.000000000 -0800 @@ -33,7 +33,7 @@ } public Object get(Object obj) throws IllegalArgumentException { - return isFlatValue() ? unsafe.getValue(base, fieldOffset, field.getType()) + return isFlattened() ? unsafe.getValue(base, fieldOffset, field.getType()) : unsafe.getReference(base, fieldOffset); } @@ -76,7 +76,7 @@ throwFinalFieldIllegalAccessException(value); } checkValue(value); - if (isFlatValue()) { + if (isFlattened()) { unsafe.putValue(obj, fieldOffset, field.getType(), value); } else { unsafe.putReference(base, fieldOffset, value); --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2018-12-11 10:44:10.000000000 -0800 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2018-12-11 10:44:09.000000000 -0800 @@ -345,7 +345,7 @@ @Test() public int test21(MyValue1 v) { - return U.getInt(v, X_OFFSET); + return U.getInt(v, X_OFFSET); } @DontCompile @@ -357,7 +357,12 @@ @Test() public void test22(MyValue1 v) { - U.putInt(v, X_OFFSET, 0); + try { + v = U.makePrivateBuffer(v); + U.putInt(v, X_OFFSET, 0); + } finally { + v = U.finishPrivateBuffer(v); + } } @DontCompile --- /dev/null 2018-12-11 10:44:15.000000000 -0800 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/UnsafeTest.java 2018-12-11 10:44:13.000000000 -0800 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package runtime.valhalla.valuetypes; + +/* + * @test UnsafeTest + * @summary unsafe get/put/with value + * @modules java.base/jdk.internal.misc + * @library /test/lib + * @compile -XDallowWithFieldOperator Point.java UnsafeTest.java + * @run main/othervm -Xint -XX:+EnableValhalla runtime.valhalla.valuetypes.UnsafeTest + * @run main/othervm -Xcomp -XX:+EnableValhalla runtime.valhalla.valuetypes.UnsafeTest + */ + +import jdk.internal.misc.Unsafe; + +import java.lang.reflect.*; +import java.util.List; +import static jdk.test.lib.Asserts.*; + +public class UnsafeTest { + static final Unsafe U = Unsafe.getUnsafe(); + + static value class Value1 { + Point point; + Point[] array; + Value1() { + this.point = Point.createPoint(1, 1); + this.array = new Point[0]; + } + + static Value1 create(Point p, Point... points) { + Value1 o = Value1.default; + o = __WithField(o.point, p); + o = __WithField(o.array, points); + return o; + } + } + + static value class Value2 { + int i; + Value1 v; + + Value2() { + this.i = 0; + this.v = Value1.create(Point.createPoint(0,0), new Point[0]); + } + + static Value2 create(Value1 v, int i) { + Value2 o = Value2.default; + o = __WithField(o.v, v); + o = __WithField(o.i, i); + return o; + } + } + + static value class Value3 { + Object o; + Value2 v; + + Value3() { + this.v = Value2.create(Value1.create(Point.createPoint(0,0), new Point[0]), 0); + this.o = new Object(); + } + + static Value3 create(Value2 v, Object ref) { + Value3 o = Value3.default; + o = __WithField(o.v, v); + o = __WithField(o.o, ref); + return o; + } + } + + + public static void main(String[] args) throws Throwable { + printValueClass(Value3.class, 0); + + Value1 v1 = Value1.create(Point.createPoint(10,10), Point.createPoint(20,20), Point.createPoint(30,30)); + Value2 v2 = Value2.create(v1, 20); + Value3 v3 = Value3.create(v2, List.of("Value3")); + long off_o = U.objectFieldOffset(Value3.class, "o"); + long off_v = U.objectFieldOffset(Value3.class, "v"); + long off_i = U.objectFieldOffset(Value2.class, "i"); + long off_v2 = U.objectFieldOffset(Value2.class, "v"); + + long off_point = U.objectFieldOffset(Value1.class, "point"); + + /* + * Layout of Value3 + * + * | valueheader | o | i | x | y | array | + * ^-------^ + * Point + * ^---------------^ + * Value1 + * + * ^-------------------^ + * Value2 + */ + Value3 v = v3; + try { + v = U.makePrivateBuffer(v); + // patch v3.o + U.putObject(v, off_o, List.of("Value1", "Value2", "Value3")); + // patch v3.v.i; + U.putInt(v, off_v + off_i - U.valueHeaderSize(Value2.class), 999); + // patch v3.v.v.point + U.putValue(v, off_v + off_v2 - U.valueHeaderSize(Value2.class) + off_point - U.valueHeaderSize(Value1.class), + Point.class, Point.createPoint(100, 100)); + } finally { + v = U.finishPrivateBuffer(v); + } + + assertEquals(v.v.v.point, Point.createPoint(100, 100)); + assertEquals(v.v.i, 999); + assertEquals(v.o, List.of("Value1", "Value2", "Value3")); + assertEquals(v.v.v.array, v1.array); + + Value1 nv1 = Value1.create(Point.createPoint(70,70), Point.createPoint(80,80), Point.createPoint(90,90)); + Value2 nv2 = Value2.create(nv1, 100); + Value3 nv3 = Value3.create(nv2, List.of("Value1", "Value2", "Value3")); + + try { + v = U.makePrivateBuffer(v); + // patch v3.v + U.putValue(v, off_v2, Value2.class, nv2); + } finally { + v = U.finishPrivateBuffer(v); + } + assertEquals(v, nv3); + } + + static void printValueClass(Class vc, int level) { + String indent = ""; + for (int i=0; i < level; i++) { + indent += " "; + } + System.out.format("%s%s header size %d%n", indent, vc, U.valueHeaderSize(vc)); + for (Field f : vc.getDeclaredFields()) { + System.out.format("%s%s: %s%s offset %d%n", indent, f.getName(), + U.isFlattened(f) ? "flattened " : "", f.getType(), + U.objectFieldOffset(vc, f.getName())); + if (U.isFlattened(f)) { + printValueClass(f.getType(), level+1); + } + } + } +}