changeset 53185:127ed6c19242 lworld

Add the new Unsafe primitives to update values in a private buffer. and test method to test if a field is flattened and if an array class is a flattened array. The Unsafe APIs for values include the following: boolean Unsafe::isFlattened(Field f); boolean Unsafe::isFlattenedArray(Class<?> arrayClass); <V> long valueHeaderSize(Class<V> vc); <V> V makePrivateBuffer(V value); <V> V finishPrivateBuffer(V value); <V> V getValue(Object o, long offset, Class<?> vc); <V> void putValue(Object o, long offset, Class<?> vc, V v); makePrivateBuffer creates a value instance whose layout and contents is exactly the same as the input value and marks it in larval state and finishPrivateBuffer will exit the larval state. In between Unsafe.putXXX on the buffer will update the larval value instance.
author mchung
date Mon, 17 Dec 2018 15:19:06 -0800
parents 2843104787a7
children 29b97940a7fb
files src/hotspot/share/oops/markOop.hpp src/hotspot/share/prims/unsafe.cpp src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java src/java.base/share/classes/java/lang/invoke/MemberName.java src/java.base/share/classes/java/lang/invoke/VarHandles.java src/java.base/share/classes/java/lang/reflect/ReflectAccess.java src/java.base/share/classes/jdk/internal/misc/Unsafe.java src/java.base/share/classes/jdk/internal/reflect/LangReflectAccess.java src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java src/java.base/share/classes/jdk/internal/reflect/UnsafeObjectFieldAccessorImpl.java src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedObjectFieldAccessorImpl.java src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl.java src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticObjectFieldAccessorImpl.java test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java test/hotspot/jtreg/runtime/valhalla/valuetypes/UnsafeTest.java
diffstat 15 files changed, 329 insertions(+), 102 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/oops/markOop.hpp	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/hotspot/share/oops/markOop.hpp	Mon Dec 17 15:19:06 2018 -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.
 //
-//    [ <unused> |1| epoch | age | 1 | 01]       permanently locked
+//    [ <unused> | 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 );
--- a/src/hotspot/share/prims/unsafe.cpp	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/hotspot/share/prims/unsafe.cpp	Mon Dec 17 15:19:06 2018 -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<ON_UNKNOWN_OOP_REF>::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)) {
+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(!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);
-  oop p = JNIHandles::resolve(obj);
-  Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(c));
-  ValueKlass* vk = ValueKlass::cast(k);
-  assert_and_log_unsafe_value_type_access(p, offset, vk);
   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)},
 
--- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Mon Dec 17 15:19:06 2018 -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()) {
--- a/src/java.base/share/classes/java/lang/invoke/MemberName.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/MemberName.java	Mon Dec 17 15:19:06 2018 -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 = "<init>";  // the ever-popular
 
--- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java	Mon Dec 17 15:19:06 2018 -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());
--- a/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java	Mon Dec 17 15:19:06 2018 -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);
-    }
 }
--- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java	Mon Dec 17 15:19:06 2018 -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 <V> 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 <V> the type of the given value instance
+     */
+    public native <V> V makePrivateBuffer(V value);
+
+    /**
+     * Exits the larval state and returns a value instance.
+     *
+     * @param value a value instance
+     * @param <V> the type of the given value instance
+     */
+    public native <V> V finishPrivateBuffer(V value);
+
+    /**
+     * Returns the header size of the given value class
+     *
+     * @param vc Value class
+     * @param <V> value clas
+     * @return the header size of the value class
+     */
+    public native <V> long valueHeaderSize(Class<V> vc);
+
     /** @see #getInt(Object, long) */
     @HotSpotIntrinsicCandidate
     public native boolean getBoolean(Object o, long offset);
--- a/src/java.base/share/classes/jdk/internal/reflect/LangReflectAccess.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/jdk/internal/reflect/LangReflectAccess.java	Mon Dec 17 15:19:06 2018 -0800
@@ -122,15 +122,4 @@
     /** Returns a new instance created by the given constructor with access check */
     public <T> T newInstance(Constructor<T> 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);
 }
--- a/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/jdk/internal/reflect/UnsafeFieldAccessorImpl.java	Mon Dec 17 15:19:06 2018 -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) {
--- a/src/java.base/share/classes/jdk/internal/reflect/UnsafeObjectFieldAccessorImpl.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/jdk/internal/reflect/UnsafeObjectFieldAccessorImpl.java	Mon Dec 17 15:19:06 2018 -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);
--- a/src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedObjectFieldAccessorImpl.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedObjectFieldAccessorImpl.java	Mon Dec 17 15:19:06 2018 -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);
--- a/src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/jdk/internal/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl.java	Mon Dec 17 15:19:06 2018 -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);
--- a/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticObjectFieldAccessorImpl.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/src/java.base/share/classes/jdk/internal/reflect/UnsafeStaticObjectFieldAccessorImpl.java	Mon Dec 17 15:19:06 2018 -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);
--- a/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java	Mon Dec 17 16:39:06 2018 +0100
+++ b/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java	Mon Dec 17 15:19:06 2018 -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	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/UnsafeTest.java	Mon Dec 17 15:19:06 2018 -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);
+            }
+        }
+    }
+}