changeset 48840:386aad4f99d4 exp

Value-based classes (vbc) / Oop value test via metadata ptr
author dsimms
date Tue, 06 Feb 2018 18:07:41 +0100
parents 8157f7244278
children bab484349467
files src/hotspot/share/classfile/classFileParser.cpp src/hotspot/share/classfile/systemDictionary.cpp src/hotspot/share/classfile/systemDictionary.hpp src/hotspot/share/memory/universe.cpp src/hotspot/share/memory/universe.hpp src/hotspot/share/oops/instanceKlass.cpp src/hotspot/share/oops/klass.cpp src/hotspot/share/oops/klass.hpp src/hotspot/share/oops/klass.inline.hpp src/hotspot/share/oops/oop.hpp src/hotspot/share/oops/oop.inline.hpp src/hotspot/share/runtime/globals.hpp src/hotspot/share/utilities/accessFlags.hpp src/hotspot/share/utilities/globalDefinitions.hpp test/hotspot/jtreg/runtime/valhalla/ValueBased.java
diffstat 15 files changed, 190 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/classfile/classFileParser.cpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/classfile/classFileParser.cpp	Tue Feb 06 18:07:41 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -5912,6 +5912,10 @@
   // asserts that that's the case.
   assert(is_internal_format(_class_name), "external class name format used internally");
 
+  if (SystemDictionary::is_value_based_classname(_class_name)) {
+    _access_flags.set_is_value_based_class();
+  }
+
   if (!is_internal()) {
     LogTarget(Debug, class, preorder) lt;
     if (lt.is_enabled()){
--- a/src/hotspot/share/classfile/systemDictionary.cpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/classfile/systemDictionary.cpp	Tue Feb 06 18:07:41 2018 +0100
@@ -112,6 +112,8 @@
 // lazily initialized klass variables
 InstanceKlass* volatile SystemDictionary::_abstract_ownable_synchronizer_klass = NULL;
 
+GrowableArray<Symbol*>* SystemDictionary::_value_based_classes    = NULL;
+
 // Default ProtectionDomainCacheSize value
 
 const int defaultProtectionDomainCacheSize = 1009;
@@ -2046,6 +2048,49 @@
   _invoke_method_table = new SymbolPropertyTable(_invoke_method_size);
   _pd_cache_table = new ProtectionDomainCacheTable(defaultProtectionDomainCacheSize);
 
+  if (ValueBasedClasses == NULL) {
+    // Quick dirty hard-coded JDK value classes...
+    ValueBasedClasses = "java/lang/Runtime$Version,"
+      "java/time/Duration,"
+      "java/time/Instant,"
+      "java/time/LocalDate,"
+      "java/time/LocalDateTime,"
+      "java/time/LocalTime,"
+      "java/time/MonthDay,"
+      "java/time/OffsetDateTime,"
+      "java/time/OffsetTime,"
+      "java/time/Period,"
+      "java/time/Year,"
+      "java/time/YearMonth,"
+      "java/time/ZonedDateTime,"
+      "java/time/ZoneId,"
+      "java/time/ZoneOffset,"
+      "java/time/chrono/HijrahDate,"
+      "java/time/chrono/JapaneseDate,"
+      "java/time/chrono/MinguoDate,"
+      "java/time/chrono/ThaiBuddhistDate,"
+      "java/util/KeyValueHolder,"
+      "java/util/Optional,"
+      "java/util/OptionalDouble,"
+      "java/util/OptionalInt,"
+      "java/util/OptionalLong";
+  }
+  if (ValueBasedClasses != NULL) {
+    Symbol* name_sym;
+    _value_based_classes = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<Symbol*>(3, true);
+    char* name = os::strdup_check_oom(ValueBasedClasses);
+    char* next = strchr(name, ',');
+    while (next != NULL) {
+      *next = '\0';
+      name_sym = SymbolTable::new_permanent_symbol(name, CHECK);
+      _value_based_classes->append(name_sym);
+      name = next + 1;
+      next = strchr(name, ',');
+    }
+    name_sym = SymbolTable::new_permanent_symbol(name, CHECK);
+    _value_based_classes->append(name_sym);
+  }
+
   // Allocate private object used as system class loader lock
   _system_loader_lock_obj = oopFactory::new_intArray(0, CHECK);
   // Initialize basic classes
--- a/src/hotspot/share/classfile/systemDictionary.hpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/classfile/systemDictionary.hpp	Tue Feb 06 18:07:41 2018 +0100
@@ -465,6 +465,11 @@
 
   static void load_abstract_ownable_synchronizer_klass(TRAPS);
 
+  static bool is_value_based_classname(Symbol* name) {
+    if (_value_based_classes == NULL) return false;
+    return _value_based_classes->find(name) >= 0;
+  }
+
 protected:
   // Tells whether ClassLoader.loadClassInternal is present
   static bool has_loadClassInternal()       { return _has_loadClassInternal; }
@@ -716,6 +721,9 @@
 
   static bool _has_loadClassInternal;
   static bool _has_checkPackageAccess;
+
+  // List of value-based classes
+  static GrowableArray<Symbol*>* _value_based_classes;
 };
 
 #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_HPP
--- a/src/hotspot/share/memory/universe.cpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/memory/universe.cpp	Tue Feb 06 18:07:41 2018 +0100
@@ -166,6 +166,8 @@
 NarrowPtrStruct Universe::_narrow_klass = { NULL, 0, true };
 address Universe::_narrow_ptrs_base;
 
+int Universe::_oop_metadata_odd_mask = 0;
+
 void Universe::basic_type_classes_do(void f(Klass*)) {
   f(boolArrayKlassObj());
   f(byteArrayKlassObj());
--- a/src/hotspot/share/memory/universe.hpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/memory/universe.hpp	Tue Feb 06 18:07:41 2018 +0100
@@ -196,6 +196,8 @@
   static struct NarrowPtrStruct _narrow_klass;
   static address _narrow_ptrs_base;
 
+  static int     _oop_metadata_odd_mask;
+
   // array of dummy objects used with +FullGCAlot
   debug_only(static objArrayOop _fullgc_alot_dummy_array;)
   // index of next entry to clear
@@ -438,8 +440,12 @@
   static void     set_narrow_klass_shift(int shift)       {
     assert(shift == 0 || shift == LogKlassAlignmentInBytes, "invalid shift for klass ptrs");
     _narrow_klass._shift   = shift;
+    _oop_metadata_odd_mask = (shift) ? 1 : KlassPtrEvenOddMask;
   }
 
+  static int oop_metadata_odd_mask() { return _oop_metadata_odd_mask; }
+
+
   // Reserve Java heap and determine CompressedOops mode
   static ReservedSpace reserve_heap(size_t heap_size, size_t alignment);
 
--- a/src/hotspot/share/oops/instanceKlass.cpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/oops/instanceKlass.cpp	Tue Feb 06 18:07:41 2018 +0100
@@ -159,7 +159,6 @@
   assert(loader_data != NULL, "invariant");
 
   InstanceKlass* ik;
-
   // Allocation
   if (REF_NONE == parser.reference_type()) {
     if (class_name == vmSymbols::java_lang_Class()) {
@@ -172,7 +171,8 @@
     }
     else {
       // normal
-      ik = new (loader_data, size, THREAD) InstanceKlass(parser, InstanceKlass::_misc_kind_other);
+      bool is_value = parser.access_flags().is_value_based_class();
+      ik = new (loader_data, size, is_value, THREAD) InstanceKlass(parser, InstanceKlass::_misc_kind_other);
     }
   }
   else {
--- a/src/hotspot/share/oops/klass.cpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/oops/klass.cpp	Tue Feb 06 18:07:41 2018 +0100
@@ -170,10 +170,20 @@
   return NULL;
 }
 
-void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {
-  return Metaspace::allocate(loader_data, word_size, MetaspaceObj::ClassType, THREAD);
+void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, bool is_value, TRAPS) throw() {
+  // Pad size in case need adjust to even/odd klass ptr
+  uintptr_t addr = (uintptr_t) Metaspace::allocate(loader_data, word_size + (1 << LogKlassAlignment), MetaspaceObj::ClassType, THREAD);
+  // values are odd, otherwise make even (and vice versa)
+  if (is_value ^ ((addr & KlassPtrEvenOddMask) >> LogKlassAlignmentInBytes)) {
+	  addr += (1 << LogKlassAlignmentInBytes);
+  }
+  assert(is_aligned(addr, (1 << LogKlassAlignmentInBytes)), "Klass base alignment incorrect");
+  assert(is_value || (addr & KlassPtrEvenOddMask) == 0,     "Klass even alignment incorrect");
+  assert((!is_value) || (addr & KlassPtrEvenOddMask),       "Klass odd alignment incorrect");
+  return (void*) addr;
 }
 
+
 // "Normal" instantiation is preceeded by a MetaspaceObj allocation
 // which zeros out memory - calloc equivalent.
 // The constructor is also used from CppVtableCloner,
--- a/src/hotspot/share/oops/klass.hpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/oops/klass.hpp	Tue Feb 06 18:07:41 2018 +0100
@@ -161,8 +161,10 @@
 
   // Constructor
   Klass();
-
-  void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw();
+  void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, bool is_value, TRAPS) throw();
+  void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {
+    return operator new (size, loader_data, word_size, false, THREAD);
+  }
 
  public:
   enum DefaultsLookupMode { find_defaults, skip_defaults };
@@ -562,7 +564,8 @@
   void set_has_miranda_methods()        { _access_flags.set_has_miranda_methods(); }
   bool is_shared() const                { return access_flags().is_shared_class(); } // shadows MetaspaceObj::is_shared)()
   void set_is_shared()                  { _access_flags.set_is_shared_class(); }
-
+  bool is_value_based() const           { return access_flags().is_value_based_class(); }
+  void set_is_value_based()             { _access_flags.set_is_value_based_class(); }
   bool is_cloneable() const;
   void set_is_cloneable();
 
@@ -677,6 +680,9 @@
 
   static Klass* decode_klass_not_null(narrowKlass v);
   static Klass* decode_klass(narrowKlass v);
+
+  static bool decode_ptr_is_value_based(narrowKlass v);
+  static bool ptr_is_value_based(Klass* v);
 };
 
 // Helper to convert the oop iterate macro suffixes into bool values that can be used by template functions.
--- a/src/hotspot/share/oops/klass.inline.hpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/oops/klass.inline.hpp	Tue Feb 06 18:07:41 2018 +0100
@@ -71,6 +71,14 @@
   return is_null(v) ? (Klass*)NULL : decode_klass_not_null(v);
 }
 
+inline bool Klass::decode_ptr_is_value_based(narrowKlass v) {
+	return (v & Universe::oop_metadata_odd_mask());
+}
+inline bool Klass::ptr_is_value_based(Klass* v) {
+	return ((uintptr_t)v & Universe::oop_metadata_odd_mask());
+}
+
+
 template <typename T>
 bool Klass::is_instanceof_or_null(T element) {
   if (oopDesc::is_null(element)) {
--- a/src/hotspot/share/oops/oop.hpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/oops/oop.hpp	Tue Feb 06 18:07:41 2018 +0100
@@ -81,6 +81,9 @@
   inline Klass** klass_addr();
   inline narrowKlass* compressed_klass_addr();
 
+  // fast oop only test (does not load klass)
+  inline bool klass_is_value_based();
+
   inline void set_klass(Klass* k);
   inline void release_set_klass(Klass* k);
 
--- a/src/hotspot/share/oops/oop.inline.hpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/oops/oop.inline.hpp	Tue Feb 06 18:07:41 2018 +0100
@@ -95,6 +95,15 @@
   return &_metadata._compressed_klass;
 }
 
+// fast oop only test (does not load klass)
+bool oopDesc::klass_is_value_based() {
+	if (UseCompressedClassPointers) {
+		return Klass::decode_ptr_is_value_based(_metadata._compressed_klass);
+	} else {
+		return Klass::ptr_is_value_based(_metadata._klass);
+	}
+}
+
 #define CHECK_SET_KLASS(k)                                                \
   do {                                                                    \
     assert(Universe::is_bootstrapping() || k != NULL, "NULL Klass");      \
--- a/src/hotspot/share/runtime/globals.hpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/runtime/globals.hpp	Tue Feb 06 18:07:41 2018 +0100
@@ -4088,7 +4088,13 @@
                                                                             \
   product(ccstr, AllocateHeapAt, NULL,                                      \
           "Path to the directoy where a temporary file will be created "    \
-          "to use as the backing store for Java Heap.")
+          "to use as the backing store for Java Heap.")                     \
+  /* Valhalla Experiments */                                                \
+  product(ccstrlist, ValueBasedClasses,                                     \
+          NULL,                                                             \
+         "A comma separated list of classes whom should be treated as "     \
+         "value-based")
+
 
 
 /*
--- a/src/hotspot/share/utilities/accessFlags.hpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/utilities/accessFlags.hpp	Tue Feb 06 18:07:41 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -65,7 +65,7 @@
   JVM_ACC_IS_CLONEABLE_FAST       = (int)0x80000000,// True if klass implements the Cloneable interface and can be optimized in generated code
   JVM_ACC_HAS_FINAL_METHOD        = 0x01000000,     // True if klass has final method
   JVM_ACC_IS_SHARED_CLASS         = 0x02000000,     // True if klass is shared
-
+  JVM_ACC_IS_VALUE_BASED_CLASS    = 0x04000000,     // True if klass is "value-based"
   // Klass* and Method* flags
   JVM_ACC_HAS_LOCAL_VARIABLE_TABLE= 0x00200000,
 
@@ -148,6 +148,7 @@
   bool has_final_method        () const { return (_flags & JVM_ACC_HAS_FINAL_METHOD       ) != 0; }
   bool is_cloneable_fast       () const { return (_flags & JVM_ACC_IS_CLONEABLE_FAST      ) != 0; }
   bool is_shared_class         () const { return (_flags & JVM_ACC_IS_SHARED_CLASS        ) != 0; }
+  bool is_value_based_class    () const { return (_flags & JVM_ACC_IS_VALUE_BASED_CLASS   ) != 0; }
 
   // Klass* and Method* flags
   bool has_localvariable_table () const { return (_flags & JVM_ACC_HAS_LOCAL_VARIABLE_TABLE) != 0; }
@@ -220,6 +221,7 @@
   void set_is_cloneable_fast()         { atomic_set_bits(JVM_ACC_IS_CLONEABLE_FAST);       }
   void set_has_miranda_methods()       { atomic_set_bits(JVM_ACC_HAS_MIRANDA_METHODS);     }
   void set_is_shared_class()           { atomic_set_bits(JVM_ACC_IS_SHARED_CLASS);         }
+  void set_is_value_based_class()      { atomic_set_bits(JVM_ACC_IS_VALUE_BASED_CLASS);    }
 
  public:
   // field flags
--- a/src/hotspot/share/utilities/globalDefinitions.hpp	Mon Feb 05 09:38:14 2018 +0530
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp	Tue Feb 06 18:07:41 2018 +0100
@@ -451,6 +451,7 @@
 const int LogKlassAlignment        = LogKlassAlignmentInBytes - LogHeapWordSize;
 const int KlassAlignmentInBytes    = 1 << LogKlassAlignmentInBytes;
 const int KlassAlignment           = KlassAlignmentInBytes / HeapWordSize;
+const int KlassPtrEvenOddMask      = (1 << (LogKlassAlignmentInBytes + 1)) - 1;
 
 // Maximal size of heap where unscaled compression can be used. Also upper bound
 // for heap placement: 4GB.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/valhalla/ValueBased.java	Tue Feb 06 18:07:41 2018 +0100
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+import java.time.*;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/*
+ * @test ValueBased
+ * @summary Verify classes may defined as "Value-based"
+ * @library /
+ * @modules java.base
+ * @run testng/othervm -XX:ValueBasedClasses=java/time/LocalTime,java/time/LocalDateTime,ValueBased$Value ValueBased
+ */
+public class ValueBased {
+
+    static final class Value {
+        final int foo;
+
+        private Value(int foo) { this.foo = foo;  }
+
+        public String toString() {
+            return "Value foo=" + foo;
+        }
+
+        public static Value make(int foo) {
+            return new Value(foo);
+        }
+    }
+
+    public static boolean testAcmp(Object obj1, Object obj2) {
+        return obj1 == obj2;
+    }
+
+    @Test
+    public void testValue() {
+        // Sanity check user defined class
+        Value v1 = Value.make(47);
+        testAcmp(v1, v1);
+    }
+
+    @Test
+    public void testJdkValueBasedClasses() {
+        // Sanity cfp is not broken on JDK class
+        LocalTime time1 = LocalTime.of(0, 0, 0, 999);
+        LocalTime time2 = LocalTime.of(0, 0, 0, 999);
+        testAcmp(time1, time2);
+    }
+}