Skip to content

Commit 475b894

Browse files
committed
8322770: Implement C2 VectorizedHashCode on AArch64
Reviewed-by: aph, adinn
1 parent 52ba728 commit 475b894

File tree

11 files changed

+1355
-580
lines changed

11 files changed

+1355
-580
lines changed

src/hotspot/cpu/aarch64/aarch64.ad

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4931,6 +4931,60 @@ operand vRegD_V7()
49314931
interface(REG_INTER);
49324932
%}
49334933

4934+
operand vRegD_V12()
4935+
%{
4936+
constraint(ALLOC_IN_RC(v12_reg));
4937+
match(RegD);
4938+
op_cost(0);
4939+
format %{ %}
4940+
interface(REG_INTER);
4941+
%}
4942+
4943+
operand vRegD_V13()
4944+
%{
4945+
constraint(ALLOC_IN_RC(v13_reg));
4946+
match(RegD);
4947+
op_cost(0);
4948+
format %{ %}
4949+
interface(REG_INTER);
4950+
%}
4951+
4952+
operand vRegD_V14()
4953+
%{
4954+
constraint(ALLOC_IN_RC(v14_reg));
4955+
match(RegD);
4956+
op_cost(0);
4957+
format %{ %}
4958+
interface(REG_INTER);
4959+
%}
4960+
4961+
operand vRegD_V15()
4962+
%{
4963+
constraint(ALLOC_IN_RC(v15_reg));
4964+
match(RegD);
4965+
op_cost(0);
4966+
format %{ %}
4967+
interface(REG_INTER);
4968+
%}
4969+
4970+
operand vRegD_V16()
4971+
%{
4972+
constraint(ALLOC_IN_RC(v16_reg));
4973+
match(RegD);
4974+
op_cost(0);
4975+
format %{ %}
4976+
interface(REG_INTER);
4977+
%}
4978+
4979+
operand vRegD_V17()
4980+
%{
4981+
constraint(ALLOC_IN_RC(v17_reg));
4982+
match(RegD);
4983+
op_cost(0);
4984+
format %{ %}
4985+
interface(REG_INTER);
4986+
%}
4987+
49344988
operand pReg()
49354989
%{
49364990
constraint(ALLOC_IN_RC(pr_reg));
@@ -16551,6 +16605,30 @@ instruct array_equalsC(iRegP_R1 ary1, iRegP_R2 ary2, iRegI_R0 result,
1655116605
ins_pipe(pipe_class_memory);
1655216606
%}
1655316607

16608+
instruct arrays_hashcode(iRegP_R1 ary, iRegI_R2 cnt, iRegI_R0 result, immI basic_type,
16609+
vRegD_V0 vtmp0, vRegD_V1 vtmp1, vRegD_V2 vtmp2, vRegD_V3 vtmp3,
16610+
vRegD_V4 vtmp4, vRegD_V5 vtmp5, vRegD_V6 vtmp6, vRegD_V7 vtmp7,
16611+
vRegD_V12 vtmp8, vRegD_V13 vtmp9, vRegD_V14 vtmp10,
16612+
vRegD_V15 vtmp11, vRegD_V16 vtmp12, vRegD_V17 vtmp13,
16613+
rFlagsReg cr)
16614+
%{
16615+
match(Set result (VectorizedHashCode (Binary ary cnt) (Binary result basic_type)));
16616+
effect(TEMP vtmp0, TEMP vtmp1, TEMP vtmp2, TEMP vtmp3, TEMP vtmp4, TEMP vtmp5, TEMP vtmp6,
16617+
TEMP vtmp7, TEMP vtmp8, TEMP vtmp9, TEMP vtmp10, TEMP vtmp11, TEMP vtmp12, TEMP vtmp13,
16618+
USE_KILL ary, USE_KILL cnt, USE basic_type, KILL cr);
16619+
16620+
format %{ "Array HashCode array[] $ary,$cnt,$result,$basic_type -> $result // KILL all" %}
16621+
ins_encode %{
16622+
address tpc = __ arrays_hashcode($ary$$Register, $cnt$$Register, $result$$Register,
16623+
(BasicType)$basic_type$$constant);
16624+
if (tpc == nullptr) {
16625+
ciEnv::current()->record_failure("CodeCache is full");
16626+
return;
16627+
}
16628+
%}
16629+
ins_pipe(pipe_class_memory);
16630+
%}
16631+
1655416632
instruct count_positives(iRegP_R1 ary1, iRegI_R2 len, iRegI_R0 result, rFlagsReg cr)
1655516633
%{
1655616634
match(Set result (CountPositives ary1 len));

src/hotspot/cpu/aarch64/assembler_aarch64.hpp

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2014, 2024, Red Hat Inc. All rights reserved.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
@@ -287,6 +287,11 @@ class Instruction_aarch64 {
287287
f(r->raw_encoding(), lsb + 4, lsb);
288288
}
289289

290+
//<0-15>reg: As `rf(FloatRegister)`, but only the lower 16 FloatRegisters are allowed.
291+
void lrf(FloatRegister r, int lsb) {
292+
f(r->raw_encoding(), lsb + 3, lsb);
293+
}
294+
290295
void prf(PRegister r, int lsb) {
291296
f(r->raw_encoding(), lsb + 3, lsb);
292297
}
@@ -765,6 +770,7 @@ class Assembler : public AbstractAssembler {
765770
#define f current_insn.f
766771
#define sf current_insn.sf
767772
#define rf current_insn.rf
773+
#define lrf current_insn.lrf
768774
#define srf current_insn.srf
769775
#define zrf current_insn.zrf
770776
#define prf current_insn.prf
@@ -1590,6 +1596,16 @@ class Assembler : public AbstractAssembler {
15901596

15911597
#undef INSN
15921598

1599+
// Load/store a register, but with a BasicType parameter. Loaded signed integer values are
1600+
// extended to 64 bits.
1601+
void load(Register Rt, const Address &adr, BasicType bt) {
1602+
int op = (is_signed_subword_type(bt) || bt == T_INT) ? 0b10 : 0b01;
1603+
ld_st2(Rt, adr, exact_log2(type2aelembytes(bt)), op);
1604+
}
1605+
void store(Register Rt, const Address &adr, BasicType bt) {
1606+
ld_st2(Rt, adr, exact_log2(type2aelembytes(bt)), 0b00);
1607+
}
1608+
15931609
/* SIMD extensions
15941610
*
15951611
* We just use FloatRegister in the following. They are exactly the same
@@ -2587,6 +2603,7 @@ template<typename R, typename... Rx>
25872603
INSN(addpv, 0, 0b101111, true); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D
25882604
INSN(smullv, 0, 0b110000, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
25892605
INSN(umullv, 1, 0b110000, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
2606+
INSN(smlalv, 0, 0b100000, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
25902607
INSN(umlalv, 1, 0b100000, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
25912608
INSN(maxv, 0, 0b011001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
25922609
INSN(minv, 0, 0b011011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
@@ -2860,6 +2877,28 @@ template<typename R, typename... Rx>
28602877
// FMULX - Vector - Scalar
28612878
INSN(fmulxvs, 1, 0b1001);
28622879

2880+
#undef INSN
2881+
2882+
#define INSN(NAME, op1, op2) \
2883+
void NAME(FloatRegister Vd, SIMD_Arrangement T, FloatRegister Vn, FloatRegister Vm, int index) { \
2884+
starti; \
2885+
assert(T == T4H || T == T8H || T == T2S || T == T4S, "invalid arrangement"); \
2886+
assert(index >= 0 && \
2887+
((T == T2S && index <= 1) || (T != T2S && index <= 3) || (T == T8H && index <= 7)), \
2888+
"invalid index"); \
2889+
assert((T != T4H && T != T8H) || Vm->encoding() < 16, "invalid source SIMD&FP register"); \
2890+
f(0, 31), f((int)T & 1, 30), f(op1, 29), f(0b01111, 28, 24); \
2891+
if (T == T4H || T == T8H) { \
2892+
f(0b01, 23, 22), f(index & 0b11, 21, 20), lrf(Vm, 16), f(index >> 2 & 1, 11); \
2893+
} else { \
2894+
f(0b10, 23, 22), f(index & 1, 21), rf(Vm, 16), f(index >> 1, 11); \
2895+
} \
2896+
f(op2, 15, 12), f(0, 10), rf(Vn, 5), rf(Vd, 0); \
2897+
}
2898+
2899+
// MUL - Vector - Scalar
2900+
INSN(mulvs, 0, 0b1000);
2901+
28632902
#undef INSN
28642903

28652904
// Floating-point Reciprocal Estimate
@@ -3023,6 +3062,33 @@ template<typename R, typename... Rx>
30233062
umov(Xd, Vn, T, index);
30243063
}
30253064

3065+
protected:
3066+
void _xaddwv(bool is_unsigned, FloatRegister Vd, FloatRegister Vn, SIMD_Arrangement Ta,
3067+
FloatRegister Vm, SIMD_Arrangement Tb) {
3068+
starti;
3069+
assert((Tb >> 1) + 1 == (Ta >> 1), "Incompatible arrangement");
3070+
f(0, 31), f((int)Tb & 1, 30), f(is_unsigned ? 1 : 0, 29), f(0b01110, 28, 24);
3071+
f((int)(Ta >> 1) - 1, 23, 22), f(1, 21), rf(Vm, 16), f(0b000100, 15, 10), rf(Vn, 5), rf(Vd, 0);
3072+
}
3073+
3074+
public:
3075+
#define INSN(NAME, assertion, is_unsigned) \
3076+
void NAME(FloatRegister Vd, FloatRegister Vn, SIMD_Arrangement Ta, FloatRegister Vm, \
3077+
SIMD_Arrangement Tb) { \
3078+
assert((assertion), "invalid arrangement"); \
3079+
_xaddwv(is_unsigned, Vd, Vn, Ta, Vm, Tb); \
3080+
}
3081+
3082+
public:
3083+
3084+
INSN(uaddwv, Tb == T8B || Tb == T4H || Tb == T2S, /*is_unsigned*/true)
3085+
INSN(uaddwv2, Tb == T16B || Tb == T8H || Tb == T4S, /*is_unsigned*/true)
3086+
INSN(saddwv, Tb == T8B || Tb == T4H || Tb == T2S, /*is_unsigned*/false)
3087+
INSN(saddwv2, Tb == T16B || Tb == T8H || Tb == T4S, /*is_unsigned*/false)
3088+
3089+
#undef INSN
3090+
3091+
30263092
private:
30273093
void _pmull(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, FloatRegister Vm, SIMD_Arrangement Tb) {
30283094
starti;

src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "opto/subnode.hpp"
3434
#include "runtime/stubRoutines.hpp"
3535
#include "utilities/globalDefinitions.hpp"
36+
#include "utilities/powerOfTwo.hpp"
3637

3738
#ifdef PRODUCT
3839
#define BLOCK_COMMENT(str) /* nothing */
@@ -46,6 +47,96 @@
4647

4748
typedef void (MacroAssembler::* chr_insn)(Register Rt, const Address &adr);
4849

50+
// jdk.internal.util.ArraysSupport.vectorizedHashCode
51+
address C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register result,
52+
BasicType eltype) {
53+
assert_different_registers(ary, cnt, result, rscratch1, rscratch2);
54+
55+
Register tmp1 = rscratch1, tmp2 = rscratch2;
56+
57+
Label TAIL, STUB_SWITCH, STUB_SWITCH_OUT, LOOP, BR_BASE, LARGE, DONE;
58+
59+
// Vectorization factor. Number of array elements loaded to one SIMD&FP registers by the stubs. We
60+
// use 8H load arrangements for chars and shorts and 8B for booleans and bytes. It's possible to
61+
// use 4H for chars and shorts instead, but using 8H gives better performance.
62+
const size_t vf = eltype == T_BOOLEAN || eltype == T_BYTE ? 8
63+
: eltype == T_CHAR || eltype == T_SHORT ? 8
64+
: eltype == T_INT ? 4
65+
: 0;
66+
guarantee(vf, "unsupported eltype");
67+
68+
// Unroll factor for the scalar loop below. The value is chosen based on performance analysis.
69+
const size_t unroll_factor = 4;
70+
71+
switch (eltype) {
72+
case T_BOOLEAN:
73+
BLOCK_COMMENT("arrays_hashcode(unsigned byte) {");
74+
break;
75+
case T_CHAR:
76+
BLOCK_COMMENT("arrays_hashcode(char) {");
77+
break;
78+
case T_BYTE:
79+
BLOCK_COMMENT("arrays_hashcode(byte) {");
80+
break;
81+
case T_SHORT:
82+
BLOCK_COMMENT("arrays_hashcode(short) {");
83+
break;
84+
case T_INT:
85+
BLOCK_COMMENT("arrays_hashcode(int) {");
86+
break;
87+
default:
88+
ShouldNotReachHere();
89+
}
90+
91+
// large_arrays_hashcode(T_INT) performs worse than the scalar loop below when the Neon loop
92+
// implemented by the stub executes just once. Call the stub only if at least two iterations will
93+
// be executed.
94+
const size_t large_threshold = eltype == T_INT ? vf * 2 : vf;
95+
cmpw(cnt, large_threshold);
96+
br(Assembler::HS, LARGE);
97+
98+
bind(TAIL);
99+
100+
// The andr performs cnt % uf where uf = unroll_factor. The subtract shifted by 3 offsets past
101+
// uf - (cnt % uf) pairs of load + madd insns i.e. it only executes cnt % uf load + madd pairs.
102+
// Iteration eats up the remainder, uf elements at a time.
103+
assert(is_power_of_2(unroll_factor), "can't use this value to calculate the jump target PC");
104+
andr(tmp2, cnt, unroll_factor - 1);
105+
adr(tmp1, BR_BASE);
106+
sub(tmp1, tmp1, tmp2, ext::sxtw, 3);
107+
movw(tmp2, 0x1f);
108+
br(tmp1);
109+
110+
bind(LOOP);
111+
for (size_t i = 0; i < unroll_factor; ++i) {
112+
load(tmp1, Address(post(ary, type2aelembytes(eltype))), eltype);
113+
maddw(result, result, tmp2, tmp1);
114+
}
115+
bind(BR_BASE);
116+
subsw(cnt, cnt, unroll_factor);
117+
br(Assembler::HS, LOOP);
118+
119+
b(DONE);
120+
121+
bind(LARGE);
122+
123+
RuntimeAddress stub = RuntimeAddress(StubRoutines::aarch64::large_arrays_hashcode(eltype));
124+
assert(stub.target() != nullptr, "array_hashcode stub has not been generated");
125+
address tpc = trampoline_call(stub);
126+
if (tpc == nullptr) {
127+
DEBUG_ONLY(reset_labels(TAIL, BR_BASE));
128+
postcond(pc() == badAddress);
129+
return nullptr;
130+
}
131+
132+
bind(DONE);
133+
134+
BLOCK_COMMENT("} // arrays_hashcode");
135+
136+
postcond(pc() != badAddress);
137+
return pc();
138+
}
139+
49140
void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register tmpReg,
50141
Register tmp2Reg, Register tmp3Reg) {
51142
Register oop = objectReg;

src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
enum shift_kind kind = Assembler::LSL, unsigned shift = 0);
3636

3737
public:
38+
// jdk.internal.util.ArraysSupport.vectorizedHashCode
39+
address arrays_hashcode(Register ary, Register cnt, Register result, BasicType eltype);
40+
3841
// Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file.
3942
void fast_lock(Register object, Register box, Register tmp, Register tmp2, Register tmp3);
4043
void fast_unlock(Register object, Register box, Register tmp, Register tmp2);

0 commit comments

Comments
 (0)