8221477: Inject os/cpu-specific constants into Unsafe from JVM
authoradinn
Fri, 05 Apr 2019 10:01:09 +0100
changeset 54439 d9b46b7de028
parent 54438 f2c23221bbd5
child 54440 23a04fe2aca2
8221477: Inject os/cpu-specific constants into Unsafe from JVM Summary: Initialize Unsafe os/cpu-specific constants using injection instead of native callouts Reviewed-by: stuefe, coleenp, dholmes, plevart
src/hotspot/share/classfile/javaClasses.cpp
src/hotspot/share/classfile/javaClasses.hpp
src/hotspot/share/classfile/systemDictionary.hpp
src/hotspot/share/classfile/vmSymbols.hpp
src/hotspot/share/prims/unsafe.cpp
src/hotspot/share/runtime/thread.cpp
src/java.base/share/classes/jdk/internal/misc/Unsafe.java
src/java.base/share/classes/jdk/internal/misc/UnsafeConstants.java
test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java
--- a/src/hotspot/share/classfile/javaClasses.cpp	Fri Apr 05 07:10:07 2019 -0400
+++ b/src/hotspot/share/classfile/javaClasses.cpp	Fri Apr 05 10:01:09 2019 +0100
@@ -218,7 +218,7 @@
 
 void java_lang_String::set_compact_strings(bool value) {
   CompactStringsFixup fix(value);
-  InstanceKlass::cast(SystemDictionary::String_klass())->do_local_static_fields(&fix);
+  SystemDictionary::String_klass()->do_local_static_fields(&fix);
 }
 
 Handle java_lang_String::basic_create(int length, bool is_latin1, TRAPS) {
@@ -3991,6 +3991,48 @@
 int java_lang_System::out_offset_in_bytes() { return static_out_offset; }
 int java_lang_System::err_offset_in_bytes() { return static_err_offset; }
 
+// Support for jdk_internal_misc_UnsafeConstants
+//
+class UnsafeConstantsFixup : public FieldClosure {
+private:
+  int _address_size;
+  int _page_size;
+  bool _big_endian;
+  bool _use_unaligned_access;
+public:
+  UnsafeConstantsFixup() {
+    // round up values for all static final fields
+    _address_size = sizeof(void*);
+    _page_size = os::vm_page_size();
+    _big_endian = LITTLE_ENDIAN_ONLY(false) BIG_ENDIAN_ONLY(true);
+    _use_unaligned_access = UseUnalignedAccesses;
+  }
+
+  void do_field(fieldDescriptor* fd) {
+    oop mirror = fd->field_holder()->java_mirror();
+    assert(mirror != NULL, "UnsafeConstants must have mirror already");
+    assert(fd->field_holder() == SystemDictionary::UnsafeConstants_klass(), "Should be UnsafeConstants");
+    assert(fd->is_final(), "fields of UnsafeConstants must be final");
+    assert(fd->is_static(), "fields of UnsafeConstants must be static");
+    if (fd->name() == vmSymbols::address_size_name()) {
+      mirror->int_field_put(fd->offset(), _address_size);
+    } else if (fd->name() == vmSymbols::page_size_name()) {
+      mirror->int_field_put(fd->offset(), _page_size);
+    } else if (fd->name() == vmSymbols::big_endian_name()) {
+      mirror->bool_field_put(fd->offset(), _big_endian);
+    } else if (fd->name() == vmSymbols::use_unaligned_access_name()) {
+      mirror->bool_field_put(fd->offset(), _use_unaligned_access);
+    } else {
+      assert(false, "unexpected UnsafeConstants field");
+    }
+  }
+};
+
+void jdk_internal_misc_UnsafeConstants::set_unsafe_constants() {
+  UnsafeConstantsFixup fixup;
+  SystemDictionary::UnsafeConstants_klass()->do_local_static_fields(&fixup);
+}
+
 int java_lang_Class::_klass_offset;
 int java_lang_Class::_array_klass_offset;
 int java_lang_Class::_oop_size_offset;
--- a/src/hotspot/share/classfile/javaClasses.hpp	Fri Apr 05 07:10:07 2019 -0400
+++ b/src/hotspot/share/classfile/javaClasses.hpp	Fri Apr 05 10:01:09 2019 +0100
@@ -81,6 +81,7 @@
   f(java_lang_StackFrameInfo) \
   f(java_lang_LiveStackFrameInfo) \
   f(java_util_concurrent_locks_AbstractOwnableSynchronizer) \
+  f(jdk_internal_misc_UnsafeConstants) \
   //end
 
 #define BASIC_JAVA_CLASSES_DO(f) \
@@ -1483,6 +1484,15 @@
   static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
 };
 
+ // Interface to jdk.internal.misc.UnsafeConsants
+
+class jdk_internal_misc_UnsafeConstants : AllStatic {
+ public:
+  static void set_unsafe_constants();
+  static void compute_offsets() { }
+  static void serialize_offsets(SerializeClosure* f) { }
+};
+
 // Use to declare fields that need to be injected into Java classes
 // for the JVM to use.  The name_index and signature_index are
 // declared in vmSymbols.  The may_be_java flag is used to declare
--- a/src/hotspot/share/classfile/systemDictionary.hpp	Fri Apr 05 07:10:07 2019 -0400
+++ b/src/hotspot/share/classfile/systemDictionary.hpp	Fri Apr 05 10:01:09 2019 +0100
@@ -177,6 +177,7 @@
   do_klass(AssertionStatusDirectives_klass,             java_lang_AssertionStatusDirectives                   ) \
   do_klass(StringBuffer_klass,                          java_lang_StringBuffer                                ) \
   do_klass(StringBuilder_klass,                         java_lang_StringBuilder                               ) \
+  do_klass(UnsafeConstants_klass,                       jdk_internal_misc_UnsafeConstants                     ) \
   do_klass(internal_Unsafe_klass,                       jdk_internal_misc_Unsafe                              ) \
   do_klass(module_Modules_klass,                        jdk_internal_module_Modules                           ) \
                                                                                                                 \
--- a/src/hotspot/share/classfile/vmSymbols.hpp	Fri Apr 05 07:10:07 2019 -0400
+++ b/src/hotspot/share/classfile/vmSymbols.hpp	Fri Apr 05 10:01:09 2019 +0100
@@ -446,6 +446,10 @@
   template(module_entry_name,                         "module_entry")                             \
   template(resolved_references_name,                  "<resolved_references>")                    \
   template(init_lock_name,                            "<init_lock>")                              \
+  template(address_size_name,                         "ADDRESS_SIZE0")                            \
+  template(page_size_name,                            "PAGE_SIZE")                                \
+  template(big_endian_name,                           "BIG_ENDIAN")                               \
+  template(use_unaligned_access_name,                 "UNALIGNED_ACCESS")                         \
                                                                                                   \
   /* name symbols needed by intrinsics */                                                         \
   VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, template, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \
@@ -1070,6 +1074,9 @@
   do_intrinsic(_updateByteBufferAdler32,  java_util_zip_Adler32,  updateByteBuffer_A_name,  updateByteBuffer_signature,  F_SN) \
    do_name(     updateByteBuffer_A_name,                          "updateByteBuffer")                                   \
                                                                                                                         \
+  /* support for UnsafeConstants */                                                                                     \
+  do_class(jdk_internal_misc_UnsafeConstants,      "jdk/internal/misc/UnsafeConstants")                                 \
+                                                                                                                        \
   /* support for Unsafe */                                                                                              \
   do_class(jdk_internal_misc_Unsafe,               "jdk/internal/misc/Unsafe")                                          \
                                                                                                                         \
--- a/src/hotspot/share/prims/unsafe.cpp	Fri Apr 05 07:10:07 2019 -0400
+++ b/src/hotspot/share/prims/unsafe.cpp	Fri Apr 05 10:01:09 2019 +0100
@@ -292,18 +292,6 @@
   return JNIHandles::make_local(env, v);
 } UNSAFE_END
 
-UNSAFE_LEAF(jboolean, Unsafe_isBigEndian0(JNIEnv *env, jobject unsafe)) {
-#ifdef VM_LITTLE_ENDIAN
-  return false;
-#else
-  return true;
-#endif
-} UNSAFE_END
-
-UNSAFE_LEAF(jint, Unsafe_unalignedAccess0(JNIEnv *env, jobject unsafe)) {
-  return UseUnalignedAccesses;
-} UNSAFE_END
-
 #define DEFINE_GETSETOOP(java_type, Type) \
  \
 UNSAFE_ENTRY(java_type, Unsafe_Get##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \
@@ -446,14 +434,6 @@
 
 ////// Random queries
 
-UNSAFE_LEAF(jint, Unsafe_AddressSize0(JNIEnv *env, jobject unsafe)) {
-  return sizeof(void*);
-} UNSAFE_END
-
-UNSAFE_LEAF(jint, Unsafe_PageSize()) {
-  return os::vm_page_size();
-} UNSAFE_END
-
 static jlong find_field_offset(jclass clazz, jstring name, TRAPS) {
   assert(clazz != NULL, "clazz must not be NULL");
   assert(name != NULL, "name must not be NULL");
@@ -1073,8 +1053,6 @@
     {CC "ensureClassInitialized0", CC "(" CLS ")V",      FN_PTR(Unsafe_EnsureClassInitialized0)},
     {CC "arrayBaseOffset0",   CC "(" CLS ")I",           FN_PTR(Unsafe_ArrayBaseOffset0)},
     {CC "arrayIndexScale0",   CC "(" CLS ")I",           FN_PTR(Unsafe_ArrayIndexScale0)},
-    {CC "addressSize0",       CC "()I",                  FN_PTR(Unsafe_AddressSize0)},
-    {CC "pageSize",           CC "()I",                  FN_PTR(Unsafe_PageSize)},
 
     {CC "defineClass0",       CC "(" DC_Args ")" CLS,    FN_PTR(Unsafe_DefineClass0)},
     {CC "allocateInstance",   CC "(" CLS ")" OBJ,        FN_PTR(Unsafe_AllocateInstance)},
@@ -1102,9 +1080,6 @@
     {CC "loadFence",          CC "()V",                  FN_PTR(Unsafe_LoadFence)},
     {CC "storeFence",         CC "()V",                  FN_PTR(Unsafe_StoreFence)},
     {CC "fullFence",          CC "()V",                  FN_PTR(Unsafe_FullFence)},
-
-    {CC "isBigEndian0",       CC "()Z",                  FN_PTR(Unsafe_isBigEndian0)},
-    {CC "unalignedAccess0",   CC "()Z",                  FN_PTR(Unsafe_unalignedAccess0)}
 };
 
 #undef CC
--- a/src/hotspot/share/runtime/thread.cpp	Fri Apr 05 07:10:07 2019 -0400
+++ b/src/hotspot/share/runtime/thread.cpp	Fri Apr 05 10:01:09 2019 +0100
@@ -3628,6 +3628,7 @@
   initialize_class(vmSymbols::java_lang_Thread(), CHECK);
   oop thread_object = create_initial_thread(thread_group, main_thread, CHECK);
   main_thread->set_threadObj(thread_object);
+
   // Set thread status to running since main thread has
   // been started and running.
   java_lang_Thread::set_thread_status(thread_object,
@@ -3636,6 +3637,15 @@
   // The VM creates objects of this class.
   initialize_class(vmSymbols::java_lang_Module(), CHECK);
 
+#ifdef ASSERT
+  InstanceKlass *k = SystemDictionary::UnsafeConstants_klass();
+  assert(k->is_not_initialized(), "UnsafeConstants should not already be initialized");
+#endif
+
+  // initialize the hardware-specific constants needed by Unsafe
+  initialize_class(vmSymbols::jdk_internal_misc_UnsafeConstants(), CHECK);
+  jdk_internal_misc_UnsafeConstants::set_unsafe_constants();
+
   // The VM preresolves methods to these classes. Make sure that they get initialized
   initialize_class(vmSymbols::java_lang_reflect_Method(), CHECK);
   initialize_class(vmSymbols::java_lang_ref_Finalizer(), CHECK);
--- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java	Fri Apr 05 07:10:07 2019 -0400
+++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java	Fri Apr 05 10:01:09 2019 +0100
@@ -33,6 +33,7 @@
 import java.lang.reflect.Field;
 import java.security.ProtectionDomain;
 
+import static jdk.internal.misc.UnsafeConstants.*;
 
 /**
  * A collection of methods for performing low-level, unsafe operations.
@@ -1166,14 +1167,13 @@
     }
 
     /** The value of {@code addressSize()} */
-    public static final int ADDRESS_SIZE = theUnsafe.addressSize0();
+    public static final int ADDRESS_SIZE = ADDRESS_SIZE0;
 
     /**
      * Reports the size in bytes of a native memory page (whatever that is).
      * This value will always be a power of two.
      */
-    public native int pageSize();
-
+    public int pageSize() { return PAGE_SIZE; }
 
     /// random trusted operations from JNI:
 
@@ -1417,7 +1417,7 @@
                                              byte x) {
         long wordOffset = offset & ~3;
         int shift = (int) (offset & 3) << 3;
-        if (BE) {
+        if (BIG_ENDIAN) {
             shift = 24 - shift;
         }
         int mask           = 0xFF << shift;
@@ -1491,7 +1491,7 @@
         }
         long wordOffset = offset & ~3;
         int shift = (int) (offset & 3) << 3;
-        if (BE) {
+        if (BIG_ENDIAN) {
             shift = 16 - shift;
         }
         int mask           = 0xFFFF << shift;
@@ -3354,14 +3354,14 @@
      * @return Returns true if the native byte ordering of this
      * platform is big-endian, false if it is little-endian.
      */
-    public final boolean isBigEndian() { return BE; }
+    public final boolean isBigEndian() { return BIG_ENDIAN; }
 
     /**
      * @return Returns true if this platform is capable of performing
      * accesses at addresses which are not aligned for the type of the
      * primitive type being accessed, false otherwise.
      */
-    public final boolean unalignedAccess() { return unalignedAccess; }
+    public final boolean unalignedAccess() { return UNALIGNED_ACCESS; }
 
     /**
      * Fetches a value at some byte offset into a given Java object.
@@ -3603,14 +3603,7 @@
         putCharUnaligned(o, offset, convEndian(bigEndian, x));
     }
 
-    // JVM interface methods
-    // BE is true iff the native endianness of this platform is big.
-    private static final boolean BE = theUnsafe.isBigEndian0();
-
-    // unalignedAccess is true iff this platform can perform unaligned accesses.
-    private static final boolean unalignedAccess = theUnsafe.unalignedAccess0();
-
-    private static int pickPos(int top, int pos) { return BE ? top - pos : pos; }
+    private static int pickPos(int top, int pos) { return BIG_ENDIAN ? top - pos : pos; }
 
     // These methods construct integers from bytes.  The byte ordering
     // is the native endianness of this platform.
@@ -3649,9 +3642,9 @@
                      | (toUnsignedInt(i1) << pickPos(8, 8)));
     }
 
-    private static byte  pick(byte  le, byte  be) { return BE ? be : le; }
-    private static short pick(short le, short be) { return BE ? be : le; }
-    private static int   pick(int   le, int   be) { return BE ? be : le; }
+    private static byte  pick(byte  le, byte  be) { return BIG_ENDIAN ? be : le; }
+    private static short pick(short le, short be) { return BIG_ENDIAN ? be : le; }
+    private static int   pick(int   le, int   be) { return BIG_ENDIAN ? be : le; }
 
     // These methods write integers to memory from smaller parts
     // provided by their caller.  The ordering in which these parts
@@ -3699,10 +3692,10 @@
     private static long toUnsignedLong(int n)   { return n & 0xffffffffl; }
 
     // Maybe byte-reverse an integer
-    private static char convEndian(boolean big, char n)   { return big == BE ? n : Character.reverseBytes(n); }
-    private static short convEndian(boolean big, short n) { return big == BE ? n : Short.reverseBytes(n)    ; }
-    private static int convEndian(boolean big, int n)     { return big == BE ? n : Integer.reverseBytes(n)  ; }
-    private static long convEndian(boolean big, long n)   { return big == BE ? n : Long.reverseBytes(n)     ; }
+    private static char convEndian(boolean big, char n)   { return big == BIG_ENDIAN ? n : Character.reverseBytes(n); }
+    private static short convEndian(boolean big, short n) { return big == BIG_ENDIAN ? n : Short.reverseBytes(n)    ; }
+    private static int convEndian(boolean big, int n)     { return big == BIG_ENDIAN ? n : Integer.reverseBytes(n)  ; }
+    private static long convEndian(boolean big, long n)   { return big == BIG_ENDIAN ? n : Long.reverseBytes(n)     ; }
 
 
 
@@ -3721,11 +3714,8 @@
     private native void ensureClassInitialized0(Class<?> c);
     private native int arrayBaseOffset0(Class<?> arrayClass);
     private native int arrayIndexScale0(Class<?> arrayClass);
-    private native int addressSize0();
     private native Class<?> defineAnonymousClass0(Class<?> hostClass, byte[] data, Object[] cpPatches);
     private native int getLoadAverage0(double[] loadavg, int nelems);
-    private native boolean unalignedAccess0();
-    private native boolean isBigEndian0();
 
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/UnsafeConstants.java	Fri Apr 05 10:01:09 2019 +0100
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2019, Red Hat Inc. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.internal.misc;
+
+/**
+ * A class used to expose details of the underlying hardware that
+ * configure the operation of class Unsafe.  This class is
+ * package-private as the only intended client is class Unsafe.
+ * All fields in this class must be static final constants.
+ *
+ * @since 13
+ *
+ * @implNote
+ *
+ * The JVM injects hardware-specific values into all the static fields
+ * of this class during JVM initialization. The static initialization
+ * block is executed when the class is initialized then JVM injection
+ * updates the fields with the correct constants. The static block
+ * is required to prevent the fields from being considered constant
+ * variables, so the field values will be not be compiled directly into
+ * any class that uses them.
+ */
+
+final class UnsafeConstants {
+
+    /**
+     * This constructor is private because the class is not meant to
+     * be instantiated.
+     */
+    private UnsafeConstants() {}
+
+    /**
+     * The size in bytes of a native pointer, as stored via {@link
+     * #putAddress}.  This value will be either 4 or 8.  Note that the
+     * sizes of other primitive types (as stored in native memory
+     * blocks) is determined fully by their information content.
+     *
+     * @implNote
+     * The actual value for this field is injected by the JVM.
+     */
+
+    static final int ADDRESS_SIZE0;
+
+    /**
+     * The size in bytes of a native memory page (whatever that is).
+     * This value will always be a power of two.
+     *
+     * @implNote
+     * The actual value for this field is injected by the JVM.
+     */
+
+    static final int PAGE_SIZE;
+
+    /**
+     * Flag whose value is true if and only if the native endianness
+     * of this platform is big.
+     *
+     * @implNote
+     * The actual value for this field is injected by the JVM.
+     */
+
+    static final boolean BIG_ENDIAN;
+
+    /**
+     * Flag whose value is true if and only if the platform can
+     * perform unaligned accesses
+     *
+     * @implNote
+     * The actual value for this field is injected by the JVM.
+     */
+
+    static final boolean UNALIGNED_ACCESS;
+
+    static {
+        ADDRESS_SIZE0 = 0;
+        PAGE_SIZE = 0;
+        BIG_ENDIAN = false;
+        UNALIGNED_ACCESS = false;
+    }
+}
--- a/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java	Fri Apr 05 07:10:07 2019 -0400
+++ b/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java	Fri Apr 05 10:01:09 2019 +0100
@@ -94,7 +94,7 @@
      * for instance members
      */
     public void testInstanceMethod() throws Exception {
-        Method m = Unsafe.class.getDeclaredMethod("addressSize0");
+        Method m = Unsafe.class.getDeclaredMethod("allocateMemory0", long.class);
         assertFalse(m.canAccess(INSTANCE));
 
         try {