8281146: Replace StringCoding.hasNegatives with countPositives

Co-authored-by: Lutz Schmidt <lucy@openjdk.org>
Co-authored-by: Martin Doerr <mdoerr@openjdk.org>
Reviewed-by: kvn, lucy, rriggs
This commit is contained in:
Claes Redestad 2022-03-17 09:20:24 +00:00
parent 249d553659
commit beedae1141
36 changed files with 552 additions and 335 deletions

View File

@ -17080,13 +17080,13 @@ instruct array_equalsC(iRegP_R1 ary1, iRegP_R2 ary2, iRegI_R0 result,
ins_pipe(pipe_class_memory);
%}
instruct has_negatives(iRegP_R1 ary1, iRegI_R2 len, iRegI_R0 result, rFlagsReg cr)
instruct count_positives(iRegP_R1 ary1, iRegI_R2 len, iRegI_R0 result, rFlagsReg cr)
%{
match(Set result (HasNegatives ary1 len));
match(Set result (CountPositives ary1 len));
effect(USE_KILL ary1, USE_KILL len, KILL cr);
format %{ "has negatives byte[] $ary1,$len -> $result" %}
format %{ "count positives byte[] $ary1,$len -> $result" %}
ins_encode %{
address tpc = __ has_negatives($ary1$$Register, $len$$Register, $result$$Register);
address tpc = __ count_positives($ary1$$Register, $len$$Register, $result$$Register);
if (tpc == NULL) {
ciEnv::current()->record_failure("CodeCache is full");
return;

View File

@ -4339,16 +4339,17 @@ void MacroAssembler::remove_frame(int framesize) {
}
// This method checks if provided byte array contains byte with highest bit set.
address MacroAssembler::has_negatives(Register ary1, Register len, Register result) {
// This method counts leading positive bytes (highest bit not set) in provided byte array
address MacroAssembler::count_positives(Register ary1, Register len, Register result) {
// Simple and most common case of aligned small array which is not at the
// end of memory page is placed here. All other cases are in stub.
Label LOOP, END, STUB, STUB_LONG, SET_RESULT, DONE;
const uint64_t UPPER_BIT_MASK=0x8080808080808080;
assert_different_registers(ary1, len, result);
mov(result, len);
cmpw(len, 0);
br(LE, SET_RESULT);
br(LE, DONE);
cmpw(len, 4 * wordSize);
br(GE, STUB_LONG); // size > 32 then go to stub
@ -4367,19 +4368,20 @@ address MacroAssembler::has_negatives(Register ary1, Register len, Register resu
subs(len, len, wordSize);
br(GE, LOOP);
cmpw(len, -wordSize);
br(EQ, SET_RESULT);
br(EQ, DONE);
BIND(END);
ldr(result, Address(ary1));
sub(len, zr, len, LSL, 3); // LSL 3 is to get bits from bytes
lslv(result, result, len);
tst(result, UPPER_BIT_MASK);
b(SET_RESULT);
ldr(rscratch1, Address(ary1));
sub(rscratch2, zr, len, LSL, 3); // LSL 3 is to get bits from bytes
lslv(rscratch1, rscratch1, rscratch2);
tst(rscratch1, UPPER_BIT_MASK);
br(NE, SET_RESULT);
b(DONE);
BIND(STUB);
RuntimeAddress has_neg = RuntimeAddress(StubRoutines::aarch64::has_negatives());
assert(has_neg.target() != NULL, "has_negatives stub has not been generated");
address tpc1 = trampoline_call(has_neg);
RuntimeAddress count_pos = RuntimeAddress(StubRoutines::aarch64::count_positives());
assert(count_pos.target() != NULL, "count_positives stub has not been generated");
address tpc1 = trampoline_call(count_pos);
if (tpc1 == NULL) {
DEBUG_ONLY(reset_labels(STUB_LONG, SET_RESULT, DONE));
postcond(pc() == badAddress);
@ -4388,9 +4390,9 @@ address MacroAssembler::has_negatives(Register ary1, Register len, Register resu
b(DONE);
BIND(STUB_LONG);
RuntimeAddress has_neg_long = RuntimeAddress(StubRoutines::aarch64::has_negatives_long());
assert(has_neg_long.target() != NULL, "has_negatives stub has not been generated");
address tpc2 = trampoline_call(has_neg_long);
RuntimeAddress count_pos_long = RuntimeAddress(StubRoutines::aarch64::count_positives_long());
assert(count_pos_long.target() != NULL, "count_positives_long stub has not been generated");
address tpc2 = trampoline_call(count_pos_long);
if (tpc2 == NULL) {
DEBUG_ONLY(reset_labels(SET_RESULT, DONE));
postcond(pc() == badAddress);
@ -4399,7 +4401,9 @@ address MacroAssembler::has_negatives(Register ary1, Register len, Register resu
b(DONE);
BIND(SET_RESULT);
cset(result, NE); // set true or false
add(len, len, wordSize);
sub(result, result, len);
BIND(DONE);
postcond(pc() != badAddress);

View File

@ -1234,7 +1234,7 @@ public:
Register table0, Register table1, Register table2, Register table3,
bool upper = false);
address has_negatives(Register ary1, Register len, Register result);
address count_positives(Register ary1, Register len, Register result);
address arrays_equals(Register a1, Register a2, Register result, Register cnt1,
Register tmp1, Register tmp2, Register tmp3, int elem_size);

View File

@ -4657,7 +4657,7 @@ class StubGenerator: public StubCodeGenerator {
return start;
}
address generate_has_negatives(address &has_negatives_long) {
address generate_count_positives(address &count_positives_long) {
const u1 large_loop_size = 64;
const uint64_t UPPER_BIT_MASK=0x8080808080808080;
int dcache_line = VM_Version::dcache_line_size();
@ -4666,13 +4666,15 @@ class StubGenerator: public StubCodeGenerator {
__ align(CodeEntryAlignment);
StubCodeMark mark(this, "StubRoutines", "has_negatives");
StubCodeMark mark(this, "StubRoutines", "count_positives");
address entry = __ pc();
__ enter();
// precondition: a copy of len is already in result
// __ mov(result, len);
Label RET_TRUE, RET_TRUE_NO_POP, RET_FALSE, ALIGNED, LOOP16, CHECK_16,
Label RET_ADJUST, RET_ADJUST_16, RET_ADJUST_LONG, RET_NO_POP, RET_LEN, ALIGNED, LOOP16, CHECK_16,
LARGE_LOOP, POST_LOOP16, LEN_OVER_15, LEN_OVER_8, POST_LOOP16_LOAD_TAIL;
__ cmp(len, (u1)15);
@ -4686,25 +4688,26 @@ class StubGenerator: public StubCodeGenerator {
__ sub(rscratch1, zr, len, __ LSL, 3); // LSL 3 is to get bits from bytes.
__ lsrv(rscratch2, rscratch2, rscratch1);
__ tst(rscratch2, UPPER_BIT_MASK);
__ cset(result, Assembler::NE);
__ csel(result, zr, result, Assembler::NE);
__ leave();
__ ret(lr);
__ bind(LEN_OVER_8);
__ ldp(rscratch1, rscratch2, Address(ary1, -16));
__ sub(len, len, 8); // no data dep., then sub can be executed while loading
__ tst(rscratch2, UPPER_BIT_MASK);
__ br(Assembler::NE, RET_TRUE_NO_POP);
__ br(Assembler::NE, RET_NO_POP);
__ sub(rscratch2, zr, len, __ LSL, 3); // LSL 3 is to get bits from bytes
__ lsrv(rscratch1, rscratch1, rscratch2);
__ tst(rscratch1, UPPER_BIT_MASK);
__ cset(result, Assembler::NE);
__ bind(RET_NO_POP);
__ csel(result, zr, result, Assembler::NE);
__ leave();
__ ret(lr);
Register tmp1 = r3, tmp2 = r4, tmp3 = r5, tmp4 = r6, tmp5 = r7, tmp6 = r10;
const RegSet spilled_regs = RegSet::range(tmp1, tmp5) + tmp6;
has_negatives_long = __ pc(); // 2nd entry point
count_positives_long = __ pc(); // 2nd entry point
__ enter();
@ -4716,10 +4719,10 @@ class StubGenerator: public StubCodeGenerator {
__ mov(tmp5, 16);
__ sub(rscratch1, tmp5, rscratch2); // amount of bytes until aligned address
__ add(ary1, ary1, rscratch1);
__ sub(len, len, rscratch1);
__ orr(tmp6, tmp6, tmp1);
__ tst(tmp6, UPPER_BIT_MASK);
__ br(Assembler::NE, RET_TRUE);
__ br(Assembler::NE, RET_ADJUST);
__ sub(len, len, rscratch1);
__ bind(ALIGNED);
__ cmp(len, large_loop_size);
@ -4734,7 +4737,7 @@ class StubGenerator: public StubCodeGenerator {
__ sub(len, len, 16);
__ orr(tmp6, tmp6, tmp1);
__ tst(tmp6, UPPER_BIT_MASK);
__ br(Assembler::NE, RET_TRUE);
__ br(Assembler::NE, RET_ADJUST_16);
__ cmp(len, large_loop_size);
__ br(Assembler::LT, CHECK_16);
@ -4766,7 +4769,7 @@ class StubGenerator: public StubCodeGenerator {
__ orr(rscratch1, rscratch1, tmp6);
__ orr(tmp2, tmp2, rscratch1);
__ tst(tmp2, UPPER_BIT_MASK);
__ br(Assembler::NE, RET_TRUE);
__ br(Assembler::NE, RET_ADJUST_LONG);
__ cmp(len, large_loop_size);
__ br(Assembler::GE, LARGE_LOOP);
@ -4779,7 +4782,7 @@ class StubGenerator: public StubCodeGenerator {
__ sub(len, len, 16);
__ orr(tmp2, tmp2, tmp3);
__ tst(tmp2, UPPER_BIT_MASK);
__ br(Assembler::NE, RET_TRUE);
__ br(Assembler::NE, RET_ADJUST_16);
__ cmp(len, (u1)16);
__ br(Assembler::GE, LOOP16); // 16-byte load loop end
@ -4787,31 +4790,36 @@ class StubGenerator: public StubCodeGenerator {
__ cmp(len, (u1)8);
__ br(Assembler::LE, POST_LOOP16_LOAD_TAIL);
__ ldr(tmp3, Address(__ post(ary1, 8)));
__ sub(len, len, 8);
__ tst(tmp3, UPPER_BIT_MASK);
__ br(Assembler::NE, RET_TRUE);
__ br(Assembler::NE, RET_ADJUST);
__ sub(len, len, 8);
__ bind(POST_LOOP16_LOAD_TAIL);
__ cbz(len, RET_FALSE); // Can't shift left by 64 when len==0
__ cbz(len, RET_LEN); // Can't shift left by 64 when len==0
__ ldr(tmp1, Address(ary1));
__ mov(tmp2, 64);
__ sub(tmp4, tmp2, len, __ LSL, 3);
__ lslv(tmp1, tmp1, tmp4);
__ tst(tmp1, UPPER_BIT_MASK);
__ br(Assembler::NE, RET_TRUE);
__ br(Assembler::NE, RET_ADJUST);
// Fallthrough
__ bind(RET_FALSE);
__ bind(RET_LEN);
__ pop(spilled_regs, sp);
__ leave();
__ mov(result, zr);
__ ret(lr);
__ bind(RET_TRUE);
// difference result - len is the count of guaranteed to be
// positive bytes
__ bind(RET_ADJUST_LONG);
__ add(len, len, (u1)(large_loop_size - 16));
__ bind(RET_ADJUST_16);
__ add(len, len, 16);
__ bind(RET_ADJUST);
__ pop(spilled_regs, sp);
__ bind(RET_TRUE_NO_POP);
__ leave();
__ mov(result, 1);
__ sub(result, result, len);
__ ret(lr);
return entry;
@ -7515,8 +7523,8 @@ class StubGenerator: public StubCodeGenerator {
// arraycopy stubs used by compilers
generate_arraycopy_stubs();
// has negatives stub for large arrays.
StubRoutines::aarch64::_has_negatives = generate_has_negatives(StubRoutines::aarch64::_has_negatives_long);
// countPositives stub for large arrays.
StubRoutines::aarch64::_count_positives = generate_count_positives(StubRoutines::aarch64::_count_positives_long);
// array equals stub for large arrays.
if (!UseSimpleArrayEquals) {

View File

@ -45,8 +45,8 @@ address StubRoutines::aarch64::_float_sign_flip = NULL;
address StubRoutines::aarch64::_double_sign_mask = NULL;
address StubRoutines::aarch64::_double_sign_flip = NULL;
address StubRoutines::aarch64::_zero_blocks = NULL;
address StubRoutines::aarch64::_has_negatives = NULL;
address StubRoutines::aarch64::_has_negatives_long = NULL;
address StubRoutines::aarch64::_count_positives = NULL;
address StubRoutines::aarch64::_count_positives_long = NULL;
address StubRoutines::aarch64::_large_array_equals = NULL;
address StubRoutines::aarch64::_compare_long_string_LL = NULL;
address StubRoutines::aarch64::_compare_long_string_UU = NULL;

View File

@ -76,8 +76,8 @@ class aarch64 {
public:
static address _has_negatives;
static address _has_negatives_long;
static address _count_positives;
static address _count_positives_long;
static address get_previous_sp_entry()
{
@ -132,12 +132,12 @@ class aarch64 {
return _zero_blocks;
}
static address has_negatives() {
return _has_negatives;
static address count_positives() {
return _count_positives;
}
static address has_negatives_long() {
return _has_negatives_long;
static address count_positives_long() {
return _count_positives_long;
}
static address large_array_equals() {

View File

@ -565,16 +565,16 @@ void C2_MacroAssembler::string_indexof_char(Register result, Register haystack,
} // string_indexof_char
void C2_MacroAssembler::has_negatives(Register src, Register cnt, Register result,
Register tmp1, Register tmp2) {
void C2_MacroAssembler::count_positives(Register src, Register cnt, Register result,
Register tmp1, Register tmp2) {
const Register tmp0 = R0;
assert_different_registers(src, result, cnt, tmp0, tmp1, tmp2);
Label Lfastloop, Lslow, Lloop, Lnoneg, Ldone;
Label Lfastloop, Lslow, Lloop, Ldone;
// Check if cnt >= 8 (= 16 bytes)
lis(tmp1, (int)(short)0x8080); // tmp1 = 0x8080808080808080
srwi_(tmp2, cnt, 4);
li(result, 1); // Assume there's a negative byte.
mr(result, src); // Use result reg to point to the current position.
beq(CCR0, Lslow);
ori(tmp1, tmp1, 0x8080);
rldimi(tmp1, tmp1, 32, 0);
@ -582,30 +582,28 @@ void C2_MacroAssembler::has_negatives(Register src, Register cnt, Register resul
// 2x unrolled loop
bind(Lfastloop);
ld(tmp2, 0, src);
ld(tmp0, 8, src);
ld(tmp2, 0, result);
ld(tmp0, 8, result);
orr(tmp0, tmp2, tmp0);
and_(tmp0, tmp0, tmp1);
bne(CCR0, Ldone); // Found negative byte.
addi(src, src, 16);
bne(CCR0, Lslow); // Found negative byte.
addi(result, result, 16);
bdnz(Lfastloop);
bind(Lslow); // Fallback to slow version
rldicl_(tmp0, cnt, 0, 64-4);
beq(CCR0, Lnoneg);
bind(Lslow); // Fallback to slow version.
subf(tmp0, src, result); // Bytes known positive.
subf_(tmp0, tmp0, cnt); // Remaining Bytes.
beq(CCR0, Ldone);
mtctr(tmp0);
bind(Lloop);
lbz(tmp0, 0, src);
addi(src, src, 1);
lbz(tmp0, 0, result);
andi_(tmp0, tmp0, 0x80);
bne(CCR0, Ldone); // Found negative byte.
addi(result, result, 1);
bdnz(Lloop);
bind(Lnoneg);
li(result, 0);
bind(Ldone);
subf(result, src, result); // Result is offset from src.
}

View File

@ -63,6 +63,6 @@
void string_indexof_char(Register result, Register haystack, Register haycnt,
Register needle, jchar needleChar, Register tmp1, Register tmp2, bool is_byte);
void has_negatives(Register src, Register cnt, Register result, Register tmp1, Register tmp2);
void count_positives(Register src, Register cnt, Register result, Register tmp1, Register tmp2);
#endif // CPU_PPC_C2_MACROASSEMBLER_PPC_HPP

View File

@ -12779,16 +12779,16 @@ instruct string_inflate(Universe dummy, rarg1RegP src, rarg2RegP dst, iRegIsrc l
%}
// StringCoding.java intrinsics
instruct has_negatives(rarg1RegP ary1, iRegIsrc len, iRegIdst result, iRegLdst tmp1, iRegLdst tmp2,
regCTR ctr, flagsRegCR0 cr0)
instruct count_positives(iRegPsrc ary1, iRegIsrc len, iRegIdst result, iRegLdst tmp1, iRegLdst tmp2,
regCTR ctr, flagsRegCR0 cr0)
%{
match(Set result (HasNegatives ary1 len));
effect(TEMP_DEF result, USE_KILL ary1, TEMP tmp1, TEMP tmp2, KILL ctr, KILL cr0);
match(Set result (CountPositives ary1 len));
effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, KILL ctr, KILL cr0);
ins_cost(300);
format %{ "has negatives byte[] $ary1,$len -> $result \t// KILL $tmp1, $tmp2" %}
format %{ "count positives byte[] $ary1,$len -> $result \t// KILL $tmp1, $tmp2" %}
ins_encode %{
__ has_negatives($ary1$$Register, $len$$Register, $result$$Register,
$tmp1$$Register, $tmp2$$Register);
__ count_positives($ary1$$Register, $len$$Register, $result$$Register,
$tmp1$$Register, $tmp2$$Register);
%}
ins_pipe(pipe_class_default);
%}

View File

@ -823,52 +823,64 @@ unsigned int C2_MacroAssembler::string_inflate_const(Register src, Register dst,
return offset() - block_start;
}
// Kills src.
unsigned int C2_MacroAssembler::has_negatives(Register result, Register src, Register cnt,
Register odd_reg, Register even_reg, Register tmp) {
int block_start = offset();
Label Lloop1, Lloop2, Lslow, Lnotfound, Ldone;
const Register addr = src, mask = tmp;
// Returns the number of non-negative bytes (aka US-ASCII characters) found
// before the first negative byte is encountered.
unsigned int C2_MacroAssembler::count_positives(Register result, Register src, Register cnt, Register tmp) {
const unsigned int block_start = offset();
const unsigned int byte_mask = 0x80;
const unsigned int twobyte_mask = byte_mask<<8 | byte_mask;
const unsigned int unroll_factor = 16;
const unsigned int log_unroll_factor = exact_log2(unroll_factor);
Register pos = src; // current position in src array, restored at end
Register ctr = result; // loop counter, result value
Register mask = tmp; // holds the sign detection mask
Label unrolledLoop, unrolledDone, byteLoop, allDone;
BLOCK_COMMENT("has_negatives {");
assert_different_registers(result, src, cnt, tmp);
z_llgfr(Z_R1, cnt); // Number of bytes to read. (Must be a positive simm32.)
z_llilf(mask, 0x80808080);
z_lhi(result, 1); // Assume true.
// Last possible addr for fast loop.
z_lay(odd_reg, -16, Z_R1, src);
z_chi(cnt, 16);
z_brl(Lslow);
BLOCK_COMMENT("count_positives {");
// ind1: index, even_reg: index increment, odd_reg: index limit
z_iihf(mask, 0x80808080);
z_lghi(even_reg, 16);
lgr_if_needed(pos, src); // current position in src array
z_srak(ctr, cnt, log_unroll_factor); // # iterations of unrolled loop
z_brnh(unrolledDone); // array too short for unrolled loop
bind(Lloop1); // 16 bytes per iteration.
z_lg(Z_R0, Address(addr));
z_lg(Z_R1, Address(addr, 8));
z_ogr(Z_R0, Z_R1);
z_ngr(Z_R0, mask);
z_brne(Ldone); // If found return 1.
z_brxlg(addr, even_reg, Lloop1);
z_iilf(mask, twobyte_mask<<16 | twobyte_mask);
z_iihf(mask, twobyte_mask<<16 | twobyte_mask);
bind(Lslow);
z_aghi(odd_reg, 16-1); // Last possible addr for slow loop.
z_lghi(even_reg, 1);
z_cgr(addr, odd_reg);
z_brh(Lnotfound);
bind(unrolledLoop);
z_lmg(Z_R0, Z_R1, 0, pos);
z_ogr(Z_R0, Z_R1);
z_ngr(Z_R0, mask);
z_brne(unrolledDone); // There is a negative byte somewhere.
// ctr and pos are not updated yet ->
// delegate finding correct pos to byteLoop.
add2reg(pos, unroll_factor);
z_brct(ctr, unrolledLoop);
bind(Lloop2); // 1 byte per iteration.
z_cli(Address(addr), 0x80);
z_brnl(Ldone); // If found return 1.
z_brxlg(addr, even_reg, Lloop2);
// Once we arrive here, we have to examine at most (unroll_factor - 1) bytes more.
// We then either have reached the end of the array or we hit a negative byte.
bind(unrolledDone);
z_sll(ctr, log_unroll_factor); // calculate # bytes not processed by unrolled loop
// > 0 only if a negative byte was found
z_lr(Z_R0, cnt); // calculate remainder bytes
z_nilf(Z_R0, unroll_factor - 1);
z_ar(ctr, Z_R0); // remaining bytes
z_brnh(allDone); // shortcut if nothing left to do
bind(Lnotfound);
z_lhi(result, 0);
bind(byteLoop);
z_cli(0, pos, byte_mask); // unsigned comparison! byte@pos must be smaller that byte_mask
z_brnl(allDone); // negative byte found.
bind(Ldone);
add2reg(pos, 1);
z_brct(ctr, byteLoop);
BLOCK_COMMENT("} has_negatives");
bind(allDone);
z_srk(ctr, cnt, ctr); // # bytes actually processed (= cnt or index of first negative byte)
z_sgfr(pos, ctr); // restore src
z_lgfr(result, ctr); // unnecessary. Only there to be sure the high word has a defined state.
BLOCK_COMMENT("} count_positives");
return offset() - block_start;
}

View File

@ -57,9 +57,7 @@
// len is signed int. Counts # characters, not bytes.
unsigned int string_inflate_const(Register src, Register dst, Register tmp, int len);
// Kills src.
unsigned int has_negatives(Register result, Register src, Register cnt,
Register odd_reg, Register even_reg, Register tmp);
unsigned int count_positives(Register result, Register src, Register cnt, Register tmp);
unsigned int string_compare(Register str1, Register str2, Register cnt1, Register cnt2,
Register odd_reg, Register even_reg, Register result, int ae);

View File

@ -10273,14 +10273,13 @@ instruct string_inflate_const(Universe dummy, iRegP src, iRegP dst, iRegI tmp, i
%}
// StringCoding.java intrinsics
instruct has_negatives(rarg5RegP ary1, iRegI len, iRegI result, roddRegI oddReg, revenRegI evenReg, iRegI tmp, flagsReg cr) %{
match(Set result (HasNegatives ary1 len));
effect(TEMP_DEF result, USE_KILL ary1, TEMP oddReg, TEMP evenReg, TEMP tmp, KILL cr); // R0, R1 are killed, too.
instruct count_positives(iRegP ary1, iRegI len, iRegI result, iRegI tmp, flagsReg cr) %{
match(Set result (CountPositives ary1 len));
effect(TEMP_DEF result, TEMP tmp, KILL cr); // R0, R1 are killed, too.
ins_cost(300);
format %{ "has negatives byte[] $ary1($len) -> $result" %}
format %{ "count positives byte[] $ary1($len) -> $result" %}
ins_encode %{
__ has_negatives($result$$Register, $ary1$$Register, $len$$Register,
$oddReg$$Register, $evenReg$$Register, $tmp$$Register);
__ count_positives($result$$Register, $ary1$$Register, $len$$Register, $tmp$$Register);
%}
ins_pipe(pipe_class_dummy);
%}

View File

@ -3374,18 +3374,19 @@ void C2_MacroAssembler::string_compare(Register str1, Register str2,
}
// Search for Non-ASCII character (Negative byte value) in a byte array,
// return true if it has any and false otherwise.
// return the index of the first such character, otherwise the length
// of the array segment searched.
// ..\jdk\src\java.base\share\classes\java\lang\StringCoding.java
// @IntrinsicCandidate
// private static boolean hasNegatives(byte[] ba, int off, int len) {
// public static int countPositives(byte[] ba, int off, int len) {
// for (int i = off; i < off + len; i++) {
// if (ba[i] < 0) {
// return true;
// return i - off;
// }
// }
// return false;
// return len;
// }
void C2_MacroAssembler::has_negatives(Register ary1, Register len,
void C2_MacroAssembler::count_positives(Register ary1, Register len,
Register result, Register tmp1,
XMMRegister vec1, XMMRegister vec2, KRegister mask1, KRegister mask2) {
// rsi: byte array
@ -3394,17 +3395,18 @@ void C2_MacroAssembler::has_negatives(Register ary1, Register len,
ShortBranchVerifier sbv(this);
assert_different_registers(ary1, len, result, tmp1);
assert_different_registers(vec1, vec2);
Label TRUE_LABEL, FALSE_LABEL, DONE, COMPARE_CHAR, COMPARE_VECTORS, COMPARE_BYTE;
Label ADJUST, TAIL_ADJUST, DONE, TAIL_START, CHAR_ADJUST, COMPARE_CHAR, COMPARE_VECTORS, COMPARE_BYTE;
movl(result, len); // copy
// len == 0
testl(len, len);
jcc(Assembler::zero, FALSE_LABEL);
jcc(Assembler::zero, DONE);
if ((AVX3Threshold == 0) && (UseAVX > 2) && // AVX512
VM_Version::supports_avx512vlbw() &&
VM_Version::supports_bmi2()) {
Label test_64_loop, test_tail;
Label test_64_loop, test_tail, BREAK_LOOP;
Register tmp3_aliased = len;
movl(tmp1, len);
@ -3421,16 +3423,15 @@ void C2_MacroAssembler::has_negatives(Register ary1, Register len,
// Check whether our 64 elements of size byte contain negatives
evpcmpgtb(mask1, vec2, Address(ary1, len, Address::times_1), Assembler::AVX_512bit);
kortestql(mask1, mask1);
jcc(Assembler::notZero, TRUE_LABEL);
jcc(Assembler::notZero, BREAK_LOOP);
addptr(len, 64);
jccb(Assembler::notZero, test_64_loop);
bind(test_tail);
// bail out when there is nothing to be done
testl(tmp1, -1);
jcc(Assembler::zero, FALSE_LABEL);
jcc(Assembler::zero, DONE);
// ~(~0 << len) applied up to two times (for 32-bit scenario)
#ifdef _LP64
@ -3467,21 +3468,30 @@ void C2_MacroAssembler::has_negatives(Register ary1, Register len,
#endif
evpcmpgtb(mask1, mask2, vec2, Address(ary1, 0), Assembler::AVX_512bit);
ktestq(mask1, mask2);
jcc(Assembler::notZero, TRUE_LABEL);
jcc(Assembler::zero, DONE);
jmp(FALSE_LABEL);
bind(BREAK_LOOP);
// At least one byte in the last 64 bytes is negative.
// Set up to look at the last 64 bytes as if they were a tail
lea(ary1, Address(ary1, len, Address::times_1));
addptr(result, len);
// Ignore the very last byte: if all others are positive,
// it must be negative, so we can skip right to the 2+1 byte
// end comparison at this point
orl(result, 63);
movl(len, 63);
// Fallthru to tail compare
} else {
movl(result, len); // copy
if (UseAVX >= 2 && UseSSE >= 2) {
// With AVX2, use 32-byte vector compare
Label COMPARE_WIDE_VECTORS, COMPARE_TAIL;
Label COMPARE_WIDE_VECTORS, BREAK_LOOP;
// Compare 32-byte vectors
andl(result, 0x0000001f); // tail count (in bytes)
andl(len, 0xffffffe0); // vector count (in bytes)
jccb(Assembler::zero, COMPARE_TAIL);
testl(len, 0xffffffe0); // vector count (in bytes)
jccb(Assembler::zero, TAIL_START);
andl(len, 0xffffffe0);
lea(ary1, Address(ary1, len, Address::times_1));
negptr(len);
@ -3492,30 +3502,42 @@ void C2_MacroAssembler::has_negatives(Register ary1, Register len,
bind(COMPARE_WIDE_VECTORS);
vmovdqu(vec1, Address(ary1, len, Address::times_1));
vptest(vec1, vec2);
jccb(Assembler::notZero, TRUE_LABEL);
jccb(Assembler::notZero, BREAK_LOOP);
addptr(len, 32);
jcc(Assembler::notZero, COMPARE_WIDE_VECTORS);
jccb(Assembler::notZero, COMPARE_WIDE_VECTORS);
testl(result, result);
jccb(Assembler::zero, FALSE_LABEL);
testl(result, 0x0000001f); // any bytes remaining?
jcc(Assembler::zero, DONE);
vmovdqu(vec1, Address(ary1, result, Address::times_1, -32));
vptest(vec1, vec2);
jccb(Assembler::notZero, TRUE_LABEL);
jmpb(FALSE_LABEL);
bind(COMPARE_TAIL); // len is zero
// Quick test using the already prepared vector mask
movl(len, result);
andl(len, 0x0000001f);
vmovdqu(vec1, Address(ary1, len, Address::times_1, -32));
vptest(vec1, vec2);
jcc(Assembler::zero, DONE);
// There are zeros, jump to the tail to determine exactly where
jmpb(TAIL_START);
bind(BREAK_LOOP);
// At least one byte in the last 32-byte vector is negative.
// Set up to look at the last 32 bytes as if they were a tail
lea(ary1, Address(ary1, len, Address::times_1));
addptr(result, len);
// Ignore the very last byte: if all others are positive,
// it must be negative, so we can skip right to the 2+1 byte
// end comparison at this point
orl(result, 31);
movl(len, 31);
// Fallthru to tail compare
} else if (UseSSE42Intrinsics) {
// With SSE4.2, use double quad vector compare
Label COMPARE_WIDE_VECTORS, COMPARE_TAIL;
Label COMPARE_WIDE_VECTORS, BREAK_LOOP;
// Compare 16-byte vectors
andl(result, 0x0000000f); // tail count (in bytes)
andl(len, 0xfffffff0); // vector count (in bytes)
jcc(Assembler::zero, COMPARE_TAIL);
testl(len, 0xfffffff0); // vector count (in bytes)
jcc(Assembler::zero, TAIL_START);
andl(len, 0xfffffff0);
lea(ary1, Address(ary1, len, Address::times_1));
negptr(len);
@ -3526,23 +3548,36 @@ void C2_MacroAssembler::has_negatives(Register ary1, Register len,
bind(COMPARE_WIDE_VECTORS);
movdqu(vec1, Address(ary1, len, Address::times_1));
ptest(vec1, vec2);
jcc(Assembler::notZero, TRUE_LABEL);
jccb(Assembler::notZero, BREAK_LOOP);
addptr(len, 16);
jcc(Assembler::notZero, COMPARE_WIDE_VECTORS);
jccb(Assembler::notZero, COMPARE_WIDE_VECTORS);
testl(result, result);
jcc(Assembler::zero, FALSE_LABEL);
testl(result, 0x0000000f); // len is zero, any bytes remaining?
jcc(Assembler::zero, DONE);
movdqu(vec1, Address(ary1, result, Address::times_1, -16));
ptest(vec1, vec2);
jccb(Assembler::notZero, TRUE_LABEL);
jmpb(FALSE_LABEL);
bind(COMPARE_TAIL); // len is zero
// Quick test using the already prepared vector mask
movl(len, result);
andl(len, 0x0000000f); // tail count (in bytes)
movdqu(vec1, Address(ary1, len, Address::times_1, -16));
ptest(vec1, vec2);
jcc(Assembler::zero, DONE);
jmpb(TAIL_START);
bind(BREAK_LOOP);
// At least one byte in the last 16-byte vector is negative.
// Set up and look at the last 16 bytes as if they were a tail
lea(ary1, Address(ary1, len, Address::times_1));
addptr(result, len);
// Ignore the very last byte: if all others are positive,
// it must be negative, so we can skip right to the 2+1 byte
// end comparison at this point
orl(result, 15);
movl(len, 15);
// Fallthru to tail compare
}
}
bind(TAIL_START);
// Compare 4-byte vectors
andl(len, 0xfffffffc); // vector count (in bytes)
jccb(Assembler::zero, COMPARE_CHAR);
@ -3553,34 +3588,45 @@ void C2_MacroAssembler::has_negatives(Register ary1, Register len,
bind(COMPARE_VECTORS);
movl(tmp1, Address(ary1, len, Address::times_1));
andl(tmp1, 0x80808080);
jccb(Assembler::notZero, TRUE_LABEL);
jccb(Assembler::notZero, TAIL_ADJUST);
addptr(len, 4);
jcc(Assembler::notZero, COMPARE_VECTORS);
jccb(Assembler::notZero, COMPARE_VECTORS);
// Compare trailing char (final 2 bytes), if any
// Compare trailing char (final 2-3 bytes), if any
bind(COMPARE_CHAR);
testl(result, 0x2); // tail char
jccb(Assembler::zero, COMPARE_BYTE);
load_unsigned_short(tmp1, Address(ary1, 0));
andl(tmp1, 0x00008080);
jccb(Assembler::notZero, TRUE_LABEL);
subptr(result, 2);
jccb(Assembler::notZero, CHAR_ADJUST);
lea(ary1, Address(ary1, 2));
bind(COMPARE_BYTE);
testl(result, 0x1); // tail byte
jccb(Assembler::zero, FALSE_LABEL);
jccb(Assembler::zero, DONE);
load_unsigned_byte(tmp1, Address(ary1, 0));
andl(tmp1, 0x00000080);
jccb(Assembler::notEqual, TRUE_LABEL);
jmpb(FALSE_LABEL);
bind(TRUE_LABEL);
movl(result, 1); // return true
testl(tmp1, 0x00000080);
jccb(Assembler::zero, DONE);
subptr(result, 1);
jmpb(DONE);
bind(FALSE_LABEL);
xorl(result, result); // return false
bind(TAIL_ADJUST);
// there are negative bits in the last 4 byte block.
// Adjust result and check the next three bytes
addptr(result, len);
orl(result, 3);
lea(ary1, Address(ary1, len, Address::times_1));
jmpb(COMPARE_CHAR);
bind(CHAR_ADJUST);
// We are looking at a char + optional byte tail, and found that one
// of the bytes in the char is negative. Adjust the result, check the
// first byte and readjust if needed.
andl(result, 0xfffffffc);
testl(tmp1, 0x00000080); // little-endian, so lowest byte comes first
jccb(Assembler::notZero, DONE);
addptr(result, 1);
// That's it
bind(DONE);
@ -3590,6 +3636,7 @@ void C2_MacroAssembler::has_negatives(Register ary1, Register len,
vpxor(vec2, vec2);
}
}
// Compare char[] or byte[] arrays aligned to 4 bytes or substrings.
void C2_MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register ary2,
Register limit, Register result, Register chr,

View File

@ -271,11 +271,10 @@ public:
XMMRegister vec1, int ae, KRegister mask = knoreg);
// Search for Non-ASCII character (Negative byte value) in a byte array,
// return true if it has any and false otherwise.
void has_negatives(Register ary1, Register len,
Register result, Register tmp1,
XMMRegister vec1, XMMRegister vec2, KRegister mask1 = knoreg, KRegister mask2 = knoreg);
// return index of the first such character, otherwise len.
void count_positives(Register ary1, Register len,
Register result, Register tmp1,
XMMRegister vec1, XMMRegister vec2, KRegister mask1 = knoreg, KRegister mask2 = knoreg);
// Compare char[] or byte[] arrays.
void arrays_equals(bool is_array_equ, Register ary1, Register ary2,
Register limit, Register result, Register chr,

View File

@ -12122,34 +12122,34 @@ instruct array_equalsC_evex(eDIRegP ary1, eSIRegP ary2, eAXRegI result,
ins_pipe( pipe_slow );
%}
instruct has_negatives(eSIRegP ary1, eCXRegI len, eAXRegI result,
regD tmp1, regD tmp2, eBXRegI tmp3, eFlagsReg cr)
instruct count_positives(eSIRegP ary1, eCXRegI len, eAXRegI result,
regD tmp1, regD tmp2, eBXRegI tmp3, eFlagsReg cr)
%{
predicate(!VM_Version::supports_avx512vlbw() || !VM_Version::supports_bmi2());
match(Set result (HasNegatives ary1 len));
match(Set result (CountPositives ary1 len));
effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL len, KILL tmp3, KILL cr);
format %{ "has negatives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %}
format %{ "countPositives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %}
ins_encode %{
__ has_negatives($ary1$$Register, $len$$Register,
$result$$Register, $tmp3$$Register,
$tmp1$$XMMRegister, $tmp2$$XMMRegister, knoreg, knoreg);
__ count_positives($ary1$$Register, $len$$Register,
$result$$Register, $tmp3$$Register,
$tmp1$$XMMRegister, $tmp2$$XMMRegister, knoreg, knoreg);
%}
ins_pipe( pipe_slow );
%}
instruct has_negatives_evex(eSIRegP ary1, eCXRegI len, eAXRegI result,
regD tmp1, regD tmp2, kReg ktmp1, kReg ktmp2, eBXRegI tmp3, eFlagsReg cr)
instruct count_positives_evex(eSIRegP ary1, eCXRegI len, eAXRegI result,
regD tmp1, regD tmp2, kReg ktmp1, kReg ktmp2, eBXRegI tmp3, eFlagsReg cr)
%{
predicate(VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2());
match(Set result (HasNegatives ary1 len));
match(Set result (CountPositives ary1 len));
effect(TEMP tmp1, TEMP tmp2, TEMP ktmp1, TEMP ktmp2, USE_KILL ary1, USE_KILL len, KILL tmp3, KILL cr);
format %{ "has negatives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %}
format %{ "countPositives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %}
ins_encode %{
__ has_negatives($ary1$$Register, $len$$Register,
$result$$Register, $tmp3$$Register,
$tmp1$$XMMRegister, $tmp2$$XMMRegister, $ktmp1$$KRegister, $ktmp2$$KRegister);
__ count_positives($ary1$$Register, $len$$Register,
$result$$Register, $tmp3$$Register,
$tmp1$$XMMRegister, $tmp2$$XMMRegister, $ktmp1$$KRegister, $ktmp2$$KRegister);
%}
ins_pipe( pipe_slow );
%}

View File

@ -11685,34 +11685,34 @@ instruct array_equalsC_evex(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result,
ins_pipe( pipe_slow );
%}
instruct has_negatives(rsi_RegP ary1, rcx_RegI len, rax_RegI result,
legRegD tmp1, legRegD tmp2, rbx_RegI tmp3, rFlagsReg cr,)
instruct count_positives(rsi_RegP ary1, rcx_RegI len, rax_RegI result,
legRegD tmp1, legRegD tmp2, rbx_RegI tmp3, rFlagsReg cr,)
%{
predicate(!VM_Version::supports_avx512vlbw() || !VM_Version::supports_bmi2());
match(Set result (HasNegatives ary1 len));
match(Set result (CountPositives ary1 len));
effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL len, KILL tmp3, KILL cr);
format %{ "has negatives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %}
format %{ "countPositives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %}
ins_encode %{
__ has_negatives($ary1$$Register, $len$$Register,
$result$$Register, $tmp3$$Register,
$tmp1$$XMMRegister, $tmp2$$XMMRegister, knoreg, knoreg);
__ count_positives($ary1$$Register, $len$$Register,
$result$$Register, $tmp3$$Register,
$tmp1$$XMMRegister, $tmp2$$XMMRegister, knoreg, knoreg);
%}
ins_pipe( pipe_slow );
%}
instruct has_negatives_evex(rsi_RegP ary1, rcx_RegI len, rax_RegI result,
legRegD tmp1, legRegD tmp2, kReg ktmp1, kReg ktmp2, rbx_RegI tmp3, rFlagsReg cr,)
instruct count_positives_evex(rsi_RegP ary1, rcx_RegI len, rax_RegI result,
legRegD tmp1, legRegD tmp2, kReg ktmp1, kReg ktmp2, rbx_RegI tmp3, rFlagsReg cr,)
%{
predicate(VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2());
match(Set result (HasNegatives ary1 len));
match(Set result (CountPositives ary1 len));
effect(TEMP tmp1, TEMP tmp2, TEMP ktmp1, TEMP ktmp2, USE_KILL ary1, USE_KILL len, KILL tmp3, KILL cr);
format %{ "has negatives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %}
format %{ "countPositives byte[] $ary1,$len -> $result // KILL $tmp1, $tmp2, $tmp3" %}
ins_encode %{
__ has_negatives($ary1$$Register, $len$$Register,
$result$$Register, $tmp3$$Register,
$tmp1$$XMMRegister, $tmp2$$XMMRegister, $ktmp1$$KRegister, $ktmp2$$KRegister);
__ count_positives($ary1$$Register, $len$$Register,
$result$$Register, $tmp3$$Register,
$tmp1$$XMMRegister, $tmp2$$XMMRegister, $ktmp1$$KRegister, $ktmp2$$KRegister);
%}
ins_pipe( pipe_slow );
%}

View File

@ -612,7 +612,7 @@ bool InstructForm::needs_anti_dependence_check(FormDict &globals) const {
strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOf" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOfChar" )==0 ||
strcmp(_matrule->_rChild->_opType,"HasNegatives" )==0 ||
strcmp(_matrule->_rChild->_opType,"CountPositives" )==0 ||
strcmp(_matrule->_rChild->_opType,"AryEq" )==0 ))
return true;
@ -902,7 +902,7 @@ uint InstructForm::oper_input_base(FormDict &globals) {
strcmp(_matrule->_rChild->_opType,"StrCompressedCopy" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOf")==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOfChar")==0 ||
strcmp(_matrule->_rChild->_opType,"HasNegatives")==0 ||
strcmp(_matrule->_rChild->_opType,"CountPositives")==0 ||
strcmp(_matrule->_rChild->_opType,"EncodeISOArray")==0)) {
// String.(compareTo/equals/indexOf) and Arrays.equals
// and sun.nio.cs.iso8859_1$Encoder.EncodeISOArray

View File

@ -229,7 +229,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_loadFence:
case vmIntrinsics::_storeFence:
case vmIntrinsics::_fullFence:
case vmIntrinsics::_hasNegatives:
case vmIntrinsics::_countPositives:
case vmIntrinsics::_Reference_get:
break;
default:

View File

@ -354,9 +354,9 @@ class methodHandle;
do_signature(Preconditions_checkLongIndex_signature, "(JJLjava/util/function/BiFunction;)J") \
\
do_class(java_lang_StringCoding, "java/lang/StringCoding") \
do_intrinsic(_hasNegatives, java_lang_StringCoding, hasNegatives_name, hasNegatives_signature, F_S) \
do_name( hasNegatives_name, "hasNegatives") \
do_signature(hasNegatives_signature, "([BII)Z") \
do_intrinsic(_countPositives, java_lang_StringCoding, countPositives_name, countPositives_signature, F_S) \
do_name( countPositives_name, "countPositives") \
do_signature(countPositives_signature, "([BII)I") \
\
do_class(sun_nio_cs_iso8859_1_Encoder, "sun/nio/cs/ISO_8859_1$Encoder") \
do_intrinsic(_encodeISOArray, sun_nio_cs_iso8859_1_Encoder, encodeISOArray_name, encodeISOArray_signature, F_S) \
@ -459,9 +459,8 @@ class methodHandle;
\
/* support for sun.security.provider.DigestBase */ \
do_class(sun_security_provider_digestbase, "sun/security/provider/DigestBase") \
do_intrinsic(_digestBase_implCompressMB, sun_security_provider_digestbase, implCompressMB_name, implCompressMB_signature, F_R) \
do_intrinsic(_digestBase_implCompressMB, sun_security_provider_digestbase, implCompressMB_name, countPositives_signature, F_R) \
do_name( implCompressMB_name, "implCompressMultiBlock0") \
do_signature(implCompressMB_signature, "([BII)I") \
\
/* support for java.util.Base64.Encoder*/ \
do_class(java_util_Base64_Encoder, "java/util/Base64$Encoder") \

View File

@ -567,7 +567,7 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) {
{ { 2, ShenandoahLoad }, { 3, ShenandoahLoad } },
Op_EncodeISOArray,
{ { 2, ShenandoahLoad }, { 3, ShenandoahStore } },
Op_HasNegatives,
Op_CountPositives,
{ { 2, ShenandoahLoad }, { -1, ShenandoahNone} },
Op_CastP2X,
{ { 1, ShenandoahLoad }, { -1, ShenandoahNone} },

View File

@ -742,8 +742,8 @@
#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \
static_field(VM_Version, _zva_length, int) \
static_field(StubRoutines::aarch64, _has_negatives, address) \
static_field(StubRoutines::aarch64, _has_negatives_long, address) \
static_field(StubRoutines::aarch64, _count_positives, address) \
static_field(StubRoutines::aarch64, _count_positives_long, address) \
static_field(VM_Version, _rop_protection, bool) \
volatile_nonstatic_field(JavaFrameAnchor, _last_Java_fp, intptr_t*)

View File

@ -234,8 +234,8 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt
case vmIntrinsics::_encodeByteISOArray:
if (!Matcher::match_rule_supported(Op_EncodeISOArray)) return false;
break;
case vmIntrinsics::_hasNegatives:
if (!Matcher::match_rule_supported(Op_HasNegatives)) return false;
case vmIntrinsics::_countPositives:
if (!Matcher::match_rule_supported(Op_CountPositives)) return false;
break;
case vmIntrinsics::_bitCount_i:
if (!Matcher::match_rule_supported(Op_PopCountI)) return false;

View File

@ -174,7 +174,7 @@ macro(FmaD)
macro(FmaF)
macro(Goto)
macro(Halt)
macro(HasNegatives)
macro(CountPositives)
macro(If)
macro(RangeCheck)
macro(IfFalse)

View File

@ -635,7 +635,7 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de
break;
}
case Op_AryEq:
case Op_HasNegatives:
case Op_CountPositives:
case Op_StrComp:
case Op_StrEquals:
case Op_StrIndexOf:
@ -773,7 +773,7 @@ void ConnectionGraph::add_final_edges(Node *n) {
break;
}
case Op_AryEq:
case Op_HasNegatives:
case Op_CountPositives:
case Op_StrComp:
case Op_StrEquals:
case Op_StrIndexOf:
@ -3344,7 +3344,8 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist,
memnode_worklist.append_if_missing(use);
} else if (!(op == Op_CmpP || op == Op_Conv2B ||
op == Op_CastP2X || op == Op_StoreCM ||
op == Op_FastLock || op == Op_AryEq || op == Op_StrComp || op == Op_HasNegatives ||
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_SubTypeCheck ||
@ -3475,7 +3476,7 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &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_HasNegatives ||
op == Op_AryEq || op == Op_StrComp || op == Op_CountPositives ||
op == Op_StrCompressedCopy || op == Op_StrInflatedCopy ||
op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar)) {
n->dump();

View File

@ -157,13 +157,13 @@ class AryEqNode: public StrIntrinsicNode {
virtual const Type* bottom_type() const { return TypeInt::BOOL; }
};
//------------------------------HasNegatives---------------------------------
class HasNegativesNode: public StrIntrinsicNode {
//------------------------------CountPositives------------------------------
class CountPositivesNode: public StrIntrinsicNode {
public:
HasNegativesNode(Node* control, Node* char_array_mem, Node* s1, Node* c1):
CountPositivesNode(Node* control, Node* char_array_mem, Node* s1, Node* c1):
StrIntrinsicNode(control, char_array_mem, s1, c1, none) {};
virtual int Opcode() const;
virtual const Type* bottom_type() const { return TypeInt::BOOL; }
virtual const Type* bottom_type() const { return TypeInt::POS; }
};

View File

@ -203,7 +203,7 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo
case Op_StrInflatedCopy:
case Op_StrCompressedCopy:
case Op_EncodeISOArray:
case Op_HasNegatives:
case Op_CountPositives:
// Not a legit memory op for implicit null check regardless of
// embedded loads
continue;

View File

@ -617,8 +617,8 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_isCompileConstant:
return inline_isCompileConstant();
case vmIntrinsics::_hasNegatives:
return inline_hasNegatives();
case vmIntrinsics::_countPositives:
return inline_countPositives();
case vmIntrinsics::_fmaD:
case vmIntrinsics::_fmaF:
@ -1011,13 +1011,13 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) {
return true;
}
//------------------------------inline_hasNegatives------------------------------
bool LibraryCallKit::inline_hasNegatives() {
//------------------------------inline_countPositives------------------------------
bool LibraryCallKit::inline_countPositives() {
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
return false;
}
assert(callee()->signature()->size() == 3, "hasNegatives has 3 parameters");
assert(callee()->signature()->size() == 3, "countPositives has 3 parameters");
// no receiver since it is static method
Node* ba = argument(0);
Node* offset = argument(1);
@ -1031,7 +1031,7 @@ bool LibraryCallKit::inline_hasNegatives() {
return true;
}
Node* ba_start = array_element_address(ba, offset, T_BYTE);
Node* result = new HasNegativesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len);
Node* result = new CountPositivesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len);
set_result(_gvn.transform(result));
return true;
}

View File

@ -296,7 +296,7 @@ class LibraryCallKit : public GraphKit {
bool inline_updateBytesAdler32();
bool inline_updateByteBufferAdler32();
bool inline_multiplyToLen();
bool inline_hasNegatives();
bool inline_countPositives();
bool inline_squareToLen();
bool inline_mulAdd();
bool inline_montgomeryMultiply();

View File

@ -828,7 +828,7 @@ bool IdealLoopTree::policy_maximally_unroll(PhaseIdealLoop* phase) const {
case Op_StrIndexOfChar:
case Op_EncodeISOArray:
case Op_AryEq:
case Op_HasNegatives: {
case Op_CountPositives: {
return false;
}
#if INCLUDE_RTM_OPT
@ -981,7 +981,7 @@ bool IdealLoopTree::policy_unroll(PhaseIdealLoop *phase) {
case Op_StrIndexOfChar:
case Op_EncodeISOArray:
case Op_AryEq:
case Op_HasNegatives: {
case Op_CountPositives: {
// Do not unroll a loop with String intrinsics code.
// String intrinsics are large and have loops.
return false;

View File

@ -5775,7 +5775,7 @@ void PhaseIdealLoop::build_loop_late_post_work(Node *n, bool pinned) {
case Op_StrIndexOf:
case Op_StrIndexOfChar:
case Op_AryEq:
case Op_HasNegatives:
case Op_CountPositives:
pinned = false;
}
if (n->is_CMove() || n->is_ConstraintCast()) {

View File

@ -1066,7 +1066,7 @@ static void match_alias_type(Compile* C, Node* n, Node* m) {
case Op_StrIndexOf:
case Op_StrIndexOfChar:
case Op_AryEq:
case Op_HasNegatives:
case Op_CountPositives:
case Op_MemBarVolatile:
case Op_MemBarCPUOrder: // %%% these ideals should have narrower adr_type?
case Op_StrInflatedCopy:
@ -2252,7 +2252,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_HasNegatives:
case Op_CountPositives:
case Op_StrInflatedCopy:
case Op_StrCompressedCopy:
case Op_EncodeISOArray:

View File

@ -525,56 +525,63 @@ public final class String
this.value = "".value;
this.coder = "".coder;
} else if (charset == UTF_8.INSTANCE) {
if (COMPACT_STRINGS && !StringCoding.hasNegatives(bytes, offset, length)) {
this.value = Arrays.copyOfRange(bytes, offset, offset + length);
this.coder = LATIN1;
} else {
if (COMPACT_STRINGS) {
int dp = StringCoding.countPositives(bytes, offset, length);
if (dp == length) {
this.value = Arrays.copyOfRange(bytes, offset, offset + length);
this.coder = LATIN1;
return;
}
int sl = offset + length;
int dp = 0;
byte[] dst = null;
if (COMPACT_STRINGS) {
dst = new byte[length];
while (offset < sl) {
int b1 = bytes[offset];
if (b1 >= 0) {
dst[dp++] = (byte)b1;
byte[] dst = new byte[length];
if (dp > 0) {
System.arraycopy(bytes, offset, dst, 0, dp);
offset += dp;
}
while (offset < sl) {
int b1 = bytes[offset++];
if (b1 >= 0) {
dst[dp++] = (byte)b1;
continue;
}
if ((b1 & 0xfe) == 0xc2 && offset < sl) { // b1 either 0xc2 or 0xc3
int b2 = bytes[offset];
if (b2 < -64) { // continuation bytes are always negative values in the range -128 to -65
dst[dp++] = (byte)decode2(b1, b2);
offset++;
continue;
}
if ((b1 & 0xfe) == 0xc2 && offset + 1 < sl) { // b1 either 0xc2 or 0xc3
int b2 = bytes[offset + 1];
if (!isNotContinuation(b2)) {
dst[dp++] = (byte)decode2(b1, b2);
offset += 2;
continue;
}
}
// anything not a latin1, including the repl
// we have to go with the utf16
break;
}
if (offset == sl) {
if (dp != dst.length) {
dst = Arrays.copyOf(dst, dp);
}
this.value = dst;
this.coder = LATIN1;
return;
// anything not a latin1, including the REPL
// we have to go with the utf16
offset--;
break;
}
if (offset == sl) {
if (dp != dst.length) {
dst = Arrays.copyOf(dst, dp);
}
this.value = dst;
this.coder = LATIN1;
return;
}
if (dp == 0 || dst == null) {
dst = new byte[length << 1];
} else {
byte[] buf = new byte[length << 1];
StringLatin1.inflate(dst, 0, buf, 0, dp);
dst = buf;
}
byte[] buf = new byte[length << 1];
StringLatin1.inflate(dst, 0, buf, 0, dp);
dst = buf;
dp = decodeUTF8_UTF16(bytes, offset, sl, dst, dp, true);
if (dp != length) {
dst = Arrays.copyOf(dst, dp << 1);
}
this.value = dst;
this.coder = UTF16;
} else { // !COMPACT_STRINGS
byte[] dst = new byte[length << 1];
int dp = decodeUTF8_UTF16(bytes, offset, offset + length, dst, 0, true);
if (dp != length) {
dst = Arrays.copyOf(dst, dp << 1);
}
this.value = dst;
this.coder = UTF16;
}
} else if (charset == ISO_8859_1.INSTANCE) {
if (COMPACT_STRINGS) {
@ -682,41 +689,43 @@ public final class String
if (length == 0) {
return "";
}
if (COMPACT_STRINGS && !StringCoding.hasNegatives(bytes, offset, length)) {
return new String(Arrays.copyOfRange(bytes, offset, offset + length), LATIN1);
} else {
int dp;
byte[] dst;
if (COMPACT_STRINGS) {
dp = StringCoding.countPositives(bytes, offset, length);
int sl = offset + length;
int dp = 0;
byte[] dst = null;
if (COMPACT_STRINGS) {
dst = new byte[length];
while (offset < sl) {
int b1 = bytes[offset];
if (b1 >= 0) {
dst[dp++] = (byte) b1;
if (dp == length) {
return new String(Arrays.copyOfRange(bytes, offset, offset + length), LATIN1);
}
dst = new byte[length];
System.arraycopy(bytes, offset, dst, 0, dp);
offset += dp;
while (offset < sl) {
int b1 = bytes[offset++];
if (b1 >= 0) {
dst[dp++] = (byte)b1;
continue;
}
if ((b1 & 0xfe) == 0xc2 && offset < sl) { // b1 either 0xc2 or 0xc3
int b2 = bytes[offset];
if (b2 < -64) { // continuation bytes are always negative values in the range -128 to -65
dst[dp++] = (byte)decode2(b1, b2);
offset++;
continue;
}
if ((b1 & 0xfe) == 0xc2 && offset + 1 < sl) { // b1 either 0xc2 or 0xc3
int b2 = bytes[offset + 1];
if (!isNotContinuation(b2)) {
dst[dp++] = (byte) decode2(b1, b2);
offset += 2;
continue;
}
}
// anything not a latin1, including the REPL
// we have to go with the utf16
break;
}
if (offset == sl) {
if (dp != dst.length) {
dst = Arrays.copyOf(dst, dp);
}
return new String(dst, LATIN1);
}
// anything not a latin1, including the REPL
// we have to go with the utf16
offset--;
break;
}
if (dp == 0 || dst == null) {
if (offset == sl) {
if (dp != dst.length) {
dst = Arrays.copyOf(dst, dp);
}
return new String(dst, LATIN1);
}
if (dp == 0) {
dst = new byte[length << 1];
} else {
byte[] buf = new byte[length << 1];
@ -724,11 +733,14 @@ public final class String
dst = buf;
}
dp = decodeUTF8_UTF16(bytes, offset, sl, dst, dp, false);
if (dp != length) {
dst = Arrays.copyOf(dst, dp << 1);
}
return new String(dst, UTF16);
} else { // !COMPACT_STRINGS
dst = new byte[length << 1];
dp = decodeUTF8_UTF16(bytes, offset, offset + length, dst, 0, false);
}
if (dp != length) {
dst = Arrays.copyOf(dst, dp << 1);
}
return new String(dst, UTF16);
}
static String newStringNoRepl(byte[] src, Charset cs) throws CharacterCodingException {
@ -1019,17 +1031,9 @@ public final class String
*/
/* package-private */
static int decodeASCII(byte[] sa, int sp, char[] da, int dp, int len) {
if (!StringCoding.hasNegatives(sa, sp, len)) {
StringLatin1.inflate(sa, sp, da, dp, len);
return len;
} else {
int start = sp;
int end = sp + len;
while (sp < end && sa[sp] >= 0) {
da[dp++] = (char) sa[sp++];
}
return sp - start;
}
int count = StringCoding.countPositives(sa, sp, len);
StringLatin1.inflate(sa, sp, da, dp, count);
return count;
}
private static boolean isNotContinuation(int b) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2022, 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
@ -34,14 +34,27 @@ class StringCoding {
private StringCoding() { }
@IntrinsicCandidate
public static boolean hasNegatives(byte[] ba, int off, int len) {
for (int i = off; i < off + len; i++) {
return countPositives(ba, off, len) != len;
}
/**
* Count the number of leading positive bytes in the range.
*
* @implSpec the implementation must return len if there are no negative
* bytes in the range. If there are negative bytes, the implementation must return
* a value that is less than or equal to the index of the first negative byte
* in the range.
*/
@IntrinsicCandidate
public static int countPositives(byte[] ba, int off, int len) {
int limit = off + len;
for (int i = off; i < limit; i++) {
if (ba[i] < 0) {
return true;
return i - off;
}
}
return false;
return len;
}
@IntrinsicCandidate

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2022, 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 compiler.intrinsics.string;
/*
* @test
* @bug 8999999
* @summary Validates StringCoding.countPositives intrinsic with a small range of tests.
* @library /compiler/patches
*
* @build java.base/java.lang.Helper
* @run main compiler.intrinsics.string.TestCountPositives
*/
public class TestCountPositives {
private static byte[] tBa = new byte[4096 + 16];
/**
* Completely initialize the test array, preparing it for tests of the
* StringCoding.hasNegatives method with a given array segment offset,
* length, and number of negative bytes.
*/
public static void initialize(int off, int len, int neg) {
assert (len + off <= tBa.length);
// insert "canary" (negative) values before offset
for (int i = 0; i < off; ++i) {
tBa[i] = (byte) (((i + 15) & 0x7F) | 0x80);
}
// fill the array segment
for (int i = off; i < len + off; ++i) {
tBa[i] = (byte) (((i - off + 15) & 0x7F));
}
if (neg != 0) {
// modify a number (neg) disparate array bytes inside
// segment to be negative.
int div = (neg > 1) ? (len - 1) / (neg - 1) : 0;
int idx;
for (int i = 0; i < neg; ++i) {
idx = off + (len - 1) - div * i;
tBa[idx] = (byte) (0x80 | tBa[idx]);
}
}
// insert "canary" negative values after array segment
for (int i = len + off; i < tBa.length; ++i) {
tBa[i] = (byte) (((i + 15) & 0x7F) | 0x80);
}
}
/** Sizes of array segments to test. */
private static int sizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 17, 19, 23, 37, 61, 131,
4099 };
/**
* Test different array segment sizes, offsets, and number of negative
* bytes.
*/
public static void test_countPositives() throws Exception {
int len, off;
int ng;
for (ng = 0; ng < 57; ++ng) { // number of negatives in array segment
for (off = 0; off < 8; ++off) { // starting offset of array segment
for (int i = 0; i < sizes.length; ++i) { // array segment size
// choice
len = sizes[i];
if (len + off > tBa.length)
continue;
initialize(off, len, ng);
int calculated = Helper.StringCodingCountPositives(tBa, off, len);
int expected = countPositives(tBa, off, len);
if (calculated != expected) {
if (expected != len && calculated >= 0 && calculated < expected) {
// allow intrinsics to return early with a lower value,
// but only if we're not expecting the full length (no
// negative bytes)
continue;
}
throw new Exception("Failed test countPositives " + "offset: " + off + " "
+ "length: " + len + " " + "return: " + calculated + " expected: " + expected + " negatives: "
+ ng);
}
}
}
}
}
private static int countPositives(byte[] ba, int off, int len) {
int limit = off + len;
for (int i = off; i < limit; i++) {
if (ba[i] < 0) {
return i - off;
}
}
return len;
}
public void run() throws Exception {
// iterate to eventually get intrinsic inlined
for (int j = 0; j < 1000; ++j) {
test_countPositives();
}
}
public static void main(String[] args) throws Exception {
(new TestCountPositives()).run();
System.out.println("countPositives validated");
}
}

View File

@ -32,6 +32,11 @@ public class Helper {
return StringCoding.hasNegatives(ba, off, len);
}
@jdk.internal.vm.annotation.ForceInline
public static int StringCodingCountPositives(byte[] ba, int off, int len) {
return StringCoding.countPositives(ba, off, len);
}
@jdk.internal.vm.annotation.ForceInline
public static byte[] compressByte(byte[] src, int srcOff, int dstSize, int dstOff, int len) {
byte[] dst = new byte[dstSize];

View File

@ -87,7 +87,7 @@ public class StringDecode {
bh.consume(new String(asciiString, charset));
bh.consume(new String(longAsciiString, 0, 15, charset));
bh.consume(new String(asciiString, 0, 3, charset));
bh.consume(new String(longAsciiString, 512, 512 + 7, charset));
bh.consume(new String(longAsciiString, 512, 7, charset));
}
@Benchmark
@ -103,7 +103,7 @@ public class StringDecode {
bh.consume(new String(latin1String, charset));
bh.consume(new String(latin1String, 0, 15, charset));
bh.consume(new String(latin1String, 0, 3, charset));
bh.consume(new String(longLatin1OnlyString, 512, 512 + 7, charset));
bh.consume(new String(longLatin1OnlyString, 512, 7, charset));
}
@Benchmark