8282664: Unroll by hand StringUTF16 and StringLatin1 polynomial hash loops

Co-authored-by: Sandhya Viswanathan <sviswanathan@openjdk.org>
Co-authored-by: Ludovic Henry <luhenry@openjdk.org>
Co-authored-by: Claes Redestad <redestad@openjdk.org>
Reviewed-by: vlivanov, sviswanathan, luhenry
This commit is contained in:
Claes Redestad 2023-01-17 21:06:22 +00:00
parent ade08e190c
commit e37078f5bb
33 changed files with 1053 additions and 87 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, 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
@ -3203,6 +3203,195 @@ void C2_MacroAssembler::stringL_indexof_char(Register str1, Register cnt1, Regis
bind(DONE_LABEL);
} // stringL_indexof_char
int C2_MacroAssembler::arrays_hashcode_elsize(BasicType eltype) {
switch (eltype) {
case T_BOOLEAN: return sizeof(jboolean);
case T_BYTE: return sizeof(jbyte);
case T_SHORT: return sizeof(jshort);
case T_CHAR: return sizeof(jchar);
case T_INT: return sizeof(jint);
default:
ShouldNotReachHere();
return -1;
}
}
void C2_MacroAssembler::arrays_hashcode_elload(Register dst, Address src, BasicType eltype) {
switch (eltype) {
// T_BOOLEAN used as surrogate for unsigned byte
case T_BOOLEAN: movzbl(dst, src); break;
case T_BYTE: movsbl(dst, src); break;
case T_SHORT: movswl(dst, src); break;
case T_CHAR: movzwl(dst, src); break;
case T_INT: movl(dst, src); break;
default:
ShouldNotReachHere();
}
}
void C2_MacroAssembler::arrays_hashcode_elvload(XMMRegister dst, Address src, BasicType eltype) {
load_vector(dst, src, arrays_hashcode_elsize(eltype) * 8);
}
void C2_MacroAssembler::arrays_hashcode_elvload(XMMRegister dst, AddressLiteral src, BasicType eltype) {
load_vector(dst, src, arrays_hashcode_elsize(eltype) * 8);
}
void C2_MacroAssembler::arrays_hashcode_elvcast(XMMRegister dst, BasicType eltype) {
const int vlen = Assembler::AVX_256bit;
switch (eltype) {
case T_BOOLEAN: vector_unsigned_cast(dst, dst, vlen, T_BYTE, T_INT); break;
case T_BYTE: vector_signed_cast(dst, dst, vlen, T_BYTE, T_INT); break;
case T_SHORT: vector_signed_cast(dst, dst, vlen, T_SHORT, T_INT); break;
case T_CHAR: vector_unsigned_cast(dst, dst, vlen, T_SHORT, T_INT); break;
case T_INT:
// do nothing
break;
default:
ShouldNotReachHere();
}
}
void C2_MacroAssembler::arrays_hashcode(Register ary1, Register cnt1, Register result,
Register index, Register tmp2, Register tmp3, XMMRegister vnext,
XMMRegister vcoef0, XMMRegister vcoef1, XMMRegister vcoef2, XMMRegister vcoef3,
XMMRegister vresult0, XMMRegister vresult1, XMMRegister vresult2, XMMRegister vresult3,
XMMRegister vtmp0, XMMRegister vtmp1, XMMRegister vtmp2, XMMRegister vtmp3,
BasicType eltype) {
ShortBranchVerifier sbv(this);
assert(UseAVX >= 2, "AVX2 intrinsics are required");
assert_different_registers(ary1, cnt1, result, index, tmp2, tmp3);
assert_different_registers(vnext, vcoef0, vcoef1, vcoef2, vcoef3, vresult0, vresult1, vresult2, vresult3, vtmp0, vtmp1, vtmp2, vtmp3);
Label SHORT_UNROLLED_BEGIN, SHORT_UNROLLED_LOOP_BEGIN,
SHORT_UNROLLED_LOOP_EXIT,
UNROLLED_SCALAR_LOOP_BEGIN, UNROLLED_SCALAR_SKIP, UNROLLED_SCALAR_RESUME,
UNROLLED_VECTOR_LOOP_BEGIN,
END;
switch (eltype) {
case T_BOOLEAN: BLOCK_COMMENT("arrays_hashcode(unsigned byte) {"); break;
case T_CHAR: BLOCK_COMMENT("arrays_hashcode(char) {"); break;
case T_BYTE: BLOCK_COMMENT("arrays_hashcode(byte) {"); break;
case T_SHORT: BLOCK_COMMENT("arrays_hashcode(short) {"); break;
case T_INT: BLOCK_COMMENT("arrays_hashcode(int) {"); break;
default: BLOCK_COMMENT("arrays_hashcode {"); break;
}
// For "renaming" for readibility of the code
XMMRegister vcoef[] = { vcoef0, vcoef1, vcoef2, vcoef3 },
vresult[] = { vresult0, vresult1, vresult2, vresult3 },
vtmp[] = { vtmp0, vtmp1, vtmp2, vtmp3 };
const int elsize = arrays_hashcode_elsize(eltype);
/*
if (cnt1 >= 2) {
if (cnt1 >= 32) {
UNROLLED VECTOR LOOP
}
UNROLLED SCALAR LOOP
}
SINGLE SCALAR
*/
cmpl(cnt1, 32);
jcc(Assembler::less, SHORT_UNROLLED_BEGIN);
// cnt1 >= 32 && generate_vectorized_loop
xorl(index, index);
// vresult = IntVector.zero(I256);
for (int idx = 0; idx < 4; idx++) {
vpxor(vresult[idx], vresult[idx]);
}
// vnext = IntVector.broadcast(I256, power_of_31_backwards[0]);
Register bound = tmp2;
Register next = tmp3;
lea(tmp2, ExternalAddress(StubRoutines::x86::arrays_hashcode_powers_of_31() + (0 * sizeof(jint))));
movl(next, Address(tmp2, 0));
movdl(vnext, next);
vpbroadcastd(vnext, vnext, Assembler::AVX_256bit);
// index = 0;
// bound = cnt1 & ~(32 - 1);
movl(bound, cnt1);
andl(bound, ~(32 - 1));
// for (; index < bound; index += 32) {
bind(UNROLLED_VECTOR_LOOP_BEGIN);
// result *= next;
imull(result, next);
// loop fission to upfront the cost of fetching from memory, OOO execution
// can then hopefully do a better job of prefetching
for (int idx = 0; idx < 4; idx++) {
arrays_hashcode_elvload(vtmp[idx], Address(ary1, index, Address::times(elsize), 8 * idx * elsize), eltype);
}
// vresult = vresult * vnext + ary1[index+8*idx:index+8*idx+7];
for (int idx = 0; idx < 4; idx++) {
vpmulld(vresult[idx], vresult[idx], vnext, Assembler::AVX_256bit);
arrays_hashcode_elvcast(vtmp[idx], eltype);
vpaddd(vresult[idx], vresult[idx], vtmp[idx], Assembler::AVX_256bit);
}
// index += 32;
addl(index, 32);
// index < bound;
cmpl(index, bound);
jcc(Assembler::less, UNROLLED_VECTOR_LOOP_BEGIN);
// }
lea(ary1, Address(ary1, bound, Address::times(elsize)));
subl(cnt1, bound);
// release bound
// vresult *= IntVector.fromArray(I256, power_of_31_backwards, 1);
for (int idx = 0; idx < 4; idx++) {
lea(tmp2, ExternalAddress(StubRoutines::x86::arrays_hashcode_powers_of_31() + ((8 * idx + 1) * sizeof(jint))));
arrays_hashcode_elvload(vcoef[idx], Address(tmp2, 0), T_INT);
vpmulld(vresult[idx], vresult[idx], vcoef[idx], Assembler::AVX_256bit);
}
// result += vresult.reduceLanes(ADD);
for (int idx = 0; idx < 4; idx++) {
reduceI(Op_AddReductionVI, 256/(sizeof(jint) * 8), result, result, vresult[idx], vtmp[(idx * 2 + 0) % 4], vtmp[(idx * 2 + 1) % 4]);
}
// } else if (cnt1 < 32) {
bind(SHORT_UNROLLED_BEGIN);
// int i = 1;
movl(index, 1);
cmpl(index, cnt1);
jcc(Assembler::greaterEqual, SHORT_UNROLLED_LOOP_EXIT);
// for (; i < cnt1 ; i += 2) {
bind(SHORT_UNROLLED_LOOP_BEGIN);
movl(tmp3, 961);
imull(result, tmp3);
arrays_hashcode_elload(tmp2, Address(ary1, index, Address::times(elsize), -elsize), eltype);
movl(tmp3, tmp2);
shll(tmp3, 5);
subl(tmp3, tmp2);
addl(result, tmp3);
arrays_hashcode_elload(tmp3, Address(ary1, index, Address::times(elsize)), eltype);
addl(result, tmp3);
addl(index, 2);
cmpl(index, cnt1);
jccb(Assembler::less, SHORT_UNROLLED_LOOP_BEGIN);
// }
// if (i >= cnt1) {
bind(SHORT_UNROLLED_LOOP_EXIT);
jccb(Assembler::greater, END);
movl(tmp2, result);
shll(result, 5);
subl(result, tmp2);
arrays_hashcode_elload(tmp3, Address(ary1, index, Address::times(elsize), -elsize), eltype);
addl(result, tmp3);
// }
bind(END);
BLOCK_COMMENT("} // arrays_hashcode");
} // arrays_hashcode
// helper function for string_compare
void C2_MacroAssembler::load_next_elements(Register elem1, Register elem2, Register str1, Register str2,
Address::ScaleFactor scale, Address::ScaleFactor scale1,
@ -4685,6 +4874,33 @@ void C2_MacroAssembler::vector_unsigned_cast(XMMRegister dst, XMMRegister src, i
}
}
void C2_MacroAssembler::vector_signed_cast(XMMRegister dst, XMMRegister src, int vlen_enc,
BasicType from_elem_bt, BasicType to_elem_bt) {
switch (from_elem_bt) {
case T_BYTE:
switch (to_elem_bt) {
case T_SHORT: vpmovsxbw(dst, src, vlen_enc); break;
case T_INT: vpmovsxbd(dst, src, vlen_enc); break;
case T_LONG: vpmovsxbq(dst, src, vlen_enc); break;
default: ShouldNotReachHere();
}
break;
case T_SHORT:
switch (to_elem_bt) {
case T_INT: vpmovsxwd(dst, src, vlen_enc); break;
case T_LONG: vpmovsxwq(dst, src, vlen_enc); break;
default: ShouldNotReachHere();
}
break;
case T_INT:
assert(to_elem_bt == T_LONG, "");
vpmovsxdq(dst, src, vlen_enc);
break;
default:
ShouldNotReachHere();
}
}
void C2_MacroAssembler::vector_mask_cast(XMMRegister dst, XMMRegister src,
BasicType dst_bt, BasicType src_bt, int vlen) {
int vlen_enc = vector_length_encoding(MAX2(type2aelembytes(src_bt), type2aelembytes(dst_bt)) * vlen);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, 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
@ -292,6 +292,19 @@ public:
Register limit, Register result, Register chr,
XMMRegister vec1, XMMRegister vec2, bool is_char, KRegister mask = knoreg);
void arrays_hashcode(Register str1, Register cnt1, Register result,
Register tmp1, Register tmp2, Register tmp3, XMMRegister vnext,
XMMRegister vcoef0, XMMRegister vcoef1, XMMRegister vcoef2, XMMRegister vcoef3,
XMMRegister vresult0, XMMRegister vresult1, XMMRegister vresult2, XMMRegister vresult3,
XMMRegister vtmp0, XMMRegister vtmp1, XMMRegister vtmp2, XMMRegister vtmp3,
BasicType eltype);
// helper functions for arrays_hashcode
int arrays_hashcode_elsize(BasicType eltype);
void arrays_hashcode_elload(Register dst, Address src, BasicType eltype);
void arrays_hashcode_elvload(XMMRegister dst, Address src, BasicType eltype);
void arrays_hashcode_elvload(XMMRegister dst, AddressLiteral src, BasicType eltype);
void arrays_hashcode_elvcast(XMMRegister dst, BasicType eltype);
void evmasked_op(int ideal_opc, BasicType eType, KRegister mask,
XMMRegister dst, XMMRegister src1, XMMRegister src2,
@ -310,6 +323,9 @@ public:
void vector_unsigned_cast(XMMRegister dst, XMMRegister src, int vlen_enc,
BasicType from_elem_bt, BasicType to_elem_bt);
void vector_signed_cast(XMMRegister dst, XMMRegister src, int vlen_enc,
BasicType from_elem_bt, BasicType to_elem_bt);
void vector_cast_int_to_subword(BasicType to_elem_bt, XMMRegister dst, XMMRegister zero,
XMMRegister xtmp, Register rscratch, int vec_enc);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2023, 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
@ -227,6 +227,43 @@ juint StubRoutines::x86::_shuf_table_crc32_avx512[] =
};
#endif // _LP64
jint StubRoutines::x86::_arrays_hashcode_powers_of_31[] =
{
2111290369,
-2010103841,
350799937,
11316127,
693101697,
-254736545,
961614017,
31019807,
-2077209343,
-67006753,
1244764481,
-2038056289,
211350913,
-408824225,
-844471871,
-997072353,
1353309697,
-510534177,
1507551809,
-505558625,
-293403007,
129082719,
-1796951359,
-196513505,
-1807454463,
1742810335,
887503681,
28629151,
923521,
29791,
961,
31,
1,
};
#define D 32
#define P 0x82F63B78 // Reflection of Castagnoli (0x11EDC6F41)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2023, 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
@ -137,6 +137,8 @@ class x86 {
#endif // _LP64
// table for CRC32C
static juint* _crc32c_table;
// table for arrays_hashcode
static jint _arrays_hashcode_powers_of_31[];
// upper word mask for sha1
static address _upper_word_mask_addr;
@ -325,6 +327,7 @@ class x86 {
static address base64_decoding_table_addr() { return _decoding_table_base64; }
#endif
static address pshuffle_byte_flip_mask_addr() { return _pshuffle_byte_flip_mask_addr; }
static address arrays_hashcode_powers_of_31() { return (address)_arrays_hashcode_powers_of_31; }
static void generate_CRC32C_table(bool is_pclmulqdq_supported);
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, 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
@ -1695,6 +1695,13 @@ void VM_Version::get_processor_features() {
warning("vectorizedMismatch intrinsics are not available on this CPU");
FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false);
}
if (UseAVX >= 2) {
FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, true);
} else if (UseVectorizedHashCodeIntrinsic) {
if (!FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic))
warning("vectorizedHashCode intrinsics are not available on this CPU");
FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, false);
}
#else
if (UseVectorizedMismatchIntrinsic) {
if (!FLAG_IS_DEFAULT(UseVectorizedMismatchIntrinsic)) {
@ -1702,6 +1709,12 @@ void VM_Version::get_processor_features() {
}
FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false);
}
if (UseVectorizedHashCodeIntrinsic) {
if (!FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) {
warning("vectorizedHashCode intrinsic is not available in 32-bit VM");
}
FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, false);
}
#endif // _LP64
// Use count leading zeros count instruction if available.

View File

@ -305,10 +305,10 @@ reg_class int_rbx_reg(RBX);
// Singleton class for RCX int register
reg_class int_rcx_reg(RCX);
// Singleton class for RCX int register
// Singleton class for RDX int register
reg_class int_rdx_reg(RDX);
// Singleton class for RCX int register
// Singleton class for RDI int register
reg_class int_rdi_reg(RDI);
// Singleton class for instruction pointer
@ -12074,6 +12074,32 @@ instruct array_equalsC_evex(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result,
ins_pipe( pipe_slow );
%}
instruct arrays_hashcode(rdi_RegP ary1, rdx_RegI cnt1, rbx_RegI result, immU8 basic_type,
legRegD tmp_vec1, legRegD tmp_vec2, legRegD tmp_vec3, legRegD tmp_vec4,
legRegD tmp_vec5, legRegD tmp_vec6, legRegD tmp_vec7, legRegD tmp_vec8,
legRegD tmp_vec9, legRegD tmp_vec10, legRegD tmp_vec11, legRegD tmp_vec12,
legRegD tmp_vec13, rRegI tmp1, rRegI tmp2, rRegI tmp3, rFlagsReg cr)
%{
predicate(UseAVX >= 2);
match(Set result (VectorizedHashCode (Binary ary1 cnt1) (Binary result basic_type)));
effect(TEMP tmp_vec1, TEMP tmp_vec2, TEMP tmp_vec3, TEMP tmp_vec4, TEMP tmp_vec5, TEMP tmp_vec6,
TEMP tmp_vec7, TEMP tmp_vec8, TEMP tmp_vec9, TEMP tmp_vec10, TEMP tmp_vec11, TEMP tmp_vec12,
TEMP tmp_vec13, TEMP tmp1, TEMP tmp2, TEMP tmp3, USE_KILL ary1, USE_KILL cnt1,
USE basic_type, KILL cr);
format %{ "Array HashCode array[] $ary1,$cnt1,$result,$basic_type -> $result // KILL all" %}
ins_encode %{
__ arrays_hashcode($ary1$$Register, $cnt1$$Register, $result$$Register,
$tmp1$$Register, $tmp2$$Register, $tmp3$$Register,
$tmp_vec1$$XMMRegister, $tmp_vec2$$XMMRegister, $tmp_vec3$$XMMRegister,
$tmp_vec4$$XMMRegister, $tmp_vec5$$XMMRegister, $tmp_vec6$$XMMRegister,
$tmp_vec7$$XMMRegister, $tmp_vec8$$XMMRegister, $tmp_vec9$$XMMRegister,
$tmp_vec10$$XMMRegister, $tmp_vec11$$XMMRegister, $tmp_vec12$$XMMRegister,
$tmp_vec13$$XMMRegister, (BasicType)$basic_type$$constant);
%}
ins_pipe( pipe_slow );
%}
instruct count_positives(rsi_RegP ary1, rcx_RegI len, rax_RegI result,
legRegD tmp1, legRegD tmp2, rbx_RegI tmp3, rFlagsReg cr,)
%{

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2023, 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
@ -605,15 +605,16 @@ bool InstructForm::needs_anti_dependence_check(FormDict &globals) const {
// TEMPORARY
// if( is_simple_chain_rule(globals) ) return false;
// String.(compareTo/equals/indexOf) and Arrays.equals use many memorys edges,
// but writes none
// String.(compareTo/equals/indexOf/hashCode) and Arrays.(equals/hashCode)
// use many memorys edges, but writes none
if( _matrule && _matrule->_rChild &&
( strcmp(_matrule->_rChild->_opType,"StrComp" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOf" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOfChar" )==0 ||
strcmp(_matrule->_rChild->_opType,"CountPositives" )==0 ||
strcmp(_matrule->_rChild->_opType,"AryEq" )==0 ))
strcmp(_matrule->_rChild->_opType,"AryEq" )==0 ||
strcmp(_matrule->_rChild->_opType,"VectorizedHashCode")==0 ))
return true;
// Check if instruction has a USE of a memory operand class, but no defs
@ -896,6 +897,7 @@ uint InstructForm::oper_input_base(FormDict &globals) {
if( _matrule->_rChild &&
( strcmp(_matrule->_rChild->_opType,"AryEq" )==0 ||
strcmp(_matrule->_rChild->_opType,"VectorizedHashCode")==0 ||
strcmp(_matrule->_rChild->_opType,"StrComp" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrInflatedCopy" )==0 ||
@ -904,7 +906,7 @@ uint InstructForm::oper_input_base(FormDict &globals) {
strcmp(_matrule->_rChild->_opType,"StrIndexOfChar")==0 ||
strcmp(_matrule->_rChild->_opType,"CountPositives")==0 ||
strcmp(_matrule->_rChild->_opType,"EncodeISOArray")==0)) {
// String.(compareTo/equals/indexOf) and Arrays.equals
// String.(compareTo/equals/indexOf/hashCode) and Arrays.equals
// and sun.nio.cs.iso8859_1$Encoder.EncodeISOArray
// take 1 control and 1 memory edges.
// Also String.(compressedCopy/inflatedCopy).

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, 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
@ -221,6 +221,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_equalsL:
case vmIntrinsics::_equalsU:
case vmIntrinsics::_equalsC:
case vmIntrinsics::_vectorizedHashCode:
case vmIntrinsics::_getCharStringU:
case vmIntrinsics::_putCharStringU:
case vmIntrinsics::_compressStringC:
@ -527,6 +528,9 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_equalsU:
if (!SpecialStringEquals) return true;
break;
case vmIntrinsics::_vectorizedHashCode:
if (!UseVectorizedHashCodeIntrinsic) return true;
break;
case vmIntrinsics::_equalsB:
case vmIntrinsics::_equalsC:
if (!SpecialArraysEquals) return true;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, 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
@ -350,6 +350,10 @@ class methodHandle;
do_intrinsic(_equalsB, java_util_Arrays, equals_name, equalsB_signature, F_S) \
do_signature(equalsB_signature, "([B[B)Z") \
\
do_intrinsic(_vectorizedHashCode, jdk_internal_util_ArraysSupport, vectorizedHashCode_name, vectorizedHashCode_signature, F_S) \
do_name( vectorizedHashCode_name, "vectorizedHashCode") \
do_signature(vectorizedHashCode_signature, "(Ljava/lang/Object;IIII)I") \
\
do_intrinsic(_compressStringC, java_lang_StringUTF16, compress_name, encodeISOArray_signature, F_S) \
do_name( compress_name, "compress") \
do_intrinsic(_compressStringB, java_lang_StringUTF16, compress_name, indexOfI_signature, F_S) \

View File

@ -565,6 +565,8 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) {
{ { 2, ShenandoahLoad }, { 4, ShenandoahLoad } },
Op_StrEquals,
{ { 2, ShenandoahLoad }, { 3, ShenandoahLoad } },
Op_VectorizedHashCode,
{ { 2, ShenandoahLoad }, { -1, ShenandoahNone } },
Op_EncodeISOArray,
{ { 2, ShenandoahLoad }, { 3, ShenandoahStore } },
Op_CountPositives,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -221,6 +221,9 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt
case vmIntrinsics::_equalsU:
if (!Matcher::match_rule_supported(Op_StrEquals)) return false;
break;
case vmIntrinsics::_vectorizedHashCode:
if (!Matcher::match_rule_supported(Op_VectorizedHashCode)) return false;
break;
case vmIntrinsics::_equalsB:
case vmIntrinsics::_equalsC:
if (!Matcher::match_rule_supported(Op_AryEq)) return false;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, 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
@ -511,6 +511,7 @@ macro(VectorCastHF2F)
macro(VectorUCastB2X)
macro(VectorUCastS2X)
macro(VectorUCastI2X)
macro(VectorizedHashCode)
macro(VectorInsert)
macro(MaskAll)
macro(AndVMask)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2023, 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
@ -809,6 +809,7 @@ void ConnectionGraph::add_final_edges(Node *n) {
add_final_edges_unsafe_access(n, opcode);
break;
}
case Op_VectorizedHashCode:
case Op_AryEq:
case Op_CountPositives:
case Op_StrComp:
@ -3441,10 +3442,11 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist,
memnode_worklist.append_if_missing(use);
} else if (!(op == Op_CmpP || op == Op_Conv2B ||
op == Op_CastP2X || op == Op_StoreCM ||
op == Op_FastLock || op == Op_AryEq || op == Op_StrComp ||
op == Op_CountPositives ||
op == Op_FastLock || op == Op_AryEq ||
op == Op_StrComp || op == Op_CountPositives ||
op == Op_StrCompressedCopy || op == Op_StrInflatedCopy ||
op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar ||
op == Op_StrEquals || op == Op_VectorizedHashCode ||
op == Op_StrIndexOf || op == Op_StrIndexOfChar ||
op == Op_SubTypeCheck ||
BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use))) {
n->dump();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023, 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
@ -83,6 +83,21 @@ Node* StrInflatedCopyNode::Ideal(PhaseGVN* phase, bool can_reshape) {
return remove_dead_region(phase, can_reshape) ? this : NULL;
}
uint VectorizedHashCodeNode::match_edge(uint idx) const {
// Do not match memory edge.
return idx >= 2 && idx <= 5; // VectorizedHashCodeNode (Binary ary1 cnt1) (Binary result bt)
}
Node* VectorizedHashCodeNode::Ideal(PhaseGVN* phase, bool can_reshape) {
return remove_dead_region(phase, can_reshape) ? this : NULL;
}
const Type* VectorizedHashCodeNode::Value(PhaseGVN* phase) const {
if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP;
return bottom_type();
}
//=============================================================================
//------------------------------match_edge-------------------------------------
// Do not match memory edge

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023, 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
@ -166,6 +166,20 @@ class CountPositivesNode: public StrIntrinsicNode {
virtual const Type* bottom_type() const { return TypeInt::POS; }
};
//------------------------------VectorizedHashCodeNode----------------------
class VectorizedHashCodeNode: public Node {
public:
VectorizedHashCodeNode(Node* control, Node* ary_mem, Node* arg1, Node* cnt1, Node* result, Node* basic_type)
: Node(control, ary_mem, arg1, cnt1, result, basic_type) {};
virtual int Opcode() const;
virtual bool depends_only_on_test() const { return false; }
virtual const Type* bottom_type() const { return TypeInt::INT; }
virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; }
virtual uint match_edge(uint idx) const;
virtual uint ideal_reg() const { return Op_RegI; }
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
virtual const Type* Value(PhaseGVN* phase) const;
};
//------------------------------EncodeISOArray--------------------------------
// encode char[] to byte[] in ISO_8859_1 or ASCII

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2023, 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
@ -201,6 +201,7 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo
case Op_StrIndexOf:
case Op_StrIndexOfChar:
case Op_AryEq:
case Op_VectorizedHashCode:
case Op_StrInflatedCopy:
case Op_StrCompressedCopy:
case Op_EncodeISOArray:

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -307,6 +307,8 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_equalsL: return inline_string_equals(StrIntrinsicNode::LL);
case vmIntrinsics::_equalsU: return inline_string_equals(StrIntrinsicNode::UU);
case vmIntrinsics::_vectorizedHashCode: return inline_vectorizedHashCode();
case vmIntrinsics::_toBytesStringU: return inline_string_toBytesU();
case vmIntrinsics::_getCharsStringU: return inline_string_getCharsU();
case vmIntrinsics::_getCharStringU: return inline_string_char_access(!is_store);
@ -1066,6 +1068,7 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) {
return true;
}
//------------------------------inline_countPositives------------------------------
bool LibraryCallKit::inline_countPositives() {
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
@ -5924,6 +5927,38 @@ bool LibraryCallKit::inline_vectorizedMismatch() {
return true;
}
//------------------------------inline_vectorizedHashcode----------------------------
bool LibraryCallKit::inline_vectorizedHashCode() {
assert(UseVectorizedHashCodeIntrinsic, "not implemented on this platform");
assert(callee()->signature()->size() == 5, "vectorizedHashCode has 5 parameters");
Node* array = argument(0);
Node* offset = argument(1);
Node* length = argument(2);
Node* initialValue = argument(3);
Node* basic_type = argument(4);
array = must_be_not_null(array, true);
if (basic_type == top()) {
return false; // failed input validation
}
const TypeInt* basic_type_t = _gvn.type(basic_type)->is_int();
if (!basic_type_t->is_con()) {
return false; // Only intrinsify if mode argument is constant
}
BasicType bt = (BasicType)basic_type_t->get_con();
// Resolve address of first element
Node* array_start = array_element_address(array, offset, bt);
set_result(_gvn.transform(new VectorizedHashCodeNode(control(), memory(TypeAryPtr::get_array_body_type(bt)),
array_start, length, initialValue, basic_type)));
clear_upper_avx();
return true;
}
/**
* Calculate CRC32 for byte.
* int java.util.zip.CRC32.update(int crc, int b)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, 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
@ -195,6 +195,7 @@ class LibraryCallKit : public GraphKit {
RegionNode* region, Node* phi, StrIntrinsicNode::ArgEnc ae);
bool inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae);
bool inline_string_equals(StrIntrinsicNode::ArgEnc ae);
bool inline_vectorizedHashCode();
bool inline_string_toBytesU();
bool inline_string_getCharsU();
bool inline_string_copy(bool compress);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2023, 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
@ -849,6 +849,7 @@ bool IdealLoopTree::policy_maximally_unroll(PhaseIdealLoop* phase) const {
switch (n->Opcode()) {
case Op_StrComp:
case Op_StrEquals:
case Op_VectorizedHashCode:
case Op_StrIndexOf:
case Op_StrIndexOfChar:
case Op_EncodeISOArray:
@ -1017,6 +1018,7 @@ bool IdealLoopTree::policy_unroll(PhaseIdealLoop *phase) {
case Op_StrIndexOfChar:
case Op_EncodeISOArray:
case Op_AryEq:
case Op_VectorizedHashCode:
case Op_CountPositives: {
// Do not unroll a loop with String intrinsics code.
// String intrinsics are large and have loops.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2023, 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
@ -5828,6 +5828,7 @@ void PhaseIdealLoop::build_loop_late_post_work(Node *n, bool pinned) {
case Op_StrIndexOf:
case Op_StrIndexOfChar:
case Op_AryEq:
case Op_VectorizedHashCode:
case Op_CountPositives:
pinned = false;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, 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
@ -1067,6 +1067,7 @@ static void match_alias_type(Compile* C, Node* n, Node* m) {
case Op_StrIndexOf:
case Op_StrIndexOfChar:
case Op_AryEq:
case Op_VectorizedHashCode:
case Op_CountPositives:
case Op_MemBarVolatile:
case Op_MemBarCPUOrder: // %%% these ideals should have narrower adr_type?
@ -2244,6 +2245,7 @@ bool Matcher::find_shared_visit(MStack& mstack, Node* n, uint opcode, bool& mem_
case Op_StrIndexOf:
case Op_StrIndexOfChar:
case Op_AryEq:
case Op_VectorizedHashCode:
case Op_CountPositives:
case Op_StrInflatedCopy:
case Op_StrCompressedCopy:
@ -2383,12 +2385,6 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) {
n->del_req(3);
break;
}
case Op_VectorCmpMasked: {
Node* pair1 = new BinaryNode(n->in(2), n->in(3));
n->set_req(2, pair1);
n->del_req(3);
break;
}
case Op_MacroLogicV: {
Node* pair1 = new BinaryNode(n->in(1), n->in(2));
Node* pair2 = new BinaryNode(n->in(3), n->in(4));
@ -2420,7 +2416,8 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) {
break;
}
case Op_StrComp:
case Op_StrIndexOf: {
case Op_StrIndexOf:
case Op_VectorizedHashCode: {
Node* pair1 = new BinaryNode(n->in(2), n->in(3));
n->set_req(2, pair1);
Node* pair2 = new BinaryNode(n->in(4),n->in(5));
@ -2429,9 +2426,9 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) {
n->del_req(4);
break;
}
case Op_EncodeISOArray:
case Op_StrCompressedCopy:
case Op_StrInflatedCopy:
case Op_EncodeISOArray: {
case Op_StrInflatedCopy: {
// Restructure into a binary tree for Matching.
Node* pair = new BinaryNode(n->in(3), n->in(4));
n->set_req(3, pair);
@ -2458,6 +2455,7 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) {
n->del_req(3);
break;
}
case Op_VectorCmpMasked:
case Op_CopySignD:
case Op_SignumVF:
case Op_SignumVD:

View File

@ -357,6 +357,9 @@ const int ObjectAlignmentInBytes = 8;
product(bool, UseVectorizedMismatchIntrinsic, false, DIAGNOSTIC, \
"Enables intrinsification of ArraysSupport.vectorizedMismatch()") \
\
product(bool, UseVectorizedHashCodeIntrinsic, false, DIAGNOSTIC, \
"Enables intrinsification of ArraysSupport.vectorizedHashCode()") \
\
product(bool, UseCopySignIntrinsic, false, DIAGNOSTIC, \
"Enables intrinsification of Math.copySign") \
\

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023, 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
@ -189,11 +189,11 @@ final class StringLatin1 {
}
public static int hashCode(byte[] value) {
int h = 0;
for (byte v : value) {
h = 31 * h + (v & 0xff);
}
return h;
return switch (value.length) {
case 0 -> 0;
case 1 -> value[0] & 0xff;
default -> ArraysSupport.vectorizedHashCode(value, 0, value.length, 0, ArraysSupport.T_BOOLEAN);
};
}
public static int indexOf(byte[] value, int ch, int fromIndex) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023, 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
@ -412,12 +412,11 @@ final class StringUTF16 {
}
public static int hashCode(byte[] value) {
int h = 0;
int length = value.length >> 1;
for (int i = 0; i < length; i++) {
h = 31 * h + getChar(value, i);
}
return h;
return switch (value.length) {
case 0 -> 0;
case 2 -> getChar(value, 0);
default -> ArraysSupport.vectorizedHashCode(value, 0, value.length >> 1, 0, ArraysSupport.T_CHAR);
};
}
public static int indexOf(byte[] value, int ch, int fromIndex) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2023, 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
@ -2464,7 +2464,9 @@ public final class System {
public String newStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException {
return String.newStringNoRepl(bytes, cs);
}
public char getUTF16Char(byte[] bytes, int index) {
return StringUTF16.getChar(bytes, index);
}
public byte[] getBytesNoRepl(String s, Charset cs) throws CharacterCodingException {
return String.getBytesNoRepl(s, cs);
}

View File

@ -25,6 +25,7 @@
package java.util;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
import jdk.internal.vm.annotation.IntrinsicCandidate;
@ -4266,15 +4267,14 @@ public class Arrays {
* @since 1.5
*/
public static int hashCode(long[] a) {
if (a == null)
if (a == null) {
return 0;
}
int result = 1;
for (long element : a) {
int elementHash = (int)(element ^ (element >>> 32));
result = 31 * result + elementHash;
}
return result;
}
@ -4295,14 +4295,14 @@ public class Arrays {
* @since 1.5
*/
public static int hashCode(int[] a) {
if (a == null)
if (a == null) {
return 0;
int result = 1;
for (int element : a)
result = 31 * result + element;
return result;
}
return switch (a.length) {
case 0 -> 1;
case 1 -> 31 + a[0];
default -> ArraysSupport.vectorizedHashCode(a, 0, a.length, 1, ArraysSupport.T_INT);
};
}
/**
@ -4322,14 +4322,14 @@ public class Arrays {
* @since 1.5
*/
public static int hashCode(short[] a) {
if (a == null)
if (a == null) {
return 0;
int result = 1;
for (short element : a)
result = 31 * result + element;
return result;
}
return switch (a.length) {
case 0 -> 1;
case 1 -> 31 + (int)a[0];
default -> ArraysSupport.vectorizedHashCode(a, 0, a.length, 1, ArraysSupport.T_SHORT);
};
}
/**
@ -4349,14 +4349,14 @@ public class Arrays {
* @since 1.5
*/
public static int hashCode(char[] a) {
if (a == null)
if (a == null) {
return 0;
int result = 1;
for (char element : a)
result = 31 * result + element;
return result;
}
return switch (a.length) {
case 0 -> 1;
case 1 -> 31 + (int)a[0];
default -> ArraysSupport.vectorizedHashCode(a, 0, a.length, 1, ArraysSupport.T_CHAR);
};
}
/**
@ -4376,14 +4376,14 @@ public class Arrays {
* @since 1.5
*/
public static int hashCode(byte[] a) {
if (a == null)
if (a == null) {
return 0;
int result = 1;
for (byte element : a)
result = 31 * result + element;
return result;
}
return switch (a.length) {
case 0 -> 1;
case 1 -> 31 + (int)a[0];
default -> ArraysSupport.vectorizedHashCode(a, 0, a.length, 1, ArraysSupport.T_BYTE);
};
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2023, 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
@ -342,6 +342,16 @@ public interface JavaLangAccess {
*/
String newStringUTF8NoRepl(byte[] bytes, int off, int len);
/**
* Get the char at index in a byte[] in internal UTF-16 representation,
* with no bounds checks.
*
* @param bytes the UTF-16 encoded bytes
* @param index of the char to retrieve, 0 <= index < (bytes.length >> 1)
* @return the char value
*/
char getUTF16Char(byte[] bytes, int index);
/**
* Encode the given string into a sequence of bytes using utf8.
*

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023, 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
@ -24,6 +24,8 @@
*/
package jdk.internal.util;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.IntrinsicCandidate;
@ -160,6 +162,105 @@ public class ArraysSupport {
}
}
// Possible values for the type operand of the NEWARRAY instruction.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.newarray.
public static final int T_BOOLEAN = 4;
public static final int T_CHAR = 5;
public static final int T_FLOAT = 6;
public static final int T_DOUBLE = 7;
public static final int T_BYTE = 8;
public static final int T_SHORT = 9;
public static final int T_INT = 10;
public static final int T_LONG = 11;
/**
* Calculate the hash code for an array in a way that enables efficient
* vectorization.
*
* <p>This method does not perform type checks or bounds checks. It is the
* responsibility of the caller to perform such checks before calling this
* method.
*
* @param array for which to calculate hash code
* @param fromIndex start index, scaled to basicType
* @param length number of elements to include in the hash
* @param initialValue the initial value for the hash (typically constant 0 or 1)
* @param basicType type constant denoting how to interpret the array content.
* T_BOOLEAN is used to signify unsigned bytes, and T_CHAR might be used
* even if array is a byte[].
* @implNote currently basicType must be constant at the call site for this method
* to be intrinsified.
*
* @return the calculated hash value
*/
@IntrinsicCandidate
public static int vectorizedHashCode(Object array, int fromIndex, int length, int initialValue,
int basicType) {
return switch (basicType) {
case T_BOOLEAN -> signedHashCode(initialValue, (byte[]) array, fromIndex, length);
case T_CHAR -> array instanceof byte[]
? utf16hashCode(initialValue, (byte[]) array, fromIndex, length)
: hashCode(initialValue, (char[]) array, fromIndex, length);
case T_BYTE -> hashCode(initialValue, (byte[]) array, fromIndex, length);
case T_SHORT -> hashCode(initialValue, (short[]) array, fromIndex, length);
case T_INT -> hashCode(initialValue, (int[]) array, fromIndex, length);
default -> throw new IllegalArgumentException("unrecognized basic type: " + basicType);
};
}
private static int signedHashCode(int result, byte[] a, int fromIndex, int length) {
int end = fromIndex + length;
for (int i = fromIndex; i < end; i++) {
result = 31 * result + (a[i] & 0xff);
}
return result;
}
private static int hashCode(int result, byte[] a, int fromIndex, int length) {
int end = fromIndex + length;
for (int i = fromIndex; i < end; i++) {
result = 31 * result + a[i];
}
return result;
}
private static int hashCode(int result, char[] a, int fromIndex, int length) {
int end = fromIndex + length;
for (int i = fromIndex; i < end; i++) {
result = 31 * result + a[i];
}
return result;
}
private static int hashCode(int result, short[] a, int fromIndex, int length) {
int end = fromIndex + length;
for (int i = fromIndex; i < end; i++) {
result = 31 * result + a[i];
}
return result;
}
private static int hashCode(int result, int[] a, int fromIndex, int length) {
int end = fromIndex + length;
for (int i = fromIndex; i < end; i++) {
result = 31 * result + a[i];
}
return result;
}
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
/*
* fromIndex and length must be scaled to char indexes.
*/
public static int utf16hashCode(int result, byte[] value, int fromIndex, int length) {
int end = fromIndex + length;
for (int i = fromIndex; i < end; i++) {
result = 31 * result + JLA.getUTF16Char(value, i);
}
return result;
}
// Booleans
// Each boolean element takes up one byte

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2021, Datadog, 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.
*/
/**
* @test
* @summary Basic hashCode functionality and stability
* @run main/othervm -XX:+CompactStrings HashCode
* @run main/othervm -XX:-CompactStrings HashCode
*/
public class HashCode {
private static String [] tests = { "", " ", "a", "å",
"It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only. -- Charles Dickens, Tale of Two Cities",
"C'était le meilleur des temps, c'était le pire des temps, c'était l'âge de la sagesse, c'était l'âge de la folie, c'était l'époque de la croyance, c'était l'époque de l'incrédulité, c'était la saison de la Lumière, c'était C'était la saison des Ténèbres, c'était le printemps de l'espoir, c'était l'hiver du désespoir, nous avions tout devant nous, nous n'avions rien devant nous, nous allions tous directement au Ciel, nous allions tous directement dans l'autre sens bref, la période ressemblait tellement à la période actuelle, que certaines de ses autorités les plus bruyantes ont insisté pour qu'elle soit reçue, pour le bien ou pour le mal, au degré superlatif de la comparaison seulement. -- Charles Dickens, Tale of Two Cities (in French)",
"",
"禅道修行を志した雲水は、一般に参禅のしきたりを踏んだうえで一人の師につき、各地にある専門道場と呼ばれる養成寺院に入門し、与えられた公案に取り組むことになる。公案は、師家(老師)から雲水が悟りの境地へと進んで行くために手助けとして課す問題であり、悟りの境地に達していない人には容易に理解し難い難問だが、屁理屈や詭弁が述べられているわけではなく、頓知や謎かけとも異なる。"
};
private static int [] expected = { 0, 32, 97, 229, 1094896285, -331808333, 31109, 349367663 };
public static void main(String [] args) throws Exception {
for (int j = 0; j < 20_000; j++) {
for (int i = 0; i < tests.length; i++) {
// Force use of a new String without cached hash
String s = new String(tests[i].getBytes("UTF-8"), "UTF-8");
int e = expected[i];
int hashCode = s.hashCode();
if (hashCode != e)
throw new RuntimeException("String \"" + s + "\": "
+ " e = " + e
+ ", hashCode = " + hashCode
+ ", repetition = " + j);
}
}
}
}

View File

@ -0,0 +1,173 @@
/*
* Copyright (c) 2022, 2023, Oracle, 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.
*/
/**
* @test
* @summary Basic array hashCode functionality
* @run main/othervm --add-exports java.base/jdk.internal.util=ALL-UNNAMED -Xcomp -Xbatch HashCode
*/
import java.lang.reflect.Method;
import java.util.Arrays;
public class HashCode {
private static String[] tests = { "", " ", "a", "abcdefg",
"It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way- in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only. -- Charles Dickens, Tale of Two Cities",
"C'était le meilleur des temps, c'était le pire des temps, c'était l'âge de la sagesse, c'était l'âge de la folie, c'était l'époque de la croyance, c'était l'époque de l'incrédulité, c'était la saison de la Lumière, c'était C'était la saison des Ténèbres, c'était le printemps de l'espoir, c'était l'hiver du désespoir, nous avions tout devant nous, nous n'avions rien devant nous, nous allions tous directement au Ciel, nous allions tous directement dans l'autre sens bref, la période ressemblait tellement à la période actuelle, que certaines de ses autorités les plus bruyantes ont insisté pour qu'elle soit reçue, pour le bien ou pour le mal, au degré superlatif de la comparaison seulement. -- Charles Dickens, Tale of Two Cities (in French)",
"禅道修行を志した雲水は、一般に参禅のしきたりを踏んだうえで一人の師につき、各地にある専門道場と呼ばれる養成寺院に入門し、与えられた公案に取り組むことになる。公案は、師家(老師)から雲水が悟りの境地へと進んで行くために手助けとして課す問題であり、悟りの境地に達していない人には容易に理解し難い難問だが、屁理屈や詭弁が述べられているわけではなく、頓知や謎かけとも異なる。"
};
byte[][] zeroes = new byte[64][];
private static byte[][] testBytes = new byte[tests.length][];
private static short[][] testShorts = new short[tests.length][];
private static char[][] testChars = new char[tests.length][];
private static int[][] testInts = new int[tests.length][];
private static int[] expected = { 1, 63, 128, 536518979, -1174896354, -1357593156, 428276276};
private static int[] expectedUnsigned = { 1, 63, 128, 536518979, -1174896354, 584369596, -2025326028};
public static void main(String[] args) throws Exception {
// Deep introspection into range-based hash functions
Class<?> arraysSupport = Class.forName("jdk.internal.util.ArraysSupport");
Method vectorizedHashCode = arraysSupport.getDeclaredMethod("vectorizedHashCode", Object.class, int.class, int.class, int.class, int.class);
vectorizedHashCode.setAccessible(true);
for (int i = 0; i < tests.length; i++) {
testBytes[i] = tests[i].getBytes("UTF-8");
int len = testBytes[i].length;
testChars[i] = new char[len];
testShorts[i] = new short[len];
testInts[i] = new int[len];
for (int j = 0; j < len; j++) {
testChars[i][j] = (char) testBytes[i][j];
testShorts[i][j] = testBytes[i][j];
testInts[i][j] = testBytes[i][j];
}
}
boolean failed = false;
try {
int zeroResult = 1;
for (int i = 0; i < 64; i++) {
byte[] zeroes = new byte[i];
byte[] extraZeroes = new byte[i + 47];
for (int j = 0; j < 10_000; j++) {
int hashCode = Arrays.hashCode(zeroes);
if (hashCode != zeroResult) {
throw new RuntimeException("byte[] \"" + Arrays.toString(zeroes) + "\": "
+ " e = " + zeroResult
+ ", hashCode = " + hashCode
+ ", repetition = " + j);
}
hashCode = (int) vectorizedHashCode.invoke(null, extraZeroes, 17, i, 1, /* ArraysSupport.T_BYTE */ 8);
if (hashCode != zeroResult) {
throw new RuntimeException("byte[] subrange \"" + Arrays.toString(extraZeroes)
+ "\" at offset 17, limit " + i + ": "
+ " e = " + zeroResult
+ ", hashCode = " + hashCode
+ ", repetition = " + j);
}
}
zeroResult *= 31;
}
for (int i = 0; i < tests.length; i++) {
for (int j = 0; j < 64; j++) {
int e = expected[i];
int hashCode = Arrays.hashCode(testBytes[i]);
if (hashCode != e) {
throw new RuntimeException("byte[] \"" + Arrays.toString(testBytes[i]) + "\": "
+ " e = " + e
+ ", hashCode = " + hashCode
+ ", repetition = " + j);
}
}
}
System.out.println("byte[] tests passed");
} catch (RuntimeException e) {
System.out.println(e.getMessage());
failed = true;
}
try {
for (int i = 0; i < tests.length; i++) {
for (int j = 0; j < 64; j++) {
int e = expected[i];
int hashCode = Arrays.hashCode(testShorts[i]);
if (hashCode != e) {
throw new RuntimeException("short[] \"" + Arrays.toString(testShorts[i]) + "\": "
+ " e = " + e
+ ", hashCode = " + hashCode
+ ", repetition = " + j);
}
}
}
System.out.println("short[] tests passed");
} catch (RuntimeException e) {
System.out.println(e.getMessage());
failed = true;
}
try {
for (int i = 0; i < tests.length; i++) {
for (int j = 0; j < 64; j++) {
int e = expected[i];
int hashCode = Arrays.hashCode(testInts[i]);
if (hashCode != e) {
throw new RuntimeException("int[] \"" + Arrays.toString(testInts[i]) + "\": "
+ " e = " + e
+ ", hashCode = " + hashCode
+ ", repetition = " + j);
}
}
}
System.out.println("int[] tests passed");
} catch (RuntimeException e) {
System.out.println(e.getMessage());
failed = true;
}
try {
for (int i = 0; i < tests.length; i++) {
for (int j = 0; j < 64; j++) {
int e = expectedUnsigned[i];
int hashCode = Arrays.hashCode(testChars[i]);
if (hashCode != e) {
throw new RuntimeException("char[] \"" + Arrays.toString(testChars[i]) + "\": "
+ " e = " + e
+ ", hashCode = " + hashCode
+ ", repetition = " + j);
}
}
}
System.out.println("char[] tests passed");
} catch (RuntimeException e) {
System.out.println(e.getMessage());
failed = true;
}
if (failed) {
throw new RuntimeException("Some tests failed");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2004, 2023, 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

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2023, 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
@ -28,12 +28,19 @@ import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.io.UnsupportedEncodingException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
/**
* Performance test of String.hashCode() function
@ -83,4 +90,66 @@ public class StringHashCode {
public int empty() {
return empty.hashCode();
}
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(value = 3, jvmArgsAppend = {"--add-exports", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang=ALL-UNNAMED"})
public static class Algorithm {
private final static String alphabet = "abcdefghijklmnopqrstuvwxyz";
private final static MethodHandle defaultLatin1HashCodeMH;
private final static MethodHandle defaultUTF16HashCodeMH;
static {
try {
Class<?> stringLatin1 = Class.forName("java.lang.StringLatin1");
Method stringLatin1HashCode = stringLatin1.getDeclaredMethod("hashCode", byte[].class);
stringLatin1HashCode.setAccessible(true);
defaultLatin1HashCodeMH = MethodHandles.lookup().unreflect(stringLatin1HashCode);
Class<?> stringUTF16 = Class.forName("java.lang.StringUTF16");
Method stringUTF16HashCode = stringUTF16.getDeclaredMethod("hashCode", byte[].class);
stringUTF16HashCode.setAccessible(true);
defaultUTF16HashCodeMH = MethodHandles.lookup().unreflect(stringUTF16HashCode);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Param({"1", "10", "100", "10000"})
private int size;
private byte[] latin1;
private byte[] utf16;
@Setup
public void setup() throws UnsupportedEncodingException, ClassNotFoundException, NoSuchMethodException, Throwable {
Random rnd = new Random(42);
char[] str = new char[size];
for (int i = 0; i < size; i++) {
str[i] = alphabet.charAt(rnd.nextInt(alphabet.length()));
}
latin1 = new String(str).getBytes("US-ASCII");
utf16 = new String(str).getBytes("UTF-16");
// strip out byte order byte(s)
utf16 = Arrays.copyOfRange(utf16, utf16.length - str.length * 2, utf16.length);
}
@Benchmark
public int defaultLatin1() throws Throwable {
return (int)defaultLatin1HashCodeMH.invokeExact(latin1);
}
@Benchmark
public int defaultUTF16() throws Throwable {
return (int)defaultUTF16HashCodeMH.invokeExact(utf16);
}
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright (c) 2022, 2023, 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 org.openjdk.bench.java.lang;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OperationsPerInvocation;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.io.UnsupportedEncodingException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
/**
* Performance test of Arrays.hashCode() methods
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(value = 3)
public class ArraysHashCode {
@Param({"1", "10", "100", "10000"})
private int size;
private byte[] bytes;
private char[] chars;
private short[] shorts;
private int[] ints;
private byte[][] multibytes;
private char[][] multichars;
private short[][] multishorts;
private int[][] multiints;
@Setup
public void setup() throws UnsupportedEncodingException, ClassNotFoundException, NoSuchMethodException, Throwable {
Random rnd = new Random(42);
bytes = new byte[size];
chars = new char[size];
shorts = new short[size];
ints = new int[size];
for (int i = 0; i < size; i++) {
int next = rnd.nextInt();
bytes[i] = (byte)next;
chars[i] = (char)next;
shorts[i] = (short)next;
ints[i] = next;
}
multibytes = new byte[100][];
multichars = new char[100][];
multishorts = new short[100][];
multiints = new int[100][];
for (int i = 0; i < 100; i++) {
int next = rnd.nextInt(size + 1);
multibytes[i] = new byte[next];
multichars[i] = new char[next];
multishorts[i] = new short[next];
multiints[i] = new int[next];
for (int j = 0; j < next; j++) {
int nextj = rnd.nextInt();
multibytes[i][j] = (byte)nextj;
multichars[i][j] = (char)nextj;
multishorts[i][j] = (short)nextj;
multiints[i][j] = nextj;
}
}
}
@Benchmark
public int bytes() throws Throwable {
return Arrays.hashCode(bytes);
}
@Benchmark
public int chars() throws Throwable {
return Arrays.hashCode(chars);
}
@Benchmark
public int shorts() throws Throwable {
return Arrays.hashCode(shorts);
}
@Benchmark
public int ints() throws Throwable {
return Arrays.hashCode(ints);
}
@Benchmark
@OperationsPerInvocation(100)
public void multibytes(Blackhole bh) throws Throwable {
for (int i = 0; i < multibytes.length; i++) {
bh.consume(Arrays.hashCode(multibytes[i]));
}
}
@Benchmark
@OperationsPerInvocation(100)
public void multichars(Blackhole bh) throws Throwable {
for (int i = 0; i < multichars.length; i++) {
bh.consume(Arrays.hashCode(multichars[i]));
}
}
@Benchmark
@OperationsPerInvocation(100)
public void multishorts(Blackhole bh) throws Throwable {
for (int i = 0; i < multishorts.length; i++) {
bh.consume(Arrays.hashCode(multishorts[i]));
}
}
@Benchmark
@OperationsPerInvocation(100)
public void multiints(Blackhole bh) throws Throwable {
for (int i = 0; i < multibytes.length; i++) {
bh.consume(Arrays.hashCode(multiints[i]));
}
}
}