From 4da3d71efadff9d2f3db235c5e838c6af0a66a7e Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Tue, 21 Sep 2021 23:58:21 +0200 Subject: [PATCH 01/15] Enhance UTF_8.Encoder by using StringUTF16.compress for ASCII --- .../share/classes/java/lang/System.java | 4 +++ .../jdk/internal/access/JavaLangAccess.java | 7 ++++ .../share/classes/sun/nio/cs/UTF_8.java | 34 +++++++++++++++---- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index edb636bfe210c..5c65a15dc20df 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2419,6 +2419,10 @@ public int decodeASCII(byte[] src, int srcOff, char[] dst, int dstOff, int len) return String.decodeASCII(src, srcOff, dst, dstOff, len); } + public int compressCharsToBytes(char[] src, int srcOff, byte[] dst, int dstOff, int len) { + return StringUTF16.compress(src, srcOff, dst, dstOff, len); + } + public void setCause(Throwable t, Throwable cause) { t.setCause(cause); } diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index f6fe5f6113166..513b89ccd7bc4 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -356,6 +356,13 @@ public interface JavaLangAccess { */ int decodeASCII(byte[] src, int srcOff, char[] dst, int dstOff, int len); + /** + * Compressed copy char[] -> byte[] + * + * @return the number of bytes successfully encoded, or 0 if none + */ + int compressCharsToBytes(char[] src, int srcOff, byte[] dst, int dstOff, int len); + /** * Set the cause of Throwable * @param cause set t's cause to new value diff --git a/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/src/java.base/share/classes/sun/nio/cs/UTF_8.java index 1a7d8c4d1e1a5..683672825f29f 100644 --- a/src/java.base/share/classes/sun/nio/cs/UTF_8.java +++ b/src/java.base/share/classes/sun/nio/cs/UTF_8.java @@ -83,9 +83,9 @@ static final void updatePositions(Buffer src, int sp, dst.position(dp - dst.arrayOffset()); } - private static class Decoder extends CharsetDecoder { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static class Decoder extends CharsetDecoder { private Decoder(Charset cs) { super(cs, 1.0f, 1.0f); @@ -443,8 +443,7 @@ private static CoderResult overflow(CharBuffer src, int mark) { private Surrogate.Parser sgp; private CoderResult encodeArrayLoop(CharBuffer src, - ByteBuffer dst) - { + ByteBuffer dst) { char[] sa = src.array(); int sp = src.arrayOffset() + src.position(); int sl = src.arrayOffset() + src.limit(); @@ -452,11 +451,32 @@ private CoderResult encodeArrayLoop(CharBuffer src, byte[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); - int dlASCII = dp + Math.min(sl - sp, dl - dp); + int slASCII = sp + Math.min(sl - sp, dl - dp); // ASCII only loop - while (dp < dlASCII && sa[sp] < '\u0080') - da[dp++] = (byte) sa[sp++]; + // Since UTF8 is ASCII-compatible and encodes ASCII as single-byte, + // we can reuse StringUTF16.compress to speed up the actual copy of the ASCII data + int lastAscii = sp; + while (lastAscii < slASCII && sa[lastAscii] < '\u0080') { + lastAscii++; + } + if (lastAscii > sp) { + int len = lastAscii - sp; + JLA.compressCharsToBytes(sa, sp, da, dp, len); + sp = lastAscii; + dp += len; + } + + if (sp < sl) { + return encodeArrayLoopSlow(src, sa, sp, sl, dst, da, dp, dl); + } else { + updatePositions(src, sp, dst, dp); + return CoderResult.UNDERFLOW; + } + } + + private CoderResult encodeArrayLoopSlow(CharBuffer src, char[] sa, int sp, int sl, + ByteBuffer dst, byte[] da, int dp, int dl) { while (sp < sl) { char c = sa[sp]; if (c < 0x80) { From cef05f44fd482646c5df496a50bdf78527d908cb Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Thu, 23 Sep 2021 01:45:06 +0200 Subject: [PATCH 02/15] Implement intrinsified ASCII fast-path by copying and adapting encodeISOArray intrinsic (currently x86 only) --- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 144 ++++++++++++++++++ src/hotspot/cpu/x86/macroAssembler_x86.hpp | 3 + src/hotspot/cpu/x86/x86_32.ad | 15 ++ src/hotspot/cpu/x86/x86_64.ad | 16 ++ src/hotspot/share/adlc/formssel.cpp | 3 +- src/hotspot/share/classfile/vmIntrinsics.cpp | 1 + src/hotspot/share/classfile/vmIntrinsics.hpp | 3 + .../gc/shenandoah/c2/shenandoahSupport.cpp | 2 + src/hotspot/share/opto/c2compiler.cpp | 3 + src/hotspot/share/opto/classes.hpp | 1 + src/hotspot/share/opto/escape.cpp | 14 +- src/hotspot/share/opto/intrinsicnode.cpp | 20 +++ src/hotspot/share/opto/intrinsicnode.hpp | 15 ++ src/hotspot/share/opto/library_call.cpp | 51 +++++++ src/hotspot/share/opto/library_call.hpp | 1 + src/hotspot/share/opto/loopTransform.cpp | 2 + src/hotspot/share/opto/macro.cpp | 4 +- src/hotspot/share/opto/matcher.cpp | 5 +- .../share/classes/java/lang/StringCoding.java | 16 +- .../share/classes/java/lang/System.java | 4 +- .../jdk/internal/access/JavaLangAccess.java | 6 +- .../share/classes/sun/nio/cs/CESU_8.java | 22 +-- .../share/classes/sun/nio/cs/US_ASCII.java | 8 +- .../share/classes/sun/nio/cs/UTF_8.java | 20 +-- 24 files changed, 338 insertions(+), 41 deletions(-) diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index b573adc3acdd0..0ebec7162765e 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5558,6 +5558,150 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, bind(L_done); } + // encode char[] to byte[] in ASCII + //@IntrinsicCandidate + //private static int implEncodeAsciiArray(char[] sa, int sp, + // byte[] da, int dp, int len) { + // int i = 0; + // for (; i < len; i++) { + // char c = sa[sp++]; + // if (c >= '\u0080') + // break; + // da[dp++] = (byte)c; + // } + // return i; + //} +void MacroAssembler::encode_ascii_array(Register src, Register dst, Register len, + XMMRegister tmp1Reg, XMMRegister tmp2Reg, + XMMRegister tmp3Reg, XMMRegister tmp4Reg, + Register tmp5, Register result) { + + // rsi: src + // rdi: dst + // rdx: len + // rcx: tmp5 + // rax: result + ShortBranchVerifier sbv(this); + assert_different_registers(src, dst, len, tmp5, result); + Label L_done, L_copy_1_char, L_copy_1_char_exit; + + // set result + xorl(result, result); + // check for zero length + testl(len, len); + jcc(Assembler::zero, L_done); + + movl(result, len); + + // Setup pointers + lea(src, Address(src, len, Address::times_2)); // char[] + lea(dst, Address(dst, len, Address::times_1)); // byte[] + negptr(len); + + if (UseSSE42Intrinsics || UseAVX >= 2) { + Label L_copy_8_chars, L_copy_8_chars_exit; + Label L_chars_16_check, L_copy_16_chars, L_copy_16_chars_exit; + + if (UseAVX >= 2) { + Label L_chars_32_check, L_copy_32_chars, L_copy_32_chars_exit; + movl(tmp5, 0xff80ff80); // create mask to test for non-ASCII chars in vector + movdl(tmp1Reg, tmp5); + vpbroadcastd(tmp1Reg, tmp1Reg, Assembler::AVX_256bit); + jmp(L_chars_32_check); + + bind(L_copy_32_chars); + vmovdqu(tmp3Reg, Address(src, len, Address::times_2, -64)); + vmovdqu(tmp4Reg, Address(src, len, Address::times_2, -32)); + vpor(tmp2Reg, tmp3Reg, tmp4Reg, /* vector_len */ 1); + vptest(tmp2Reg, tmp1Reg); // check for non-ASCII chars in vector + jccb(Assembler::notZero, L_copy_32_chars_exit); + vpackuswb(tmp3Reg, tmp3Reg, tmp4Reg, /* vector_len */ 1); + vpermq(tmp4Reg, tmp3Reg, 0xD8, /* vector_len */ 1); + vmovdqu(Address(dst, len, Address::times_1, -32), tmp4Reg); + + bind(L_chars_32_check); + addptr(len, 32); + jcc(Assembler::lessEqual, L_copy_32_chars); + + bind(L_copy_32_chars_exit); + subptr(len, 16); + jccb(Assembler::greater, L_copy_16_chars_exit); + + } else if (UseSSE42Intrinsics) { + movl(tmp5, 0xff80ff80); // create mask to test for non-ASCII chars in vector + movdl(tmp1Reg, tmp5); + pshufd(tmp1Reg, tmp1Reg, 0); + jmpb(L_chars_16_check); + } + + bind(L_copy_16_chars); + if (UseAVX >= 2) { + vmovdqu(tmp2Reg, Address(src, len, Address::times_2, -32)); + vptest(tmp2Reg, tmp1Reg); + jcc(Assembler::notZero, L_copy_16_chars_exit); + vpackuswb(tmp2Reg, tmp2Reg, tmp1Reg, /* vector_len */ 1); + vpermq(tmp3Reg, tmp2Reg, 0xD8, /* vector_len */ 1); + } else { + if (UseAVX > 0) { + movdqu(tmp3Reg, Address(src, len, Address::times_2, -32)); + movdqu(tmp4Reg, Address(src, len, Address::times_2, -16)); + vpor(tmp2Reg, tmp3Reg, tmp4Reg, /* vector_len */ 0); + } else { + movdqu(tmp3Reg, Address(src, len, Address::times_2, -32)); + por(tmp2Reg, tmp3Reg); + movdqu(tmp4Reg, Address(src, len, Address::times_2, -16)); + por(tmp2Reg, tmp4Reg); + } + ptest(tmp2Reg, tmp1Reg); // check for non-ascii chars in vector + jccb(Assembler::notZero, L_copy_16_chars_exit); + packuswb(tmp3Reg, tmp4Reg); + } + movdqu(Address(dst, len, Address::times_1, -16), tmp3Reg); + + bind(L_chars_16_check); + addptr(len, 16); + jcc(Assembler::lessEqual, L_copy_16_chars); + + bind(L_copy_16_chars_exit); + if (UseAVX >= 2) { + // clean upper bits of YMM registers + vpxor(tmp2Reg, tmp2Reg); + vpxor(tmp3Reg, tmp3Reg); + vpxor(tmp4Reg, tmp4Reg); + movdl(tmp1Reg, tmp5); + pshufd(tmp1Reg, tmp1Reg, 0); + } + subptr(len, 8); + jccb(Assembler::greater, L_copy_8_chars_exit); + + bind(L_copy_8_chars); + movdqu(tmp3Reg, Address(src, len, Address::times_2, -16)); + ptest(tmp3Reg, tmp1Reg); + jccb(Assembler::notZero, L_copy_8_chars_exit); + packuswb(tmp3Reg, tmp1Reg); + movq(Address(dst, len, Address::times_1, -8), tmp3Reg); + addptr(len, 8); + jccb(Assembler::lessEqual, L_copy_8_chars); + + bind(L_copy_8_chars_exit); + subptr(len, 8); + jccb(Assembler::zero, L_done); + } + + bind(L_copy_1_char); + load_unsigned_short(tmp5, Address(src, len, Address::times_2, 0)); + testl(tmp5, 0xff80); // check if non-ASCII char + jccb(Assembler::notZero, L_copy_1_char_exit); + movb(Address(dst, len, Address::times_1, 0), tmp5); + addptr(len, 1); + jccb(Assembler::less, L_copy_1_char); + + bind(L_copy_1_char_exit); + addptr(result, len); // len is negative count of not processed elements + + bind(L_done); +} + #ifdef _LP64 /** * Helper for multiply_to_len(). diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 4ecbe6add71e3..d10307e4044b8 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -1725,6 +1725,9 @@ class MacroAssembler: public Assembler { void encode_iso_array(Register src, Register dst, Register len, XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3, XMMRegister tmp4, Register tmp5, Register result); + void encode_ascii_array(Register src, Register dst, Register len, + XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3, + XMMRegister tmp4, Register tmp5, Register result); #ifdef _LP64 void add2_with_carry(Register dest_hi, Register dest_lo, Register src1, Register src2); diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 7fb9e4e66a024..2e6a39ddcc0dc 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -12211,6 +12211,21 @@ instruct encode_iso_array(eSIRegP src, eDIRegP dst, eDXRegI len, ins_pipe( pipe_slow ); %} +// encode char[] to byte[] in ASCII +instruct encode_ascii_array(eSIRegP src, eDIRegP dst, eDXRegI len, + regD tmp1, regD tmp2, regD tmp3, regD tmp4, + eCXRegI tmp5, eAXRegI result, eFlagsReg cr) %{ + match(Set result (EncodeAsciiArray src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); + + format %{ "Encode array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} + ins_encode %{ + __ encode_ascii_array($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + %} + ins_pipe( pipe_slow ); +%} //----------Control Flow Instructions------------------------------------------ // Signed compare Instructions diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index ad6c36c51d75a..891e9da6640f8 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -11782,6 +11782,22 @@ instruct encode_iso_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, ins_pipe( pipe_slow ); %} +// encode char[] to byte[] in ASCII +instruct encode_ascii_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, + legRegD tmp1, legRegD tmp2, legRegD tmp3, legRegD tmp4, + rcx_RegI tmp5, rax_RegI result, rFlagsReg cr) %{ + match(Set result (EncodeAsciiArray src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); + + format %{ "Encode array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} + ins_encode %{ + __ encode_ascii_array($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + %} + ins_pipe( pipe_slow ); +%} + //----------Overflow Math Instructions----------------------------------------- instruct overflowAddI_rReg(rFlagsReg cr, rax_RegI op1, rRegI op2) diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 3b244ceda1fd7..ddb88d7cab2bd 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -903,7 +903,8 @@ uint InstructForm::oper_input_base(FormDict &globals) { strcmp(_matrule->_rChild->_opType,"StrIndexOf")==0 || strcmp(_matrule->_rChild->_opType,"StrIndexOfChar")==0 || strcmp(_matrule->_rChild->_opType,"HasNegatives")==0 || - strcmp(_matrule->_rChild->_opType,"EncodeISOArray")==0)) { + strcmp(_matrule->_rChild->_opType,"EncodeISOArray")==0 || + strcmp(_matrule->_rChild->_opType,"EncodeAsciiArray")==0)) { // String.(compareTo/equals/indexOf) and Arrays.equals // and sun.nio.cs.iso8859_1$Encoder.EncodeISOArray // take 1 control and 1 memory edges. diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index 205ba8969cb0a..c15e1154b13c0 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -505,6 +505,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { if (!SpecialArraysEquals) return true; break; case vmIntrinsics::_encodeISOArray: + case vmIntrinsics::_encodeAsciiArray: case vmIntrinsics::_encodeByteISOArray: if (!SpecialEncodeISOArray) return true; break; diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 02aa3cb56155c..1d293052c12f2 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -353,6 +353,9 @@ class methodHandle; \ do_intrinsic(_encodeByteISOArray, java_lang_StringCoding, encodeISOArray_name, indexOfI_signature, F_S) \ \ + do_intrinsic(_encodeAsciiArray, java_lang_StringCoding, encodeAsciiArray_name, encodeISOArray_signature, F_S) \ + do_name( encodeAsciiArray_name, "implEncodeAsciiArray") \ + \ do_class(java_math_BigInteger, "java/math/BigInteger") \ do_intrinsic(_multiplyToLen, java_math_BigInteger, multiplyToLen_name, multiplyToLen_signature, F_S) \ do_name( multiplyToLen_name, "implMultiplyToLen") \ diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 5ba3828b2d3d7..f9c6996409c2c 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -567,6 +567,8 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { { { 2, ShenandoahLoad }, { 3, ShenandoahLoad } }, Op_EncodeISOArray, { { 2, ShenandoahLoad }, { 3, ShenandoahStore } }, + Op_EncodeAsciiArray, + { { 2, ShenandoahLoad }, { 3, ShenandoahStore } }, Op_HasNegatives, { { 2, ShenandoahLoad }, { -1, ShenandoahNone} }, Op_CastP2X, diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index d5a59a413af75..f7f53e08e68fc 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -220,6 +220,9 @@ 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::_encodeAsciiArray: + if (!Matcher::match_rule_supported(Op_EncodeAsciiArray)) return false; + break; case vmIntrinsics::_hasNegatives: if (!Matcher::match_rule_supported(Op_HasNegatives)) return false; break; diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 582ec51fbde80..709d72a36f39b 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -165,6 +165,7 @@ macro(DivL) macro(DivMod) macro(DivModI) macro(DivModL) +macro(EncodeAsciiArray) macro(EncodeISOArray) macro(EncodeP) macro(EncodePKlass) diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 54d1d2f2ea586..19fc7effea480 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -621,6 +621,7 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de case Op_StrIndexOfChar: case Op_StrInflatedCopy: case Op_StrCompressedCopy: + case Op_EncodeAsciiArray: case Op_EncodeISOArray: { add_local_var(n, PointsToNode::ArgEscape); delayed_worklist->push(n); // Process it later. @@ -759,6 +760,7 @@ void ConnectionGraph::add_final_edges(Node *n) { case Op_StrIndexOfChar: case Op_StrInflatedCopy: case Op_StrCompressedCopy: + case Op_EncodeAsciiArray: case Op_EncodeISOArray: { // char[]/byte[] arrays passed to string intrinsic do not escape but // they are not scalar replaceable. Adjust escape state for them. @@ -2924,7 +2926,8 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra if (mem->is_LoadStore()) { adr = mem->in(MemNode::Address); } else { - assert(mem->Opcode() == Op_EncodeISOArray || + assert(mem->Opcode() == Op_EncodeAsciiArray || + mem->Opcode() == Op_EncodeISOArray || mem->Opcode() == Op_StrCompressedCopy, "sanity"); adr = mem->in(3); // Memory edge corresponds to destination array } @@ -3309,9 +3312,9 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, if (m->is_MergeMem()) { assert(mergemem_worklist.contains(m->as_MergeMem()), "EA: missing MergeMem node in the worklist"); } - } else if (use->Opcode() == Op_EncodeISOArray) { + } else if (use->Opcode() == Op_EncodeAsciiArray || use->Opcode() == Op_EncodeISOArray) { if (use->in(MemNode::Memory) == n || use->in(3) == n) { - // EncodeISOArray overwrites destination array + // EncodeAsciiArray and EncodeISOArray overwrites destination array memnode_worklist.append_if_missing(use); } } else { @@ -3391,6 +3394,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, continue; } } else if (n->Opcode() == Op_StrCompressedCopy || + n->Opcode() == Op_EncodeAsciiArray || n->Opcode() == Op_EncodeISOArray) { // get the memory projection n = n->find_out_with(Op_SCMemProj); @@ -3441,9 +3445,9 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, assert(use->in(MemNode::Memory) != n, "EA: missing memory path"); } else if (use->is_MergeMem()) { assert(mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist"); - } else if (use->Opcode() == Op_EncodeISOArray) { + } else if (use->Opcode() == Op_EncodeAsciiArray || use->Opcode() == Op_EncodeISOArray) { if (use->in(MemNode::Memory) == n || use->in(3) == n) { - // EncodeISOArray overwrites destination array + // EncodeAscii/-ISOArray overwrites destination array memnode_worklist.append_if_missing(use); } } else { diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index 9f852dd637862..f11df9718d2f6 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -98,6 +98,26 @@ const Type* EncodeISOArrayNode::Value(PhaseGVN* phase) const { return bottom_type(); } +//============================================================================= +//------------------------------match_edge------------------------------------- +// Do not match memory edge +uint EncodeAsciiArrayNode::match_edge(uint idx) const { + return idx == 2 || idx == 3; // EncodeAsciiArray src (Binary dst len) +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node* EncodeAsciiArrayNode::Ideal(PhaseGVN* phase, bool can_reshape) { + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + +//------------------------------Value------------------------------------------ +const Type* EncodeAsciiArrayNode::Value(PhaseGVN* phase) const { + if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; + return bottom_type(); +} + //------------------------------CopySign----------------------------------------- CopySignDNode* CopySignDNode::make(PhaseGVN& gvn, Node* in1, Node* in2) { return new CopySignDNode(in1, in2, gvn.makecon(TypeD::ZERO)); diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp index 3d6e9a38d1225..60c9e7c8c74d6 100644 --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -182,6 +182,21 @@ class EncodeISOArrayNode: public Node { virtual const Type* Value(PhaseGVN* phase) const; }; +//------------------------------EncodeAsciiArray-------------------------------- +// encode char[] to byte[] in ASCII +class EncodeAsciiArrayNode: public Node { + public: + EncodeAsciiArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c): Node(control, arymem, s1, s2, c) {}; + virtual int Opcode() const; + virtual bool depends_only_on_test() const { return false; } + virtual const Type* bottom_type() const { return TypeInt::INT; } + virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual uint match_edge(uint idx) const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual const Type* Value(PhaseGVN* phase) const; +}; + //-------------------------------DigitNode---------------------------------------- class DigitNode : public Node { public: diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 2dbd3dc1c4c05..633bfbdb55c63 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -592,6 +592,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_encodeISOArray: case vmIntrinsics::_encodeByteISOArray: return inline_encodeISOArray(); + case vmIntrinsics::_encodeAsciiArray: + return inline_encodeAsciiArray(); case vmIntrinsics::_updateCRC32: return inline_updateCRC32(); @@ -4928,6 +4930,55 @@ bool LibraryCallKit::inline_encodeISOArray() { return true; } +//-------------inline_encodeAsciiArray----------------------------------- +// encode char[] to byte[] in ASCII +bool LibraryCallKit::inline_encodeAsciiArray() { + assert(callee()->signature()->size() == 5, "encodeAsciiArray has 5 parameters"); + // no receiver since it is static method + Node *src = argument(0); + Node *src_offset = argument(1); + Node *dst = argument(2); + Node *dst_offset = argument(3); + Node *length = argument(4); + + src = must_be_not_null(src, true); + dst = must_be_not_null(dst, true); + + const Type* src_type = src->Value(&_gvn); + const Type* dst_type = dst->Value(&_gvn); + const TypeAryPtr* top_src = src_type->isa_aryptr(); + const TypeAryPtr* top_dest = dst_type->isa_aryptr(); + if (top_src == NULL || top_src->klass() == NULL || + top_dest == NULL || top_dest->klass() == NULL) { + // failed array check + assert(false, "failed NULL checks"); + return false; + } + + // Figure out the size and type of the elements we will be copying. + BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType dst_elem = dst_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + if (src_elem != T_CHAR || dst_elem != T_BYTE) { + assert(false, "failed type checks"); + return false; + } + + Node* src_start = array_element_address(src, src_offset, T_CHAR); + Node* dst_start = array_element_address(dst, dst_offset, T_BYTE); + // 'src_start' points to src array + scaled offset + // 'dst_start' points to dst array + scaled offset + + const TypeAryPtr* mtype = TypeAryPtr::BYTES; + Node* enc = new EncodeAsciiArrayNode(control(), memory(mtype), src_start, dst_start, length); + enc = _gvn.transform(enc); + Node* res_mem = _gvn.transform(new SCMemProjNode(enc)); + set_memory(res_mem, mtype); + set_result(enc); + clear_upper_avx(); + + return true; +} + //-------------inline_multiplyToLen----------------------------------- bool LibraryCallKit::inline_multiplyToLen() { assert(UseMultiplyToLenIntrinsic, "not implemented on this platform"); diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index adb1cb06c957b..304bc31191dd1 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -286,6 +286,7 @@ class LibraryCallKit : public GraphKit { Node* get_digest_length_from_digest_object(Node *digestBase_object); Node* inline_digestBase_implCompressMB_predicate(int predicate); bool inline_encodeISOArray(); + bool inline_encodeAsciiArray(); bool inline_updateCRC32(); bool inline_updateBytesCRC32(); bool inline_updateByteBufferCRC32(); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 3ad5480cd6676..ff5b81c983267 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -827,6 +827,7 @@ bool IdealLoopTree::policy_maximally_unroll(PhaseIdealLoop* phase) const { case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_EncodeISOArray: + case Op_EncodeAsciiArray: case Op_AryEq: case Op_HasNegatives: { return false; @@ -961,6 +962,7 @@ bool IdealLoopTree::policy_unroll(PhaseIdealLoop *phase) { case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_EncodeISOArray: + case Op_EncodeAsciiArray: case Op_AryEq: case Op_HasNegatives: { // Do not unroll a loop with String intrinsics code. diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 630452179ebfa..387454f52ef9f 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -241,7 +241,8 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me adr = mem->in(MemNode::Address); } else { assert(mem->Opcode() == Op_EncodeISOArray || - mem->Opcode() == Op_StrCompressedCopy, "sanity"); + mem->Opcode() == Op_EncodeAsciiArray || + mem->Opcode() == Op_StrCompressedCopy, "sanity"); adr = mem->in(3); // Destination array } const TypePtr* atype = adr->bottom_type()->is_ptr(); @@ -416,6 +417,7 @@ Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type * } else if (val->Opcode() == Op_SCMemProj) { assert(val->in(0)->is_LoadStore() || val->in(0)->Opcode() == Op_EncodeISOArray || + val->in(0)->Opcode() == Op_EncodeAsciiArray || val->in(0)->Opcode() == Op_StrCompressedCopy, "sanity"); assert(false, "Object is not scalar replaceable if a LoadStore node accesses its field"); return NULL; diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 5b8fcb8ab607a..fb7715551f697 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -1038,6 +1038,7 @@ static void match_alias_type(Compile* C, Node* n, Node* m) { case Op_StrCompressedCopy: case Op_OnSpinWait: case Op_EncodeISOArray: + case Op_EncodeAsciiArray: nidx = Compile::AliasIdxTop; nat = NULL; break; @@ -2221,6 +2222,7 @@ bool Matcher::find_shared_visit(MStack& mstack, Node* n, uint opcode, bool& mem_ case Op_StrInflatedCopy: case Op_StrCompressedCopy: case Op_EncodeISOArray: + case Op_EncodeAsciiArray: case Op_FmaD: case Op_FmaF: case Op_FmaVD: @@ -2369,7 +2371,8 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) { } case Op_StrCompressedCopy: case Op_StrInflatedCopy: - case Op_EncodeISOArray: { + case Op_EncodeISOArray: + case Op_EncodeAsciiArray: { // Restructure into a binary tree for Matching. Node* pair = new BinaryNode(n->in(3), n->in(4)); n->set_req(3, pair); diff --git a/src/java.base/share/classes/java/lang/StringCoding.java b/src/java.base/share/classes/java/lang/StringCoding.java index c8d691675430e..ec81c3795799f 100644 --- a/src/java.base/share/classes/java/lang/StringCoding.java +++ b/src/java.base/share/classes/java/lang/StringCoding.java @@ -46,7 +46,7 @@ public static boolean hasNegatives(byte[] ba, int off, int len) { @IntrinsicCandidate public static int implEncodeISOArray(byte[] sa, int sp, - byte[] da, int dp, int len) { + byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = StringUTF16.getChar(sa, sp++); @@ -57,4 +57,18 @@ public static int implEncodeISOArray(byte[] sa, int sp, return i; } + @IntrinsicCandidate + public static int implEncodeAsciiArray(char[] sa, int sp, + byte[] da, int dp, int len) + { + int i = 0; + for (; i < len; i++) { + char c = sa[sp++]; + if (c >= '\u0080') + break; + da[dp++] = (byte)c; + } + return i; + } + } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 5c65a15dc20df..c843858712f7b 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2419,8 +2419,8 @@ public int decodeASCII(byte[] src, int srcOff, char[] dst, int dstOff, int len) return String.decodeASCII(src, srcOff, dst, dstOff, len); } - public int compressCharsToBytes(char[] src, int srcOff, byte[] dst, int dstOff, int len) { - return StringUTF16.compress(src, srcOff, dst, dstOff, len); + public int encodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len) { + return StringCoding.implEncodeAsciiArray(src, srcOff, dst, dstOff, len); } public void setCause(Throwable t, Throwable cause) { diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 513b89ccd7bc4..b68490ad7a397 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -357,11 +357,13 @@ public interface JavaLangAccess { int decodeASCII(byte[] src, int srcOff, char[] dst, int dstOff, int len); /** - * Compressed copy char[] -> byte[] + * Encodes ASCII codepoints as possible from the source array into + * the destination byte array, assuming that the encoding is ASCII + * compatible * * @return the number of bytes successfully encoded, or 0 if none */ - int compressCharsToBytes(char[] src, int srcOff, byte[] dst, int dstOff, int len); + int encodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len); /** * Set the cause of Throwable diff --git a/src/java.base/share/classes/sun/nio/cs/CESU_8.java b/src/java.base/share/classes/sun/nio/cs/CESU_8.java index b3dcebf53232d..f1fc69703c20c 100644 --- a/src/java.base/share/classes/sun/nio/cs/CESU_8.java +++ b/src/java.base/share/classes/sun/nio/cs/CESU_8.java @@ -76,11 +76,11 @@ private static final void updatePositions(Buffer src, int sp, dst.position(dp - dst.arrayOffset()); } + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static class Decoder extends CharsetDecoder implements ArrayDecoder { - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - private Decoder(Charset cs) { super(cs, 1.0f, 1.0f); } @@ -434,7 +434,6 @@ private static void to3Bytes(ByteBuffer dst, char c) { } private Surrogate.Parser sgp; - private char[] c2; private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { @@ -445,11 +444,12 @@ private CoderResult encodeArrayLoop(CharBuffer src, byte[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); - int dlASCII = dp + Math.min(sl - sp, dl - dp); - // ASCII only loop - while (dp < dlASCII && sa[sp] < '\u0080') - da[dp++] = (byte) sa[sp++]; + // Handle ASCII-only prefix + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + sp += n; + dp += n; + while (sp < sl) { char c = sa[sp]; if (c < 0x80) { @@ -549,11 +549,11 @@ protected final CoderResult encodeLoop(CharBuffer src, public int encode(char[] sa, int sp, int len, byte[] da) { int sl = sp + len; int dp = 0; - int dlASCII = dp + Math.min(len, da.length); - // ASCII only optimized loop - while (dp < dlASCII && sa[sp] < '\u0080') - da[dp++] = (byte) sa[sp++]; + // Handle ASCII-only prefix + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(len, da.length)); + sp += n; + dp += n; while (sp < sl) { char c = sa[sp++]; diff --git a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java index 04aeceb43d34a..8ff79d497fbc5 100644 --- a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java +++ b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java @@ -61,9 +61,9 @@ public CharsetEncoder newEncoder() { return new Encoder(this); } - private static class Decoder extends CharsetDecoder { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static class Decoder extends CharsetDecoder { private Decoder(Charset cs) { super(cs, 1.0f, 1.0f); @@ -159,6 +159,10 @@ private CoderResult encodeArrayLoop(CharBuffer src, assert (dp <= dl); dp = (dp <= dl ? dp : dl); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + sp += n; + dp += n; + try { while (sp < sl) { char c = sa[sp]; diff --git a/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/src/java.base/share/classes/sun/nio/cs/UTF_8.java index 683672825f29f..a27b4690f59f5 100644 --- a/src/java.base/share/classes/sun/nio/cs/UTF_8.java +++ b/src/java.base/share/classes/sun/nio/cs/UTF_8.java @@ -451,21 +451,11 @@ private CoderResult encodeArrayLoop(CharBuffer src, byte[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); - int slASCII = sp + Math.min(sl - sp, dl - dp); - - // ASCII only loop - // Since UTF8 is ASCII-compatible and encodes ASCII as single-byte, - // we can reuse StringUTF16.compress to speed up the actual copy of the ASCII data - int lastAscii = sp; - while (lastAscii < slASCII && sa[lastAscii] < '\u0080') { - lastAscii++; - } - if (lastAscii > sp) { - int len = lastAscii - sp; - JLA.compressCharsToBytes(sa, sp, da, dp, len); - sp = lastAscii; - dp += len; - } + + // Handle ASCII-only prefix + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + sp += n; + dp += n; if (sp < sl) { return encodeArrayLoopSlow(src, sa, sp, sl, dst, da, dp, dl); From a248ee8c52b804e314bad6270f6a9f4e28aa8d1f Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Thu, 23 Sep 2021 12:34:58 +0200 Subject: [PATCH 03/15] Merge MacroAssembler methods --- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 152 ++------------------- src/hotspot/cpu/x86/macroAssembler_x86.hpp | 5 +- src/hotspot/cpu/x86/x86_32.ad | 8 +- src/hotspot/cpu/x86/x86_64.ad | 8 +- 4 files changed, 21 insertions(+), 152 deletions(-) diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 0ebec7162765e..9b2735c210208 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5414,7 +5414,7 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, BIND(L_exit); } -// encode char[] to byte[] in ISO_8859_1 +// encode char[] to byte[] in ISO_8859_1 or ASCII //@IntrinsicCandidate //private static int implEncodeISOArray(byte[] sa, int sp, //byte[] da, int dp, int len) { @@ -5427,138 +5427,7 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, // } // return i; //} -void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, - XMMRegister tmp1Reg, XMMRegister tmp2Reg, - XMMRegister tmp3Reg, XMMRegister tmp4Reg, - Register tmp5, Register result) { - - // rsi: src - // rdi: dst - // rdx: len - // rcx: tmp5 - // rax: result - ShortBranchVerifier sbv(this); - assert_different_registers(src, dst, len, tmp5, result); - Label L_done, L_copy_1_char, L_copy_1_char_exit; - - // set result - xorl(result, result); - // check for zero length - testl(len, len); - jcc(Assembler::zero, L_done); - - movl(result, len); - - // Setup pointers - lea(src, Address(src, len, Address::times_2)); // char[] - lea(dst, Address(dst, len, Address::times_1)); // byte[] - negptr(len); - - if (UseSSE42Intrinsics || UseAVX >= 2) { - Label L_copy_8_chars, L_copy_8_chars_exit; - Label L_chars_16_check, L_copy_16_chars, L_copy_16_chars_exit; - - if (UseAVX >= 2) { - Label L_chars_32_check, L_copy_32_chars, L_copy_32_chars_exit; - movl(tmp5, 0xff00ff00); // create mask to test for Unicode chars in vector - movdl(tmp1Reg, tmp5); - vpbroadcastd(tmp1Reg, tmp1Reg, Assembler::AVX_256bit); - jmp(L_chars_32_check); - - bind(L_copy_32_chars); - vmovdqu(tmp3Reg, Address(src, len, Address::times_2, -64)); - vmovdqu(tmp4Reg, Address(src, len, Address::times_2, -32)); - vpor(tmp2Reg, tmp3Reg, tmp4Reg, /* vector_len */ 1); - vptest(tmp2Reg, tmp1Reg); // check for Unicode chars in vector - jccb(Assembler::notZero, L_copy_32_chars_exit); - vpackuswb(tmp3Reg, tmp3Reg, tmp4Reg, /* vector_len */ 1); - vpermq(tmp4Reg, tmp3Reg, 0xD8, /* vector_len */ 1); - vmovdqu(Address(dst, len, Address::times_1, -32), tmp4Reg); - - bind(L_chars_32_check); - addptr(len, 32); - jcc(Assembler::lessEqual, L_copy_32_chars); - - bind(L_copy_32_chars_exit); - subptr(len, 16); - jccb(Assembler::greater, L_copy_16_chars_exit); - - } else if (UseSSE42Intrinsics) { - movl(tmp5, 0xff00ff00); // create mask to test for Unicode chars in vector - movdl(tmp1Reg, tmp5); - pshufd(tmp1Reg, tmp1Reg, 0); - jmpb(L_chars_16_check); - } - - bind(L_copy_16_chars); - if (UseAVX >= 2) { - vmovdqu(tmp2Reg, Address(src, len, Address::times_2, -32)); - vptest(tmp2Reg, tmp1Reg); - jcc(Assembler::notZero, L_copy_16_chars_exit); - vpackuswb(tmp2Reg, tmp2Reg, tmp1Reg, /* vector_len */ 1); - vpermq(tmp3Reg, tmp2Reg, 0xD8, /* vector_len */ 1); - } else { - if (UseAVX > 0) { - movdqu(tmp3Reg, Address(src, len, Address::times_2, -32)); - movdqu(tmp4Reg, Address(src, len, Address::times_2, -16)); - vpor(tmp2Reg, tmp3Reg, tmp4Reg, /* vector_len */ 0); - } else { - movdqu(tmp3Reg, Address(src, len, Address::times_2, -32)); - por(tmp2Reg, tmp3Reg); - movdqu(tmp4Reg, Address(src, len, Address::times_2, -16)); - por(tmp2Reg, tmp4Reg); - } - ptest(tmp2Reg, tmp1Reg); // check for Unicode chars in vector - jccb(Assembler::notZero, L_copy_16_chars_exit); - packuswb(tmp3Reg, tmp4Reg); - } - movdqu(Address(dst, len, Address::times_1, -16), tmp3Reg); - - bind(L_chars_16_check); - addptr(len, 16); - jcc(Assembler::lessEqual, L_copy_16_chars); - - bind(L_copy_16_chars_exit); - if (UseAVX >= 2) { - // clean upper bits of YMM registers - vpxor(tmp2Reg, tmp2Reg); - vpxor(tmp3Reg, tmp3Reg); - vpxor(tmp4Reg, tmp4Reg); - movdl(tmp1Reg, tmp5); - pshufd(tmp1Reg, tmp1Reg, 0); - } - subptr(len, 8); - jccb(Assembler::greater, L_copy_8_chars_exit); - - bind(L_copy_8_chars); - movdqu(tmp3Reg, Address(src, len, Address::times_2, -16)); - ptest(tmp3Reg, tmp1Reg); - jccb(Assembler::notZero, L_copy_8_chars_exit); - packuswb(tmp3Reg, tmp1Reg); - movq(Address(dst, len, Address::times_1, -8), tmp3Reg); - addptr(len, 8); - jccb(Assembler::lessEqual, L_copy_8_chars); - - bind(L_copy_8_chars_exit); - subptr(len, 8); - jccb(Assembler::zero, L_done); - } - - bind(L_copy_1_char); - load_unsigned_short(tmp5, Address(src, len, Address::times_2, 0)); - testl(tmp5, 0xff00); // check if Unicode char - jccb(Assembler::notZero, L_copy_1_char_exit); - movb(Address(dst, len, Address::times_1, 0), tmp5); - addptr(len, 1); - jccb(Assembler::less, L_copy_1_char); - - bind(L_copy_1_char_exit); - addptr(result, len); // len is negative count of not processed elements - - bind(L_done); -} - - // encode char[] to byte[] in ASCII + // //@IntrinsicCandidate //private static int implEncodeAsciiArray(char[] sa, int sp, // byte[] da, int dp, int len) { @@ -5571,10 +5440,10 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, // } // return i; //} -void MacroAssembler::encode_ascii_array(Register src, Register dst, Register len, +void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, XMMRegister tmp1Reg, XMMRegister tmp2Reg, XMMRegister tmp3Reg, XMMRegister tmp4Reg, - Register tmp5, Register result) { + Register tmp5, Register result, bool ascii) { // rsi: src // rdi: dst @@ -5585,6 +5454,9 @@ void MacroAssembler::encode_ascii_array(Register src, Register dst, Register len assert_different_registers(src, dst, len, tmp5, result); Label L_done, L_copy_1_char, L_copy_1_char_exit; + int mask = ascii ? 0xff80ff80 : 0xff00ff00; + int short_mask = ascii ? 0xff80 : 0xff00; + // set result xorl(result, result); // check for zero length @@ -5604,7 +5476,7 @@ void MacroAssembler::encode_ascii_array(Register src, Register dst, Register len if (UseAVX >= 2) { Label L_chars_32_check, L_copy_32_chars, L_copy_32_chars_exit; - movl(tmp5, 0xff80ff80); // create mask to test for non-ASCII chars in vector + movl(tmp5, mask); // create mask to test for Unicode or non-ASCII chars in vector movdl(tmp1Reg, tmp5); vpbroadcastd(tmp1Reg, tmp1Reg, Assembler::AVX_256bit); jmp(L_chars_32_check); @@ -5613,7 +5485,7 @@ void MacroAssembler::encode_ascii_array(Register src, Register dst, Register len vmovdqu(tmp3Reg, Address(src, len, Address::times_2, -64)); vmovdqu(tmp4Reg, Address(src, len, Address::times_2, -32)); vpor(tmp2Reg, tmp3Reg, tmp4Reg, /* vector_len */ 1); - vptest(tmp2Reg, tmp1Reg); // check for non-ASCII chars in vector + vptest(tmp2Reg, tmp1Reg); // check for Unicode or non-ASCII chars in vector jccb(Assembler::notZero, L_copy_32_chars_exit); vpackuswb(tmp3Reg, tmp3Reg, tmp4Reg, /* vector_len */ 1); vpermq(tmp4Reg, tmp3Reg, 0xD8, /* vector_len */ 1); @@ -5628,7 +5500,7 @@ void MacroAssembler::encode_ascii_array(Register src, Register dst, Register len jccb(Assembler::greater, L_copy_16_chars_exit); } else if (UseSSE42Intrinsics) { - movl(tmp5, 0xff80ff80); // create mask to test for non-ASCII chars in vector + movl(tmp5, mask); // create mask to test for Unicode or non-ASCII chars in vector movdl(tmp1Reg, tmp5); pshufd(tmp1Reg, tmp1Reg, 0); jmpb(L_chars_16_check); @@ -5652,7 +5524,7 @@ void MacroAssembler::encode_ascii_array(Register src, Register dst, Register len movdqu(tmp4Reg, Address(src, len, Address::times_2, -16)); por(tmp2Reg, tmp4Reg); } - ptest(tmp2Reg, tmp1Reg); // check for non-ascii chars in vector + ptest(tmp2Reg, tmp1Reg); // check for Unicode or non-ASCII chars in vector jccb(Assembler::notZero, L_copy_16_chars_exit); packuswb(tmp3Reg, tmp4Reg); } @@ -5690,7 +5562,7 @@ void MacroAssembler::encode_ascii_array(Register src, Register dst, Register len bind(L_copy_1_char); load_unsigned_short(tmp5, Address(src, len, Address::times_2, 0)); - testl(tmp5, 0xff80); // check if non-ASCII char + testl(tmp5, short_mask); // check if Unicode or non-ASCII char jccb(Assembler::notZero, L_copy_1_char_exit); movb(Address(dst, len, Address::times_1, 0), tmp5); addptr(len, 1); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index d10307e4044b8..da14e7bfe8a2b 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -1724,10 +1724,7 @@ class MacroAssembler: public Assembler { void encode_iso_array(Register src, Register dst, Register len, XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3, - XMMRegister tmp4, Register tmp5, Register result); - void encode_ascii_array(Register src, Register dst, Register len, - XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3, - XMMRegister tmp4, Register tmp5, Register result); + XMMRegister tmp4, Register tmp5, Register result, bool ascii); #ifdef _LP64 void add2_with_carry(Register dest_hi, Register dest_lo, Register src1, Register src2); diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 2e6a39ddcc0dc..4b7d56189dd37 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -12206,7 +12206,7 @@ instruct encode_iso_array(eSIRegP src, eDIRegP dst, eDXRegI len, ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, - $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register, false); %} ins_pipe( pipe_slow ); %} @@ -12220,9 +12220,9 @@ instruct encode_ascii_array(eSIRegP src, eDIRegP dst, eDXRegI len, format %{ "Encode array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} ins_encode %{ - __ encode_ascii_array($src$$Register, $dst$$Register, $len$$Register, - $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, - $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register, true); %} ins_pipe( pipe_slow ); %} diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 891e9da6640f8..b16b1ede1ea46 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -11777,7 +11777,7 @@ instruct encode_iso_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, - $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register, false); %} ins_pipe( pipe_slow ); %} @@ -11791,9 +11791,9 @@ instruct encode_ascii_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, format %{ "Encode array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} ins_encode %{ - __ encode_ascii_array($src$$Register, $dst$$Register, $len$$Register, - $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, - $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register, true); %} ins_pipe( pipe_slow ); %} From 25ba1722f91803f82a5266ad3e5aea5ed4cf8cbf Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Thu, 23 Sep 2021 13:45:34 +0200 Subject: [PATCH 04/15] Remove EncodeAsciiArray node and instead extend EncodeISOArray with an is_ascii predicate --- src/hotspot/cpu/x86/x86_32.ad | 4 +- src/hotspot/cpu/x86/x86_64.ad | 4 +- src/hotspot/share/adlc/formssel.cpp | 3 +- .../gc/shenandoah/c2/shenandoahSupport.cpp | 2 - src/hotspot/share/opto/c2compiler.cpp | 7 ++- src/hotspot/share/opto/classes.hpp | 1 - src/hotspot/share/opto/escape.cpp | 14 ++--- src/hotspot/share/opto/intrinsicnode.cpp | 20 ------- src/hotspot/share/opto/intrinsicnode.hpp | 21 ++----- src/hotspot/share/opto/library_call.cpp | 59 ++----------------- src/hotspot/share/opto/library_call.hpp | 3 +- src/hotspot/share/opto/loopTransform.cpp | 2 - src/hotspot/share/opto/macro.cpp | 2 - src/hotspot/share/opto/matcher.cpp | 5 +- 14 files changed, 28 insertions(+), 119 deletions(-) diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 4b7d56189dd37..4c723ebccd91a 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -12199,6 +12199,7 @@ instruct string_inflate_evex(Universe dummy, eSIRegP src, eDIRegP dst, eDXRegI l instruct encode_iso_array(eSIRegP src, eDIRegP dst, eDXRegI len, regD tmp1, regD tmp2, regD tmp3, regD tmp4, eCXRegI tmp5, eAXRegI result, eFlagsReg cr) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); @@ -12215,7 +12216,8 @@ instruct encode_iso_array(eSIRegP src, eDIRegP dst, eDXRegI len, instruct encode_ascii_array(eSIRegP src, eDIRegP dst, eDXRegI len, regD tmp1, regD tmp2, regD tmp3, regD tmp4, eCXRegI tmp5, eAXRegI result, eFlagsReg cr) %{ - match(Set result (EncodeAsciiArray src (Binary dst len))); + predicate(((EncodeISOArrayNode*)n)->is_ascii()); + match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); format %{ "Encode array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index b16b1ede1ea46..b5d820ebbf195 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -11770,6 +11770,7 @@ instruct string_inflate_evex(Universe dummy, rsi_RegP src, rdi_RegP dst, rdx_Reg instruct encode_iso_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, legRegD tmp1, legRegD tmp2, legRegD tmp3, legRegD tmp4, rcx_RegI tmp5, rax_RegI result, rFlagsReg cr) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); @@ -11786,7 +11787,8 @@ instruct encode_iso_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, instruct encode_ascii_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, legRegD tmp1, legRegD tmp2, legRegD tmp3, legRegD tmp4, rcx_RegI tmp5, rax_RegI result, rFlagsReg cr) %{ - match(Set result (EncodeAsciiArray src (Binary dst len))); + predicate(((EncodeISOArrayNode*)n)->is_ascii()); + match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); format %{ "Encode array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index ddb88d7cab2bd..3b244ceda1fd7 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -903,8 +903,7 @@ uint InstructForm::oper_input_base(FormDict &globals) { strcmp(_matrule->_rChild->_opType,"StrIndexOf")==0 || strcmp(_matrule->_rChild->_opType,"StrIndexOfChar")==0 || strcmp(_matrule->_rChild->_opType,"HasNegatives")==0 || - strcmp(_matrule->_rChild->_opType,"EncodeISOArray")==0 || - strcmp(_matrule->_rChild->_opType,"EncodeAsciiArray")==0)) { + strcmp(_matrule->_rChild->_opType,"EncodeISOArray")==0)) { // String.(compareTo/equals/indexOf) and Arrays.equals // and sun.nio.cs.iso8859_1$Encoder.EncodeISOArray // take 1 control and 1 memory edges. diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index f9c6996409c2c..5ba3828b2d3d7 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -567,8 +567,6 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { { { 2, ShenandoahLoad }, { 3, ShenandoahLoad } }, Op_EncodeISOArray, { { 2, ShenandoahLoad }, { 3, ShenandoahStore } }, - Op_EncodeAsciiArray, - { { 2, ShenandoahLoad }, { 3, ShenandoahStore } }, Op_HasNegatives, { { 2, ShenandoahLoad }, { -1, ShenandoahNone} }, Op_CastP2X, diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index f7f53e08e68fc..78156b1495c78 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -216,13 +216,14 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_copyMemory: if (StubRoutines::unsafe_arraycopy() == NULL) return false; break; + case vmIntrinsics::_encodeAsciiArray: +#if defined(PPC64) || defined(S390) + return false; // not yet implemented +#endif case vmIntrinsics::_encodeISOArray: case vmIntrinsics::_encodeByteISOArray: if (!Matcher::match_rule_supported(Op_EncodeISOArray)) return false; break; - case vmIntrinsics::_encodeAsciiArray: - if (!Matcher::match_rule_supported(Op_EncodeAsciiArray)) return false; - break; case vmIntrinsics::_hasNegatives: if (!Matcher::match_rule_supported(Op_HasNegatives)) return false; break; diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 709d72a36f39b..582ec51fbde80 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -165,7 +165,6 @@ macro(DivL) macro(DivMod) macro(DivModI) macro(DivModL) -macro(EncodeAsciiArray) macro(EncodeISOArray) macro(EncodeP) macro(EncodePKlass) diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 19fc7effea480..54d1d2f2ea586 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -621,7 +621,6 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de case Op_StrIndexOfChar: case Op_StrInflatedCopy: case Op_StrCompressedCopy: - case Op_EncodeAsciiArray: case Op_EncodeISOArray: { add_local_var(n, PointsToNode::ArgEscape); delayed_worklist->push(n); // Process it later. @@ -760,7 +759,6 @@ void ConnectionGraph::add_final_edges(Node *n) { case Op_StrIndexOfChar: case Op_StrInflatedCopy: case Op_StrCompressedCopy: - case Op_EncodeAsciiArray: case Op_EncodeISOArray: { // char[]/byte[] arrays passed to string intrinsic do not escape but // they are not scalar replaceable. Adjust escape state for them. @@ -2926,8 +2924,7 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra if (mem->is_LoadStore()) { adr = mem->in(MemNode::Address); } else { - assert(mem->Opcode() == Op_EncodeAsciiArray || - mem->Opcode() == Op_EncodeISOArray || + assert(mem->Opcode() == Op_EncodeISOArray || mem->Opcode() == Op_StrCompressedCopy, "sanity"); adr = mem->in(3); // Memory edge corresponds to destination array } @@ -3312,9 +3309,9 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, if (m->is_MergeMem()) { assert(mergemem_worklist.contains(m->as_MergeMem()), "EA: missing MergeMem node in the worklist"); } - } else if (use->Opcode() == Op_EncodeAsciiArray || use->Opcode() == Op_EncodeISOArray) { + } else if (use->Opcode() == Op_EncodeISOArray) { if (use->in(MemNode::Memory) == n || use->in(3) == n) { - // EncodeAsciiArray and EncodeISOArray overwrites destination array + // EncodeISOArray overwrites destination array memnode_worklist.append_if_missing(use); } } else { @@ -3394,7 +3391,6 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, continue; } } else if (n->Opcode() == Op_StrCompressedCopy || - n->Opcode() == Op_EncodeAsciiArray || n->Opcode() == Op_EncodeISOArray) { // get the memory projection n = n->find_out_with(Op_SCMemProj); @@ -3445,9 +3441,9 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, assert(use->in(MemNode::Memory) != n, "EA: missing memory path"); } else if (use->is_MergeMem()) { assert(mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist"); - } else if (use->Opcode() == Op_EncodeAsciiArray || use->Opcode() == Op_EncodeISOArray) { + } else if (use->Opcode() == Op_EncodeISOArray) { if (use->in(MemNode::Memory) == n || use->in(3) == n) { - // EncodeAscii/-ISOArray overwrites destination array + // EncodeISOArray overwrites destination array memnode_worklist.append_if_missing(use); } } else { diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index f11df9718d2f6..9f852dd637862 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -98,26 +98,6 @@ const Type* EncodeISOArrayNode::Value(PhaseGVN* phase) const { return bottom_type(); } -//============================================================================= -//------------------------------match_edge------------------------------------- -// Do not match memory edge -uint EncodeAsciiArrayNode::match_edge(uint idx) const { - return idx == 2 || idx == 3; // EncodeAsciiArray src (Binary dst len) -} - -//------------------------------Ideal------------------------------------------ -// Return a node which is more "ideal" than the current node. Strip out -// control copies -Node* EncodeAsciiArrayNode::Ideal(PhaseGVN* phase, bool can_reshape) { - return remove_dead_region(phase, can_reshape) ? this : NULL; -} - -//------------------------------Value------------------------------------------ -const Type* EncodeAsciiArrayNode::Value(PhaseGVN* phase) const { - if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; - return bottom_type(); -} - //------------------------------CopySign----------------------------------------- CopySignDNode* CopySignDNode::make(PhaseGVN& gvn, Node* in1, Node* in2) { return new CopySignDNode(in1, in2, gvn.makecon(TypeD::ZERO)); diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp index 60c9e7c8c74d6..cf90ec52b577c 100644 --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -170,23 +170,12 @@ class HasNegativesNode: public StrIntrinsicNode { //------------------------------EncodeISOArray-------------------------------- // encode char[] to byte[] in ISO_8859_1 class EncodeISOArrayNode: public Node { + bool ascii; public: - EncodeISOArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c): Node(control, arymem, s1, s2, c) {}; - virtual int Opcode() const; - virtual bool depends_only_on_test() const { return false; } - virtual const Type* bottom_type() const { return TypeInt::INT; } - virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } - virtual uint match_edge(uint idx) const; - virtual uint ideal_reg() const { return Op_RegI; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual const Type* Value(PhaseGVN* phase) const; -}; - -//------------------------------EncodeAsciiArray-------------------------------- -// encode char[] to byte[] in ASCII -class EncodeAsciiArrayNode: public Node { - public: - EncodeAsciiArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c): Node(control, arymem, s1, s2, c) {}; + EncodeISOArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c, bool ascii): + Node(control, arymem, s1, s2, c), ascii(ascii) { + }; + bool is_ascii() { return ascii; } virtual int Opcode() const; virtual bool depends_only_on_test() const { return false; } virtual const Type* bottom_type() const { return TypeInt::INT; } diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 633bfbdb55c63..3f5a861bba7af 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -591,9 +591,9 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_encodeISOArray: case vmIntrinsics::_encodeByteISOArray: - return inline_encodeISOArray(); + return inline_encodeISOArray(false); case vmIntrinsics::_encodeAsciiArray: - return inline_encodeAsciiArray(); + return inline_encodeISOArray(true); case vmIntrinsics::_updateCRC32: return inline_updateCRC32(); @@ -4884,8 +4884,8 @@ LibraryCallKit::tightly_coupled_allocation(Node* ptr) { } //-------------inline_encodeISOArray----------------------------------- -// encode char[] to byte[] in ISO_8859_1 -bool LibraryCallKit::inline_encodeISOArray() { +// encode char[] to byte[] in ISO_8859_1 or ASCII +bool LibraryCallKit::inline_encodeISOArray(bool ascii) { assert(callee()->signature()->size() == 5, "encodeISOArray has 5 parameters"); // no receiver since it is static method Node *src = argument(0); @@ -4920,56 +4920,7 @@ bool LibraryCallKit::inline_encodeISOArray() { // 'dst_start' points to dst array + scaled offset const TypeAryPtr* mtype = TypeAryPtr::BYTES; - Node* enc = new EncodeISOArrayNode(control(), memory(mtype), src_start, dst_start, length); - enc = _gvn.transform(enc); - Node* res_mem = _gvn.transform(new SCMemProjNode(enc)); - set_memory(res_mem, mtype); - set_result(enc); - clear_upper_avx(); - - return true; -} - -//-------------inline_encodeAsciiArray----------------------------------- -// encode char[] to byte[] in ASCII -bool LibraryCallKit::inline_encodeAsciiArray() { - assert(callee()->signature()->size() == 5, "encodeAsciiArray has 5 parameters"); - // no receiver since it is static method - Node *src = argument(0); - Node *src_offset = argument(1); - Node *dst = argument(2); - Node *dst_offset = argument(3); - Node *length = argument(4); - - src = must_be_not_null(src, true); - dst = must_be_not_null(dst, true); - - const Type* src_type = src->Value(&_gvn); - const Type* dst_type = dst->Value(&_gvn); - const TypeAryPtr* top_src = src_type->isa_aryptr(); - const TypeAryPtr* top_dest = dst_type->isa_aryptr(); - if (top_src == NULL || top_src->klass() == NULL || - top_dest == NULL || top_dest->klass() == NULL) { - // failed array check - assert(false, "failed NULL checks"); - return false; - } - - // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType dst_elem = dst_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - if (src_elem != T_CHAR || dst_elem != T_BYTE) { - assert(false, "failed type checks"); - return false; - } - - Node* src_start = array_element_address(src, src_offset, T_CHAR); - Node* dst_start = array_element_address(dst, dst_offset, T_BYTE); - // 'src_start' points to src array + scaled offset - // 'dst_start' points to dst array + scaled offset - - const TypeAryPtr* mtype = TypeAryPtr::BYTES; - Node* enc = new EncodeAsciiArrayNode(control(), memory(mtype), src_start, dst_start, length); + Node* enc = new EncodeISOArrayNode(control(), memory(mtype), src_start, dst_start, length, ascii); enc = _gvn.transform(enc); Node* res_mem = _gvn.transform(new SCMemProjNode(enc)); set_memory(res_mem, mtype); diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 304bc31191dd1..b3010f1cb5ba3 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -285,8 +285,7 @@ class LibraryCallKit : public GraphKit { Node* get_state_from_digest_object(Node *digestBase_object, const char* state_type); Node* get_digest_length_from_digest_object(Node *digestBase_object); Node* inline_digestBase_implCompressMB_predicate(int predicate); - bool inline_encodeISOArray(); - bool inline_encodeAsciiArray(); + bool inline_encodeISOArray(bool ascii); bool inline_updateCRC32(); bool inline_updateBytesCRC32(); bool inline_updateByteBufferCRC32(); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index ff5b81c983267..3ad5480cd6676 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -827,7 +827,6 @@ bool IdealLoopTree::policy_maximally_unroll(PhaseIdealLoop* phase) const { case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_EncodeISOArray: - case Op_EncodeAsciiArray: case Op_AryEq: case Op_HasNegatives: { return false; @@ -962,7 +961,6 @@ bool IdealLoopTree::policy_unroll(PhaseIdealLoop *phase) { case Op_StrIndexOf: case Op_StrIndexOfChar: case Op_EncodeISOArray: - case Op_EncodeAsciiArray: case Op_AryEq: case Op_HasNegatives: { // Do not unroll a loop with String intrinsics code. diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 387454f52ef9f..41170e708ade8 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -241,7 +241,6 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me adr = mem->in(MemNode::Address); } else { assert(mem->Opcode() == Op_EncodeISOArray || - mem->Opcode() == Op_EncodeAsciiArray || mem->Opcode() == Op_StrCompressedCopy, "sanity"); adr = mem->in(3); // Destination array } @@ -417,7 +416,6 @@ Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type * } else if (val->Opcode() == Op_SCMemProj) { assert(val->in(0)->is_LoadStore() || val->in(0)->Opcode() == Op_EncodeISOArray || - val->in(0)->Opcode() == Op_EncodeAsciiArray || val->in(0)->Opcode() == Op_StrCompressedCopy, "sanity"); assert(false, "Object is not scalar replaceable if a LoadStore node accesses its field"); return NULL; diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index fb7715551f697..5b8fcb8ab607a 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -1038,7 +1038,6 @@ static void match_alias_type(Compile* C, Node* n, Node* m) { case Op_StrCompressedCopy: case Op_OnSpinWait: case Op_EncodeISOArray: - case Op_EncodeAsciiArray: nidx = Compile::AliasIdxTop; nat = NULL; break; @@ -2222,7 +2221,6 @@ bool Matcher::find_shared_visit(MStack& mstack, Node* n, uint opcode, bool& mem_ case Op_StrInflatedCopy: case Op_StrCompressedCopy: case Op_EncodeISOArray: - case Op_EncodeAsciiArray: case Op_FmaD: case Op_FmaF: case Op_FmaVD: @@ -2371,8 +2369,7 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) { } case Op_StrCompressedCopy: case Op_StrInflatedCopy: - case Op_EncodeISOArray: - case Op_EncodeAsciiArray: { + case Op_EncodeISOArray: { // Restructure into a binary tree for Matching. Node* pair = new BinaryNode(n->in(3), n->in(4)); n->set_req(3, pair); From 6d4698459f986319db39c620079cfba3dfc1b4fb Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Thu, 23 Sep 2021 16:18:27 +0200 Subject: [PATCH 05/15] Fix indentation --- src/hotspot/share/opto/macro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 41170e708ade8..630452179ebfa 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -241,7 +241,7 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me adr = mem->in(MemNode::Address); } else { assert(mem->Opcode() == Op_EncodeISOArray || - mem->Opcode() == Op_StrCompressedCopy, "sanity"); + mem->Opcode() == Op_StrCompressedCopy, "sanity"); adr = mem->in(3); // Destination array } const TypePtr* atype = adr->bottom_type()->is_ptr(); From 066609cc8a9567b6cb4dfb5ecf733b50c511cd11 Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Thu, 23 Sep 2021 16:19:07 +0200 Subject: [PATCH 06/15] Exclude encodeAsciiArray intrinsic on all non-X86 platforms --- src/hotspot/share/opto/c2compiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 78156b1495c78..63c548c88f988 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -217,7 +217,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt if (StubRoutines::unsafe_arraycopy() == NULL) return false; break; case vmIntrinsics::_encodeAsciiArray: -#if defined(PPC64) || defined(S390) +#if !defined(X86) return false; // not yet implemented #endif case vmIntrinsics::_encodeISOArray: From 11ebcb02df83baf88b069a3b1679f1e8c05ef8af Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Fri, 24 Sep 2021 00:51:34 +0200 Subject: [PATCH 07/15] Move and adapt defunct Test6896617 test to test more Charsets without using internal APIs; remove adhoc performance tests --- .../jtreg/compiler/codegen/Test6896617.java | 346 ------------------ .../string/TestEncodeIntrinsics.java | 248 +++++++++++++ 2 files changed, 248 insertions(+), 346 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/codegen/Test6896617.java create mode 100644 test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java diff --git a/test/hotspot/jtreg/compiler/codegen/Test6896617.java b/test/hotspot/jtreg/compiler/codegen/Test6896617.java deleted file mode 100644 index eddaa3231ea41..0000000000000 --- a/test/hotspot/jtreg/compiler/codegen/Test6896617.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright (c) 2013, 2020, 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. - */ - -/* - * @test - * @key randomness - * @bug 6896617 - * @summary Optimize sun.nio.cs.ISO_8859_1$Encode.encodeArrayLoop() with SSE instructions on x86 - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.base/sun.nio.cs - * java.management - * - * @ignore 8193479 - * @run main/othervm/timeout=1200 -Xbatch -Xmx256m compiler.codegen.Test6896617 - */ - -package compiler.codegen; - -import jdk.test.lib.Utils; - -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; -import java.nio.charset.CodingErrorAction; -import java.util.Arrays; -import java.util.Random; - -public class Test6896617 { - final static int SIZE = 256; - - public static void main(String[] args) { - String csn = "ISO-8859-1"; - Charset cs = Charset.forName(csn); - CharsetEncoder enc = cs.newEncoder(); - enc.onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); - CharsetDecoder dec = cs.newDecoder(); - dec.onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); - - byte repl = (byte)'?'; - enc.replaceWith(new byte[] { repl }); - - // Use internal API for tests. - sun.nio.cs.ArrayEncoder arrenc = (sun.nio.cs.ArrayEncoder)enc; - sun.nio.cs.ArrayDecoder arrdec = (sun.nio.cs.ArrayDecoder)dec; - - // Populate char[] with chars which can be encoded by ISO_8859_1 (<= 0xFF) - Random rnd = Utils.getRandomInstance(); - int maxchar = 0xFF; - char[] a = new char[SIZE]; - byte[] b = new byte[SIZE]; - char[] at = new char[SIZE]; - byte[] bt = new byte[SIZE]; - for (int i = 0; i < SIZE; i++) { - char c = (char) rnd.nextInt(maxchar); - if (!enc.canEncode(c)) { - System.out.printf("Something wrong: can't encode c=%03x\n", (int)c); - System.exit(97); - } - a[i] = c; - b[i] = (byte)c; - at[i] = (char)-1; - bt[i] = (byte)-1; - } - if (arrenc.encode(a, 0, SIZE, bt) != SIZE || !Arrays.equals(b, bt)) { - System.out.println("Something wrong: ArrayEncoder.encode failed"); - System.exit(97); - } - if (arrdec.decode(b, 0, SIZE, at) != SIZE || !Arrays.equals(a, at)) { - System.out.println("Something wrong: ArrayDecoder.decode failed"); - System.exit(97); - } - for (int i = 0; i < SIZE; i++) { - at[i] = (char)-1; - bt[i] = (byte)-1; - } - - ByteBuffer bb = ByteBuffer.wrap(b); - CharBuffer ba = CharBuffer.wrap(a); - ByteBuffer bbt = ByteBuffer.wrap(bt); - CharBuffer bat = CharBuffer.wrap(at); - if (!enc.encode(ba, bbt, true).isUnderflow() || !Arrays.equals(b, bt)) { - System.out.println("Something wrong: Encoder.encode failed"); - System.exit(97); - } - if (!dec.decode(bb, bat, true).isUnderflow() || !Arrays.equals(a, at)) { - System.out.println("Something wrong: Decoder.decode failed"); - System.exit(97); - } - for (int i = 0; i < SIZE; i++) { - at[i] = (char)-1; - bt[i] = (byte)-1; - } - - // Warm up - boolean failed = false; - int result = 0; - for (int i = 0; i < 10000; i++) { - result += arrenc.encode(a, 0, SIZE, bt); - result -= arrdec.decode(b, 0, SIZE, at); - } - for (int i = 0; i < 10000; i++) { - result += arrenc.encode(a, 0, SIZE, bt); - result -= arrdec.decode(b, 0, SIZE, at); - } - for (int i = 0; i < 10000; i++) { - result += arrenc.encode(a, 0, SIZE, bt); - result -= arrdec.decode(b, 0, SIZE, at); - } - if (result != 0 || !Arrays.equals(b, bt) || !Arrays.equals(a, at)) { - failed = true; - System.out.println("Failed: ArrayEncoder.encode char[" + SIZE + "] and ArrayDecoder.decode byte[" + SIZE + "]"); - } - for (int i = 0; i < SIZE; i++) { - at[i] = (char)-1; - bt[i] = (byte)-1; - } - - boolean is_underflow = true; - for (int i = 0; i < 10000; i++) { - ba.clear(); bb.clear(); bat.clear(); bbt.clear(); - boolean enc_res = enc.encode(ba, bbt, true).isUnderflow(); - boolean dec_res = dec.decode(bb, bat, true).isUnderflow(); - is_underflow = is_underflow && enc_res && dec_res; - } - for (int i = 0; i < SIZE; i++) { - at[i] = (char)-1; - bt[i] = (byte)-1; - } - for (int i = 0; i < 10000; i++) { - ba.clear(); bb.clear(); bat.clear(); bbt.clear(); - boolean enc_res = enc.encode(ba, bbt, true).isUnderflow(); - boolean dec_res = dec.decode(bb, bat, true).isUnderflow(); - is_underflow = is_underflow && enc_res && dec_res; - } - for (int i = 0; i < SIZE; i++) { - at[i] = (char)-1; - bt[i] = (byte)-1; - } - for (int i = 0; i < 10000; i++) { - ba.clear(); bb.clear(); bat.clear(); bbt.clear(); - boolean enc_res = enc.encode(ba, bbt, true).isUnderflow(); - boolean dec_res = dec.decode(bb, bat, true).isUnderflow(); - is_underflow = is_underflow && enc_res && dec_res; - } - if (!is_underflow || !Arrays.equals(b, bt) || !Arrays.equals(a, at)) { - failed = true; - System.out.println("Failed: Encoder.encode char[" + SIZE + "] and Decoder.decode byte[" + SIZE + "]"); - } - - // Test encoder with different source and destination sizes - System.out.println("Testing different source and destination sizes"); - for (int i = 1; i <= SIZE; i++) { - for (int j = 1; j <= SIZE; j++) { - bt = new byte[j]; - // very source's SIZE - result = arrenc.encode(a, 0, i, bt); - int l = Math.min(i, j); - if (result != l) { - failed = true; - System.out.println("Failed: encode char[" + i + "] to byte[" + j + "]: result = " + result + ", expected " + l); - } - for (int k = 0; k < l; k++) { - if (bt[k] != b[k]) { - failed = true; - System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[k]); - } - } - // very source's offset - int sz = SIZE - i + 1; - result = arrenc.encode(a, i-1, sz, bt); - l = Math.min(sz, j); - if (result != l) { - failed = true; - System.out.println("Failed: encode char[" + sz + "] to byte[" + j + "]: result = " + result + ", expected " + l); - } - for (int k = 0; k < l; k++) { - if (bt[k] != b[i+k-1]) { - failed = true; - System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[i+k-1]); - } - } - } - } - - // Test encoder with char > 0xFF - System.out.println("Testing big char"); - - byte orig = (byte)'A'; - bt = new byte[SIZE]; - for (int i = 1; i <= SIZE; i++) { - for (int j = 0; j < i; j++) { - a[j] += 0x100; - // make sure to replace a different byte - bt[j] = orig; - result = arrenc.encode(a, 0, i, bt); - if (result != i) { - failed = true; - System.out.println("Failed: encode char[" + i + "] to byte[" + i + "]: result = " + result + ", expected " + i); - } - if (bt[j] != repl) { - failed = true; - System.out.println("Failed: encoded replace byte[" + j + "] (" + bt[j] + ") != " + repl); - } - bt[j] = b[j]; // Restore to compare whole array - for (int k = 0; k < i; k++) { - if (bt[k] != b[k]) { - failed = true; - System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[k]); - } - } - a[j] -= 0x100; // Restore - } - } - - // Test sun.nio.cs.ISO_8859_1$Encode.encodeArrayLoop() performance. - - int itrs = Integer.getInteger("iterations", 1000000); - int size = Integer.getInteger("size", 256); - a = new char[size]; - b = new byte[size]; - bt = new byte[size]; - for (int i = 0; i < size; i++) { - char c = (char) rnd.nextInt(maxchar); - if (!enc.canEncode(c)) { - System.out.printf("Something wrong: can't encode c=%03x\n", (int)c); - System.exit(97); - } - a[i] = c; - b[i] = (byte)-1; - bt[i] = (byte)c; - } - ba = CharBuffer.wrap(a); - bb = ByteBuffer.wrap(b); - boolean enc_res = enc.encode(ba, bb, true).isUnderflow(); - if (!enc_res || !Arrays.equals(b, bt)) { - failed = true; - System.out.println("Failed 1: Encoder.encode char[" + size + "]"); - } - for (int i = 0; i < size; i++) { - b[i] = (byte)-1; - } - - // Make sure to recompile method if needed before performance run. - for (int i = 0; i < 10000; i++) { - ba.clear(); bb.clear(); - enc_res = enc_res && enc.encode(ba, bb, true).isUnderflow(); - } - for (int i = 0; i < size; i++) { - b[i] = (byte)-1; - } - for (int i = 0; i < 10000; i++) { - ba.clear(); bb.clear(); - enc_res = enc_res && enc.encode(ba, bb, true).isUnderflow(); - } - if (!enc_res || !Arrays.equals(b, bt)) { - failed = true; - System.out.println("Failed 2: Encoder.encode char[" + size + "]"); - } - for (int i = 0; i < size; i++) { - b[i] = (byte)-1; - } - - System.out.println("Testing ISO_8859_1$Encode.encodeArrayLoop() performance"); - long start = System.currentTimeMillis(); - for (int i = 0; i < itrs; i++) { - ba.clear(); bb.clear(); - enc_res = enc_res && enc.encode(ba, bb, true).isUnderflow(); - } - long end = System.currentTimeMillis(); - if (!enc_res || !Arrays.equals(b, bt)) { - failed = true; - System.out.println("Failed 3: Encoder.encode char[" + size + "]"); - } else { - System.out.println("size: " + size + " time: " + (end - start)); - } - - // Test sun.nio.cs.ISO_8859_1$Encode.encode() performance. - - // Make sure to recompile method if needed before performance run. - result = 0; - for (int i = 0; i < size; i++) { - b[i] = (byte)-1; - } - for (int i = 0; i < 10000; i++) { - result += arrenc.encode(a, 0, size, b); - } - for (int i = 0; i < size; i++) { - b[i] = (byte)-1; - } - for (int i = 0; i < 10000; i++) { - result += arrenc.encode(a, 0, size, b); - } - if (result != size*20000 || !Arrays.equals(b, bt)) { - failed = true; - System.out.println("Failed 1: ArrayEncoder.encode char[" + SIZE + "]"); - } - for (int i = 0; i < size; i++) { - b[i] = (byte)-1; - } - - System.out.println("Testing ISO_8859_1$Encode.encode() performance"); - result = 0; - start = System.currentTimeMillis(); - for (int i = 0; i < itrs; i++) { - result += arrenc.encode(a, 0, size, b); - } - end = System.currentTimeMillis(); - if (!Arrays.equals(b, bt)) { - failed = true; - System.out.println("Failed 2: ArrayEncoder.encode char[" + size + "]"); - } else { - System.out.println("size: " + size + " time: " + (end - start)); - } - - if (failed) { - System.out.println("FAILED"); - System.exit(97); - } - System.out.println("PASSED"); - } -} diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java new file mode 100644 index 0000000000000..e7f129971c1bc --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2013, 2021, 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. + */ + +/* + * @test + * @key randomness + * @bug 6896617 + * @summary Verify potentially intrinsified encoders behave well before and after compilation + * @library /test/lib + * + * @run main/othervm/timeout=1200 --add-opens=java.base/sun.nio.cs=ALL-UNNAMED -Xbatch -Xmx256m compiler.intrinsics.string.TestEncodeIntrinsics + */ + +package compiler.intrinsics.string; + +import jdk.test.lib.Utils; + +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.util.Arrays; +import java.util.Random; + +public class TestEncodeIntrinsics { + final static int SIZE = 256; + + public static void main(String[] args) { + + test("ISO-8859-1", false); + test("UTF-8", true); + test("US-ASCII", true); + test("CESU-8", true); + } + + private static void test(String csn, boolean asciiOnly) { + try { + System.out.println("Testing " + csn); + Charset cs = Charset.forName(csn); + CharsetEncoder enc = cs.newEncoder(); + enc.onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + CharsetDecoder dec = cs.newDecoder(); + dec.onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + + byte repl = (byte) '?'; + enc.replaceWith(new byte[]{repl}); + + // Populate char[] with chars which can be encoded by ISO_8859_1 (<= 0xFF) + Random rnd = Utils.getRandomInstance(); + int maxchar = asciiOnly ? 0x7F : 0xFF; + char[] a = new char[SIZE]; + byte[] b = new byte[SIZE]; + char[] at = new char[SIZE]; + byte[] bt = new byte[SIZE]; + for (int i = 0; i < SIZE; i++) { + char c = (char) rnd.nextInt(maxchar); + if (!enc.canEncode(c)) { + System.out.printf("Something wrong: can't encode c=%03x\n", (int) c); + System.exit(97); + } + a[i] = c; + b[i] = (byte) c; + at[i] = (char) -1; + bt[i] = (byte) -1; + } + + Method encodeArray = null; + if (csn.equals("ISO-8859-1")) { + // Use internal API for tests + encodeArray = enc.getClass().getDeclaredMethod("encodeISOArray", + char[].class, int.class, byte[].class, int.class, int.class); + encodeArray.setAccessible(true); + if ((int) encodeArray.invoke(enc, a, 0, bt, 0, SIZE) != SIZE || !Arrays.equals(b, bt)) { + System.out.println("Something wrong: ArrayEncoder.encode failed"); + System.exit(97); + } + for (int i = 0; i < SIZE; i++) { + at[i] = (char) -1; + } + } + + ByteBuffer bb = ByteBuffer.wrap(b); + CharBuffer ba = CharBuffer.wrap(a); + ByteBuffer bbt = ByteBuffer.wrap(bt); + CharBuffer bat = CharBuffer.wrap(at); + if (!enc.encode(ba, bbt, true).isUnderflow() || !Arrays.equals(b, bt)) { + System.out.println("Something wrong: Encoder.encode failed"); + System.exit(97); + } + if (!dec.decode(bb, bat, true).isUnderflow() || !Arrays.equals(a, at)) { + System.out.println("Something wrong: Decoder.decode failed (a == at: " + !Arrays.equals(a, at) + ")"); + System.exit(97); + } + for (int i = 0; i < SIZE; i++) { + at[i] = (char) -1; + bt[i] = (byte) -1; + } + + // Warm up + boolean failed = false; + + if (csn.equals("ISO-8859-1")) { + for (int i = 0; i < 10000; i++) { + failed |= (int) encodeArray.invoke(enc, a, 0, bt, 0, SIZE) != SIZE; + } + for (int i = 0; i < 10000; i++) { + failed |= (int) encodeArray.invoke(enc, a, 0, bt, 0, SIZE) != SIZE; + } + for (int i = 0; i < 10000; i++) { + failed |= (int) encodeArray.invoke(enc, a, 0, bt, 0, SIZE) != SIZE; + } + if (failed || !Arrays.equals(b, bt)) { + failed = true; + System.out.println("Failed: ISO_8859_1$Encoder.encode char[" + SIZE + "]"); + } + } + + for (int i = 0; i < SIZE; i++) { + at[i] = (char) -1; + bt[i] = (byte) -1; + } + + boolean is_underflow = true; + for (int i = 0; i < 10000; i++) { + ba.clear(); + bb.clear(); + bat.clear(); + bbt.clear(); + boolean enc_res = enc.encode(ba, bbt, true).isUnderflow(); + boolean dec_res = dec.decode(bb, bat, true).isUnderflow(); + is_underflow = is_underflow && enc_res && dec_res; + } + for (int i = 0; i < SIZE; i++) { + at[i] = (char) -1; + bt[i] = (byte) -1; + } + for (int i = 0; i < 10000; i++) { + ba.clear(); + bb.clear(); + bat.clear(); + bbt.clear(); + boolean enc_res = enc.encode(ba, bbt, true).isUnderflow(); + boolean dec_res = dec.decode(bb, bat, true).isUnderflow(); + is_underflow = is_underflow && enc_res && dec_res; + } + for (int i = 0; i < SIZE; i++) { + at[i] = (char) -1; + bt[i] = (byte) -1; + } + for (int i = 0; i < 10000; i++) { + ba.clear(); + bb.clear(); + bat.clear(); + bbt.clear(); + boolean enc_res = enc.encode(ba, bbt, true).isUnderflow(); + boolean dec_res = dec.decode(bb, bat, true).isUnderflow(); + is_underflow = is_underflow && enc_res && dec_res; + } + if (!is_underflow) { + failed = true; + System.out.println("Failed: got a non-underflow"); + } + if (!Arrays.equals(b, bt)) { + failed = true; + System.out.println("Failed: b != bt"); + } + if (!Arrays.equals(a, at)) { + failed = true; + System.out.println("Failed: a != at"); + } + + // Test encoder with char > 0xFF + System.out.println("Testing big char"); + + byte orig = (byte) 'A'; + bt = new byte[SIZE + 10]; // add some spare room to deal with encoding multi-byte + ba = CharBuffer.wrap(a); + bbt = ByteBuffer.wrap(bt); + for (int i = 1; i <= SIZE; i++) { + for (int j = 0; j < i; j++) { + char bigChar = (char)(ba.get(j) + 0x100); + ba.put(j, bigChar); + // make sure to replace a different byte + bbt.put(j, orig); + ba.clear(); + ba.limit(i); + bbt.clear(); + if (!enc.encode(ba, bbt, true).isUnderflow()) { + failed = true; + System.out.println("Failed: encode char[" + i + "] to byte[" + i + "]: expected underflow"); + } + if (bt[j] == b[j] && b[j] != repl) { // b[j] can be equal to repl; ignore + failed = true; + System.out.println("Failed: different byte expected at pos bt[" + j + "]"); + } + if (!enc.canEncode(bigChar) && bt[j] != repl) { + failed = true; + System.out.println("Failed: encoded replace byte[" + j + "] (" + bt[j] + ") != " + repl); + } + + // Check that all bytes prior to the replaced one was encoded properly + for (int k = 0; k < j; k++) { + if (bbt.get(k) != b[k]) { + failed = true; + System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[k]); + } + } + ba.put(j, (char)(ba.get(j) - 0x100)); // Restore + } + } + + if (failed) { + System.out.println("FAILED"); + System.exit(97); + } + System.out.println("PASSED"); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("FAILED"); + System.exit(97); + } + } +} From b74fdb8c0eefe044dd1510379bfa6fa3736d754b Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Fri, 24 Sep 2021 01:06:36 +0200 Subject: [PATCH 08/15] Add @bug id to test --- .../jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java index e7f129971c1bc..e114a97586e4a 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java @@ -24,7 +24,7 @@ /* * @test * @key randomness - * @bug 6896617 + * @bug 6896617 8274242 * @summary Verify potentially intrinsified encoders behave well before and after compilation * @library /test/lib * From 8edc228f64fda449b7e6c65858241d7c3571bd4d Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Fri, 24 Sep 2021 17:48:33 +0200 Subject: [PATCH 09/15] Freshen up CharsetEncodeDecode micro (still only tests ASCII), optimize ASCII-compatible SingleByte (e.g. ISO-8859-15) --- .../share/classes/sun/nio/cs/SingleByte.java | 12 +++++++++--- .../openjdk/bench/java/nio/CharsetEncodeDecode.java | 8 +++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/src/java.base/share/classes/sun/nio/cs/SingleByte.java index 88f8954844424..748659b323f15 100644 --- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.java @@ -49,11 +49,11 @@ private static final CoderResult withResult(CoderResult cr, return cr; } + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + public static final class Decoder extends CharsetDecoder implements ArrayDecoder { - private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - private final char[] b2c; private final boolean isASCIICompatible; private final boolean isLatin1Decodable; @@ -214,8 +214,14 @@ private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { byte[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); - int len = Math.min(dl - dp, sl - sp); + int len = Math.min(dl - dp, sl - sp); + if (isASCIICompatible) { + int n = JLA.encodeASCII(sa, sp, da, dp, len); + sp += n; + dp += n; + len -= n; + } while (len-- > 0) { char c = sa[sp]; int b = encode(c); diff --git a/test/micro/org/openjdk/bench/java/nio/CharsetEncodeDecode.java b/test/micro/org/openjdk/bench/java/nio/CharsetEncodeDecode.java index 51ad531e685a3..6e129a5466e89 100644 --- a/test/micro/org/openjdk/bench/java/nio/CharsetEncodeDecode.java +++ b/test/micro/org/openjdk/bench/java/nio/CharsetEncodeDecode.java @@ -24,12 +24,15 @@ import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -45,8 +48,11 @@ * char and byte arrays. */ @BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @OutputTimeUnit(TimeUnit.MICROSECONDS) @State(Scope.Thread) +@Fork(3) public class CharsetEncodeDecode { private byte[] BYTES; @@ -55,7 +61,7 @@ public class CharsetEncodeDecode { private CharsetEncoder encoder; private CharsetDecoder decoder; - @Param({"BIG5", "ISO-8859-15", "ASCII", "UTF-16"}) + @Param({"UTF-8", "BIG5", "ISO-8859-15", "ASCII", "UTF-16"}) private String type; @Param("16384") From 12ab6ff53efaf31f4c8a956a1035a5bbe1d50c5a Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Mon, 27 Sep 2021 13:33:40 +0200 Subject: [PATCH 10/15] Tobias review --- src/hotspot/cpu/x86/x86_32.ad | 8 ++++---- src/hotspot/cpu/x86/x86_64.ad | 4 ++-- src/hotspot/share/opto/intrinsicnode.hpp | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 4c723ebccd91a..18d213cc31a04 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -12203,7 +12203,7 @@ instruct encode_iso_array(eSIRegP src, eDIRegP dst, eDXRegI len, match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); - format %{ "Encode array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} + format %{ "Encode iso array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, @@ -12214,13 +12214,13 @@ instruct encode_iso_array(eSIRegP src, eDIRegP dst, eDXRegI len, // encode char[] to byte[] in ASCII instruct encode_ascii_array(eSIRegP src, eDIRegP dst, eDXRegI len, - regD tmp1, regD tmp2, regD tmp3, regD tmp4, - eCXRegI tmp5, eAXRegI result, eFlagsReg cr) %{ + regD tmp1, regD tmp2, regD tmp3, regD tmp4, + eCXRegI tmp5, eAXRegI result, eFlagsReg cr) %{ predicate(((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); - format %{ "Encode array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} + format %{ "Encode ascii array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index b5d820ebbf195..14671c39640c1 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -11774,7 +11774,7 @@ instruct encode_iso_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); - format %{ "Encode array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} + format %{ "Encode iso array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, @@ -11791,7 +11791,7 @@ instruct encode_ascii_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); - format %{ "Encode array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} + format %{ "Encode ascii array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp index cf90ec52b577c..ab8a834bb28ad 100644 --- a/src/hotspot/share/opto/intrinsicnode.hpp +++ b/src/hotspot/share/opto/intrinsicnode.hpp @@ -168,13 +168,13 @@ class HasNegativesNode: public StrIntrinsicNode { //------------------------------EncodeISOArray-------------------------------- -// encode char[] to byte[] in ISO_8859_1 +// encode char[] to byte[] in ISO_8859_1 or ASCII class EncodeISOArrayNode: public Node { bool ascii; public: - EncodeISOArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c, bool ascii): - Node(control, arymem, s1, s2, c), ascii(ascii) { - }; + EncodeISOArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c, bool ascii) + : Node(control, arymem, s1, s2, c), ascii(ascii) {} + bool is_ascii() { return ascii; } virtual int Opcode() const; virtual bool depends_only_on_test() const { return false; } From 9800a99ad30dc9443c62fd34d015202360f96b1e Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Mon, 27 Sep 2021 14:07:22 +0200 Subject: [PATCH 11/15] Add Matcher predicate to avoid changing shared code as non-x86 platforms implements support for the _encodeAsciiArray intrinsic --- src/hotspot/cpu/aarch64/matcher_aarch64.hpp | 3 +++ src/hotspot/cpu/arm/matcher_arm.hpp | 3 +++ src/hotspot/cpu/ppc/matcher_ppc.hpp | 2 ++ src/hotspot/cpu/s390/matcher_s390.hpp | 3 +++ src/hotspot/cpu/x86/matcher_x86.hpp | 3 +++ src/hotspot/share/opto/c2compiler.cpp | 5 ++--- 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp index 0a7f14fa23bbd..f2f55e504accd 100644 --- a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp @@ -160,4 +160,7 @@ return true; } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = false; + #endif // CPU_AARCH64_MATCHER_AARCH64_HPP diff --git a/src/hotspot/cpu/arm/matcher_arm.hpp b/src/hotspot/cpu/arm/matcher_arm.hpp index 56f77409a89e0..7552b014c061e 100644 --- a/src/hotspot/cpu/arm/matcher_arm.hpp +++ b/src/hotspot/cpu/arm/matcher_arm.hpp @@ -152,4 +152,7 @@ return false; } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = false; + #endif // CPU_ARM_MATCHER_ARM_HPP diff --git a/src/hotspot/cpu/ppc/matcher_ppc.hpp b/src/hotspot/cpu/ppc/matcher_ppc.hpp index d1bb8b21dbf16..0e2bafcc33cca 100644 --- a/src/hotspot/cpu/ppc/matcher_ppc.hpp +++ b/src/hotspot/cpu/ppc/matcher_ppc.hpp @@ -161,5 +161,7 @@ return VM_Version::has_fcfids(); } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = false; #endif // CPU_PPC_MATCHER_PPC_HPP diff --git a/src/hotspot/cpu/s390/matcher_s390.hpp b/src/hotspot/cpu/s390/matcher_s390.hpp index bc6956c445d3a..d95e3c17f4337 100644 --- a/src/hotspot/cpu/s390/matcher_s390.hpp +++ b/src/hotspot/cpu/s390/matcher_s390.hpp @@ -149,4 +149,7 @@ return true; } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = false; + #endif // CPU_S390_MATCHER_S390_HPP diff --git a/src/hotspot/cpu/x86/matcher_x86.hpp b/src/hotspot/cpu/x86/matcher_x86.hpp index f8c675c87bda7..bc5a3b7aca724 100644 --- a/src/hotspot/cpu/x86/matcher_x86.hpp +++ b/src/hotspot/cpu/x86/matcher_x86.hpp @@ -192,4 +192,7 @@ return true; } + // Implements a variant of EncodeISOArrayNode that encode ASCII only + static const bool supports_encode_ascii_array = true; + #endif // CPU_X86_MATCHER_X86_HPP diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 63c548c88f988..27dc678a5ad72 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -217,9 +217,8 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt if (StubRoutines::unsafe_arraycopy() == NULL) return false; break; case vmIntrinsics::_encodeAsciiArray: -#if !defined(X86) - return false; // not yet implemented -#endif + if (!Matcher::match_rule_supported(Op_EncodeISOArray) && !Matcher::supports_encode_ascii_array) return false; + break; case vmIntrinsics::_encodeISOArray: case vmIntrinsics::_encodeByteISOArray: if (!Matcher::match_rule_supported(Op_EncodeISOArray)) return false; From 47633cbaf8d739565c3d7320a649fe8065baaa2c Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Tue, 28 Sep 2021 12:06:12 +0200 Subject: [PATCH 12/15] Fix logic error --- src/hotspot/share/opto/c2compiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 27dc678a5ad72..c5949ed57f48d 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -217,7 +217,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt if (StubRoutines::unsafe_arraycopy() == NULL) return false; break; case vmIntrinsics::_encodeAsciiArray: - if (!Matcher::match_rule_supported(Op_EncodeISOArray) && !Matcher::supports_encode_ascii_array) return false; + if (!Matcher::match_rule_supported(Op_EncodeISOArray) || !Matcher::supports_encode_ascii_array) return false; break; case vmIntrinsics::_encodeISOArray: case vmIntrinsics::_encodeByteISOArray: From b4a5d105e4b49b369427702ec5310df62abe1eda Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Tue, 28 Sep 2021 13:44:20 +0200 Subject: [PATCH 13/15] Add fail-safe predicate to all encode_iso_array to only match non-ASCII EncodeISOArrayNodes --- src/hotspot/cpu/aarch64/aarch64.ad | 1 + src/hotspot/cpu/ppc/ppc.ad | 1 + src/hotspot/cpu/s390/s390.ad | 1 + 3 files changed, 3 insertions(+) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 9d36a89329421..b4ab48e8ee12f 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -16855,6 +16855,7 @@ instruct encode_iso_array(iRegP_R2 src, iRegP_R1 dst, iRegI_R3 len, vRegD_V2 Vtmp3, vRegD_V3 Vtmp4, iRegI_R0 result, rFlagsReg cr) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(USE_KILL src, USE_KILL dst, USE_KILL len, KILL Vtmp1, KILL Vtmp2, KILL Vtmp3, KILL Vtmp4, KILL cr); diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 9a883c7231082..ddb6e04918ab6 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -12789,6 +12789,7 @@ instruct has_negatives(rarg1RegP ary1, iRegIsrc len, iRegIdst result, iRegLdst t // encode char[] to byte[] in ISO_8859_1 instruct encode_iso_array(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst result, iRegLdst tmp1, iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, iRegLdst tmp5, regCTR ctr, flagsRegCR0 cr0) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, USE_KILL src, USE_KILL dst, KILL ctr, KILL cr0); diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index cd22b795886d7..63004f8e26343 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -10282,6 +10282,7 @@ instruct has_negatives(rarg5RegP ary1, iRegI len, iRegI result, roddRegI oddReg, // encode char[] to byte[] in ISO_8859_1 instruct encode_iso_array(iRegP src, iRegP dst, iRegI result, iRegI len, iRegI tmp, flagsReg cr) %{ + predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); effect(TEMP_DEF result, TEMP tmp, KILL cr); // R0, R1 are killed, too. ins_cost(300); From 4ad9c08ac90bdd2518ea111bd0cb1feeb8e0cc07 Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Wed, 29 Sep 2021 14:30:35 +0200 Subject: [PATCH 14/15] Clean up and make TestEncodeIntrinsics fail on the particular scenario where the ISO intrinsic was used in place of the ASCII-only intrinsic --- .../intrinsics/string/TestEncodeIntrinsics.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java index e114a97586e4a..aa95b4994b18a 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java @@ -197,16 +197,16 @@ private static void test(String csn, boolean asciiOnly) { // Test encoder with char > 0xFF System.out.println("Testing big char"); - byte orig = (byte) 'A'; bt = new byte[SIZE + 10]; // add some spare room to deal with encoding multi-byte ba = CharBuffer.wrap(a); bbt = ByteBuffer.wrap(bt); for (int i = 1; i <= SIZE; i++) { for (int j = 0; j < i; j++) { - char bigChar = (char)(ba.get(j) + 0x100); - ba.put(j, bigChar); - // make sure to replace a different byte - bbt.put(j, orig); + char bigChar = (char)((asciiOnly ? 0x7F : 0xFF) + 1 + rnd.nextInt(0x100)); + char aOrig = a[j]; + a[j] = bigChar; + // make sure to replace with a different byte + bt[j] = (byte)(bt[j] + 1); ba.clear(); ba.limit(i); bbt.clear(); @@ -225,12 +225,12 @@ private static void test(String csn, boolean asciiOnly) { // Check that all bytes prior to the replaced one was encoded properly for (int k = 0; k < j; k++) { - if (bbt.get(k) != b[k]) { + if (bt[k] != b[k]) { failed = true; System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[k]); } } - ba.put(j, (char)(ba.get(j) - 0x100)); // Restore + a[j] = aOrig; // Restore } } From ebe509ef400e7ed982b2ae409e372d16d812ffeb Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Wed, 29 Sep 2021 14:47:12 +0200 Subject: [PATCH 15/15] Clean up some stale test comments --- .../jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java index aa95b4994b18a..38a516e7521a6 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java @@ -71,6 +71,7 @@ private static void test(String csn, boolean asciiOnly) { enc.replaceWith(new byte[]{repl}); // Populate char[] with chars which can be encoded by ISO_8859_1 (<= 0xFF) + // - or ASCII (<= 0x7F) if requested Random rnd = Utils.getRandomInstance(); int maxchar = asciiOnly ? 0x7F : 0xFF; char[] a = new char[SIZE]; @@ -194,7 +195,7 @@ private static void test(String csn, boolean asciiOnly) { System.out.println("Failed: a != at"); } - // Test encoder with char > 0xFF + // Test encoder with chars outside of the range the intrinsic deals with System.out.println("Testing big char"); bt = new byte[SIZE + 10]; // add some spare room to deal with encoding multi-byte