diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 91949213a20..cebca24bbbe 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -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); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index 2bd90fc24ea..65209f6c9fd 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -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); diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.cpp b/src/hotspot/cpu/x86/stubRoutines_x86.cpp index 8f285115538..f210de8e60c 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.cpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.cpp @@ -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) diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index bb98fcf46cd..c769d8b620c 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -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); }; diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index ba2da1fcd1a..7967564699b 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -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. diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 16cd035ac79..d0d5246eddc 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -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,) %{ diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index d6d13478339..6d22e3dcb86 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -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). diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index 81b442f713b..b511165521a 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -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; diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index fb9d9ec9c95..92ab554d620 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -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) \ diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index d443bb8aa33..2086152c893 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -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, diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 5af1cbe4aa1..2399ab8bdbf 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -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; diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 112bf860c57..8a9f4146b9e 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -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) diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 308b59ab799..6e5a811ce6d 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -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 &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(); diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index 021b558d04f..3bc49091b43 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -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 diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp index 2a37606addd..e8ebad788eb 100644 --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -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 diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index e6e82e935c6..74819dc2196 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -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: diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index d4c511c672c..7108de9d730 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -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) diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 8cc6f00fdf7..d69632a0ff3 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -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); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 5aeee56bda4..5912a4b4408 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -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. diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 087137c94aa..d2444363c52 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -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; } diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index d5e3f144f75..8993040e021 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -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: diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index bbb0e179b1d..10aa5ec226d 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -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") \ \ diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java index 3c6bd0dba84..526d752f058 100644 --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -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) { diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index b59b21fd180..c65435c0ac1 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -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) { diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index cdb5ec4bcd7..08881286d6c 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -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); } diff --git a/src/java.base/share/classes/java/util/Arrays.java b/src/java.base/share/classes/java/util/Arrays.java index 693216fb6e0..4947b2bbf54 100644 --- a/src/java.base/share/classes/java/util/Arrays.java +++ b/src/java.base/share/classes/java/util/Arrays.java @@ -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); + }; } /** diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 6ccc8276a48..ba08b281ccf 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -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. * diff --git a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java index dbdc1833c76..b9d0e29e449 100644 --- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java +++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java @@ -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. + * + *

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 diff --git a/test/jdk/java/lang/String/HashCode.java b/test/jdk/java/lang/String/HashCode.java new file mode 100644 index 00000000000..b7e4f3681e6 --- /dev/null +++ b/test/jdk/java/lang/String/HashCode.java @@ -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); + } + } + } +} diff --git a/test/jdk/java/util/Arrays/HashCode.java b/test/jdk/java/util/Arrays/HashCode.java new file mode 100644 index 00000000000..c06d9100790 --- /dev/null +++ b/test/jdk/java/util/Arrays/HashCode.java @@ -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"); + } + } +} diff --git a/test/jdk/javax/net/ssl/SSLEngine/Arrays.java b/test/jdk/javax/net/ssl/SSLEngine/Arrays.java index 2ba56c85b34..c5639229e07 100644 --- a/test/jdk/javax/net/ssl/SSLEngine/Arrays.java +++ b/test/jdk/javax/net/ssl/SSLEngine/Arrays.java @@ -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 diff --git a/test/micro/org/openjdk/bench/java/lang/StringHashCode.java b/test/micro/org/openjdk/bench/java/lang/StringHashCode.java index b1a6966f050..20735a3bf76 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringHashCode.java +++ b/test/micro/org/openjdk/bench/java/lang/StringHashCode.java @@ -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); + } + } } diff --git a/test/micro/org/openjdk/bench/java/util/ArraysHashCode.java b/test/micro/org/openjdk/bench/java/util/ArraysHashCode.java new file mode 100644 index 00000000000..62a2b8313e5 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/util/ArraysHashCode.java @@ -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])); + } + } +}