8026049: (bf) Intrinsify ByteBuffer.put{Int, Double, Float, ...} methods

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
This commit is contained in:
Andrew Haley 2015-03-31 12:31:18 -07:00
parent 61aa1cfdef
commit 1dfbc44c1f
11 changed files with 486 additions and 7 deletions

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -193,6 +193,11 @@ void VM_Version::get_processor_features() {
} }
} }
// This machine allows unaligned memory accesses
if (FLAG_IS_DEFAULT(UseUnalignedAccesses)) {
FLAG_SET_DEFAULT(UseUnalignedAccesses, true);
}
#ifdef COMPILER2 #ifdef COMPILER2
if (FLAG_IS_DEFAULT(OptoScheduling)) { if (FLAG_IS_DEFAULT(OptoScheduling)) {
OptoScheduling = true; OptoScheduling = true;

View file

@ -1,6 +1,6 @@
/* /*
* 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 2012, 2014 SAP AG. All rights reserved. * Copyright 2012, 2015 SAP AG. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -172,6 +172,12 @@ void VM_Version::initialize() {
FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); 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() { void VM_Version::print_features() {

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -356,6 +356,13 @@ void VM_Version::initialize() {
(cache_line_size > ContendedPaddingWidth)) (cache_line_size > ContendedPaddingWidth))
ContendedPaddingWidth = cache_line_size; 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 #ifndef PRODUCT
if (PrintMiscellaneous && Verbose) { if (PrintMiscellaneous && Verbose) {
tty->print_cr("L1 data cache line size: %u", L1_data_cache_line_size()); tty->print_cr("L1 data cache line size: %u", L1_data_cache_line_size());

View file

@ -980,6 +980,11 @@ void VM_Version::get_processor_features() {
(cache_line_size > ContendedPaddingWidth)) (cache_line_size > ContendedPaddingWidth))
ContendedPaddingWidth = cache_line_size; ContendedPaddingWidth = cache_line_size;
// This machine allows unaligned memory accesses
if (FLAG_IS_DEFAULT(UseUnalignedAccesses)) {
FLAG_SET_DEFAULT(UseUnalignedAccesses, true);
}
#ifndef PRODUCT #ifndef PRODUCT
if (PrintMiscellaneous && Verbose) { if (PrintMiscellaneous && Verbose) {
tty->print_cr("Logical CPUs per core: %u", tty->print_cr("Logical CPUs per core: %u",

View file

@ -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. * Copyright 2009 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
@ -30,4 +30,11 @@
#include "runtime/stubCodeGenerator.hpp" #include "runtime/stubCodeGenerator.hpp"
#include "vm_version_zero.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);
}
}

View file

@ -3462,6 +3462,24 @@ bool GraphBuilder::try_inline_intrinsics(ciMethod* callee) {
case vmIntrinsics::_putFloat : return append_unsafe_put_obj(callee, T_FLOAT, false); 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::_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::_getObjectVolatile : return append_unsafe_get_obj(callee, T_OBJECT, true);
case vmIntrinsics::_getBooleanVolatile: return append_unsafe_get_obj(callee, T_BOOLEAN, 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); case vmIntrinsics::_getByteVolatile : return append_unsafe_get_obj(callee, T_BYTE, true);

View file

@ -953,6 +953,20 @@
do_intrinsic(_putFloatVolatile, sun_misc_Unsafe, putFloatVolatile_name, putFloat_signature, F_RN) \ 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_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 */ \ /* %%% these are redundant except perhaps for getAddress, but Unsafe has native methods for them */ \
do_signature(getByte_raw_signature, "(J)B") \ do_signature(getByte_raw_signature, "(J)B") \
do_signature(putByte_raw_signature, "(JB)V") \ do_signature(putByte_raw_signature, "(JB)V") \

View file

@ -553,6 +553,17 @@ CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) {
if (!Matcher::match_rule_supported(Op_OverflowMulL) || !UseMathExactIntrinsics) return NULL; if (!Matcher::match_rule_supported(Op_OverflowMulL) || !UseMathExactIntrinsics) return NULL;
break; 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: default:
assert(id <= vmIntrinsics::LAST_COMPILER_INLINE, "caller responsibility"); assert(id <= vmIntrinsics::LAST_COMPILER_INLINE, "caller responsibility");
assert(id != vmIntrinsics::_Object_init && id != vmIntrinsics::_invoke, "enum out of order?"); assert(id != vmIntrinsics::_Object_init && id != vmIntrinsics::_invoke, "enum out of order?");
@ -803,6 +814,16 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_putFloatVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_FLOAT, is_volatile); 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::_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::_compareAndSwapObject: return inline_unsafe_load_store(T_OBJECT, LS_cmpxchg);
case vmIntrinsics::_compareAndSwapInt: return inline_unsafe_load_store(T_INT, 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); case vmIntrinsics::_compareAndSwapLong: return inline_unsafe_load_store(T_LONG, LS_cmpxchg);

View file

@ -324,6 +324,24 @@ UNSAFE_END
#endif // not SUPPORTS_NATIVE_CX8 #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) \ #define DEFINE_GETSETOOP(jboolean, Boolean) \
\ \
UNSAFE_ENTRY(jboolean, Unsafe_Get##Boolean##140(JNIEnv *env, jobject unsafe, jobject obj, jint offset)) \ UNSAFE_ENTRY(jboolean, Unsafe_Get##Boolean##140(JNIEnv *env, jobject unsafe, jobject obj, jint offset)) \
@ -1261,6 +1279,9 @@ static JNINativeMethod methods[] = {
{CC"loadFence", CC"()V", FN_PTR(Unsafe_LoadFence)}, {CC"loadFence", CC"()V", FN_PTR(Unsafe_LoadFence)},
{CC"storeFence", CC"()V", FN_PTR(Unsafe_StoreFence)}, {CC"storeFence", CC"()V", FN_PTR(Unsafe_StoreFence)},
{CC"fullFence", CC"()V", FN_PTR(Unsafe_FullFence)}, {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 #undef CC

View file

@ -3912,7 +3912,10 @@ class CommandLineFlags {
"Enable event-based tracing") \ "Enable event-based tracing") \
\ \
product(bool, UseLockedTracing, false, \ 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 * Macros for factoring of globals

View file

@ -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();
}
}