8026049: (bf) Intrinsify ByteBuffer.put{Int, Double, Float, ...} methods
authoraph
Tue, 31 Mar 2015 12:31:18 -0700
changeset 30209 8ea30dc99369
parent 30208 ed11124f18e1
child 30210 507826ef56fd
8026049: (bf) Intrinsify ByteBuffer.put{Int, Double, Float, ...} methods Summary: Use unaligned Unsafe loads and stores for ByteBuffer access on platforms which support unaligned access. Add intrinsics for Unsafe.{get,put}-X-Unaligned methods. Reviewed-by: dholmes, jrose, psandoz, kvn
hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp
hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp
hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp
hotspot/src/cpu/x86/vm/vm_version_x86.cpp
hotspot/src/cpu/zero/vm/vm_version_zero.cpp
hotspot/src/share/vm/c1/c1_GraphBuilder.cpp
hotspot/src/share/vm/classfile/vmSymbols.hpp
hotspot/src/share/vm/opto/library_call.cpp
hotspot/src/share/vm/prims/unsafe.cpp
hotspot/src/share/vm/runtime/globals.hpp
hotspot/test/compiler/intrinsics/unsafe/HeapByteBufferTest.java
--- a/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/cpu/aarch64/vm/vm_version_aarch64.cpp	Tue Mar 31 12:31:18 2015 -0700
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Red Hat Inc. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -193,6 +193,11 @@
     }
   }
 
+  // This machine allows unaligned memory accesses
+  if (FLAG_IS_DEFAULT(UseUnalignedAccesses)) {
+    FLAG_SET_DEFAULT(UseUnalignedAccesses, true);
+  }
+
 #ifdef COMPILER2
   if (FLAG_IS_DEFAULT(OptoScheduling)) {
     OptoScheduling = true;
--- a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp	Tue Mar 31 12:31:18 2015 -0700
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2014 SAP AG. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012, 2015 SAP AG. 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
@@ -172,6 +172,12 @@
     FLAG_SET_DEFAULT(UseSHA512Intrinsics, false);
   }
 
+  // This machine does not allow unaligned memory accesses
+  if (UseUnalignedAccesses) {
+    if (!FLAG_IS_DEFAULT(UseUnalignedAccesses))
+      warning("Unaligned memory access is not available on this CPU");
+    FLAG_SET_DEFAULT(UseUnalignedAccesses, false);
+  }
 }
 
 void VM_Version::print_features() {
--- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp	Tue Mar 31 12:31:18 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, 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
@@ -356,6 +356,13 @@
     (cache_line_size > ContendedPaddingWidth))
     ContendedPaddingWidth = cache_line_size;
 
+  // This machine does not allow unaligned memory accesses
+  if (UseUnalignedAccesses) {
+    if (!FLAG_IS_DEFAULT(UseUnalignedAccesses))
+      warning("Unaligned memory access is not available on this CPU");
+    FLAG_SET_DEFAULT(UseUnalignedAccesses, false);
+  }
+
 #ifndef PRODUCT
   if (PrintMiscellaneous && Verbose) {
     tty->print_cr("L1 data cache line size: %u", L1_data_cache_line_size());
--- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp	Tue Mar 31 12:31:18 2015 -0700
@@ -980,6 +980,11 @@
      (cache_line_size > ContendedPaddingWidth))
      ContendedPaddingWidth = cache_line_size;
 
+  // This machine allows unaligned memory accesses
+  if (FLAG_IS_DEFAULT(UseUnalignedAccesses)) {
+    FLAG_SET_DEFAULT(UseUnalignedAccesses, true);
+  }
+
 #ifndef PRODUCT
   if (PrintMiscellaneous && Verbose) {
     tty->print_cr("Logical CPUs per core: %u",
--- a/hotspot/src/cpu/zero/vm/vm_version_zero.cpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/cpu/zero/vm/vm_version_zero.cpp	Tue Mar 31 12:31:18 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2009 Red Hat, Inc.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -30,4 +30,11 @@
 #include "runtime/stubCodeGenerator.hpp"
 #include "vm_version_zero.hpp"
 
-// This file is intentionally empty
+
+void VM_Version::initialize() {
+  // This machine does not allow unaligned memory accesses
+  if (! FLAG_IS_DEFAULT(UseUnalignedAccesses)) {
+    warning("Unaligned memory access is not available on this CPU");
+    FLAG_SET_DEFAULT(UseUnalignedAccesses, false);
+  }
+}
--- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp	Tue Mar 31 12:31:18 2015 -0700
@@ -3462,6 +3462,24 @@
     case vmIntrinsics::_putFloat  : return append_unsafe_put_obj(callee, T_FLOAT,   false);
     case vmIntrinsics::_putDouble : return append_unsafe_put_obj(callee, T_DOUBLE,  false);
 
+    case vmIntrinsics::_getShortUnaligned  :
+      return UseUnalignedAccesses ? append_unsafe_get_obj(callee, T_SHORT,   false) : false;
+    case vmIntrinsics::_getCharUnaligned   :
+      return UseUnalignedAccesses ? append_unsafe_get_obj(callee, T_CHAR,    false) : false;
+    case vmIntrinsics::_getIntUnaligned    :
+      return UseUnalignedAccesses ? append_unsafe_get_obj(callee, T_INT,     false) : false;
+    case vmIntrinsics::_getLongUnaligned   :
+      return UseUnalignedAccesses ? append_unsafe_get_obj(callee, T_LONG,    false) : false;
+
+    case vmIntrinsics::_putShortUnaligned  :
+      return UseUnalignedAccesses ? append_unsafe_put_obj(callee, T_SHORT,   false) : false;
+    case vmIntrinsics::_putCharUnaligned   :
+      return UseUnalignedAccesses ? append_unsafe_put_obj(callee, T_CHAR,    false) : false;
+    case vmIntrinsics::_putIntUnaligned    :
+      return UseUnalignedAccesses ? append_unsafe_put_obj(callee, T_INT,     false) : false;
+    case vmIntrinsics::_putLongUnaligned   :
+      return UseUnalignedAccesses ? append_unsafe_put_obj(callee, T_LONG,    false) : false;
+
     case vmIntrinsics::_getObjectVolatile : return append_unsafe_get_obj(callee, T_OBJECT,  true);
     case vmIntrinsics::_getBooleanVolatile: return append_unsafe_get_obj(callee, T_BOOLEAN, true);
     case vmIntrinsics::_getByteVolatile   : return append_unsafe_get_obj(callee, T_BYTE,    true);
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp	Tue Mar 31 12:31:18 2015 -0700
@@ -953,6 +953,20 @@
   do_intrinsic(_putFloatVolatile,         sun_misc_Unsafe,        putFloatVolatile_name, putFloat_signature,     F_RN)  \
   do_intrinsic(_putDoubleVolatile,        sun_misc_Unsafe,        putDoubleVolatile_name, putDouble_signature,   F_RN)  \
                                                                                                                         \
+  do_name(getShortUnaligned_name,"getShortUnaligned")     do_name(putShortUnaligned_name,"putShortUnaligned")           \
+  do_name(getCharUnaligned_name,"getCharUnaligned")       do_name(putCharUnaligned_name,"putCharUnaligned")             \
+  do_name(getIntUnaligned_name,"getIntUnaligned")         do_name(putIntUnaligned_name,"putIntUnaligned")               \
+  do_name(getLongUnaligned_name,"getLongUnaligned")       do_name(putLongUnaligned_name,"putLongUnaligned")             \
+                                                                                                                        \
+  do_intrinsic(_getShortUnaligned,         sun_misc_Unsafe,        getShortUnaligned_name, getShort_signature,     F_R)  \
+  do_intrinsic(_getCharUnaligned,          sun_misc_Unsafe,        getCharUnaligned_name, getChar_signature,       F_R)  \
+  do_intrinsic(_getIntUnaligned,           sun_misc_Unsafe,        getIntUnaligned_name, getInt_signature,         F_R)  \
+  do_intrinsic(_getLongUnaligned,          sun_misc_Unsafe,        getLongUnaligned_name, getLong_signature,       F_R)  \
+  do_intrinsic(_putShortUnaligned,         sun_misc_Unsafe,        putShortUnaligned_name, putShort_signature,     F_R)  \
+  do_intrinsic(_putCharUnaligned,          sun_misc_Unsafe,        putCharUnaligned_name, putChar_signature,       F_R)  \
+  do_intrinsic(_putIntUnaligned,           sun_misc_Unsafe,        putIntUnaligned_name, putInt_signature,         F_R)  \
+  do_intrinsic(_putLongUnaligned,          sun_misc_Unsafe,        putLongUnaligned_name, putLong_signature,       F_R)  \
+                                                                                                                        \
   /* %%% these are redundant except perhaps for getAddress, but Unsafe has native methods for them */                   \
   do_signature(getByte_raw_signature,     "(J)B")                                                                       \
   do_signature(putByte_raw_signature,     "(JB)V")                                                                      \
--- a/hotspot/src/share/vm/opto/library_call.cpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/share/vm/opto/library_call.cpp	Tue Mar 31 12:31:18 2015 -0700
@@ -553,6 +553,17 @@
     if (!Matcher::match_rule_supported(Op_OverflowMulL) || !UseMathExactIntrinsics) return NULL;
     break;
 
+  case vmIntrinsics::_getShortUnaligned:
+  case vmIntrinsics::_getCharUnaligned:
+  case vmIntrinsics::_getIntUnaligned:
+  case vmIntrinsics::_getLongUnaligned:
+  case vmIntrinsics::_putShortUnaligned:
+  case vmIntrinsics::_putCharUnaligned:
+  case vmIntrinsics::_putIntUnaligned:
+  case vmIntrinsics::_putLongUnaligned:
+    if (!UseUnalignedAccesses) return NULL;
+    break;
+
  default:
     assert(id <= vmIntrinsics::LAST_COMPILER_INLINE, "caller responsibility");
     assert(id != vmIntrinsics::_Object_init && id != vmIntrinsics::_invoke, "enum out of order?");
@@ -803,6 +814,16 @@
   case vmIntrinsics::_putFloatVolatile:         return inline_unsafe_access(!is_native_ptr,  is_store, T_FLOAT,    is_volatile);
   case vmIntrinsics::_putDoubleVolatile:        return inline_unsafe_access(!is_native_ptr,  is_store, T_DOUBLE,   is_volatile);
 
+  case vmIntrinsics::_getShortUnaligned:        return inline_unsafe_access(!is_native_ptr, !is_store, T_SHORT,   !is_volatile);
+  case vmIntrinsics::_getCharUnaligned:         return inline_unsafe_access(!is_native_ptr, !is_store, T_CHAR,    !is_volatile);
+  case vmIntrinsics::_getIntUnaligned:          return inline_unsafe_access(!is_native_ptr, !is_store, T_INT,     !is_volatile);
+  case vmIntrinsics::_getLongUnaligned:         return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG,    !is_volatile);
+
+  case vmIntrinsics::_putShortUnaligned:        return inline_unsafe_access(!is_native_ptr,  is_store, T_SHORT,   !is_volatile);
+  case vmIntrinsics::_putCharUnaligned:         return inline_unsafe_access(!is_native_ptr,  is_store, T_CHAR,    !is_volatile);
+  case vmIntrinsics::_putIntUnaligned:          return inline_unsafe_access(!is_native_ptr,  is_store, T_INT,     !is_volatile);
+  case vmIntrinsics::_putLongUnaligned:         return inline_unsafe_access(!is_native_ptr,  is_store, T_LONG,    !is_volatile);
+
   case vmIntrinsics::_compareAndSwapObject:     return inline_unsafe_load_store(T_OBJECT, LS_cmpxchg);
   case vmIntrinsics::_compareAndSwapInt:        return inline_unsafe_load_store(T_INT,    LS_cmpxchg);
   case vmIntrinsics::_compareAndSwapLong:       return inline_unsafe_load_store(T_LONG,   LS_cmpxchg);
--- a/hotspot/src/share/vm/prims/unsafe.cpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/share/vm/prims/unsafe.cpp	Tue Mar 31 12:31:18 2015 -0700
@@ -324,6 +324,24 @@
 
 #endif // not SUPPORTS_NATIVE_CX8
 
+UNSAFE_ENTRY(jboolean, Unsafe_isBigEndian0(JNIEnv *env, jobject unsafe))
+  UnsafeWrapper("Unsafe_IsBigEndian0");
+  {
+#ifdef VM_LITTLE_ENDIAN
+    return false;
+#else
+    return true;
+#endif
+  }
+UNSAFE_END
+
+UNSAFE_ENTRY(jint, Unsafe_unalignedAccess0(JNIEnv *env, jobject unsafe))
+  UnsafeWrapper("Unsafe_UnalignedAccess0");
+  {
+    return UseUnalignedAccesses;
+  }
+UNSAFE_END
+
 #define DEFINE_GETSETOOP(jboolean, Boolean) \
  \
 UNSAFE_ENTRY(jboolean, Unsafe_Get##Boolean##140(JNIEnv *env, jobject unsafe, jobject obj, jint offset)) \
@@ -1261,6 +1279,9 @@
     {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/hotspot/src/share/vm/runtime/globals.hpp	Fri Mar 27 08:58:45 2015 +0100
+++ b/hotspot/src/share/vm/runtime/globals.hpp	Tue Mar 31 12:31:18 2015 -0700
@@ -3912,7 +3912,10 @@
           "Enable event-based tracing")                                     \
                                                                             \
   product(bool, UseLockedTracing, false,                                    \
-          "Use locked-tracing when doing event-based tracing")
+          "Use locked-tracing when doing event-based tracing")              \
+                                                                            \
+  diagnostic(bool, UseUnalignedAccesses, false,                             \
+          "Use unaligned memory accesses in sun.misc.Unsafe")
 
 /*
  *  Macros for factoring of globals
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/intrinsics/unsafe/HeapByteBufferTest.java	Tue Mar 31 12:31:18 2015 -0700
@@ -0,0 +1,372 @@
+//
+// Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+// Copyright (c) 2015, 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.
+//
+// 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 static java.lang.Math.abs;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import static java.nio.ByteOrder.BIG_ENDIAN;
+import static java.nio.ByteOrder.LITTLE_ENDIAN;
+import java.util.SplittableRandom;
+import java.util.Arrays;
+
+/**
+ * @test
+ * @bug 8026049
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-UseUnalignedAccesses HeapByteBufferTest
+ * @run main/othervm HeapByteBufferTest
+ * @summary Verify that byte buffers are correctly accessed.
+ */
+
+// A wrapper for a ByteBuffer which maintains a backing array and a
+// position.  Whenever this wrapper is written the backing array and
+// the wrapped byte buffer are updated together, and whenever it is
+// read we check that the ByteBuffer and the backing array are identical.
+
+class MyByteBuffer {
+    final ByteBuffer buf;
+    final byte[] bytes;
+    int pos;
+    ByteOrder byteOrder = BIG_ENDIAN;
+
+    MyByteBuffer(ByteBuffer buf, byte[] bytes) {
+        this.buf = buf;
+        this.bytes = Arrays.copyOf(bytes, bytes.length);
+        pos = 0;
+    }
+
+    public final MyByteBuffer order(ByteOrder bo) {
+        byteOrder = bo;
+        buf.order(bo);
+        return this;
+    }
+
+    static MyByteBuffer wrap(byte[] bytes) {
+        return new MyByteBuffer(ByteBuffer.wrap(bytes), bytes);
+    }
+
+    int capacity() { return bytes.length; }
+    int position() {
+        if (buf.position() != pos)
+            throw new RuntimeException();
+        return buf.position();
+    }
+
+    byte[] array() { return buf.array(); }
+    byte[] backingArray() { return bytes; }
+
+    private static byte long7(long x) { return (byte)(x >> 56); }
+    private static byte long6(long x) { return (byte)(x >> 48); }
+    private static byte long5(long x) { return (byte)(x >> 40); }
+    private static byte long4(long x) { return (byte)(x >> 32); }
+    private static byte long3(long x) { return (byte)(x >> 24); }
+    private static byte long2(long x) { return (byte)(x >> 16); }
+    private static byte long1(long x) { return (byte)(x >>  8); }
+    private static byte long0(long x) { return (byte)(x      ); }
+
+    private static byte int3(int x) { return (byte)(x >> 24); }
+    private static byte int2(int x) { return (byte)(x >> 16); }
+    private static byte int1(int x) { return (byte)(x >>  8); }
+    private static byte int0(int x) { return (byte)(x      ); }
+
+    private static byte short1(short x) { return (byte)(x >> 8); }
+    private static byte short0(short x) { return (byte)(x     ); }
+
+    byte _get(long i) { return bytes[(int)i]; }
+    void _put(long i, byte x) { bytes[(int)i] = x; }
+
+    private void putLongX(long a, long x) {
+        if (byteOrder == BIG_ENDIAN) {
+            x = Long.reverseBytes(x);
+        }
+        _put(a + 7, long7(x));
+        _put(a + 6, long6(x));
+        _put(a + 5, long5(x));
+        _put(a + 4, long4(x));
+        _put(a + 3, long3(x));
+        _put(a + 2, long2(x));
+        _put(a + 1, long1(x));
+        _put(a    , long0(x));
+    }
+
+    private void putIntX(long a, int x) {
+        if (byteOrder == BIG_ENDIAN) {
+            x = Integer.reverseBytes(x);
+        }
+        _put(a + 3, int3(x));
+        _put(a + 2, int2(x));
+        _put(a + 1, int1(x));
+        _put(a    , int0(x));
+    }
+
+    private void putShortX(int bi, short x) {
+        if (byteOrder == BIG_ENDIAN) {
+            x = Short.reverseBytes(x);
+        }
+        _put(bi    , short0(x));
+        _put(bi + 1, short1(x));
+    }
+
+    static private int makeInt(byte b3, byte b2, byte b1, byte b0) {
+        return (((b3       ) << 24) |
+                ((b2 & 0xff) << 16) |
+                ((b1 & 0xff) <<  8) |
+                ((b0 & 0xff)      ));
+    }
+    int getIntX(long a) {
+        int x = makeInt(_get(a + 3),
+                _get(a + 2),
+                _get(a + 1),
+                _get(a));
+        if (byteOrder == BIG_ENDIAN) {
+            x = Integer.reverseBytes(x);
+        }
+        return x;
+    }
+
+    static private long makeLong(byte b7, byte b6, byte b5, byte b4,
+                                 byte b3, byte b2, byte b1, byte b0)
+    {
+        return ((((long)b7       ) << 56) |
+                (((long)b6 & 0xff) << 48) |
+                (((long)b5 & 0xff) << 40) |
+                (((long)b4 & 0xff) << 32) |
+                (((long)b3 & 0xff) << 24) |
+                (((long)b2 & 0xff) << 16) |
+                (((long)b1 & 0xff) <<  8) |
+                (((long)b0 & 0xff)      ));
+    }
+
+    long getLongX(long a) {
+        long x = makeLong(_get(a + 7),
+                _get(a + 6),
+                _get(a + 5),
+                _get(a + 4),
+                _get(a + 3),
+                _get(a + 2),
+                _get(a + 1),
+                _get(a));
+        if (byteOrder == BIG_ENDIAN) {
+            x = Long.reverseBytes(x);
+        }
+        return x;
+    }
+
+    static private short makeShort(byte b1, byte b0) {
+        return (short)((b1 << 8) | (b0 & 0xff));
+    }
+
+    short getShortX(long a) {
+        short x = makeShort(_get(a + 1),
+                            _get(a    ));
+        if (byteOrder == BIG_ENDIAN) {
+            x = Short.reverseBytes(x);
+        }
+        return x;
+    }
+
+    double getDoubleX(long a) {
+        long x = getLongX(a);
+        return Double.longBitsToDouble(x);
+    }
+
+    double getFloatX(long a) {
+        int x = getIntX(a);
+        return Float.intBitsToFloat(x);
+    }
+
+    void ck(long x, long y) {
+        if (x != y) {
+            throw new RuntimeException(" x = " + Long.toHexString(x) + ", y = " + Long.toHexString(y));
+        }
+    }
+
+    void ck(double x, double y) {
+        if (x == x && y == y && x != y) {
+            ck(x, y);
+        }
+    }
+
+    long getLong(int i) { ck(buf.getLong(i), getLongX(i)); return buf.getLong(i); }
+    int getInt(int i) { ck(buf.getInt(i), getIntX(i)); return buf.getInt(i); }
+    short getShort(int i) { ck(buf.getShort(i), getShortX(i)); return buf.getShort(i); }
+    char getChar(int i) { ck(buf.getChar(i), (char)getShortX(i)); return buf.getChar(i); }
+    double getDouble(int i) { ck(buf.getDouble(i), getDoubleX(i)); return buf.getDouble(i); }
+    float getFloat(int i) { ck(buf.getFloat(i), getFloatX(i)); return buf.getFloat(i); }
+
+    void putLong(int i, long x) { buf.putLong(i, x); putLongX(i, x); }
+    void putInt(int i, int x) { buf.putInt(i, x); putIntX(i, x); }
+    void putShort(int i, short x) { buf.putShort(i, x); putShortX(i, x); }
+    void putChar(int i, char x) { buf.putChar(i, x); putShortX(i, (short)x); }
+    void putDouble(int i, double x) { buf.putDouble(i, x); putLongX(i, Double.doubleToRawLongBits(x)); }
+    void putFloat(int i, float x) { buf.putFloat(i, x); putIntX(i, Float.floatToRawIntBits(x)); }
+
+    long getLong() { ck(buf.getLong(buf.position()), getLongX(pos)); long x = buf.getLong(); pos += 8; return x; }
+    int getInt() { ck(buf.getInt(buf.position()), getIntX(pos)); int x = buf.getInt(); pos += 4; return x; }
+    short getShort() { ck(buf.getShort(buf.position()), getShortX(pos)); short x = buf.getShort(); pos += 2; return x; }
+    char getChar() {  ck(buf.getChar(buf.position()), (char)getShortX(pos)); char x = buf.getChar(); pos += 2; return x; }
+    double getDouble() { ck(buf.getDouble(buf.position()), getDoubleX(pos)); double x = buf.getDouble(); pos += 8; return x; }
+    float getFloat() { ck(buf.getFloat(buf.position()), getFloatX(pos)); float x = buf.getFloat(); pos += 4; return x; }
+
+    void putLong(long x) { putLongX(pos, x); pos += 8; buf.putLong(x); }
+    void putInt(int x) { putIntX(pos, x); pos += 4; buf.putInt(x); }
+    void putShort(short x) { putShortX(pos, x); pos += 2; buf.putShort(x); }
+    void putChar(char x) { putShortX(pos, (short)x); pos += 2; buf.putChar(x); }
+    void putDouble(double x) { putLongX(pos, Double.doubleToRawLongBits(x)); pos += 8; buf.putDouble(x); }
+    void putFloat(float x) { putIntX(pos, Float.floatToRawIntBits(x)); pos += 4; buf.putFloat(x); }
+
+    void rewind() { pos = 0; buf.rewind(); }
+}
+
+public class HeapByteBufferTest implements Runnable {
+
+    SplittableRandom random = new SplittableRandom();
+    MyByteBuffer data = MyByteBuffer.wrap(new byte[1024]);
+
+    int randomOffset(SplittableRandom r, MyByteBuffer buf, int size) {
+        return abs(r.nextInt()) % (buf.capacity() - size);
+    }
+
+    long iterations;
+
+    HeapByteBufferTest(long iterations) {
+        this.iterations = iterations;
+    }
+
+    // The core of the test.  Walk over the buffer reading and writing
+    // random data, XORing it as we go.  We can detect writes in the
+    // wrong place, writes which are too long or too short, and reads
+    // or writes of the wrong data,
+    void step(SplittableRandom r) {
+        data.order((r.nextInt() & 1) != 0 ? BIG_ENDIAN : LITTLE_ENDIAN);
+
+        data.rewind();
+        while (data.position() < data.capacity())
+            data.putLong(data.getLong() ^ random.nextLong());
+
+        data.rewind();
+        while (data.position() < data.capacity())
+            data.putInt(data.getInt() ^ random.nextInt());
+
+        data.rewind();
+        while (data.position() < data.capacity())
+            data.putShort((short)(data.getShort() ^ random.nextInt()));
+
+        data.rewind();
+        while (data.position() < data.capacity())
+            data.putChar((char)(data.getChar() ^ random.nextInt()));
+
+        data.rewind();
+        while (data.position() < data.capacity()) {
+            data.putDouble(combine(data.getDouble(), random.nextLong()));
+        }
+
+        data.rewind();
+        while (data.position() < data.capacity())
+            data.putFloat(combine(data.getFloat(), random.nextInt()));
+
+        for (int i = 0; i < 100; i++) {
+            int offset = randomOffset(r, data, 8);
+            data.putLong(offset, data.getLong(offset) ^ random.nextLong());
+        }
+        for (int i = 0; i < 100; i++) {
+            int offset = randomOffset(r, data, 4);
+            data.putInt(offset, data.getInt(offset) ^ random.nextInt());
+        }
+        for (int i = 0; i < 100; i++) {
+            int offset = randomOffset(r, data, 4);
+            data.putShort(offset, (short)(data.getShort(offset) ^ random.nextInt()));
+        }
+        for (int i = 0; i < 100; i++) {
+            int offset = randomOffset(r, data, 4);
+            data.putChar(offset, (char)(data.getChar(offset) ^ random.nextInt()));
+        }
+        for (int i = 0; i < 100; i++) {
+            int offset = randomOffset(r, data, 8);
+            data.putDouble(offset, combine(data.getDouble(offset), random.nextLong()));
+        }
+        for (int i = 0; i < 100; i++) {
+            int offset = randomOffset(r, data, 4);
+            data.putFloat(offset, combine(data.getFloat(offset), random.nextInt()));
+        }
+    }
+
+    // XOR the bit pattern of a double and a long, returning the
+    // result as a double.
+    //
+    // We convert signalling NaNs to quiet NaNs.  We need to do this
+    // because some platforms (in particular legacy 80x87) do not
+    // provide transparent conversions between integer and
+    // floating-point types even when using raw conversions but
+    // quietly convert sNaN to qNaN.  This causes spurious test
+    // failures when the template interpreter uses 80x87 and the JITs
+    // use XMM registers.
+    //
+    public double combine(double prev, long bits) {
+        bits ^= Double.doubleToRawLongBits(prev);
+        double result = Double.longBitsToDouble(bits);
+        if (Double.isNaN(result)) {
+            result = Double.longBitsToDouble(bits | 0x8000000000000l);
+        }
+        return result;
+    }
+
+    // XOR the bit pattern of a float and an int, returning the result
+    // as a float.  Convert sNaNs to qNaNs.
+    public Float combine(float prev, int bits) {
+        bits ^= Float.floatToRawIntBits(prev);
+        Float result = Float.intBitsToFloat(bits);
+        if (Float.isNaN(result)) {
+            result = Float.intBitsToFloat(bits | 0x400000);
+        }
+        return result;
+    }
+
+    public void run() {
+        SplittableRandom r = new SplittableRandom();
+
+        for (int i = 0; i < data.capacity(); i += 8) {
+            data.putLong(i, random.nextLong());
+        }
+
+        for (int i = 0; i < iterations; i++) {
+            step(r);
+        }
+
+        if (!Arrays.equals(data.array(), data.backingArray())) {
+            throw new RuntimeException();
+        }
+    }
+
+    public static void main(String[] args) {
+        // The number of iterations is high to ensure that tiered
+        // compilation kicks in all the way up to C2.
+        long iterations = 100000;
+        if (args.length > 0)
+            iterations = Long.parseLong(args[0]);
+
+        new HeapByteBufferTest(iterations).run();
+    }
+}