diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 123c821b169ef..22eb3ba7018a2 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -2962,6 +2962,292 @@ 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_BYTE: return sizeof(jbyte); + case T_SHORT: return sizeof(jshort); + case T_CHAR: return sizeof(jchar); + case T_INT: return sizeof(jint); + case T_FLOAT: return sizeof(jfloat); + default: + ShouldNotReachHere(); + return -1; + } +} + +void C2_MacroAssembler::arrays_hashcode_elload(Register dst, Address src, BasicType eltype) { + switch (eltype) { + case T_BYTE: + movzbl(dst, src); + break; + case T_SHORT: + case T_CHAR: + movzwl(dst, src); + break; + case T_INT: + case T_FLOAT: + 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) { + switch (eltype) { + case T_BYTE: + vector_unsigned_cast(dst, dst, Assembler::AVX_256bit, T_BYTE, T_INT); + break; + case T_SHORT: + case T_CHAR: + vector_unsigned_cast(dst, dst, Assembler::AVX_256bit, T_SHORT, T_INT); + break; + case T_INT: + case T_FLOAT: + // do nothing + break; + default: + ShouldNotReachHere(); + } +} + +void C2_MacroAssembler::arrays_hashcode(Register ary1, Register cnt1, Register result, + Register i, Register coef, Register tmp, 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, bool is_string_hashcode) { + ShortBranchVerifier sbv(this); + assert(UseAVX >= 2, "AVX2 intrinsics are required"); + + Label SHORT, SHORT_UNROLLED_LOOP_BEGIN, SHORT_UNROLLED_LOOP_END, SHORT_SCALAR_LOOP_BEGIN, SHORT_SCALAR_LOOP_END, + LONG, LONG_INIT, LONG_SCALAR_LOOP_BEGIN, LONG_SCALAR_LOOP_END, LONG_VECTOR_LOOP_BEGIN, LONG_VECTOR_LOOP_END, + NONNULL, END; + + // For "renaming" for readibility of the code + Register bound; + + XMMRegister vcoef[] = { vcoef0, vcoef1, vcoef2, vcoef3 }, + vresult[] = { vresult0, vresult1, vresult2, vresult3 }, + vtmp[] = { vtmp0, vtmp1, vtmp2, vtmp3 }; + + const int elsize = arrays_hashcode_elsize(eltype); + + int length_offset = arrayOopDesc::length_offset_in_bytes(); + int base_offset = arrayOopDesc::base_offset_in_bytes(eltype); + + if (!is_string_hashcode) { + testptr(ary1, ary1); + jcc(Assembler::notZero, NONNULL); + movl(result, 0); + jmp(END); + bind(NONNULL); + movl(cnt1, Address(ary1, length_offset)); + lea(ary1, Address(ary1, base_offset)); + } + + // int result = 0|1; + movl(result, is_string_hashcode ? 0 : 1); + + // if (cnt1 == 0) { + cmpl(cnt1, 0); + jcc(Assembler::equal, END); + + // cnt1 /= elsize + if (Address::times(elsize) != 0) { + shrl(cnt1, Address::times(elsize)); + } + + // } else if (cnt1 < 32) { + bind(SHORT); + cmpl(cnt1, 32); + jcc(Assembler::greaterEqual, LONG); + + // int i = 0; + movl(i, 0); + // int bound = cnt1 & ~(4 - 1); + bound = coef; + movl(bound, cnt1); + andl(bound, ~(4-1)); + + // for (; i < bound; i += 4) { + bind(SHORT_UNROLLED_LOOP_BEGIN); + // i < bound; + cmpl(i, bound); + jcc(Assembler::greaterEqual, SHORT_UNROLLED_LOOP_END); + for (int idx = 0; idx < 4; idx++) { + // h = h << 5 - 31; + movl(tmp, result); + shll(result, 5); + subl(result, tmp); + // h += ary1[i]; + arrays_hashcode_elload(tmp, Address(ary1, i, Address::times(elsize), idx*elsize), eltype); + addl(result, tmp); + } + addl(i, 4); + jmp(SHORT_UNROLLED_LOOP_BEGIN); + bind(SHORT_UNROLLED_LOOP_END); + // } + + // for (; i < cnt1; i += 1) { + bind(SHORT_SCALAR_LOOP_BEGIN); + // i < cnt1; + cmpl(i, cnt1); + jcc(Assembler::greaterEqual, SHORT_SCALAR_LOOP_END); + // h = h << 5 - h; + movl(tmp, result); + shll(result, 5); + subl(result, tmp); + // h += ary1[i]; + arrays_hashcode_elload(tmp, Address(ary1, i, Address::times(elsize)), eltype); + addl(result, tmp); + // i += 1; + addl(i, 1); + jmp(SHORT_SCALAR_LOOP_BEGIN); + bind(SHORT_SCALAR_LOOP_END); + // } + + jmp(END); + + // } else { // cnt1 >= 32 + bind(LONG); + + jmp(LONG_INIT); + address power_of_31_backwards = pc(); + emit_int32( 2111290369); + emit_int32(-2010103841); + emit_int32( 350799937); + emit_int32( 11316127); + emit_int32( 693101697); + emit_int32( -254736545); + emit_int32( 961614017); + emit_int32( 31019807); + emit_int32(-2077209343); + emit_int32( -67006753); + emit_int32( 1244764481); + emit_int32(-2038056289); + emit_int32( 211350913); + emit_int32( -408824225); + emit_int32( -844471871); + emit_int32( -997072353); + emit_int32( 1353309697); + emit_int32( -510534177); + emit_int32( 1507551809); + emit_int32( -505558625); + emit_int32( -293403007); + emit_int32( 129082719); + emit_int32(-1796951359); + emit_int32( -196513505); + emit_int32(-1807454463); + emit_int32( 1742810335); + emit_int32( 887503681); + emit_int32( 28629151); + emit_int32( 923521); + emit_int32( 29791); + emit_int32( 961); + emit_int32( 31); + emit_int32( 1); + bind(LONG_INIT); + + // int coef = 1; + movl(coef, 1); + // int i = cnt1 - 1; + movl(i, cnt1); + subl(i, 1); + // bound = cnt1 & ~(32-1); + bound = cnt1; + movl(bound, cnt1); + andl(bound, ~(32-1)); + + if (!is_string_hashcode) { + // result = 0; + movl(result, 0); + } + + // for (; i >= bound; i -= 1) { + bind(LONG_SCALAR_LOOP_BEGIN); + // i >= bound; + cmpl(i, bound); + jcc(Assembler::less, LONG_SCALAR_LOOP_END); + // result += coef * ary1[i]; + arrays_hashcode_elload(tmp, Address(ary1, i, Address::times(elsize)), eltype); + imull(tmp, coef); + addl(result, tmp); + // coef *= 31; + movl(tmp, 31); + imull(coef, tmp); + // i -= 1; + subl(i, 1); + jmp(LONG_SCALAR_LOOP_BEGIN); + bind(LONG_SCALAR_LOOP_END); + // } + + for (int idx = 0; idx < 4; idx++) { + // vresult = IntVector.zero(I256); + vpxor(vresult[idx], vresult[idx]); + } + // vnext = IntVector.broadcast(I256, power_of_31_backwards[0]); + movdl(vnext, InternalAddress(power_of_31_backwards+(0*sizeof(jint)))); + vpbroadcastd(vnext, vnext, Assembler::AVX_256bit); + // vcoef = IntVector.fromArray(I256, power_of_31_backwards, 1); + for (int idx = 0; idx < 4; idx++) { + arrays_hashcode_elvload(vcoef[idx], InternalAddress(power_of_31_backwards+((8*idx+1)*sizeof(jint))), T_INT); + } + // vcoef *= coef + movdl(vtmp0, coef); + vpbroadcastd(vtmp0, vtmp0, Assembler::AVX_256bit); + for (int idx = 0; idx < 4; idx++) { + vpmulld(vcoef[idx], vcoef[idx], vtmp0, Assembler::AVX_256bit); + } + + // for (i &= ~(8*4-1); i >= 0; i -= 8*4) { + // i &= ~(8*4-1); + andl(i, ~(8*4-1)); + bind(LONG_VECTOR_LOOP_BEGIN); + // 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, i, Address::times(elsize), 8*idx*elsize), eltype); + } + // vresult += vcoef * ary1[i+8*idx:i+8*idx+7]; vcoef *= vnext; + for (int idx = 0; idx < 4; idx++) { + arrays_hashcode_elvcast(vtmp[idx], eltype); + vpmulld(vtmp[idx], vtmp[idx], vcoef[idx], Assembler::AVX_256bit); + vpaddd(vresult[idx], vresult[idx], vtmp[idx], Assembler::AVX_256bit); + vpmulld(vcoef[idx], vcoef[idx], vnext, Assembler::AVX_256bit); + } + // i -= 8*4; + subl(i, 8*4); + // i >= 0; + cmpl(i, 0); + jcc(Assembler::greaterEqual, LONG_VECTOR_LOOP_BEGIN); + // } + + if (!is_string_hashcode) { + // result += vcoef0[0]; + movdl(tmp, vcoef0); + addl(result, tmp); + } + + // 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]); + } + + // } + + bind(END); + +} // 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, diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index 7f06d98d8f574..887e239ef5eb4 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -276,6 +276,20 @@ 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 i, Register coef, Register tmp, 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, bool is_string_hashcode); + + // 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, diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 92b790eb76877..19556fb182dc1 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1502,6 +1502,11 @@ const bool Matcher::match_rule_supported(int opcode) { return false; } break; + case Op_StrHashCode: + if (!UseSSE42Intrinsics) { + return false; + } + break; case Op_OnSpinWait: if (VM_Version::supports_on_spin_wait() == false) { return false; diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index b9e67caa11f77..b70afedcdb75b 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -305,12 +305,27 @@ 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 RSI int register +reg_class int_rsi_reg(RSI); + +// Singleton class for R8 int register +reg_class int_r8_reg(R8); + +// Singleton class for R9 int register +reg_class int_r9_reg(R9); + +// Singleton class for R10 int register +reg_class int_r10_reg(R10); + +// Singleton class for R11 int register +reg_class int_r11_reg(R11); + // Singleton class for instruction pointer // reg_class ip_reg(RIP); @@ -3364,6 +3379,56 @@ operand rdi_RegI() interface(REG_INTER); %} +operand rsi_RegI() +%{ + constraint(ALLOC_IN_RC(int_rsi_reg)); + match(RegI); + match(rRegI); + + format %{ "RSI" %} + interface(REG_INTER); +%} + +operand r8_RegI() +%{ + constraint(ALLOC_IN_RC(int_r8_reg)); + match(RegI); + match(rRegI); + + format %{ "R8" %} + interface(REG_INTER); +%} + +operand r9_RegI() +%{ + constraint(ALLOC_IN_RC(int_r9_reg)); + match(RegI); + match(rRegI); + + format %{ "R9" %} + interface(REG_INTER); +%} + +operand r10_RegI() +%{ + constraint(ALLOC_IN_RC(int_r10_reg)); + match(RegI); + match(rRegI); + + format %{ "R10" %} + interface(REG_INTER); +%} + +operand r11_RegI() +%{ + constraint(ALLOC_IN_RC(int_r11_reg)); + match(RegI); + match(rRegI); + + format %{ "R11" %} + interface(REG_INTER); +%} + operand no_rax_rdx_RegI() %{ constraint(ALLOC_IN_RC(int_no_rax_rdx_reg)); @@ -11686,6 +11751,59 @@ instruct string_equals_evex(rdi_RegP str1, rsi_RegP str2, rcx_RegI cnt, rax_RegI ins_pipe( pipe_slow ); %} +// fast string hashCode +instruct string_hashcodeL(rdi_RegP str1, rsi_RegI cnt1, rbx_RegI result, + 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, r8_RegI tmp1, r9_RegI tmp2, r10_RegI tmp3, + rFlagsReg cr) +%{ + predicate(UseAVX >= 2 && (((StrHashCodeNode*)n)->encoding() == StrIntrinsicNode::LL)); + match(Set result (StrHashCode (Binary str1 cnt1))); + 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 str1, USE_KILL cnt1, KILL cr); + + format %{ "String HashCode byte[] $str1,$cnt1 -> $result // KILL all" %} + ins_encode %{ + __ arrays_hashcode($str1$$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, T_BYTE, true); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_hashcodeU(rdi_RegP str1, rsi_RegI cnt1, rbx_RegI result, + 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, r8_RegI tmp1, r9_RegI tmp2, r10_RegI tmp3, + rFlagsReg cr) +%{ + predicate(UseAVX >= 2 && (((StrHashCodeNode*)n)->encoding() == StrIntrinsicNode::UU)); + match(Set result (StrHashCode (Binary str1 cnt1))); + 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 str1, USE_KILL cnt1, KILL cr); + + format %{ "String HashCode char[] $str1,$cnt1 -> $result // KILL all" %} + ins_encode %{ + __ arrays_hashcode($str1$$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, T_CHAR, true); + %} + ins_pipe( pipe_slow ); +%} + // fast array equals instruct array_equalsB(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result, legRegD tmp1, legRegD tmp2, rcx_RegI tmp3, rbx_RegI tmp4, rFlagsReg cr) @@ -11751,6 +11869,136 @@ instruct array_equalsC_evex(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result, ins_pipe( pipe_slow ); %} +instruct array_hashcodeI(rdi_RegP ary1, rax_RegI result, + 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, r8_RegI tmp1, r9_RegI tmp2, r10_RegI tmp3, r11_RegI tmp4, + rFlagsReg cr) +%{ + predicate(UseAVX >= 2 && (((AryHashCodeNode*)n)->type() == T_INT)); + match(Set result (AryHashCode ary1)); + 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, TEMP tmp4, USE_KILL ary1, KILL cr); + + format %{ "Array HashCode int[] $ary1 -> $result // KILL all" %} + ins_encode %{ + __ arrays_hashcode($ary1$$Register, $tmp4$$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, T_INT, false); + %} + ins_pipe( pipe_slow ); +%} + +instruct array_hashcodeS(rdi_RegP ary1, rax_RegI result, + 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, r8_RegI tmp1, r9_RegI tmp2, r10_RegI tmp3, r11_RegI tmp4, + rFlagsReg cr) +%{ + predicate(UseAVX >= 2 && (((AryHashCodeNode*)n)->type() == T_SHORT)); + match(Set result (AryHashCode ary1)); + 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, TEMP tmp4, USE_KILL ary1, KILL cr); + + format %{ "Array HashCode short[] $ary1 -> $result // KILL all" %} + ins_encode %{ + __ arrays_hashcode($ary1$$Register, $tmp4$$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, T_SHORT, false); + %} + ins_pipe( pipe_slow ); +%} + +instruct array_hashcodeC(rdi_RegP ary1, rax_RegI result, + 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, r8_RegI tmp1, r9_RegI tmp2, r10_RegI tmp3, r11_RegI tmp4, + rFlagsReg cr) +%{ + predicate(UseAVX >= 2 && (((AryHashCodeNode*)n)->type() == T_CHAR)); + match(Set result (AryHashCode ary1)); + 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, TEMP tmp4, USE_KILL ary1, KILL cr); + + format %{ "Array HashCode char[] $ary1 -> $result // KILL all" %} + ins_encode %{ + __ arrays_hashcode($ary1$$Register, $tmp4$$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, T_CHAR, false); + %} + ins_pipe( pipe_slow ); +%} + +instruct array_hashcodeB(rdi_RegP ary1, rax_RegI result, + 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, r8_RegI tmp1, r9_RegI tmp2, r10_RegI tmp3, r11_RegI tmp4, + rFlagsReg cr) +%{ + predicate(UseAVX >= 2 && (((AryHashCodeNode*)n)->type() == T_BYTE)); + match(Set result (AryHashCode ary1)); + 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, TEMP tmp4, USE_KILL ary1, KILL cr); + + format %{ "Array HashCode byte[] $ary1 -> $result // KILL all" %} + ins_encode %{ + __ arrays_hashcode($ary1$$Register, $tmp4$$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, T_BYTE, false); + %} + ins_pipe( pipe_slow ); +%} + +instruct array_hashcodeF(rdi_RegP ary1, rax_RegI result, + 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, r8_RegI tmp1, r9_RegI tmp2, r10_RegI tmp3, r11_RegI tmp4, + rFlagsReg cr) +%{ + predicate(UseAVX >= 2 && (((AryHashCodeNode*)n)->type() == T_FLOAT)); + match(Set result (AryHashCode ary1)); + 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, TEMP tmp4, USE_KILL ary1, KILL cr); + + format %{ "Array HashCode float[] $ary1 -> $result // KILL all" %} + ins_encode %{ + __ arrays_hashcode($ary1$$Register, $tmp4$$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, T_FLOAT, false); + %} + 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 42aaa1139c7b8..cbffb0d073ff1 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -605,15 +605,17 @@ 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,"StrHashCode")==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,"AryHashCode")==0 )) return true; // Check if instruction has a USE of a memory operand class, but no defs @@ -896,15 +898,17 @@ uint InstructForm::oper_input_base(FormDict &globals) { if( _matrule->_rChild && ( strcmp(_matrule->_rChild->_opType,"AryEq" )==0 || + strcmp(_matrule->_rChild->_opType,"AryHashCode")==0 || strcmp(_matrule->_rChild->_opType,"StrComp" )==0 || strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 || + strcmp(_matrule->_rChild->_opType,"StrHashCode")==0 || strcmp(_matrule->_rChild->_opType,"StrInflatedCopy" )==0 || strcmp(_matrule->_rChild->_opType,"StrCompressedCopy" )==0 || strcmp(_matrule->_rChild->_opType,"StrIndexOf")==0 || 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 deb1982b16e3e..57fc18df729f9 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -221,6 +221,13 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_equalsL: case vmIntrinsics::_equalsU: case vmIntrinsics::_equalsC: + case vmIntrinsics::_hashCodeL: + case vmIntrinsics::_hashCodeU: + case vmIntrinsics::_hashCodeI: + case vmIntrinsics::_hashCodeS: + case vmIntrinsics::_hashCodeC: + case vmIntrinsics::_hashCodeB: + case vmIntrinsics::_hashCodeF: case vmIntrinsics::_getCharStringU: case vmIntrinsics::_putCharStringU: case vmIntrinsics::_compressStringC: @@ -520,10 +527,21 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_equalsU: if (!SpecialStringEquals) return true; break; + case vmIntrinsics::_hashCodeL: + case vmIntrinsics::_hashCodeU: + if (!SpecialStringHashCode) return true; + break; case vmIntrinsics::_equalsB: case vmIntrinsics::_equalsC: if (!SpecialArraysEquals) return true; break; + case vmIntrinsics::_hashCodeI: + case vmIntrinsics::_hashCodeS: + case vmIntrinsics::_hashCodeC: + case vmIntrinsics::_hashCodeB: + case vmIntrinsics::_hashCodeF: + if (!SpecialArraysHashCode) return true; + break; case vmIntrinsics::_encodeISOArray: case vmIntrinsics::_encodeAsciiArray: case vmIntrinsics::_encodeByteISOArray: diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 67ddd1fbf0fe1..1df289f6cf178 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -317,6 +317,17 @@ class methodHandle; do_intrinsic(_equalsB, java_util_Arrays, equals_name, equalsB_signature, F_S) \ do_signature(equalsB_signature, "([B[B)Z") \ \ + do_intrinsic(_hashCodeI, java_util_Arrays, hashCode_name, hashCodeI_signature, F_S) \ + do_signature(hashCodeI_signature, "([I)I") \ + do_intrinsic(_hashCodeS, java_util_Arrays, hashCode_name, hashCodeS_signature, F_S) \ + do_signature(hashCodeS_signature, "([S)I") \ + do_intrinsic(_hashCodeC, java_util_Arrays, hashCode_name, hashCodeC_signature, F_S) \ + do_signature(hashCodeC_signature, "([C)I") \ + do_intrinsic(_hashCodeB, java_util_Arrays, hashCode_name, hashCodeB_signature, F_S) \ + do_signature(hashCodeB_signature, "([B)I") \ + do_intrinsic(_hashCodeF, java_util_Arrays, hashCode_name, hashCodeF_signature, F_S) \ + do_signature(hashCodeF_signature, "([F)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) \ @@ -358,6 +369,8 @@ class methodHandle; do_signature(indexOfChar_signature, "([BIII)I") \ do_intrinsic(_equalsL, java_lang_StringLatin1,equals_name, equalsB_signature, F_S) \ do_intrinsic(_equalsU, java_lang_StringUTF16, equals_name, equalsB_signature, F_S) \ + do_intrinsic(_hashCodeL, java_lang_StringLatin1,hashCode_name, hashCodeB_signature, F_S) \ + do_intrinsic(_hashCodeU, java_lang_StringUTF16, hashCode_name, hashCodeB_signature, F_S) \ \ do_intrinsic(_isDigit, java_lang_CharacterDataLatin1, isDigit_name, int_bool_signature, F_R) \ do_name( isDigit_name, "isDigit") \ diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 0a0e643abb5bd..e59546c5d4279 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_StrHashCode, + // { { 2, ShenandoahLoad }, { 3, ShenandoahLoad } }, //FIXME: what variables are these Op_EncodeISOArray, { { 2, ShenandoahLoad }, { 3, ShenandoahStore } }, Op_CountPositives, diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 65995f7bdda7b..cca11a08db10b 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -552,9 +552,15 @@ product(bool, SpecialStringEquals, true, DIAGNOSTIC, \ "special version of string equals") \ \ + product(bool, SpecialStringHashCode, true, DIAGNOSTIC, \ + "special version of string hashCode") \ + \ product(bool, SpecialArraysEquals, true, DIAGNOSTIC, \ "special version of Arrays.equals(char[],char[])") \ \ + product(bool, SpecialArraysHashCode, true, DIAGNOSTIC, \ + "special version of Arrays.hashCode") \ + \ product(bool, SpecialEncodeISOArray, true, DIAGNOSTIC, \ "special version of ISO_8859_1$Encoder.encodeISOArray") \ \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 075191ff91239..b2b4c1dc5916e 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -220,10 +220,21 @@ 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::_hashCodeL: + case vmIntrinsics::_hashCodeU: + if (!Matcher::match_rule_supported(Op_StrHashCode)) return false; + break; case vmIntrinsics::_equalsB: case vmIntrinsics::_equalsC: if (!Matcher::match_rule_supported(Op_AryEq)) return false; break; + case vmIntrinsics::_hashCodeI: + case vmIntrinsics::_hashCodeS: + case vmIntrinsics::_hashCodeC: + case vmIntrinsics::_hashCodeB: + case vmIntrinsics::_hashCodeF: + if (!Matcher::match_rule_supported(Op_AryHashCode)) return false; + break; case vmIntrinsics::_copyMemory: if (StubRoutines::unsafe_arraycopy() == NULL) return false; break; diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 860daa6ff4e5e..e36ba59bc5f7f 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -42,6 +42,7 @@ macro(AndI) macro(AndL) macro(ArrayCopy) macro(AryEq) +macro(AryHashCode) macro(AtanD) macro(Binary) macro(Blackhole) @@ -345,6 +346,7 @@ macro(StoreNKlass) macro(StrComp) macro(StrCompressedCopy) macro(StrEquals) +macro(StrHashCode) macro(StrIndexOf) macro(StrIndexOfChar) macro(StrInflatedCopy) diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 3dfa84b76d1c3..5179581d91933 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -635,9 +635,11 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de break; } case Op_AryEq: + // case Op_AryHashCode: case Op_CountPositives: case Op_StrComp: case Op_StrEquals: + // case Op_StrHashCode: case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_StrInflatedCopy: @@ -791,9 +793,11 @@ void ConnectionGraph::add_final_edges(Node *n) { break; } case Op_AryEq: + // case Op_AryHashCode: case Op_CountPositives: case Op_StrComp: case Op_StrEquals: + // case Op_StrHashCode: case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_StrInflatedCopy: @@ -3386,10 +3390,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_AryHashCode ||*/ + 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_StrHashCode ||*/ + op == Op_StrIndexOf || op == Op_StrIndexOfChar || op == Op_SubTypeCheck || BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use))) { n->dump(); @@ -3518,9 +3523,9 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, // They overwrite memory edge corresponding to destination array, memnode_worklist.append_if_missing(use); } else if (!(BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use) || - op == Op_AryEq || op == Op_StrComp || op == Op_CountPositives || + op == Op_AryEq || /*op == Op_AryHashCode ||*/ 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_StrHashCode ||*/ op == Op_StrIndexOf || op == Op_StrIndexOfChar)) { n->dump(); use->dump(); assert(false, "EA: missing memory path"); diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index 9f852dd637862..6155a5820aa74 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -78,6 +78,20 @@ Node* StrInflatedCopyNode::Ideal(PhaseGVN* phase, bool can_reshape) { return remove_dead_region(phase, can_reshape) ? this : NULL; } +uint AryHashCodeNode::match_edge(uint idx) const { + return idx == 2; // AryHashCode ary1 +} + +Node* AryHashCodeNode::Ideal(PhaseGVN* phase, bool can_reshape) { + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + +const Type* AryHashCodeNode::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 477842869c33e..f7764e6fefb55 100644 --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -103,6 +103,16 @@ class StrEqualsNode: public StrIntrinsicNode { virtual const Type* bottom_type() const { return TypeInt::BOOL; } }; +//------------------------------StrHashCode------------------------------------- +class StrHashCodeNode: public StrIntrinsicNode { + public: + StrHashCodeNode(Node* control, Node* char_array_mem, + Node* s1, Node* c1, ArgEncoding encoding): + StrIntrinsicNode(control, char_array_mem, s1, c1, encoding) {}; + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeInt::INT; } +}; + //------------------------------StrIndexOf------------------------------------- class StrIndexOfNode: public StrIntrinsicNode { public: @@ -166,6 +176,22 @@ class CountPositivesNode: public StrIntrinsicNode { virtual const Type* bottom_type() const { return TypeInt::POS; } }; +//------------------------------AryHashCode--------------------------------------- +class AryHashCodeNode: public Node { + BasicType _type; + public: + AryHashCodeNode(Node* control, Node* ary_mem, Node* s1, BasicType type) + : Node(control, ary_mem, s1), _type(type) {}; + BasicType type() const { return _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 TypeAryPtr::get_array_body_type(_type); } + 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 3bbc46f4e1a45..eb9375292cb39 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -197,9 +197,11 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo break; // Found a memory op? case Op_StrComp: case Op_StrEquals: + // case Op_StrHashCode: case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_AryEq: + case Op_AryHashCode: 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 060d2a183524f..b5f1a4886c118 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -307,6 +307,9 @@ 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::_hashCodeL: return inline_string_hashCode(StrIntrinsicNode::LL); + case vmIntrinsics::_hashCodeU: return inline_string_hashCode(StrIntrinsicNode::UU); + case vmIntrinsics::_toBytesStringU: return inline_string_toBytesU(); case vmIntrinsics::_getCharsStringU: return inline_string_getCharsU(); case vmIntrinsics::_getCharStringU: return inline_string_char_access(!is_store); @@ -491,6 +494,11 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_copyOfRange: return inline_array_copyOf(true); case vmIntrinsics::_equalsB: return inline_array_equals(StrIntrinsicNode::LL); case vmIntrinsics::_equalsC: return inline_array_equals(StrIntrinsicNode::UU); + case vmIntrinsics::_hashCodeI: return inline_array_hashcode(T_INT); + case vmIntrinsics::_hashCodeS: return inline_array_hashcode(T_SHORT); + case vmIntrinsics::_hashCodeC: return inline_array_hashcode(T_CHAR); + case vmIntrinsics::_hashCodeB: return inline_array_hashcode(T_BYTE); + case vmIntrinsics::_hashCodeF: return inline_array_hashcode(T_FLOAT); case vmIntrinsics::_Preconditions_checkIndex: return inline_preconditions_checkIndex(T_INT); case vmIntrinsics::_Preconditions_checkLongIndex: return inline_preconditions_checkIndex(T_LONG); case vmIntrinsics::_clone: return inline_native_clone(intrinsic()->is_virtual()); @@ -983,6 +991,26 @@ bool LibraryCallKit::inline_string_compareTo(StrIntrinsicNode::ArgEnc ae) { return true; } + +//------------------------------inline_string_hashCode------------------------ +bool LibraryCallKit::inline_string_hashCode(StrIntrinsicNode::ArgEnc ae) { + Node* arg1 = argument(0); + + arg1 = must_be_not_null(arg1, true); + + // Get start addr and length of first argument + Node* arg1_start = array_element_address(arg1, intcon(0), T_BYTE); + Node* arg1_cnt = load_array_length(arg1); + + Node* result = new StrHashCodeNode(control(), memory(TypeAryPtr::BYTES), + arg1_start, arg1_cnt, ae); + + clear_upper_avx(); + + set_result(_gvn.transform(result)); + return true; +} + //------------------------------inline_string_equals------------------------ bool LibraryCallKit::inline_string_equals(StrIntrinsicNode::ArgEnc ae) { Node* arg1 = argument(0); @@ -1044,6 +1072,18 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { return true; } +//------------------------------inline_array_hashcode---------------------------- +bool LibraryCallKit::inline_array_hashcode(BasicType type) { + assert(type == T_INT || type == T_SHORT || type == T_CHAR || type == T_BYTE || type == T_FLOAT, "unsupported array types"); + Node* arg1 = argument(0); + + const TypeAryPtr* mtype = TypeAryPtr::get_array_body_type(type); + set_result(_gvn.transform(new AryHashCodeNode(control(), memory(mtype), arg1, type))); + clear_upper_avx(); + + return true; +} + //------------------------------inline_countPositives------------------------------ bool LibraryCallKit::inline_countPositives() { if (too_many_traps(Deoptimization::Reason_intrinsic)) { diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 3135356e565a8..52fa1f576a739 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -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_string_hashCode(StrIntrinsicNode::ArgEnc ae); bool inline_string_toBytesU(); bool inline_string_getCharsU(); bool inline_string_copy(bool compress); @@ -254,6 +255,7 @@ class LibraryCallKit : public GraphKit { bool inline_native_getLength(); bool inline_array_copyOf(bool is_copyOfRange); bool inline_array_equals(StrIntrinsicNode::ArgEnc ae); + bool inline_array_hashcode(BasicType type); bool inline_preconditions_checkIndex(BasicType bt); void copy_to_clone(Node* obj, Node* alloc_obj, Node* obj_size, bool is_array); bool inline_native_clone(bool is_virtual); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 2c77fb79d6fec..fb1cbbbf4e257 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -824,10 +824,12 @@ bool IdealLoopTree::policy_maximally_unroll(PhaseIdealLoop* phase) const { switch (n->Opcode()) { case Op_StrComp: case Op_StrEquals: + case Op_StrHashCode: case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_EncodeISOArray: case Op_AryEq: + case Op_AryHashCode: case Op_CountPositives: { return false; } @@ -983,10 +985,12 @@ bool IdealLoopTree::policy_unroll(PhaseIdealLoop *phase) { } break; case Op_StrComp: case Op_StrEquals: + case Op_StrHashCode: case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_EncodeISOArray: case Op_AryEq: + case Op_AryHashCode: 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 742e745a97806..41c1d99e33aa4 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -5805,9 +5805,11 @@ void PhaseIdealLoop::build_loop_late_post_work(Node *n, bool pinned) { case Op_LoadL_unaligned: case Op_StrComp: // Does a bunch of load-like effects case Op_StrEquals: + case Op_StrHashCode: case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_AryEq: + case Op_AryHashCode: case Op_CountPositives: pinned = false; } diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 537a44c2492ed..8aaa649faf718 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -1063,9 +1063,11 @@ static void match_alias_type(Compile* C, Node* n, Node* m) { switch (n->Opcode()) { case Op_StrComp: case Op_StrEquals: + case Op_StrHashCode: case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_AryEq: + case Op_AryHashCode: case Op_CountPositives: case Op_MemBarVolatile: case Op_MemBarCPUOrder: // %%% these ideals should have narrower adr_type? @@ -1711,7 +1713,10 @@ Node* Matcher::Label_Root(const Node* n, State* svec, Node* control, Node*& mem) break; if (x >= _LAST_MACH_OPER) { + fprintf(stderr, "x = %d, _LAST_MACH_OPER = %d\n", x, _LAST_MACH_OPER); + fprintf(stderr, "dump n\n"); n->dump(); + fprintf(stderr, "dump svec\n"); svec->dump(); assert( false, "bad AD file" ); } @@ -2249,9 +2254,11 @@ bool Matcher::find_shared_visit(MStack& mstack, Node* n, uint opcode, bool& mem_ return true; // while (mstack.is_nonempty()) case Op_StrComp: case Op_StrEquals: + case Op_StrHashCode: case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_AryEq: + case Op_AryHashCode: case Op_CountPositives: case Op_StrInflatedCopy: case Op_StrCompressedCopy: @@ -2416,6 +2423,13 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) { n->del_req(4); break; } + case Op_StrHashCode: { + Node* pair1 = new BinaryNode(n->in(2), n->in(3)); + n->set_req(2, pair1); + // n->set_req(3, n->in(4)); + n->del_req(3); + break; + } case Op_StrComp: case Op_StrIndexOf: { Node* pair1 = new BinaryNode(n->in(2), n->in(3)); diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java index 3c6bd0dba84a4..efe736aad8fa1 100644 --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -188,6 +188,7 @@ public static int compareToCI_UTF16(byte[] value, byte[] other) { return len1 - len2; } + @IntrinsicCandidate public static int hashCode(byte[] value) { int h = 0; for (byte v : value) { diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index b59b21fd18062..193a8babf6608 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -411,6 +411,7 @@ public static int compareToCI_Latin1(byte[] value, byte[] other) { return -StringLatin1.compareToCI_UTF16(other, value); } + @IntrinsicCandidate public static int hashCode(byte[] value) { int h = 0; int length = value.length >> 1; diff --git a/src/java.base/share/classes/java/util/Arrays.java b/src/java.base/share/classes/java/util/Arrays.java index 8c7dbccc4a109..098c931dd3315 100644 --- a/src/java.base/share/classes/java/util/Arrays.java +++ b/src/java.base/share/classes/java/util/Arrays.java @@ -4294,6 +4294,7 @@ public static int hashCode(long[] a) { * @return a content-based hash code for {@code a} * @since 1.5 */ + @IntrinsicCandidate public static int hashCode(int[] a) { if (a == null) return 0; @@ -4321,6 +4322,7 @@ public static int hashCode(int[] a) { * @return a content-based hash code for {@code a} * @since 1.5 */ + @IntrinsicCandidate public static int hashCode(short[] a) { if (a == null) return 0; @@ -4348,6 +4350,7 @@ public static int hashCode(short[] a) { * @return a content-based hash code for {@code a} * @since 1.5 */ + @IntrinsicCandidate public static int hashCode(char[] a) { if (a == null) return 0; @@ -4375,6 +4378,7 @@ public static int hashCode(char[] a) { * @return a content-based hash code for {@code a} * @since 1.5 */ + @IntrinsicCandidate public static int hashCode(byte[] a) { if (a == null) return 0; @@ -4429,6 +4433,7 @@ public static int hashCode(boolean[] a) { * @return a content-based hash code for {@code a} * @since 1.5 */ + @IntrinsicCandidate public static int hashCode(float[] a) { if (a == null) return 0; diff --git a/test/jdk/java/lang/String/HashCode.java b/test/jdk/java/lang/String/HashCode.java new file mode 100644 index 0000000000000..9cc2d6e5290a4 --- /dev/null +++ b/test/jdk/java/lang/String/HashCode.java @@ -0,0 +1,52 @@ +/* + * 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 + * @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, 1094896285, -331808333, 349367663 }; + + public static void main(String [] args) { + for (int i = 0; i < tests.length; i++) { + String s = tests[i]; + int e = expected[i]; + int hashCode = s.hashCode(); + + if (hashCode != e) + throw new RuntimeException("String \"" + s + "\": " + + " e = " + e + + ", hashCode = " + hashCode); + } + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/StringHashCode.java b/test/micro/org/openjdk/bench/java/lang/StringHashCode.java index 96dbb0c5bdaa2..7a02dad841348 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringHashCode.java +++ b/test/micro/org/openjdk/bench/java/lang/StringHashCode.java @@ -24,13 +24,20 @@ import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; 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 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 @@ -77,4 +84,369 @@ public int notCached() { public int empty() { return empty.hashCode(); } + + @BenchmarkMode(Mode.AverageTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @State(Scope.Thread) + @Fork(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({"0", "1", "10", "100", "1000", "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"); + } + + @Benchmark + public int defaultLatin1() throws Throwable { + return (int)defaultLatin1HashCodeMH.invokeExact(latin1); + } + + @Benchmark + public int scalarLatin1() { + int h = 0; + int i = 0, len = latin1.length; + for (; i < len; i++) { + h = 31 * h + (latin1[i] & 0xff); + } + return h; + } + + @Benchmark + public int scalarLatin1Unrolled8() { + int h = 0; + int i = 0, len = latin1.length; + for (; i < (len & ~(8 - 1)); i += 8) { + h = -1807454463 * h + + 1742810335 * (latin1[i+0] & 0xff) + + 887503681 * (latin1[i+1] & 0xff) + + 28629151 * (latin1[i+2] & 0xff) + + 923521 * (latin1[i+3] & 0xff) + + 29791 * (latin1[i+4] & 0xff) + + 961 * (latin1[i+5] & 0xff) + + 31 * (latin1[i+6] & 0xff) + + 1 * (latin1[i+7] & 0xff); + } + for (; i < len; i++) { + h = 31 * h + (latin1[i] & 0xff); + } + return h; + } + + @Benchmark + public int scalarLatin1Inverted() { + int h = 0; + int len = latin1.length, i = len - 1; + int coef = 1; + for (; i >= 0; i -= 1) { + h = h + coef * (latin1[i] & 0xff); + coef = coef * 31; + } + return h; + } + + @Benchmark + public int scalarLatin1InvertedUnrolled8() { + int h = 0; + int len = latin1.length, i = len - 1; + int coef = 1; + for (int bound = len - (len % (8*(1-0))); i >= bound /* align on 8 elements */; i -= 1) { + h = h + coef * (latin1[i] & 0xff); + coef = coef * 31; + } + if (i-(8*(1-0)-1) >= 0) { + int h0 = 0; + int h1 = 0; + int h2 = 0; + int h3 = 0; + int h4 = 0; + int h5 = 0; + int h6 = 0; + int h7 = 0; + int coef0 = 31*31*31*31*31*31*31*coef; + int coef1 = 31*31*31*31*31*31*coef; + int coef2 = 31*31*31*31*31*coef; + int coef3 = 31*31*31*31*coef; + int coef4 = 31*31*31*coef; + int coef5 = 31*31*coef; + int coef6 = 31*coef; + int coef7 = coef; + for (; i-(8-1) >= 0; i -= 8) { + h0 += coef0 * (latin1[i-(8-1)+0] & 0xff); + h1 += coef1 * (latin1[i-(8-1)+1] & 0xff); + h2 += coef2 * (latin1[i-(8-1)+2] & 0xff); + h3 += coef3 * (latin1[i-(8-1)+3] & 0xff); + h4 += coef4 * (latin1[i-(8-1)+4] & 0xff); + h5 += coef5 * (latin1[i-(8-1)+5] & 0xff); + h6 += coef6 * (latin1[i-(8-1)+6] & 0xff); + h7 += coef7 * (latin1[i-(8-1)+7] & 0xff); + coef0 = 31*31*31*31*31*31*31*31 * coef0; + coef1 = 31*31*31*31*31*31*31*31 * coef1; + coef2 = 31*31*31*31*31*31*31*31 * coef2; + coef3 = 31*31*31*31*31*31*31*31 * coef3; + coef4 = 31*31*31*31*31*31*31*31 * coef4; + coef5 = 31*31*31*31*31*31*31*31 * coef5; + coef6 = 31*31*31*31*31*31*31*31 * coef6; + coef7 = 31*31*31*31*31*31*31*31 * coef7; + } + h += h0 + h1 + h2 + h3 + h4 + h5 + h6 + h7; + } + return h; + } + + @Benchmark + public int scalarLatin1InvertedUnrolled32() { + int h = 0; + int len = latin1.length, i = len - 1; + int coef = 1; + for (int bound = len - (len % 32); i >= bound /* align on 32 elements */; i -= 1) { + h = h + coef * (latin1[i] & 0xff); + coef = coef * 31; + } + if (i-(32-1) >= 0) { + int h0 = 0; + int h1 = 0; + int h2 = 0; + int h3 = 0; + int h4 = 0; + int h5 = 0; + int h6 = 0; + int h7 = 0; + int h8 = 0; + int h9 = 0; + int h10 = 0; + int h11 = 0; + int h12 = 0; + int h13 = 0; + int h14 = 0; + int h15 = 0; + int h16 = 0; + int h17 = 0; + int h18 = 0; + int h19 = 0; + int h20 = 0; + int h21 = 0; + int h22 = 0; + int h23 = 0; + int h24 = 0; + int h25 = 0; + int h26 = 0; + int h27 = 0; + int h28 = 0; + int h29 = 0; + int h30 = 0; + int h31 = 0; + int coef0 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef1 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef2 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef3 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef4 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef5 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef6 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef7 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef8 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef9 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef10 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef11 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef12 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef13 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef14 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef15 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef16 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef17 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef18 = 31*31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef19 = 31*31*31*31*31*31*31*31*31*31*31*31*coef; + int coef20 = 31*31*31*31*31*31*31*31*31*31*31*coef; + int coef21 = 31*31*31*31*31*31*31*31*31*31*coef; + int coef22 = 31*31*31*31*31*31*31*31*31*coef; + int coef23 = 31*31*31*31*31*31*31*31*coef; + int coef24 = 31*31*31*31*31*31*31*coef; + int coef25 = 31*31*31*31*31*31*coef; + int coef26 = 31*31*31*31*31*coef; + int coef27 = 31*31*31*31*coef; + int coef28 = 31*31*31*coef; + int coef29 = 31*31*coef; + int coef30 = 31*coef; + int coef31 = coef; + for (; i-(32-1) >= 0; i -= 32) { + h0 += coef0 * (latin1[i-(32-1)+0] & 0xff); + h1 += coef1 * (latin1[i-(32-1)+1] & 0xff); + h2 += coef2 * (latin1[i-(32-1)+2] & 0xff); + h3 += coef3 * (latin1[i-(32-1)+3] & 0xff); + h4 += coef4 * (latin1[i-(32-1)+4] & 0xff); + h5 += coef5 * (latin1[i-(32-1)+5] & 0xff); + h6 += coef6 * (latin1[i-(32-1)+6] & 0xff); + h7 += coef7 * (latin1[i-(32-1)+7] & 0xff); + h8 += coef8 * (latin1[i-(32-1)+8] & 0xff); + h9 += coef9 * (latin1[i-(32-1)+9] & 0xff); + h10 += coef10 * (latin1[i-(32-1)+10] & 0xff); + h11 += coef11 * (latin1[i-(32-1)+11] & 0xff); + h12 += coef12 * (latin1[i-(32-1)+12] & 0xff); + h13 += coef13 * (latin1[i-(32-1)+13] & 0xff); + h14 += coef14 * (latin1[i-(32-1)+14] & 0xff); + h15 += coef15 * (latin1[i-(32-1)+15] & 0xff); + h16 += coef16 * (latin1[i-(32-1)+16] & 0xff); + h17 += coef17 * (latin1[i-(32-1)+17] & 0xff); + h18 += coef18 * (latin1[i-(32-1)+18] & 0xff); + h19 += coef19 * (latin1[i-(32-1)+19] & 0xff); + h20 += coef20 * (latin1[i-(32-1)+20] & 0xff); + h21 += coef21 * (latin1[i-(32-1)+21] & 0xff); + h22 += coef22 * (latin1[i-(32-1)+22] & 0xff); + h23 += coef23 * (latin1[i-(32-1)+23] & 0xff); + h24 += coef24 * (latin1[i-(32-1)+24] & 0xff); + h25 += coef25 * (latin1[i-(32-1)+25] & 0xff); + h26 += coef26 * (latin1[i-(32-1)+26] & 0xff); + h27 += coef27 * (latin1[i-(32-1)+27] & 0xff); + h28 += coef28 * (latin1[i-(32-1)+28] & 0xff); + h29 += coef29 * (latin1[i-(32-1)+29] & 0xff); + h30 += coef30 * (latin1[i-(32-1)+30] & 0xff); + h31 += coef31 * (latin1[i-(32-1)+31] & 0xff); + coef0 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef0; + coef1 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef1; + coef2 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef2; + coef3 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef3; + coef4 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef4; + coef5 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef5; + coef6 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef6; + coef7 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef7; + coef8 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef8; + coef9 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef9; + coef10 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef10; + coef11 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef11; + coef12 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef12; + coef13 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef13; + coef14 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef14; + coef15 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef15; + coef16 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef16; + coef17 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef17; + coef18 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef18; + coef19 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef19; + coef20 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef20; + coef21 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef21; + coef22 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef22; + coef23 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef23; + coef24 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef24; + coef25 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef25; + coef26 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef26; + coef27 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef27; + coef28 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef28; + coef29 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef29; + coef30 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef30; + coef31 = 31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31*31 * coef31; + } + h += h0 + h1 + h2 + h3 + h4 + h5 + h6 + h7 + + h8 + h9 + h10 + h11 + h12 + h13 + h14 + h15 + + h16 + h17 + h18 + h19 + h20 + h21 + h22 + h23 + + h24 + h25 + h26 + h27 + h28 + h29 + h30 + h31; + } + return h; + } + + @Benchmark + public int defaultUTF16() throws Throwable { + return (int)defaultUTF16HashCodeMH.invokeExact(utf16); + } + + char getCharUTF16(byte[] value, int index) { + index <<= 1; + // assuming little endian + return (char)(((value[index++] & 0xff) << 0) | + ((value[index] & 0xff) << 8)); + } + + @Benchmark + public int scalarUTF16() { + int h = 0; + int i = 0, len = utf16.length / 2; + for (; i < len; i++) { + h = 31 * h + getCharUTF16(utf16, i); + } + return h; + } + + @Benchmark + public int scalarUTF16Unrolled8() { + int h = 0; + int i = 0, len = utf16.length / 2; + for (; i < (len & ~(8 - 1)); i += 8) { + h = -1807454463 * h + + 1742810335 * getCharUTF16(utf16, i+0) + + 887503681 * getCharUTF16(utf16, i+1) + + 28629151 * getCharUTF16(utf16, i+2) + + 923521 * getCharUTF16(utf16, i+3) + + 29791 * getCharUTF16(utf16, i+4) + + 961 * getCharUTF16(utf16, i+5) + + 31 * getCharUTF16(utf16, i+6) + + 1 * getCharUTF16(utf16, i+7); + } + for (; i < len; i++) { + h = 31 * h + getCharUTF16(utf16, i); + } + return h; + } + + @Benchmark + public int scalarUTF16Unrolled16() { + int h = 0; + int i = 0, len = utf16.length / 2; + for (; i < (len & ~(16 - 1)); i += 16) { + h = 1353309697 * h + + -510534177 * getCharUTF16(utf16, i+0) + + 1507551809 * getCharUTF16(utf16, i+1) + + -505558625 * getCharUTF16(utf16, i+2) + + -293403007 * getCharUTF16(utf16, i+3) + + 129082719 * getCharUTF16(utf16, i+4) + + -1796951359 * getCharUTF16(utf16, i+5) + + -196513505 * getCharUTF16(utf16, i+6) + + -1807454463 * getCharUTF16(utf16, i+7) + + 1742810335 * getCharUTF16(utf16, i+8) + + 887503681 * getCharUTF16(utf16, i+9) + + 28629151 * getCharUTF16(utf16, i+10) + + 923521 * getCharUTF16(utf16, i+11) + + 29791 * getCharUTF16(utf16, i+12) + + 961 * getCharUTF16(utf16, i+13) + + 31 * getCharUTF16(utf16, i+14) + + 1 * getCharUTF16(utf16, i+15); + } + for (; i < len; i++) { + h = 31 * h + getCharUTF16(utf16, i); + } + return h; + } + } }