diff --git a/src/hotspot/cpu/s390/assembler_s390.hpp b/src/hotspot/cpu/s390/assembler_s390.hpp index cf80d164fafe6..f472af134a358 100644 --- a/src/hotspot/cpu/s390/assembler_s390.hpp +++ b/src/hotspot/cpu/s390/assembler_s390.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2023 SAP SE. All rights reserved. + * Copyright (c) 2016, 2024 SAP SE. 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 @@ -986,6 +986,9 @@ class Assembler : public AbstractAssembler { #define BCR_ZOPC (unsigned int)(7 << 8) #define BALR_ZOPC (unsigned int)(5 << 8) #define BASR_ZOPC (unsigned int)(13 << 8) +#define BCT_ZOPC (unsigned int)(70 << 24) +#define BCTR_ZOPC (unsigned int)(6 << 8) +#define BCTG_ZOPC (unsigned int)(227L << 40 | 70) #define BCTGR_ZOPC (unsigned long)(0xb946 << 16) // Absolute #define BC_ZOPC (unsigned int)(71 << 24) @@ -1887,7 +1890,14 @@ class Assembler : public AbstractAssembler { //inline void z_brcl(branch_condition i1, int64_t i2); // branch i1 ? pc = pc + i2_imm32 inline void z_brcl(branch_condition i1, address a); // branch i1 ? pc = a inline void z_brcl(branch_condition i1, Label& L); // branch i1 ? pc = Label - inline void z_bctgr(Register r1, Register r2); // branch on count r1 -= 1; (r1!=0) ? pc = r2 ; r1 is int64 + + // branch on count Instructions + inline void z_bct( Register r1, int64_t d2, Register x2, Register b2); // branch on count r1 -= 1; (r1!=0) ? pc = (d2_uimm12+x2+b2) ; r1 is int32 + inline void z_bct( Register r1, const Address &a); // branch on count r1 -= 1; (r1!=0) ? pc = *(a); r1 is int32 + inline void z_bctr( Register r1, Register r2); // branch on count r1 -= 1; (r1!=0) ? pc = r2 ; r1 is int32 + inline void z_bctgr(Register r1, Register r2); // branch on count r1 -= 1; (r1!=0) ? pc = r2 ; r1 is int64 + inline void z_bctg( Register r1, const Address &a); // branch on count r1 -= 1; (r1!=0) ? pc = *(a); r1 is int64 + inline void z_bctg( Register r1, int64_t d2, Register x2, Register b2); // branch on count r1 -= 1; (r1!=0) ? pc = (d2_imm20+x2+b2) ; r1 is int64 // branch unconditional / always inline void z_br(Register r2); // branch to r2, nop if r2 == Z_R0 @@ -3061,6 +3071,10 @@ class Assembler : public AbstractAssembler { inline void z_braz(Label& L); inline void z_brnp(Label& L); + // Branch on count; + inline void z_bct( Register r1, int64_t d2, Register b2); + inline void z_bctg(Register r1, int64_t d2, Register b2); + inline void z_btrue( Label& L); inline void z_bfalse(Label& L); diff --git a/src/hotspot/cpu/s390/assembler_s390.inline.hpp b/src/hotspot/cpu/s390/assembler_s390.inline.hpp index 126dd83ee22f4..51b2cbe0a3e8a 100644 --- a/src/hotspot/cpu/s390/assembler_s390.inline.hpp +++ b/src/hotspot/cpu/s390/assembler_s390.inline.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2023 SAP SE. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 SAP SE. 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 @@ -724,7 +724,14 @@ inline void Assembler::z_bcr( branch_condition m1, Register r2) { emit_16( BCR_Z inline void Assembler::z_brc( branch_condition i1, int64_t i2) { emit_32( BRC_ZOPC | uimm4(i1, 8, 32) | simm16(i2, 16, 32)); } inline void Assembler::z_brc( branch_condition i1, address a) { emit_32( BRC_ZOPC | uimm4(i1, 8, 32) | simm16(RelAddr::pcrel_off16(a, pc()), 16, 32)); } inline void Assembler::z_brcl(branch_condition i1, address a) { emit_48( BRCL_ZOPC | uimm4(i1, 8, 48) | simm32(RelAddr::pcrel_off32(a, pc()), 16, 48)); } -inline void Assembler::z_bctgr(Register r1, Register r2) { emit_32( BCTGR_ZOPC | reg( r1, 24, 32) | reg( r2, 28, 32)); }; + +// branch on count +inline void Assembler::z_bct( Register r1, const Address &a) { z_bct( r1, a.disp(), a.indexOrR0(), a.baseOrR0()); } +inline void Assembler::z_bct( Register r1, int64_t d2, Register x2, Register b2) { emit_32( BCT_ZOPC | reg(r1, 8, 32) | rxmask_32(d2, x2, b2)); } +inline void Assembler::z_bctr (Register r1, Register r2) { emit_16( BCTR_ZOPC | reg( r1, 8, 16) | reg( r2, 12, 16)); }; +inline void Assembler::z_bctgr(Register r1, Register r2) { emit_32( BCTGR_ZOPC | reg( r1, 24, 32) | reg( r2, 28, 32)); }; +inline void Assembler::z_bctg( Register r1, const Address &a) { z_bctg( r1, a.disp(), a.indexOrR0(), a.baseOrR0()); } +inline void Assembler::z_bctg( Register r1, int64_t d2, Register x2, Register b2) { emit_48( BCTG_ZOPC | reg(r1, 8, 48) | rxymask_48(d2, x2, b2)); } inline void Assembler::z_basr( Register r1, Register r2) { emit_16( BASR_ZOPC | regt(r1, 8, 16) | reg(r2, 12, 16)); } inline void Assembler::z_brasl(Register r1, address a) { emit_48( BRASL_ZOPC | regt(r1, 8, 48) | simm32(RelAddr::pcrel_off32(a, pc()), 16, 48)); } @@ -1396,6 +1403,8 @@ inline void Assembler::z_brno( Label& L) { z_brc(bcondNotOrdered, target(L)); } inline void Assembler::z_brc( branch_condition m, Label& L) { z_brc(m, target(L)); } inline void Assembler::z_brcl(branch_condition m, Label& L) { z_brcl(m, target(L)); } +inline void Assembler::z_bct( Register r1, int64_t d2, Register b2) { z_bct( r1, d2, Z_R0, b2);} +inline void Assembler::z_bctg(Register r1, int64_t d2, Register b2) { z_bctg(r1, d2, Z_R0, b2);} // Instruction len bits must be stored right-justified in argument. inline unsigned int Assembler::instr_len(unsigned char len_bits) { diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 50f957aef999c..bec63210df9c5 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -4491,13 +4491,21 @@ void C2_MacroAssembler::count_positives(Register ary1, Register len, // Compare char[] or byte[] arrays aligned to 4 bytes or substrings. void C2_MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register ary2, Register limit, Register result, Register chr, - XMMRegister vec1, XMMRegister vec2, bool is_char, KRegister mask) { + XMMRegister vec1, XMMRegister vec2, bool is_char, + KRegister mask, bool expand_ary2) { + // for expand_ary2, limit is the (smaller) size of the second array. ShortBranchVerifier sbv(this); Label TRUE_LABEL, FALSE_LABEL, DONE, COMPARE_VECTORS, COMPARE_CHAR, COMPARE_BYTE; + assert((!expand_ary2) || ((expand_ary2) && (UseAVX == 2)), + "Expansion only implemented for AVX2"); + int length_offset = arrayOopDesc::length_offset_in_bytes(); int base_offset = arrayOopDesc::base_offset_in_bytes(is_char ? T_CHAR : T_BYTE); + Address::ScaleFactor scaleFactor = expand_ary2 ? Address::times_2 : Address::times_1; + int scaleIncr = expand_ary2 ? 8 : 16; + if (is_array_equ) { // Check the input args cmpoop(ary1, ary2); @@ -4533,14 +4541,20 @@ void C2_MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register if (UseAVX >= 2) { // With AVX2, use 32-byte vector compare - Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; + Label COMPARE_WIDE_VECTORS, COMPARE_WIDE_VECTORS_16, COMPARE_TAIL, COMPARE_TAIL_16; // Compare 32-byte vectors - andl(result, 0x0000001f); // tail count (in bytes) - andl(limit, 0xffffffe0); // vector count (in bytes) - jcc(Assembler::zero, COMPARE_TAIL); + if (expand_ary2) { + andl(result, 0x0000000f); // tail count (in bytes) + andl(limit, 0xfffffff0); // vector count (in bytes) + jcc(Assembler::zero, COMPARE_TAIL); + } else { + andl(result, 0x0000001f); // tail count (in bytes) + andl(limit, 0xffffffe0); // vector count (in bytes) + jcc(Assembler::zero, COMPARE_TAIL_16); + } - lea(ary1, Address(ary1, limit, Address::times_1)); + lea(ary1, Address(ary1, limit, scaleFactor)); lea(ary2, Address(ary2, limit, Address::times_1)); negptr(limit); @@ -4583,25 +4597,59 @@ void C2_MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register }//if (VM_Version::supports_avx512vlbw()) #endif //_LP64 bind(COMPARE_WIDE_VECTORS); - vmovdqu(vec1, Address(ary1, limit, Address::times_1)); - vmovdqu(vec2, Address(ary2, limit, Address::times_1)); + vmovdqu(vec1, Address(ary1, limit, scaleFactor)); + if (expand_ary2) { + vpmovzxbw(vec2, Address(ary2, limit, Address::times_1), Assembler::AVX_256bit); + } else { + vmovdqu(vec2, Address(ary2, limit, Address::times_1)); + } vpxor(vec1, vec2); vptest(vec1, vec1); jcc(Assembler::notZero, FALSE_LABEL); - addptr(limit, 32); + addptr(limit, scaleIncr * 2); jcc(Assembler::notZero, COMPARE_WIDE_VECTORS); testl(result, result); jcc(Assembler::zero, TRUE_LABEL); - vmovdqu(vec1, Address(ary1, result, Address::times_1, -32)); - vmovdqu(vec2, Address(ary2, result, Address::times_1, -32)); + vmovdqu(vec1, Address(ary1, result, scaleFactor, -32)); + if (expand_ary2) { + vpmovzxbw(vec2, Address(ary2, result, Address::times_1, -16), Assembler::AVX_256bit); + } else { + vmovdqu(vec2, Address(ary2, result, Address::times_1, -32)); + } vpxor(vec1, vec2); vptest(vec1, vec1); - jccb(Assembler::notZero, FALSE_LABEL); - jmpb(TRUE_LABEL); + jcc(Assembler::notZero, FALSE_LABEL); + jmp(TRUE_LABEL); + + bind(COMPARE_TAIL_16); // limit is zero + movl(limit, result); + + // Compare 16-byte chunks + andl(result, 0x0000000f); // tail count (in bytes) + andl(limit, 0xfffffff0); // vector count (in bytes) + jcc(Assembler::zero, COMPARE_TAIL); + + lea(ary1, Address(ary1, limit, scaleFactor)); + lea(ary2, Address(ary2, limit, Address::times_1)); + negptr(limit); + + bind(COMPARE_WIDE_VECTORS_16); + movdqu(vec1, Address(ary1, limit, scaleFactor)); + if (expand_ary2) { + vpmovzxbw(vec2, Address(ary2, limit, Address::times_1), Assembler::AVX_128bit); + } else { + movdqu(vec2, Address(ary2, limit, Address::times_1)); + } + pxor(vec1, vec2); + + ptest(vec1, vec1); + jcc(Assembler::notZero, FALSE_LABEL); + addptr(limit, scaleIncr); + jcc(Assembler::notZero, COMPARE_WIDE_VECTORS_16); bind(COMPARE_TAIL); // limit is zero movl(limit, result); @@ -4646,19 +4694,34 @@ void C2_MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register } // Compare 4-byte vectors - andl(limit, 0xfffffffc); // vector count (in bytes) - jccb(Assembler::zero, COMPARE_CHAR); + if (expand_ary2) { + testl(result, result); + jccb(Assembler::zero, TRUE_LABEL); + } else { + andl(limit, 0xfffffffc); // vector count (in bytes) + jccb(Assembler::zero, COMPARE_CHAR); + } - lea(ary1, Address(ary1, limit, Address::times_1)); + lea(ary1, Address(ary1, limit, scaleFactor)); lea(ary2, Address(ary2, limit, Address::times_1)); negptr(limit); bind(COMPARE_VECTORS); - movl(chr, Address(ary1, limit, Address::times_1)); - cmpl(chr, Address(ary2, limit, Address::times_1)); - jccb(Assembler::notEqual, FALSE_LABEL); - addptr(limit, 4); - jcc(Assembler::notZero, COMPARE_VECTORS); + if (expand_ary2) { + // There are no "vector" operations for bytes to shorts + movzbl(chr, Address(ary2, limit, Address::times_1)); + cmpw(Address(ary1, limit, Address::times_2), chr); + jccb(Assembler::notEqual, FALSE_LABEL); + addptr(limit, 1); + jcc(Assembler::notZero, COMPARE_VECTORS); + jmp(TRUE_LABEL); + } else { + movl(chr, Address(ary1, limit, Address::times_1)); + cmpl(chr, Address(ary2, limit, Address::times_1)); + jccb(Assembler::notEqual, FALSE_LABEL); + addptr(limit, 4); + jcc(Assembler::notZero, COMPARE_VECTORS); + } // Compare trailing char (final 2 bytes), if any bind(COMPARE_CHAR); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index 8c22990892b01..676382225c241 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -289,10 +289,11 @@ void count_positives(Register ary1, Register len, Register result, Register tmp1, XMMRegister vec1, XMMRegister vec2, KRegister mask1 = knoreg, KRegister mask2 = knoreg); + // Compare char[] or byte[] arrays. - void arrays_equals(bool is_array_equ, Register ary1, Register ary2, - Register limit, Register result, Register chr, - XMMRegister vec1, XMMRegister vec2, bool is_char, KRegister mask = knoreg); + void arrays_equals(bool is_array_equ, Register ary1, Register ary2, Register limit, + Register result, Register chr, XMMRegister vec1, XMMRegister vec2, + bool is_char, KRegister mask = knoreg, bool expand_ary2 = false); void arrays_hashcode(Register str1, Register cnt1, Register result, Register tmp1, Register tmp2, Register tmp3, XMMRegister vnext, diff --git a/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp b/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp new file mode 100644 index 0000000000000..34f8bec8d116b --- /dev/null +++ b/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp @@ -0,0 +1,1837 @@ +/* + * Copyright (c) 2024, Intel Corporation. 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. + * + */ + +#include "precompiled.hpp" +#include "macroAssembler_x86.hpp" +#include "stubGenerator_x86_64.hpp" +#include "opto/c2_MacroAssembler.hpp" +#include "opto/intrinsicnode.hpp" + +/******************************************************************************/ +// String handling intrinsics +// -------------------------- +// +// Currently implements scheme described in http://0x80.pl/articles/simd-strfind.html +// Implementation can be found at https://github.com/WojciechMula/sse4-strstr +// +// The general idea is as follows: +// 1. Broadcast the first byte of the needle to a ymm register (32 bytes) +// 2. Broadcast the last byte of the needle to a different ymm register +// 3. Compare the first-byte ymm register to the first 32 bytes of the haystack +// 4. Compare the last-byte register to the 32 bytes of the haystack at the (k-1)st position +// where k is the length of the needle +// 5. Logically AND the results of the comparison +// +// The result of the AND yields the position within the haystack where both the first +// and last bytes of the needle exist in their correct relative positions. Check the full +// needle value against the haystack to confirm a match. +// +// This implementation uses memcmp to compare when the size of the needle is >= 32 bytes. +// For other needle sizes, the comparison is done with register compares to eliminate the +// overhead of the call (including range checks, etc.). The size of the comparison is +// known, and it is also known to be safe reading the haystack for the full width of the needle. +// +// The original algorithm as implemented will potentially read past the end of the haystack. +// This implementation protects against that. Instead of reading as many 32-byte chunks as +// possible and then handling the tail, we calculate the last position of a vaild 32-byte +// read and adjust the starting position of the second read such that the last read will not +// go beyond the end of the haystack. So the first comparison is to the first 32 bytes of the +// haystack, and the second is offset by an amount to make the last read legal. The remainder of +// the comparisons are done incrementing by 32 bytes. +// +// This will cause 16 bytes on average to be examined twice, but that is cheaper than the +// logic required for tail processing. +// +/******************************************************************************/ + +#define __ _masm-> +#define __C2 ((C2_MacroAssembler *) _masm)-> + +// Register definitions for consistency +// These registers can be counted on to always contain +// the correct values (once set up) +#define XMM_BYTE_0 xmm0 +#define XMM_BYTE_K xmm1 +#define XMM_BYTE_1 xmm12 +#define save_r12 xmm4 +#define save_r13 xmm5 +#define save_r14 xmm6 +#define save_r15 xmm7 +#define save_rbx xmm8 +#define nMinusK r10 + +// Global temporary xmm registers +#define XMM_TMP1 xmm15 +#define XMM_TMP2 xmm14 +#define XMM_TMP3 xmm2 +#define XMM_TMP4 xmm3 + +// This macro handles clearing the bits of the mask register depending +// on whether we're comparing bytes or words. +#define CLEAR_BIT(mask) \ + if (isU) { \ + __ blsrl(mask, mask); \ + __ blsrl(mask, mask); \ + } else { \ + __ blsrl(mask, mask); \ + } + +#define NUMBER_OF_CASES 10 + +#undef STACK_SPACE +#undef MAX_NEEDLE_LEN_TO_EXPAND +#define MAX_NEEDLE_LEN_TO_EXPAND 0x28 + +// Stack layout: +# define COPIED_HAYSTACK_STACK_OFFSET (0x0) // MUST BE ZERO! +# define COPIED_HAYSTACK_STACK_SIZE (64) // MUST BE 64! + +# define EXPANDED_NEEDLE_STACK_OFFSET (COPIED_HAYSTACK_STACK_OFFSET + COPIED_HAYSTACK_STACK_SIZE) +# define EXPANDED_NEEDLE_STACK_SIZE (MAX_NEEDLE_LEN_TO_EXPAND * 2 + 32) + +# define SAVED_HAYSTACK_STACK_OFFSET (EXPANDED_NEEDLE_STACK_OFFSET + EXPANDED_NEEDLE_STACK_SIZE) +# define SAVED_HAYSTACK_STACK_SIZE (8) + +# define SAVED_INCREMENT_STACK_OFFSET (SAVED_HAYSTACK_STACK_OFFSET + SAVED_HAYSTACK_STACK_SIZE) +# define SAVED_INCREMENT_STACK_SIZE (8) + +# define SAVED_TERM_ADDR_STACK_OFFSET (SAVED_INCREMENT_STACK_OFFSET + SAVED_INCREMENT_STACK_SIZE) +# define SAVED_TERM_ADDR_STACK_SIZE (8) + +# define STACK_SPACE \ + (COPIED_HAYSTACK_STACK_SIZE + EXPANDED_NEEDLE_STACK_SIZE + SAVED_HAYSTACK_STACK_SIZE + SAVED_INCREMENT_STACK_SIZE \ + + SAVED_TERM_ADDR_STACK_SIZE) + +// Forward declarations for helper functions +static void broadcast_additional_needles(bool sizeKnown, int size, Register needle, + Register needleLen, Register rTmp, + StrIntrinsicNode::ArgEncoding ae, MacroAssembler *_masm); + +static void broadcast_first_and_last_needle(Register needle, Register needle_len, Register rTmp, + StrIntrinsicNode::ArgEncoding ae, + MacroAssembler *_masm); + +static void compare_big_haystack_to_needle(bool sizeKnown, int size, Label &noMatch, + Register haystack, Register needleLen, Register eq_mask, + XMMRegister rxTmp1, XMMRegister rxTmp2, + XMMRegister rxTmp3, StrIntrinsicNode::ArgEncoding ae, + MacroAssembler *_masm); + +static void compare_haystack_to_needle(bool sizeKnown, int size, Label &noMatch, Register haystack, + Register eq_mask, Register needleLen, Register rTmp, + XMMRegister rxTmp1, XMMRegister rxTmp2, + StrIntrinsicNode::ArgEncoding ae, MacroAssembler *_masm); + +static void big_case_loop_helper(bool sizeKnown, int size, Label &noMatch, Label &loop_top, + Register eq_mask, Register hsPtrRet, Register needleLen, + Register needle, Register haystack, Register hsLength, + Register rTmp1, Register rTmp2, Register rTmp3, Register rTmp4, + StrIntrinsicNode::ArgEncoding ae, MacroAssembler *_masm); + +static void byte_compare_helper(int size, Label &L_noMatch, Label &L_matchFound, Register needle, + Register needleVal, Register haystack, Register mask, + Register foundIndex, Register tmp, StrIntrinsicNode::ArgEncoding ae, + MacroAssembler *_masm); + +static void highly_optimized_short_cases(StrIntrinsicNode::ArgEncoding ae, Register haystack, + Register haystack_len, Register needle, + Register needle_len, XMMRegister XMM0, XMMRegister XMM1, + Register mask, Register tmp, MacroAssembler *_masm); + +static void setup_jump_tables(StrIntrinsicNode::ArgEncoding ae, Label &L_error, Label &L_checkRange, + Label &L_fixup, address *big_jump_table, address *small_jump_table, + MacroAssembler *_masm); + +static void vpcmpeq(XMMRegister dst, XMMRegister src, Address adr, int vector_len, + StrIntrinsicNode::ArgEncoding ae, MacroAssembler *_masm) { + if ((ae == StrIntrinsicNode::UL) || (ae == StrIntrinsicNode::UU)) { + __ vpcmpeqw(dst, src, adr, vector_len); + } else { + __ vpcmpeqb(dst, src, adr, vector_len); + } +} + +static void generate_string_indexof_stubs(StubGenerator *stubgen, address *fnptrs, + StrIntrinsicNode::ArgEncoding ae, MacroAssembler *_masm); + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// Start of generator +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +void StubGenerator::generate_string_indexof(address *fnptrs) { + assert((int) StrIntrinsicNode::LL < 4, "Enum out of range"); + assert((int) StrIntrinsicNode::UL < 4, "Enum out of range"); + assert((int) StrIntrinsicNode::UU < 4, "Enum out of range"); + generate_string_indexof_stubs(this, fnptrs, StrIntrinsicNode::LL, _masm); + generate_string_indexof_stubs(this, fnptrs, StrIntrinsicNode::UU, _masm); + generate_string_indexof_stubs(this, fnptrs, StrIntrinsicNode::UL, _masm); + assert(fnptrs[StrIntrinsicNode::LL] != nullptr, "LL not generated."); + assert(fnptrs[StrIntrinsicNode::UL] != nullptr, "UL not generated."); + assert(fnptrs[StrIntrinsicNode::UU] != nullptr, "UU not generated."); +} + +static void generate_string_indexof_stubs(StubGenerator *stubgen, address *fnptrs, + StrIntrinsicNode::ArgEncoding ae, MacroAssembler *_masm) { + StubCodeMark mark(stubgen, "StubRoutines", "stringIndexOf"); + bool isLL = (ae == StrIntrinsicNode::LL); + bool isUL = (ae == StrIntrinsicNode::UL); + bool isUU = (ae == StrIntrinsicNode::UU); + bool isU = isUL || isUU; // At least one is UTF-16 + assert(isLL || isUL || isUU, "Encoding not recognized"); + + // Keep track of isUL since we need to generate UU code in the main body + // for the case where we expand the needle from bytes to words on the stack. + // This is done at L_wcharBegin. The algorithm used is: + // If the encoding is UL and the needle size is <= MAX_NEEDLE_LEN_TO_EXPAND, + // allocate space on the stack and expand the Latin-1 encoded needle. Then + // effectively "recurse" into the mainline using UU encoding (since both the + // haystack and needle are now UTF-16 encoded). + bool isReallyUL = isUL; + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // AVX2 code + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + assert(VM_Version::supports_avx2(), "Needs AVX2"); + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // Code generation explanation: + // + // The generator will generate code for three cases: + // 1. Both needle and haystack are Latin-1 (single byte) encoded (LL) + // 2. Both the needle and haystack are UTF-16 encoded (two bytes per character) (UU) + // 3. The haystack is UTF-16 encoded and the needle is Latin-1 encoded (UL) + // + // The case of the haystack being Latin-1 and the needle being UTF-16 is short-circuited + // so that we never get called in this case. + // + // For the UL case (haystack UTF-16 and needle Latin-1), the needle will be expanded + // onto the stack (for size <= MAX_NEEDLE_LEN_TO_EXPAND) and the UU code will do the work. + // For UL where the needle size is > MAX_NEEDLE_LEN_TO_EXPAND and the haystack size minus + // the needle size is less than 32 bytes, we default to a + // byte-by-byte comparison (this will be rare). + // + // Note that the code assumes MAX_NEEDLE_LEN_TO_EXPAND is >= 32. + // + // The UU and LL cases are identical except for the loop increments and loading + // of the characters into registers. UU loads and compares words, LL - bytes. + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + + const Register haystack_p = c_rarg0; + const Register haystack_len_p = c_rarg1; + const Register needle_p = c_rarg2; + const Register needle_len_p = c_rarg3; + + // Addresses of the two jump tables used for small needle processing + address big_jump_table; + address small_jump_table; + + Label L_begin; + + Label L_returnError, L_bigCaseFixupAndReturn; + Label L_bigSwitchTop, L_bigCaseDefault, L_smallCaseDefault; + Label L_nextCheck, L_checksPassed, L_return; + Label L_wcharBegin, L_continue, L_wideNoExpand, L_returnR11; + + __ align(CodeEntryAlignment); + fnptrs[ae] = __ pc(); + __ enter(); // required for proper stackwalking of RuntimeStub frame + + // Check for trivial cases + // needle length == 0? + __ cmpq(needle_len_p, 0); + __ jg_b(L_nextCheck); + __ xorl(rax, rax); + __ leave(); + __ ret(0); + + __ bind(L_nextCheck); + // haystack length >= needle length? + __ movq(rax, haystack_len_p); + __ subq(rax, needle_len_p); + __ jge_b(L_checksPassed); + + __ movq(rax, -1); + __ leave(); + __ ret(0); + + __ bind(L_checksPassed); + + // Check for highly-optimized ability - haystack <= 32 bytes and needle <= 6 bytes + // haystack_len is in elements, not bytes, for UTF-16 + __ cmpq(haystack_len_p, isU ? 16 : 32); + __ ja(L_begin); + + // needle_len is in elements, not bytes, for UTF-16 <=> UTF-16 + __ cmpq(needle_len_p, isUU ? 3 : 6); + __ ja(L_begin); + + // Handle short haystack and needle specially + // Generated code does not return - either found or not + highly_optimized_short_cases(ae, haystack_p, haystack_len_p, needle_p, needle_len_p, xmm0, xmm1, + r10, r11, _masm); + + // If we're generating UL, we need to "pretend" we're generating UU code + // for the case where the needle can be expanded onto the stack + if (isReallyUL) { + ae = StrIntrinsicNode::UU; + isUL = false; + isUU = true; + } + + // Set up jump tables. Used when needle size <= NUMBER_OF_CASES + setup_jump_tables(ae, L_returnError, L_returnR11, L_bigCaseFixupAndReturn, &big_jump_table, + &small_jump_table, _masm); + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // + // The above code handles all cases (LL, UL, UU) for haystack size <= 32 bytes + // and needle size <= 6 bytes. + // + // Main processing proceeds as follows: + // Save state and setup stack, etc. + // If UL, jump to code to handle special-case UL situations (see L_wcharBegin below) + // Broadcast the first and last needle elements to XMM_BYTE_0 and XMM_BYTE_K, respectively + // If the length in bytes of the haystack is > 32, dispatch to the big switch handling code + // If the haystack length in bytes is <= 32: + // Copy the haystack to the stack. This is done to prevent possible page faults and + // allows for reading full 32-byte chunks of the haystack. + // Dispatch to the small switch handling code + // + // Here, "big switch" and "small switch" refers to the haystack size: > 32 bytes for big + // and <= 32 bytes for small. The switches implement optimized code for handling 1 to + // NUMBER_OF_CASES (currently 10) needle sizes for both big and small. There are special + // routines for handling needle sizes > NUMBER_OF_CASES (L_{big,small}CaseDefault). These + // cases use C2's arrays_equals() to compare the needle to the haystack. The small cases + // use specialized code for comparing the needle. + // + // The algorithm currently does vector comparisons for the first, last, and second bytes + // of the needle and, where each of these needle elements matches the correct position + // within the haystack, the "in-between" bytes are compared using the most efficient + // instructions possible for short needles, or C2's arrays_equals for longer needles. + + __ align(CodeEntryAlignment); + + __ bind(L_begin); + __ movdq(save_r12, r12); + __ movdq(save_r13, r13); + __ movdq(save_r14, r14); + __ movdq(save_r15, r15); + __ movdq(save_rbx, rbx); +#ifdef _WIN64 + __ push(rsi); + __ push(rdi); + + // Move to Linux-style ABI + __ movq(rdi, rcx); + __ movq(rsi, rdx); + __ movq(rdx, r8); + __ movq(rcx, r9); +#endif + + const Register haystack = rdi; + const Register haystack_len = rsi; + const Register needle = rdx; + const Register needle_len = rcx; + const Register save_ndl_len = r12; + + __ push(rbp); + __ subptr(rsp, STACK_SPACE); + + if (isReallyUL) { + // Branch out if doing UL + __ jmp(L_wcharBegin); + } + + if (!isReallyUL && isUU) { // Adjust sizes of hs and needle + // UU passes lengths in terms of chars - convert to bytes + __ shlq(needle_len, 1); + __ shlq(haystack_len, 1); + } + + // UL processing comes here after expanding needle + __ bind(L_continue); + // nMinusK (haystack length in bytes minus needle length in bytes) is used several + // places to determine whether a compare will read past the end of the haystack. + __ movq(nMinusK, haystack_len); + __ subq(nMinusK, needle_len); + + // Set up expected registers + __ movq(save_ndl_len, needle_len); + __ movq(r14, needle); + __ movq(rbx, haystack); + + // Always need needle broadcast to ymm registers (XMM_BYTE_0 and XMM_BYTE_K) + broadcast_first_and_last_needle(needle, needle_len, rax, ae, _masm); + + // Do "big switch" if haystack size > 32 + __ cmpq(haystack_len, 0x20); + __ ja_b(L_bigSwitchTop); + + // Copy the small (< 32 byte) haystack to the stack. Allows for vector reads without page fault + // Only done for small haystacks + // + // NOTE: This code assumes that the haystack points to a java array type AND there are + // at least 16 bytes of header preceeding the haystack pointer. + // + // This means that we're copying up to 15 bytes of the header onto the stack along + // with the haystack bytes. After the copy completes, we adjust the haystack pointer + // to the valid haystack bytes on the stack. + { + Label L_moreThan16, L_adjustHaystack; + + const Register index = rax; + const Register haystack = rbx; + + // Only a single vector load/store of either 16 or 32 bytes + __ cmpq(haystack_len, 0x10); + __ ja_b(L_moreThan16); + + __ movq(index, COPIED_HAYSTACK_STACK_OFFSET + 0x10); + __ movdqu(XMM_TMP1, Address(haystack, haystack_len, Address::times_1, -0x10)); + __ movdqu(Address(rsp, COPIED_HAYSTACK_STACK_OFFSET), XMM_TMP1); + __ jmpb(L_adjustHaystack); + + __ bind(L_moreThan16); + __ movq(index, COPIED_HAYSTACK_STACK_OFFSET + 0x20); + __ vmovdqu(XMM_TMP1, Address(haystack, haystack_len, Address::times_1, -0x20)); + __ vmovdqu(Address(rsp, COPIED_HAYSTACK_STACK_OFFSET), XMM_TMP1); + + // Point the haystack at the correct location of the first byte of the "real" haystack on the stack + __ bind(L_adjustHaystack); + __ subq(index, haystack_len); + __ leaq(haystack, Address(rsp, index, Address::times_1)); + } + + // Dispatch to handlers for small needle and small haystack + // Note that needle sizes of 1-6 have been handled in highly_optimized_short_cases, + // so the dispatch only has valid entries for 7-10. + __ leaq(r13, Address(save_ndl_len, -1)); + __ cmpq(r13, NUMBER_OF_CASES - 1); + __ ja(L_smallCaseDefault); + __ lea(r15, InternalAddress(small_jump_table)); + __ jmp(Address(r15, r13, Address::times_8)); + + // Dispatch to handlers for small needle and large haystack + // For large haystacks, the jump table is fully populated (1-10) + __ bind(L_bigSwitchTop); + __ leaq(rax, Address(save_ndl_len, -1)); + __ cmpq(rax, NUMBER_OF_CASES - 1); + __ ja(L_bigCaseDefault); + __ lea(r15, InternalAddress(big_jump_table)); + __ jmp(Address(r15, rax, Address::times_8)); + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // Fixup and return routines + + // Return not found + __ bind(L_returnError); + __ movq(rax, -1); + __ jmpb(L_return); + + // At this point, rcx has &haystack where match found, rbx has &haystack, + // and r8 has the index where a match was found + __ bind(L_bigCaseFixupAndReturn); + __ subq(rcx, rbx); + __ addq(rcx, r8); + + __ movq(r11, rcx); + + // r11 will contain the valid index. + __ bind(L_returnR11); + __ movq(rax, r11); + + // Restore stack, vzeroupper and return + __ bind(L_return); + __ addptr(rsp, STACK_SPACE); + __ pop(rbp); +#ifdef _WIN64 + __ pop(rdi); + __ pop(rsi); +#endif + __ movdq(r12, save_r12); + __ movdq(r13, save_r13); + __ movdq(r14, save_r14); + __ movdq(r15, save_r15); + __ movdq(rbx, save_rbx); + + // Need to return elements for UTF-16 encodings + if (isU) { + // Return value for UTF-16 is elements, not bytes + // sar is used to preserve -1 + __ sarq(rax, 1); + } + __ vzeroupper(); + + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // + // Big case default: + // + // Handle needle sizes > 10 bytes. Uses C2's arrays_equals to compare the contents + // of the needle to the haystack. + + { + Label L_loopTop, L_innerLoop, L_found; + + const Register hsPtrRet = rax; + const Register mask = r8; + const Register index = r9; + const Register compLen = rbp; + const Register haystackStart = rcx; + const Register rScratch = r13; + const Register needleLen = r12; + const Register needle = r14; + const Register haystack = rbx; + const Register hsLength = rsi; + const Register tmp1 = rdi; + +// #define used for registers that are re-used in the code +#undef retval +#undef firstNeedleCompare +#undef tmp2 +#undef tmp3 +#define tmp2 r15 +#define tmp3 rdx + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // + // Big case default: registers on entry + // + // rbx: haystack + // rcx: k + // rdx: junk + // rsi: n + // rdi: haystack + // r10: n - k + // r12: k + // r13: junk + // r14: needle + // rbp: junk + // XMM_BYTE_0 - first element of needle broadcast + // XMM_BYTE_K - last element of needle broadcast + // + // Set up in big_case_loop_helper + // XMM_BYTE_1 - second element of needle broadcast + + __ bind(L_bigCaseDefault); + + // Loop construct handling for big haystacks + // The helper binds L_loopTop which should be jumped to if potential matches fail to compare + // equal (thus moving on to the next chunk of haystack). If we run out of haystack, the + // helper jumps to L_returnError. + big_case_loop_helper(false, 0, L_returnError, L_loopTop, mask, hsPtrRet, needleLen, needle, + haystack, hsLength, tmp1, tmp2, tmp3, rScratch, ae, _masm); + + // big_case_loop_helper will fall through to this point if one or more potential matches are found + // The mask will have a bitmask indicating the position of the potential matches within the haystack + __ align(OptoLoopAlignment); + __ bind(L_innerLoop); + __ tzcntl(index, mask); + +// Re-use of r15 and rdx +#undef tmp2 +#undef tmp3 +#define retval r15 +#define firstNeedleCompare rdx + + // Need a lot of registers here to preserve state across arrays_equals call + + // Starting address in the haystack + __ leaq(haystackStart, Address(hsPtrRet, index, Address::times_1, isU ? 4 : 2)); + // Starting address of first byte of needle to compare + __ leaq(firstNeedleCompare, Address(needle, isU ? 4 : 2)); + // Number of bytes to compare + __ leaq(compLen, Address(needleLen, isU ? -6 : -3)); + + // Call arrays_equals for both UU and LL cases as bytes should compare exact + __C2 arrays_equals(false, haystackStart, firstNeedleCompare, compLen, retval, rScratch, + XMM_TMP3, XMM_TMP4, false /* char */, knoreg); + __ testl(retval, retval); + __ jne_b(L_found); + + // If more potential matches, continue at inner loop, otherwise go get another vector + CLEAR_BIT(mask); + __ jne(L_innerLoop); + __ jmp(L_loopTop); + + // Found exact match. Compute offset from beginning of haystack + __ bind(L_found); + __ subq(hsPtrRet, haystack); + __ addq(hsPtrRet, index); + __ movq(r11, hsPtrRet); + __ jmp(L_returnR11); + +#undef retval +#undef firstNeedleCompare + } + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // + // Small case default: + // + // Handle needle sizes > 10 bytes. Uses C2's arrays_equals to compare the contents + // of the needle to the haystack. + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // + // Small case default: register on entry + // + // rbx: haystack + // r14: needle + // r13: k - 1 + // r12: k + // r10: n - k + // rbp: junk + // rdi: junk + // rsi: n + // rdx: junk + // rcx: junk + // XMM_BYTE_0 - first element of needle broadcast + // XMM_BYTE_K - last element of needle broadcast + // + // Set up in broadcast_additional_needles + // XMM_BYTE_1 - second element of needle broadcast + // + // Haystack always copied to stack, so 32-byte reads OK + // Haystack length <= 32 + // 10 < needle length <= 32 + + { + __ bind(L_smallCaseDefault); + + Label L_innerLoop; + + const Register firstNeedleCompare = rdx; + const Register compLen = r9; + const Register haystack = rbx; + const Register mask = r8; + const Register rTmp = rdi; + const Register rTmp2 = r13; + const Register rTmp3 = rax; + +// r14 and r12 will be re-used later in this procedure +#undef needle +#define needle r14 +#undef needleLen +#define needleLen r12 + + broadcast_additional_needles(false, 0 /* unknown */, needle, needleLen, rTmp3, ae, _masm); + + // For small haystacks we already know that the 1st, 2nd, and last bytes of the needle + // compare equal, so we can reduce the byte count to arrays_equals + __ leaq(firstNeedleCompare, Address(needle, isU ? 4 : 2)); + __ leaq(compLen, Address(needleLen, isU ? -6 : -3)); + + // firstNeedleCompare has address of third element of needle + // compLen has length of comparison to do (3 elements less than needle size) + + // Helper to compare the 1st, 2nd, and last byte of the needle to the haystack + // in the correct position. Since the haystack is < 32 bytes, not finding matching + // needle bytes can just return failure. Otherwise, we loop through the found + // matches. + compare_haystack_to_needle(false, 0, L_returnError, haystack, mask, needleLen, rTmp3, XMM_TMP1, + XMM_TMP2, ae, _masm); + +// NOTE: REGISTER RE-USE for r12 and r14 +#undef needle +#undef saveCompLen +#define saveCompLen r14 +#undef needleLen +#undef saveNeedleAddress +#define saveNeedleAddress r12 + + // Save registers stomped by arrays_equals + __ movq(saveCompLen, compLen); + __ movq(saveNeedleAddress, firstNeedleCompare); // Save address of 2nd element of needle + + // Find index of a potential match + __ align(OptoLoopAlignment); + __ bind(L_innerLoop); + __ tzcntl(r11, mask); + + __ leaq(rTmp, Address(haystack, r11, Address::times_1, isU ? 4 : 2)); + + // Check for needle equality. Handles UU and LL cases since byte comparison should be exact + __C2 arrays_equals(false, rTmp, firstNeedleCompare, compLen, rTmp3, rTmp2, XMM_TMP3, XMM_TMP4, + false /* char */, knoreg); + __ testl(rTmp3, rTmp3); + __ jne(L_returnR11); + + // Restore saved registers + __ movq(compLen, saveCompLen); + __ movq(firstNeedleCompare, saveNeedleAddress); + + // Jump to inner loop if more matches to check, otherwise return not found + CLEAR_BIT(mask); + __ jne(L_innerLoop); + __ jmp(L_returnError); + +#undef saveCompLen +#undef saveNeedleAddress + } + + if (isReallyUL) { + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // Wide char code + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // + // Pseudo-code: + // + // If needle length less than MAX_NEEDLE_LEN_TO_EXPAND, read the needle + // bytes from r14 and write them as words onto the stack. Then go to the + // "regular" UU code. This is equavilent to doing a UU comparison, since the + // haystack will be in UTF-16. + // + // If the needle can't be expanded, process the same way as the default + // cases above. + __ bind(L_wcharBegin); + + // Restore argument encoding from UU back to UL for helpers + ae = StrIntrinsicNode::UL; + isUL = true; + isUU = false; + + Label L_top, L_finished; + + const Register haystack = rdi; + const Register hsLen = rsi; + const Register needle = rdx; + const Register nLen = rcx; + + const Register offset = rax; + const Register index = rbx; + const Register wr_index = r13; + + assert(MAX_NEEDLE_LEN_TO_EXPAND >= 32, "Small UL needles not supported"); + + // haystack length to bytes + __ shlq(hsLen, 1); + + // Ensure haystack >= needle + __ leaq(index, Address(nLen, nLen, Address::times_1)); + __ cmpq(index, hsLen); + __ jg(L_returnError); + + // Can't expand large-ish needles + __ cmpq(nLen, MAX_NEEDLE_LEN_TO_EXPAND); + __ ja(L_wideNoExpand); + + // + // Reads of existing needle are 16-byte chunks + // Writes to copied needle are 32-byte chunks + // Don't read past the end of the existing needle + // + // Start first read at [((ndlLen % 16) - 16) & 0xf] + // outndx += 32 + // inndx += 16 + // cmp nndx, ndlLen + // jae done + // + // Final index of start of needle at ((16 - (ndlLen %16)) & 0xf) << 1 + // + // Starting read for needle at -(16 - (nLen % 16)) + // Offset of needle in stack should be (16 - (nLen % 16)) * 2 + + __ movq(index, needle_len); + __ andq(index, 0xf); // nLen % 16 + __ movq(offset, 0x10); + __ subq(offset, index); // 16 - (nLen % 16) + __ movq(index, offset); + __ shlq(offset, 1); // * 2 + __ negq(index); // -(16 - (nLen % 16)) + __ xorq(wr_index, wr_index); + + __ bind(L_top); + // load needle and expand + __ vpmovzxbw(xmm0, Address(needle, index, Address::times_1), Assembler::AVX_256bit); + // store expanded needle to stack + __ vmovdqu(Address(rsp, wr_index, Address::times_1, EXPANDED_NEEDLE_STACK_OFFSET), xmm0); + __ addq(index, 0x10); + __ cmpq(index, needle_len); + __ jae(L_finished); + __ addq(wr_index, 32); + __ jmpb(L_top); + + // adjust pointer and length of needle + __ bind(L_finished); + __ leaq(needle, Address(rsp, offset, Address::times_1, EXPANDED_NEEDLE_STACK_OFFSET)); + __ leaq(needle_len, Address(needle_len, needle_len)); + + // Go handle this the same as UU + __ jmp(L_continue); + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // + // Compare Latin-1 encoded needle against UTF-16 encoded haystack. + // + // The needle is more than MAX_NEEDLE_LEN_TO_EXPAND bytes in length, and the haystack + // is at least as big. + + // Prepare for wchar anysize + __ bind(L_wideNoExpand); + + { + Label L_loopTop, L_temp, L_innerLoop, L_found, L_compareFull; + Label doCompare, topLoop; + + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // On entry: + // + // rbx: haystack + // rcx: k + // rdx: junk + // rsi: n + // rdi: haystack + // r10: n - k + // r12: k + // r13: junk + // r14: needle + // rbp: junk + // XMM_BYTE_0 - first element of needle broadcast + // XMM_BYTE_K - last element of needle broadcast + + const Register hsPtrRet = rax; + const Register haystack = rbx; + const Register haystackStart = rcx; + const Register hsLength = rsi; + const Register tmp1 = rdi; + const Register compLen = rbp; + const Register mask = r8; + const Register index = r9; + const Register needleLen = r12; + const Register rScratch = r13; + const Register needle = r14; + + // Move registers into expected registers for rest of this routine + __ movq(rbx, rdi); + __ movq(r12, rcx); + __ movq(r14, rdx); + + // Set up nMinusK + __ movq(tmp1, needleLen); + __ shlq(tmp1, 1); + __ movq(rScratch, hsLength); + __ subq(rScratch, tmp1); + __ movq(nMinusK, rScratch); + + // Check for room for a 32-byte read for the last iteration + __ cmpq(nMinusK, 0x1f); + __ jl(L_compareFull); + + // Always need needle broadcast to ymm registers + broadcast_first_and_last_needle(needle, needleLen, tmp1, ae, _masm); + +// Register redefinition for rbx and r15 +#undef retval +#undef firstNeedleCompare +#undef tmp2 +#undef tmp3 +#define tmp2 r15 +#define tmp3 rdx + + // Loop construct handling for big haystacks + // The helper binds L_loopTop which should be jumped to if potential matches fail to compare + // equal (thus moving on to the next chunk of haystack). If we run out of haystack, the + // helper jumps to L_returnError. + big_case_loop_helper(false, 0, L_returnError, L_loopTop, mask, hsPtrRet, needleLen, needle, + haystack, hsLength, tmp1, tmp2, tmp3, rScratch, ae, _masm); + + // big_case_loop_helper will fall through to this point if one or more potential matches are + // found The mask will have a bitmask indicating the position of the potential matches within + // the haystack + __ align(OptoLoopAlignment); + __ bind(L_innerLoop); + __ tzcntl(index, mask); + +#undef tmp2 +#undef tmp3 +#define retval r15 +#define firstNeedleCompare rdx + + // Note that we're comparing the full needle here even though in some paths + // the 1st, 2nd, and last bytes are already known to be equal. This is necessary + // due to the handling of cases where nMinusK is < 32 + + // Need a lot of registers here to preserve state across arrays_equals call + + // Starting address in the haystack + __ leaq(haystackStart, Address(hsPtrRet, index)); + // Starting address of first byte of needle to compare + __ movq(firstNeedleCompare, needle); + // Number of bytes to compare + __ movq(compLen, needleLen); + + // Passing true as last parameter causes arrays_equals to expand the second array (needle) + // as the comparison is done. + __C2 arrays_equals(false, haystackStart, firstNeedleCompare, compLen, retval, rScratch, + XMM_TMP3, XMM_TMP4, false /* char */, knoreg, true /* expand_ary2 */); + __ testl(retval, retval); + __ jne_b(L_found); + + // If more potential matches, continue at inner loop, otherwise go get another vector + CLEAR_BIT(mask); + __ jne(L_innerLoop); + __ jmp(L_loopTop); + + // Found exact match. Compute offset from beginning of haystack + __ bind(L_found); + __ subq(hsPtrRet, haystack); + __ addq(hsPtrRet, index); + __ movq(r11, hsPtrRet); + __ jmp(L_returnR11); + +#undef retval +#undef firstNeedleCompare + + __ bind(L_compareFull); + + // rScratch has n - k. Compare entire string word-by-word + // Index returned in r11 + __ xorq(r11, r11); + __ movq(nMinusK, rScratch); + __ jmpb(doCompare); + + __ bind(topLoop); + __ addq(r11, 2); + __ cmpq(r11, nMinusK); + __ jg(L_returnError); + + __ bind(doCompare); + __ leaq(r9, Address(haystack, r11)); + __ leaq(r8, Address(needle, 0)); + __ movq(r13, needleLen); + + __C2 arrays_equals(false, r9, r8, r13, rax, rdx, XMM_TMP3, XMM_TMP4, false /* char */, knoreg, + true /* expand_ary2 */); + __ testq(rax, rax); + __ jz(topLoop); + + // Match found + __ jmp(L_returnR11); + } + } + + return; +} + +// Helper for broadcasting needle elements to ymm registers for compares +// Expands into XMM_BYTE_0 and XMM_BYTE_K +// +// For UTF-16 encoded needles, broadcast a word at the proper offset to the ymm +// register (case UU) +// For the UTF-16 encoded haystack with Latin1 encoded needle (case UL) we have +// to read into a temp register to zero-extend the single byte needle value, then +// broadcast words to the ymm register. +// +// Parameters: +// needle - the address of the first byte of the needle +// needle_len - length of needle if !sizeKnown +// rTmp - temp register (for UL only) +// ae - the argument encodings +// _masm - Current MacroAssembler instance pointer +// +// Modifies XMM_BYTE_0 and XMM_BYTE_K +static void broadcast_first_and_last_needle(Register needle, Register needle_len, Register rTmp, + StrIntrinsicNode::ArgEncoding ae, + MacroAssembler *_masm) { + bool isUL = (ae == StrIntrinsicNode::UL); + bool isUU = (ae == StrIntrinsicNode::UU); + bool isU = (isUU || isUL); + Label L_short; + + // Always need needle broadcast to ymm registers + // Broadcast the beginning of needle into a vector register. + if (isUU) { + __ vpbroadcastw(XMM_BYTE_0, Address(needle, 0), Assembler::AVX_256bit); + } else if (isUL) { + + __ movzbl(rTmp, Address(needle)); + __ movdl(XMM_BYTE_0, rTmp); + // 1st byte of needle in words + __ vpbroadcastw(XMM_BYTE_0, XMM_BYTE_0, Assembler::AVX_256bit); + } else { + __ vpbroadcastb(XMM_BYTE_0, Address(needle, 0), Assembler::AVX_256bit); + } + + // Broadcast the end of needle into a vector register. + // For a single-element needle this is redundant but does no harm and + // reduces code size as opposed to broadcasting only if used. + if (isUU) { + __ vpbroadcastw(XMM_BYTE_K, Address(needle, needle_len, Address::times_1, -2), + Assembler::AVX_256bit); + } else if (isUL) { + __ movzbl(rTmp, Address(needle, needle_len, Address::times_1, -1)); + __ movdl(XMM_BYTE_K, rTmp); + // last byte of needle in words + __ vpbroadcastw(XMM_BYTE_K, XMM_BYTE_K, Assembler::AVX_256bit); + } else { + __ vpbroadcastb(XMM_BYTE_K, Address(needle, needle_len, Address::times_1, -1), + Assembler::AVX_256bit); + } + + __ bind(L_short); +} + +// Helper for broadcasting the 2nd needle element to XMM_BYTE_1 +// +// For UTF-16 encoded needles, broadcast a word at the proper offset to the ymm +// register (case UU) +// For the UTF-16 encoded haystack with Latin1 encoded needle (case UL) we have +// to read into a temp register to zero-extend the single byte needle value, then +// broadcast words to the ymm register. +// +// Parameters: +// sizeKnown - True if needle size known at compile time +// size - the size of the needle. Pass 0 if unknown at compile time +// needle - the address of the first byte of the needle +// needleLen - length of needle if !sizeKnown +// rTmp - temp register (for UL only) +// ae - Argument encoding +// _masm - Current MacroAssembler instance pointer +// +// Modifies XMM_BYTE_1 +static void broadcast_additional_needles(bool sizeKnown, int size, Register needle, + Register needleLen, Register rTmp, + StrIntrinsicNode::ArgEncoding ae, MacroAssembler *_masm) { + Label L_done; + + assert_different_registers(needle, needleLen, rTmp); + + bool isUL = (ae == StrIntrinsicNode::UL); + bool isUU = (ae == StrIntrinsicNode::UU); + bool isU = (isUU || isUL); + + size = sizeKnown ? size : NUMBER_OF_CASES + 1; + + // Need code to determine whether it's valid to use second byte of + // needle if the size isn't known at compile-time + if (!sizeKnown) { + __ cmpq(needleLen, (isU ? 4 : 2)); + __ jl_b(L_done); + } + + if (size > (isU ? 4 : 2)) { + // Add compare for second byte + if (isUU) { + __ vpbroadcastw(XMM_BYTE_1, Address(needle, 2), Assembler::AVX_256bit); + } else if (isUL) { + __ movzbl(rTmp, Address(needle, 1)); + __ movdl(XMM_BYTE_1, rTmp); + // 1st byte of needle in words + __ vpbroadcastw(XMM_BYTE_1, XMM_BYTE_1, Assembler::AVX_256bit); + } else { + __ vpbroadcastb(XMM_BYTE_1, Address(needle, 1), Assembler::AVX_256bit); + } + } + + __ bind(L_done); +} + +// Helper for comparing needle elements to a big haystack +// +// This helper compares bytes or words in the ymm registers to +// the proper positions within the haystack. It will bail out early if +// no match found, otherwise it will progressively and together +// the comparison results, returning the answer at the end. +// +// On return, eq_mask will be set to the comparison mask value. If no match +// is found, this helper will jump to noMatch. +// +// Parameters: +// sizeKnown - True if size known at compile time +// size - the size of the needle in bytes. Pass 0 if unknown at compile time +// noMatch - label bound outside to jump to if there is no match +// haystack - the address of the first byte of the haystack +// hsLen - the sizeof the haystack in bytes +// needleLen - size of the needle in bytes known at runtime +// eq_mask - The bit mask returned that holds the result of the comparison +// rxTmp1 - a temporary xmm register +// rxTmp2 - a temporary xmm register +// rxTmp3 - a temporary xmm register +// ae - Argument encoding +// _masm - Current MacroAssembler instance pointer +// +// (n - k) will always be >= 32 on entry +static void compare_big_haystack_to_needle(bool sizeKnown, int size, Label &noMatch, + Register haystack, Register needleLen, Register eq_mask, + XMMRegister rxTmp1, XMMRegister rxTmp2, + XMMRegister rxTmp3, StrIntrinsicNode::ArgEncoding ae, + MacroAssembler *_masm) { + + assert_different_registers(eq_mask, haystack, needleLen, nMinusK); + + const XMMRegister result = rxTmp1; + const XMMRegister cmp_0 = rxTmp2; + const XMMRegister cmp_k = rxTmp3; + + bool isUL = (ae == StrIntrinsicNode::UL); + bool isUU = (ae == StrIntrinsicNode::UU); + bool isU = (isUU || isUL); + + int sizeIncr = isU ? 2 : 1; + + Label L_OKtoCompareFull, L_done, L_specialCase_gt2; + + assert(!sizeKnown || (sizeKnown && ((size > 0) && (size <= NUMBER_OF_CASES))), "Incorrect size given"); + + // Address of the kth byte of the needle within the haystack + Address kThByte = sizeKnown ? Address(haystack, size - sizeIncr) + : Address(haystack, needleLen, + isUL ? Address::times_2 : Address::times_1, -(sizeIncr)); + size = sizeKnown ? size : NUMBER_OF_CASES + 1; + + // Compare first byte of needle to haystack + vpcmpeq(cmp_0, XMM_BYTE_0, Address(haystack, 0), Assembler::AVX_256bit, ae, _masm); + + __ vpmovmskb(eq_mask, cmp_0, Assembler::AVX_256bit); + + // If the needle is a single element (at compile time) no need to compare more + if (size != sizeIncr) { + // Compare last byte of needle to haystack at proper position + vpcmpeq(cmp_k, XMM_BYTE_K, kThByte, Assembler::AVX_256bit, ae, _masm); + + __ vpand(result, cmp_k, cmp_0, Assembler::AVX_256bit); + + if (size > sizeIncr * 2) { + vpcmpeq(cmp_k, XMM_BYTE_1, Address(haystack, 1 * sizeIncr), Assembler::AVX_256bit, ae, _masm); + __ vpand(result, cmp_k, result, Assembler::AVX_256bit); + } + + __ vpmovmskb(eq_mask, result, Assembler::AVX_256bit); + } + + __ bind(L_done); + __ testl(eq_mask, eq_mask); + __ je(noMatch); + // At this point, we have at least one "match" where first and last bytes + // of the needle are found the correct distance apart. +} + +// Helper for comparing needle elements to a small haystack +// +// This helper compares bytes or words in the ymm registers to +// the proper positions within the haystack. It will bail out early if +// a match is not found, otherwise it will progressively and together +// the comparison results, returning the answer at the end. +// +// On return, eq_mask will be set to the comparison mask value. If no match +// is found, this helper will jump to noMatch. +// +// Parameters: +// sizeKnown - if true, size is valid and needleLen invalid. +// if false, size invalid and needleLen valid. +// size - the size of the needle. Pass 0 if unknown at compile time +// noMatch - label bound outside to jump to if there is no match +// haystack - the address of the first byte of the haystack +// eq_mask - The bit mask returned that holds the result of the comparison +// needleLen - Length of the needle in bytes. Only used if isUL true +// rTmp - temporary register +// rxTmp1 - temporary xmm register +// rxTmp2 - temporary xmm register +// ae - Argument encoding +// _masm - Current MacroAssembler instance pointer +// +// No need to worry about reading past end of haystack since haystack +// has been copied to the stack +// +// If !sizeKnown, needle is at least 11 bytes long +static void compare_haystack_to_needle(bool sizeKnown, int size, Label &noMatch, Register haystack, + Register eq_mask, Register needleLen, Register rTmp, + XMMRegister rxTmp1, XMMRegister rxTmp2, + StrIntrinsicNode::ArgEncoding ae, MacroAssembler *_masm) { + + assert_different_registers(eq_mask, haystack, needleLen, rTmp, nMinusK); + + // NOTE: cmp_0 and result are the same register + const XMMRegister cmp_0 = rxTmp1; + const XMMRegister result = rxTmp1; + const XMMRegister cmp_k = rxTmp2; + + bool isUL = (ae == StrIntrinsicNode::UL); + bool isUU = (ae == StrIntrinsicNode::UU); + bool isU = isUL || isUU; // At least one is UTF-16 + + int sizeIncr = isU ? 2 : 1; + + assert((!sizeKnown) || (((size > 0) && (size <= NUMBER_OF_CASES))), "Incorrect size given"); + + // Address of the kth byte of the needle within the haystack + Address kThByte = sizeKnown ? Address(haystack, size - sizeIncr) + : Address(haystack, needleLen, Address::times_1, -(sizeIncr)); + size = sizeKnown ? size : NUMBER_OF_CASES + 1; + + // Creates a mask of (n - k + 1) ones. This prevents + // recognizing any false-positives past the end of + // the valid haystack. + __ movq(rTmp, -1); + __ movq(eq_mask, nMinusK); + __ addq(eq_mask, 1); + __ bzhiq(rTmp, rTmp, eq_mask); + + // Compare first byte of needle to haystack + vpcmpeq(cmp_0, XMM_BYTE_0, Address(haystack, 0), Assembler::AVX_256bit, ae, _masm); + if (size != sizeIncr) { + // Compare last byte of needle to haystack at proper position + vpcmpeq(cmp_k, XMM_BYTE_K, kThByte, Assembler::AVX_256bit, ae, _masm); + + __ vpand(result, cmp_k, cmp_0, Assembler::AVX_256bit); + + if (size > (sizeIncr * 2)) { + vpcmpeq(cmp_k, XMM_BYTE_1, Address(haystack, 1 * sizeIncr), Assembler::AVX_256bit, ae, _masm); + __ vpand(result, cmp_k, result, Assembler::AVX_256bit); + } + } + + __ vpmovmskb(eq_mask, result, Assembler::AVX_256bit); + __ andl(eq_mask, rTmp); + + __ testl(eq_mask, eq_mask); + __ je(noMatch); + // At this point, we have at least one "match" where first and last bytes + // of the needle are found the correct distance apart. +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// Helper for big haystack loop construct +// +// For UTF-16 encoded needles, broadcast a word at the proper offset to the ymm +// register (case UU) +// For the UTF-16 encoded haystack with Latin1 encoded needle (case UL) we have +// to read into a temp register to zero-extend the single byte needle value, then +// broadcast words to the ymm register. +// +// Parameters: +// sizeKnown - if true, size is valid and needleLen invalid. +// size - the size of the needle. Pass 0 if unknown at compile time +// noMatch - label bound outside to jump to if there is no match +// loop_top - label bound inside this helper that should be branched to +// for additional comparisons. +// eq_mask - The bit mask returned that holds the result of the comparison +// hsPtrRet - This will hold the place within the needle where a match is found +// This is modified +// needleLen - The length of the needle +// needle - Address of the needle +// haystack - Address of the haystack +// hsLength - The length of the haystack +// rTmp1 - Temporary +// rTmp2 - Temporary +// rTmp3 - Temporary +// rTmp4 - Temporary +// ae - Argument encoding +// _masm - Current MacroAssembler instance pointer +// +// On entry: +// +// rbx: haystack +// rcx: k +// rdx: junk +// rsi: n +// rdi: haystack +// r10: n - k +// r12: k +// r13: junk +// r14: needle +// rbp: junk +// XMM_BYTE_0 - first element of needle broadcast +// XMM_BYTE_K - last element of needle broadcast + +static void big_case_loop_helper(bool sizeKnown, int size, Label &noMatch, Label &loop_top, + Register eq_mask, Register hsPtrRet, Register needleLen, + Register needle, Register haystack, Register hsLength, + Register rTmp1, Register rTmp2, Register rTmp3, Register rTmp4, + StrIntrinsicNode::ArgEncoding ae, MacroAssembler *_masm) { + Label L_midLoop, L_greaterThan32, L_out; + + assert_different_registers(eq_mask, hsPtrRet, needleLen, rdi, r15, rdx, rsi, rbx, r14, nMinusK); + + const Register last = rTmp1; + const Register temp1 = rTmp2; + const Register temp2 = rTmp3; + const Register temp3 = rTmp4; + + bool isUL = (ae == StrIntrinsicNode::UL); + bool isUU = (ae == StrIntrinsicNode::UU); + bool isU = isUL || isUU; // At least one is UTF-16 + + // Assume failure + __ movq(r11, -1); + + broadcast_additional_needles(sizeKnown, size, needle, needleLen, temp1, ae, _masm); + + __ cmpq(nMinusK, 31); + __ jae_b(L_greaterThan32); + + // Here the needle is too long, so we can't do a 32-byte read to compare the last element. + // + // Instead we match the first two characters, read from the end of the haystack + // back 32 characters, shift the result, compare and check that way. + // + // Set last to hsPtrRet so the next attempt at loop iteration ends the compare. + __ movq(last, haystack); + __ movq(hsPtrRet, haystack); + + // Compare first element of needle to haystack + vpcmpeq(XMM_TMP3, XMM_BYTE_0, Address(haystack, 0), Assembler::AVX_256bit, ae, _masm); + + __ vpmovmskb(eq_mask, XMM_TMP3, Assembler::AVX_256bit); + + if (!sizeKnown || (sizeKnown && (size > (isU ? 4 : 2)))) { + // Compare second element of needle to haystack and mask result + vpcmpeq(XMM_TMP3, XMM_BYTE_1, Address(haystack, isU ? 2 : 1), Assembler::AVX_256bit, ae, _masm); + + __ vpmovmskb(temp1, XMM_TMP3, Assembler::AVX_256bit); + __ andq(eq_mask, temp1); + } + + // Compare last element of needle to haystack, shift and mask result + vpcmpeq(XMM_TMP3, XMM_BYTE_K, Address(haystack, hsLength, Address::times_1, -32), + Assembler::AVX_256bit, ae, _masm); + + __ vpmovmskb(temp1, XMM_TMP3, Assembler::AVX_256bit); + + // Compute the proper shift value. If we let k be the needle length and n be the haystack + // length, we should be comparing to haystack[k - 1] through haystack[k - 1 + 31]. Since + // (n - k) < 32, (k - 1 + 31) would be past the end of the haystack. So the shift value + // is computed as (k + 31 - n). + // + // Clarification: The BYTE_K compare above compares haystack[(n-32):(n-1)]. We need to + // compare haystack[(k-1):(k-1+31)]. Subtracting either index gives shift value of + // (k + 31 - n): x = (k-1+31)-(n-1) = k-1+31-n+1 = k+31-n. + if (sizeKnown) { + __ movl(temp2, 31 + size); + } else { + __ movl(temp2, 31); + __ addl(temp2, needleLen); + } + __ subl(temp2, hsLength); + __ shrxl(temp1, temp1, temp2); + __ andl(eq_mask, temp1); + + __ testl(eq_mask, eq_mask); + __ je(noMatch); + + __ jmp(L_out); + + __ bind(L_greaterThan32); + + // Read 32-byte chunks at a time until the last 32-byte read would go + // past the end of the haystack. Then, set the final read to read exactly + // the number of bytes in the haystack. + // For example, if haystack length is 45 and needle length is 13, the compares + // will read the following bytes: + // + // First compare Last compare + // [ 0 : 31] [12 : 43] + // Next compare will go past end of haystack ([32:63]) + // Adjust so final read is: + // [ 1 : 32] [13 : 44] + + __ movq(hsPtrRet, haystack); + __ leaq(last, Address(haystack, nMinusK, Address::times_1, isU ? -30 : -31)); + __ jmpb(L_midLoop); + + __ align(OptoLoopAlignment); + __ bind(loop_top); + // An equal comparison indicates completion with no match + __ cmpq(hsPtrRet, last); + __ je(noMatch); + __ addq(hsPtrRet, 32); + + // If next compare will go beyond end of haystack adjust start of read + // back to last valid read position + __ cmpq(hsPtrRet, last); + __ jbe_b(L_midLoop); + __ movq(hsPtrRet, last); + + __ bind(L_midLoop); + + // compare_big_haystack_to_needle will jump to loop_top until a match has been + // found + compare_big_haystack_to_needle(sizeKnown, size, loop_top, hsPtrRet, needleLen, eq_mask, XMM_TMP1, + XMM_TMP2, XMM_TMP3, ae, _masm); + + // At this point, we have at least one "match" where first and last bytes + // of the needle are found the correct distance apart. + // + // NOTE: haystack (rbx) should be preserved; hsPtrRet(rcx) is expected to + // point to the haystack such that hsPtrRet[tzcntl(eq_mask)] points to + // the matched string. + + __ bind(L_out); +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// Helper for comparing small needles to the haystack after a potential match found. +// +// Parameters: +// size - The size of the needle in bytes +// L_noMatch - Label to jump to if needle does not match haystack at this location +// L_matchFound - Label to jump to if needle matches haystack at this location +// needle - the address of the first byte of the needle +// needleVal - The bytes of the needle to compare +// haystack - The address of the first byte of the haystack +// mask - The comparison mask from comparing the first 2 and last elements of the needle +// foundIndex - The index within the haystack of the match +// tmp - A temporary register +// ae - the argument encodings +// _masm - Current MacroAssembler instance pointer +// +// Branches to either L_noMatch or L_matchFound depending on the result of the comparison +// foundIndex will contain the index within the haystack of the match for L_matchFound + +static void byte_compare_helper(int size, Label &L_noMatch, Label &L_matchFound, Register needle, + Register needleVal, Register haystack, Register mask, + Register foundIndex, Register tmp, StrIntrinsicNode::ArgEncoding ae, + MacroAssembler *_masm) { + // Compare size bytes of needle to haystack + // + // At a minimum, the first, second and last bytes of needle already compare equal + // to the haystack, so there is no need to compare them again. + + Label L_loopTop; + + assert_different_registers(needle, needleVal, haystack, mask, foundIndex, tmp); + + bool isUL = (ae == StrIntrinsicNode::UL); + bool isUU = (ae == StrIntrinsicNode::UU); + bool isU = isUL || isUU; // At least one is UTF-16 + + int bytesAlreadyCompared = 0; + int bytesLeftToCompare = 0; + int offsetOfFirstByteToCompare = 0; + + Label temp; + + // Getting her we already have the first two and last elements of the needle + // comparing equal, so no need to compare them again + bytesAlreadyCompared = isU ? 6 : 3; + offsetOfFirstByteToCompare = isU ? 4 : 2; + + bytesLeftToCompare = size - bytesAlreadyCompared; + assert(bytesLeftToCompare <= 7, "Too many bytes left to compare"); + + // The needle is <= 3 elements long, so the ultimate result comes from the mask + if (bytesLeftToCompare <= 0) { + __ tzcntl(foundIndex, mask); + __ jmp(L_matchFound); + return; + } + + // At this point, there is at least one byte of the needle that needs to be + // compared to the haystack. + + // Pre-load the needle bytes to compare here + switch (bytesLeftToCompare) { + case 1: + case 2: + // Load for needle size of 4 and 5 bytes + __ movl(needleVal, Address(needle, (offsetOfFirstByteToCompare - 2))); + break; + + case 3: + case 4: + // Load for needle size of 6 and 7 bytes + __ movl(needleVal, Address(needle, offsetOfFirstByteToCompare)); + break; + + case 5: + case 6: + // Load for needle size of 8 and 9 bytes + __ movq(needleVal, Address(needle, (offsetOfFirstByteToCompare - 2))); + break; + + case 7: + // Load for needle size of 10 bytes + __ movq(needleVal, Address(needle, offsetOfFirstByteToCompare)); + break; + + default: + break; + } + + __ align(OptoLoopAlignment); + __ bind(L_loopTop); + __ tzcntl(foundIndex, mask); // Index of match within haystack + + switch (bytesLeftToCompare) { + case 1: + case 2: + // Comparison for needle size of 4 and 5 bytes + __ cmpl(Address(haystack, foundIndex, Address::times_1, offsetOfFirstByteToCompare - 2), + needleVal); + __ je(L_matchFound); + break; + + case 3: + case 4: + // Comparison for needle size of 6 and 7 bytes + __ cmpl(Address(haystack, foundIndex, Address::times_1, offsetOfFirstByteToCompare), needleVal); + __ je(L_matchFound); + break; + + case 5: + case 6: + // Comparison for needle size of 8 and 9 bytes + __ cmpq(Address(haystack, foundIndex, Address::times_1, offsetOfFirstByteToCompare - 2), + needleVal); + __ je(L_matchFound); + break; + + case 7: + // Comparison for needle size of 10 bytes + __ cmpq(Address(haystack, foundIndex, Address::times_1, offsetOfFirstByteToCompare), needleVal); + __ je(L_matchFound); + break; + + default: + break; + } + + CLEAR_BIT(mask); // Loop as long as there are other bits set + __ jne(L_loopTop); + __ jmp(L_noMatch); +} + +// highly_optimized_short_cases +// We can handle the cases where haystack size is <= 32 bytes and needle size <= 6 bytes +// as a special case. We first copy the haystack tpo the stack to avoid page faults. A mask is +// generated with (n - k + 1) bits set that ensures matches past the end of the original +// haystack do not get considered during compares. In this equation, n is length of haystack +// and k is length of needle. +// +// A vector compare for the first needle byte is done against the haystack and anded with the mask. +// For needle size == 1, if there's a match we found it, otherwise failure. The 2nd position +// of the needle is compared starting from the 2nd position of the haystack and anded with the +// mask. If needle size == 2 and a match is found, success else failure. This continues for +// all needle sizes up to 6 bytes. +// +// ae - Argument encoding +// haystack - The address of the haystack +// haystack_len - the length of the haystack in elements +// needle - The address of the needle +// needle_len - the length of the needle in elements +// XMM0 - Temporary xmm register +// XMM1 - Temporary xmm register +// mask - Used to hold comparison mask +// tmp - Temporary register +// _masm - Current MacroAssembler instance pointer +static void highly_optimized_short_cases(StrIntrinsicNode::ArgEncoding ae, Register haystack, + Register haystack_len, Register needle, + Register needle_len, XMMRegister XMM0, XMMRegister XMM1, + Register mask, Register tmp, MacroAssembler *_masm) { + // Highly optimized special-cases + Label L_noMatch, L_foundall, L_out; + + bool isUL = (ae == StrIntrinsicNode::UL); + bool isUU = (ae == StrIntrinsicNode::UU); + bool isU = isUL || isUU; // At least one is UTF-16 + + // Only optimize when haystack can fit on stack with room + // left over for page fault prevention + assert((COPIED_HAYSTACK_STACK_OFFSET == 0), "Must be zero!"); + assert((COPIED_HAYSTACK_STACK_SIZE == 64), "Must be 64!"); + + // Copy incoming haystack onto stack + { + Label L_adjustHaystack, L_moreThan16; + + // Copy haystack to stack (haystack <= 32 bytes) + __ subptr(rsp, COPIED_HAYSTACK_STACK_SIZE); + __ cmpq(haystack_len, isU ? 0x8 : 0x10); + __ ja_b(L_moreThan16); + + __ movq(tmp, COPIED_HAYSTACK_STACK_OFFSET + 0x10); + __ movdqu(XMM0, Address(haystack, haystack_len, isU ? Address::times_2 : Address::times_1, -0x10)); + __ movdqu(Address(rsp, COPIED_HAYSTACK_STACK_OFFSET), XMM0); + __ jmpb(L_adjustHaystack); + + __ bind(L_moreThan16); + __ movq(tmp, COPIED_HAYSTACK_STACK_OFFSET + 0x20); + __ vmovdqu(XMM0, Address(haystack, haystack_len, isU ? Address::times_2 : Address::times_1, -0x20)); + __ vmovdqu(Address(rsp, COPIED_HAYSTACK_STACK_OFFSET), XMM0); + + __ bind(L_adjustHaystack); + __ subptr(tmp, haystack_len); + + if (isU) { + // For UTF-16, lengths are half + __ subptr(tmp, haystack_len); + } + // Point the haystack to the stack + __ leaq(haystack, Address(rsp, tmp, Address::times_1)); + } + + // Creates a mask of (n - k + 1) ones. This prevents recognizing any false-positives + // past the end of the valid haystack. + __ movq(mask, -1); + __ subq(haystack_len, needle_len); + __ incrementq(haystack_len); + if (isU) { + __ shlq(haystack_len, 1); + } + __ bzhiq(mask, mask, haystack_len); + + // Loop for each needle size from 1 to 6 bytes long. For UU, only 3 elements. + for (int size = 1; size <= (isUU ? 3 : 6); size++) { + // Broadcast next needle byte into ymm register + int needle_position = isUU ? (size - 1) * 2 : size - 1; + int haystack_position = isU ? (size - 1) * 2 : size - 1; + if (isUU) { + __ vpbroadcastw(XMM0, Address(needle, needle_position), Assembler::AVX_256bit); + } else if (isUL) { + // Expand needle + __ movzbl(tmp, Address(needle, needle_position)); + __ movdl(XMM0, tmp); + // Byte of needle to words + __ vpbroadcastw(XMM0, XMM0, Assembler::AVX_256bit); + } else { + __ vpbroadcastb(XMM0, Address(needle, needle_position), Assembler::AVX_256bit); + } + + // Compare next byte. Keep the comparison mask in mask, which will + // accumulate + vpcmpeq(XMM1, XMM0, Address(haystack, haystack_position), Assembler::AVX_256bit, ae, _masm); + __ vpmovmskb(tmp, XMM1, Assembler::AVX_256bit); + __ andq(mask, tmp); // Accumulate matched bytes + __ testl(mask, mask); + __ je(L_noMatch); + + if (size != (isUU ? 3 : 6)) { + // Found a match for this needle size + __ cmpq(needle_len, size); + __ je(L_foundall); + } + } + + __ bind(L_foundall); + __ tzcntl(rax, mask); + + if (isU) { + __ shrl(rax, 1); + } + + __ bind(L_out); + __ addptr(rsp, COPIED_HAYSTACK_STACK_SIZE); + __ vzeroupper(); + __ leave(); + __ ret(0); + + __ bind(L_noMatch); + __ movq(rax, -1); + __ jmpb(L_out); +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// +// Set up jump table entries for both small and large haystack switches. +// +// ae - Argument encoding +// L_error - Label to branch to if no match found +// L_checkRange - label to jump to when match found. Checks validity of returned index +// L_fixup - Jump to here for big cases. Return value is pointer to matching haystack byte +// *big_jump_table - Address of pointer to the first element of big jump table +// *small_jump_table - Address of pointer to the first element of small jump table +// _masm - Current MacroAssembler instance pointer + +static void setup_jump_tables(StrIntrinsicNode::ArgEncoding ae, Label &L_error, Label &L_checkRange, + Label &L_fixup, address *big_jump_table, address *small_jump_table, + MacroAssembler *_masm) { + bool isUL = (ae == StrIntrinsicNode::UL); + bool isUU = (ae == StrIntrinsicNode::UU); + bool isU = isUL || isUU; // At least one is UTF-16 + const XMMRegister byte_1 = XMM_BYTE_1; + + address big_hs_jmp_table[NUMBER_OF_CASES]; // Jump table for large haystacks + address small_hs_jmp_table[NUMBER_OF_CASES]; // Jump table for small haystacks + int jmp_ndx = 0; + + //////////////////////////////////////////////// + // On entry to each case, the register state is: + // + // rax = unused + // rbx = &haystack + // rcx = haystack length + // rdx = &needle + // rsi = haystack length + // rdi = &haystack + // rbp = unused + // r8 = unused + // r9 = unused + // r10 = hs_len - needle len + // r11 = unused + // r12 = needle length + // r13 = (needle length - 1) + // r14 = &needle + // r15 = unused + // XMM_BYTE_0 - first element of needle, broadcast + // XMM_BYTE_K - last element of needle, broadcast + + { + //////////////////////////////////////////////////////////////////////////////////////// + // + // Small haystack (<=32 bytes) switch + // + // Handle cases that were not handled in highly_optimized_short_cases, which will be + // haystack size <= 32 bytes with 6 < needle size < NUMBER_OF_CASES bytes. + + //////////////////////////////////////////////// + // The haystack is <= 32 bytes + // + // If a match is not found, branch to L_error (which will always + // return -1). + // + // If a match is found, jump to L_checkRange, which ensures the + // matched needle is not past the end of the haystack. + // + // The index where a match is found is returned in set_bit (r11). + + const Register haystack = rbx; + const Register needle = r14; + const Register needle_val = r8; + const Register set_bit = r11; + const Register eq_mask = rsi; + const Register rTmp = rax; + + for (int i = 6; i < NUMBER_OF_CASES; i++) { + small_hs_jmp_table[i] = __ pc(); + if (isU && ((i + 1) & 1)) { + continue; + } else { + broadcast_additional_needles(true, i + 1, needle, noreg, rTmp, ae, _masm); + + compare_haystack_to_needle(true, i + 1, L_error, haystack, eq_mask, noreg, rTmp, XMM_TMP1, + XMM_TMP2, ae, _masm); + + byte_compare_helper(i + 1, L_error, L_checkRange, needle, needle_val, haystack, eq_mask, + set_bit, rTmp, ae, _masm); + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Large haystack (> 32 bytes) switch + + { + //////////////////////////////////////////////// + // The haystack is > 32 bytes + // + // The value returned on a match is in hs_ptr (rcx) which is the address + // of the first matching byte within the haystack. The L_fixup label + // takes hs_ptr (rcx), haystack (rbx), and set_bit (r8) to compute the + // index as: hs_ptr - haystack + r8. hs_ptr - haystack is the offset + // within the haystack of the 32-byte chunk wherein a match was found, + // and set_bit is the index within that 32-byte chunk of the matching string. + + const Register haystack = rbx; + const Register needle = r14; + const Register needle_len = r12; + const Register needle_val = r15; + const Register set_bit = r8; + const Register eq_mask = r9; + const Register hs_ptr = rcx; + const Register hsLength = rsi; + const Register rTmp1 = rdi; + const Register rTmp2 = r15; + const Register rTmp3 = rdx; + const Register rTmp4 = r13; + + for (int i = 0; i < NUMBER_OF_CASES; i++) { + big_hs_jmp_table[i] = __ pc(); + if (isU && ((i + 1) & 1)) { + continue; + } else { + Label L_loopTop; + + big_case_loop_helper(true, i + 1, L_error, L_loopTop, eq_mask, hs_ptr, needle_len, + needle, haystack, hsLength, rTmp1, rTmp2, rTmp3, rTmp4, ae, _masm); + byte_compare_helper(i + 1, L_loopTop, L_fixup, needle, needle_val, hs_ptr, eq_mask, set_bit, + rTmp4, ae, _masm); + } + } + } + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////// + // JUMP TABLES + __ align(8); + + *big_jump_table = __ pc(); + + for (jmp_ndx = 0; jmp_ndx < NUMBER_OF_CASES; jmp_ndx++) { + __ emit_address(big_hs_jmp_table[jmp_ndx]); + } + + *small_jump_table = __ pc(); + + for (jmp_ndx = 0; jmp_ndx < NUMBER_OF_CASES; jmp_ndx++) { + __ emit_address(small_hs_jmp_table[jmp_ndx]); + } +} + +#undef STACK_SPACE +#undef MAX_NEEDLE_LEN_TO_EXPAND +#undef CLEAR_BIT +#undef XMM_BYTE_0 +#undef XMM_BYTE_K +#undef XMM_BYTE_1 +#undef XMM_TMP1 +#undef XMM_TMP2 +#undef XMM_TMP3 +#undef XMM_TMP4 + +#undef __ diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp index d73a8d0317107..edcd9b58963a4 100644 --- a/src/hotspot/cpu/x86/globals_x86.hpp +++ b/src/hotspot/cpu/x86/globals_x86.hpp @@ -115,6 +115,10 @@ define_pd_global(intx, InitArrayShortSize, 8*BytesPerLong); "Highest supported AVX instructions set on x86/x64") \ range(0, 3) \ \ + \ + product(bool, UseAPX, false, EXPERIMENTAL, \ + "Use Intel Advanced Performance Extensions") \ + \ product(bool, UseKNLSetting, false, DIAGNOSTIC, \ "Control whether Knights platform setting should be used") \ \ @@ -234,8 +238,6 @@ define_pd_global(intx, InitArrayShortSize, 8*BytesPerLong); "Turn off JVM mitigations related to Intel micro code " \ "mitigations for the Intel JCC erratum") \ \ - product(bool, UseAPX, false, EXPERIMENTAL, \ - "Use Advanced Performance Extensions on x86") \ // end of ARCH_FLAGS #endif // CPU_X86_GLOBALS_X86_HPP diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 577c56cb7a268..c9c4b056eb59d 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -4241,6 +4241,12 @@ void StubGenerator::generate_compiler_stubs() { generate_chacha_stubs(); +#ifdef COMPILER2 + if ((UseAVX == 2) && EnableX86ECoreOpts) { + generate_string_indexof(StubRoutines::_string_indexof_array); + } +#endif + if (UseAdler32Intrinsics) { StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32(); } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 02435bd172c47..374679750a489 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -575,6 +575,9 @@ class StubGenerator: public StubCodeGenerator { void generate_libm_stubs(); +#ifdef COMPILER2 + void generate_string_indexof(address *fnptrs); +#endif address generate_cont_thaw(const char* label, Continuation::thaw_kind kind); address generate_cont_thaw(); diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index cfb91c5c08368..b5ed3719897ff 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -37,7 +37,7 @@ enum platform_dependent_constants { _continuation_stubs_code_size = 1000 LP64_ONLY(+1000), // AVX512 intrinsics add more code in 64-bit VM, // Windows have more code to save/restore registers - _compiler_stubs_code_size = 20000 LP64_ONLY(+39000) WINDOWS_ONLY(+2000), + _compiler_stubs_code_size = 20000 LP64_ONLY(+46000) WINDOWS_ONLY(+2000), _final_stubs_code_size = 10000 LP64_ONLY(+20000) WINDOWS_ONLY(+2000) ZGC_ONLY(+20000) }; diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index f5389d0ef9016..cdefa3922be1a 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -56,6 +56,10 @@ const char* VM_Version::_features_names[] = { CPU_FEATURE_FLAGS(DECLARE_CPU_FEAT address VM_Version::_cpuinfo_segv_addr = 0; // Address of instruction after the one which causes SEGV address VM_Version::_cpuinfo_cont_addr = 0; +// Address of instruction which causes APX specific SEGV +address VM_Version::_cpuinfo_segv_addr_apx = 0; +// Address of instruction after the one which causes APX specific SEGV +address VM_Version::_cpuinfo_cont_addr_apx = 0; static BufferBlob* stub_blob; static const int stub_size = 2000; @@ -63,9 +67,11 @@ static const int stub_size = 2000; extern "C" { typedef void (*get_cpu_info_stub_t)(void*); typedef void (*detect_virt_stub_t)(uint32_t, uint32_t*); + typedef void (*clear_apx_test_state_t)(void); } static get_cpu_info_stub_t get_cpu_info_stub = nullptr; static detect_virt_stub_t detect_virt_stub = nullptr; +static clear_apx_test_state_t clear_apx_test_state_stub = nullptr; #ifdef _LP64 @@ -102,6 +108,27 @@ class VM_Version_StubGenerator: public StubCodeGenerator { VM_Version_StubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} + address clear_apx_test_state() { +# define __ _masm-> + address start = __ pc(); + // EGPRs are call clobbered registers, Explicit clearing of r16 and r31 during signal + // handling guarantees that preserved register values post signal handling were + // re-instantiated by operating system and not because they were not modified externally. + + /* FIXME Uncomment following code after OS enablement of + bool save_apx = UseAPX; + VM_Version::set_apx_cpuFeatures(); + UseAPX = true; + // EGPR state save/restoration. + __ mov64(r16, 0L); + __ mov64(r31, 0L); + UseAPX = save_apx; + VM_Version::clean_cpuFeatures(); + */ + __ ret(0); + return start; + } + address generate_get_cpu_info() { // Flags to test CPU type. const uint32_t HS_EFL_AC = 0x40000; @@ -113,7 +140,8 @@ class VM_Version_StubGenerator: public StubCodeGenerator { bool use_evex = FLAG_IS_DEFAULT(UseAVX) || (UseAVX > 2); Label detect_486, cpu486, detect_586, std_cpuid1, std_cpuid4; - Label sef_cpuid, ext_cpuid, ext_cpuid1, ext_cpuid5, ext_cpuid7, ext_cpuid8, done, wrapup; + Label sef_cpuid, sefsl1_cpuid, ext_cpuid, ext_cpuid1, ext_cpuid5, ext_cpuid7; + Label ext_cpuid8, done, wrapup, vector_save_restore, apx_save_restore_warning; Label legacy_setup, save_restore_except, legacy_save_restore, start_simd_check; StubCodeMark mark(this, "VM_Version", "get_cpu_info_stub"); @@ -288,7 +316,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movl(Address(rsi, 4), rdx); // - // cpuid(0x7) Structured Extended Features + // cpuid(0x7) Structured Extended Features Enumeration Leaf. // __ bind(sef_cpuid); __ movl(rax, 7); @@ -303,12 +331,16 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movl(Address(rsi, 8), rcx); __ movl(Address(rsi, 12), rdx); - // ECX = 1 + // + // cpuid(0x7) Structured Extended Features Enumeration Sub-Leaf 1. + // + __ bind(sefsl1_cpuid); __ movl(rax, 7); __ movl(rcx, 1); __ cpuid(); - __ lea(rsi, Address(rbp, in_bytes(VM_Version::sef_cpuid7_ecx1_offset()))); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::sefsl1_cpuid7_offset()))); __ movl(Address(rsi, 0), rax); + __ movl(Address(rsi, 4), rdx); // // Extended cpuid(0x80000000) @@ -387,6 +419,46 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movl(Address(rsi, 8), rcx); __ movl(Address(rsi,12), rdx); +#ifndef PRODUCT + // + // Check if OS has enabled XGETBV instruction to access XCR0 + // (OSXSAVE feature flag) and CPU supports APX + // + // To enable APX, check CPUID.EAX=7.ECX=1.EDX[21] bit for HW support + // and XCRO[19] bit for OS support to save/restore extended GPR state. + __ lea(rsi, Address(rbp, in_bytes(VM_Version::sefsl1_cpuid7_offset()))); + __ movl(rax, 0x200000); + __ andl(rax, Address(rsi, 4)); + __ cmpl(rax, 0x200000); + __ jcc(Assembler::notEqual, vector_save_restore); + // check _cpuid_info.xem_xcr0_eax.bits.apx_f + __ movl(rax, 0x80000); + __ andl(rax, Address(rbp, in_bytes(VM_Version::xem_xcr0_offset()))); // xcr0 bits apx_f + __ cmpl(rax, 0x80000); + __ jcc(Assembler::notEqual, vector_save_restore); + + /* FIXME: Uncomment while integrating JDK-8329032 + bool save_apx = UseAPX; + VM_Version::set_apx_cpuFeatures(); + UseAPX = true; + __ mov64(r16, VM_Version::egpr_test_value()); + __ mov64(r31, VM_Version::egpr_test_value()); + */ + __ xorl(rsi, rsi); + VM_Version::set_cpuinfo_segv_addr_apx(__ pc()); + // Generate SEGV + __ movl(rax, Address(rsi, 0)); + + VM_Version::set_cpuinfo_cont_addr_apx(__ pc()); + /* FIXME: Uncomment after integration of JDK-8329032 + __ lea(rsi, Address(rbp, in_bytes(VM_Version::apx_save_offset()))); + __ movq(Address(rsi, 0), r16); + __ movq(Address(rsi, 8), r31); + + UseAPX = save_apx; + */ +#endif + __ bind(vector_save_restore); // // Check if OS has enabled XGETBV instruction to access XCR0 // (OSXSAVE feature flag) and CPU supports AVX @@ -580,6 +652,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ vmovdqu(xmm7, Address(rsp, 0)); __ addptr(rsp, 32); #endif // _WINDOWS + generate_vzeroupper(wrapup); VM_Version::clean_cpuFeatures(); UseAVX = saved_useavx; @@ -940,6 +1013,7 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseAVX, use_avx_limit); } } + if (UseAVX > use_avx_limit) { if (UseSSE < 4) { warning("UseAVX=%d requires UseSSE=4, setting it to UseAVX=0", UseAVX); @@ -963,6 +1037,16 @@ void VM_Version::get_processor_features() { _features &= ~CPU_AVX512_VBMI2; _features &= ~CPU_AVX512_BITALG; _features &= ~CPU_AVX512_IFMA; + _features &= ~CPU_APX_F; + } + + // Currently APX support is only enabled for targets supporting AVX512VL feature. + bool apx_supported = os_supports_apx_egprs() && supports_apx_f() && supports_avx512vl(); + if (UseAPX && !apx_supported) { + warning("UseAPX is not supported on this CPU, setting it to false"); + FLAG_SET_DEFAULT(UseAPX, false); + } else if (FLAG_IS_DEFAULT(UseAPX)) { + FLAG_SET_DEFAULT(UseAPX, apx_supported ? true : false); } if (UseAVX < 2) { @@ -1002,14 +1086,6 @@ void VM_Version::get_processor_features() { } } - // APX support not enabled yet - if (UseAPX) { - if (!FLAG_IS_DEFAULT(UseAPX)) { - warning("APX is not supported on this CPU."); - } - FLAG_SET_DEFAULT(UseAPX, false); - } - if (FLAG_IS_DEFAULT(IntelJccErratumMitigation)) { _has_intel_jcc_erratum = compute_has_intel_jcc_erratum(); } else { @@ -2143,6 +2219,10 @@ int VM_Version::avx3_threshold() { FLAG_IS_DEFAULT(AVX3Threshold)) ? 0 : AVX3Threshold; } +void VM_Version::clear_apx_test_state() { + clear_apx_test_state_stub(); +} + static bool _vm_version_initialized = false; void VM_Version::initialize() { @@ -2160,6 +2240,8 @@ void VM_Version::initialize() { detect_virt_stub = CAST_TO_FN_PTR(detect_virt_stub_t, g.generate_detect_virt()); + clear_apx_test_state_stub = CAST_TO_FN_PTR(clear_apx_test_state_t, + g.clear_apx_test_state()); get_processor_features(); LP64_ONLY(Assembler::precompute_instructions();) @@ -2958,6 +3040,10 @@ uint64_t VM_Version::CpuidInfo::feature_flags() const { result |= CPU_SSE4_2; if (std_cpuid1_ecx.bits.popcnt != 0) result |= CPU_POPCNT; + if (sefsl1_cpuid7_edx.bits.apx_f != 0 && + xem_xcr0_eax.bits.apx_f != 0) { + result |= CPU_APX_F; + } if (std_cpuid1_ecx.bits.avx != 0 && std_cpuid1_ecx.bits.osxsave != 0 && xem_xcr0_eax.bits.sse != 0 && @@ -2968,7 +3054,7 @@ uint64_t VM_Version::CpuidInfo::feature_flags() const { result |= CPU_F16C; if (sef_cpuid7_ebx.bits.avx2 != 0) { result |= CPU_AVX2; - if (sef_cpuid7_ecx1_eax.bits.avx_ifma != 0) + if (sefsl1_cpuid7_eax.bits.avx_ifma != 0) result |= CPU_AVX_IFMA; } if (sef_cpuid7_ecx.bits.gfni != 0) @@ -3142,6 +3228,17 @@ bool VM_Version::os_supports_avx_vectors() { return retVal; } +bool VM_Version::os_supports_apx_egprs() { + if (!supports_apx_f()) { + return false; + } + if (_cpuid_info.apx_save[0] != egpr_test_value() || + _cpuid_info.apx_save[1] != egpr_test_value()) { + return false; + } + return true; +} + uint VM_Version::cores_per_cpu() { uint result = 1; if (is_intel()) { diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index 18b272ab58b0b..dc5fb960060bd 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -26,6 +26,7 @@ #define CPU_X86_VM_VERSION_X86_HPP #include "runtime/abstract_vm_version.hpp" +#include "utilities/debug.hpp" #include "utilities/macros.hpp" #include "utilities/sizes.hpp" @@ -279,7 +280,7 @@ class VM_Version : public Abstract_VM_Version { } bits; }; - union SefCpuid7Ecx1Eax { + union SefCpuid7SubLeaf1Eax { uint32_t value; struct { uint32_t : 23, @@ -288,6 +289,15 @@ class VM_Version : public Abstract_VM_Version { } bits; }; + union SefCpuid7SubLeaf1Edx { + uint32_t value; + struct { + uint32_t : 21, + apx_f : 1, + : 10; + } bits; + }; + union ExtCpuid1EEbx { uint32_t value; struct { @@ -308,7 +318,9 @@ class VM_Version : public Abstract_VM_Version { opmask : 1, zmm512 : 1, zmm32 : 1, - : 24; + : 11, + apx_f : 1, + : 12; } bits; }; @@ -319,8 +331,10 @@ class VM_Version : public Abstract_VM_Version { static bool _has_intel_jcc_erratum; - static address _cpuinfo_segv_addr; // address of instruction which causes SEGV - static address _cpuinfo_cont_addr; // address of instruction after the one which causes SEGV + static address _cpuinfo_segv_addr; // address of instruction which causes SEGV + static address _cpuinfo_cont_addr; // address of instruction after the one which causes SEGV + static address _cpuinfo_segv_addr_apx; // address of instruction which causes APX specific SEGV + static address _cpuinfo_cont_addr_apx; // address of instruction after the one which causes APX specific SEGV /* * Update following files when declaring new flags: @@ -400,7 +414,8 @@ class VM_Version : public Abstract_VM_Version { decl(CET_IBT, "cet_ibt", 56) /* Control Flow Enforcement - Indirect Branch Tracking */ \ decl(CET_SS, "cet_ss", 57) /* Control Flow Enforcement - Shadow Stack */ \ decl(AVX512_IFMA, "avx512_ifma", 58) /* Integer Vector FMA instructions*/ \ - decl(AVX_IFMA, "avx_ifma", 59) /* 256-bit VEX-coded variant of AVX512-IFMA*/ + decl(AVX_IFMA, "avx_ifma", 59) /* 256-bit VEX-coded variant of AVX512-IFMA*/ \ + decl(APX_F, "apx_f", 60) /* Intel Advanced Performance Extensions*/ #define DECLARE_CPU_FEATURE_FLAG(id, name, bit) CPU_##id = (1ULL << bit), CPU_FEATURE_FLAGS(DECLARE_CPU_FEATURE_FLAG) @@ -458,14 +473,17 @@ class VM_Version : public Abstract_VM_Version { uint32_t dcp_cpuid4_ecx; // unused currently uint32_t dcp_cpuid4_edx; // unused currently - // cpuid function 7 (structured extended features) - // ECX = 0 before calling cpuid() + // cpuid function 7 (structured extended features enumeration leaf) + // eax = 7, ecx = 0 SefCpuid7Eax sef_cpuid7_eax; SefCpuid7Ebx sef_cpuid7_ebx; SefCpuid7Ecx sef_cpuid7_ecx; SefCpuid7Edx sef_cpuid7_edx; - // ECX = 1 before calling cpuid() - SefCpuid7Ecx1Eax sef_cpuid7_ecx1_eax; + + // cpuid function 7 (structured extended features enumeration sub-leaf 1) + // eax = 7, ecx = 1 + SefCpuid7SubLeaf1Eax sefsl1_cpuid7_eax; + SefCpuid7SubLeaf1Edx sefsl1_cpuid7_edx; // cpuid function 0xB (processor topology) // ecx = 0 @@ -537,6 +555,9 @@ class VM_Version : public Abstract_VM_Version { // Space to save zmm registers after signal handle int zmm_save[16*4]; // Save zmm0, zmm7, zmm8, zmm31 + // Space to save apx registers after signal handle + jlong apx_save[2]; // Save r16 and r31 + uint64_t feature_flags() const; // Asserts @@ -576,6 +597,7 @@ class VM_Version : public Abstract_VM_Version { static bool compute_has_intel_jcc_erratum(); static bool os_supports_avx_vectors(); + static bool os_supports_apx_egprs(); static void get_processor_features(); public: @@ -584,7 +606,7 @@ class VM_Version : public Abstract_VM_Version { static ByteSize std_cpuid1_offset() { return byte_offset_of(CpuidInfo, std_cpuid1_eax); } static ByteSize dcp_cpuid4_offset() { return byte_offset_of(CpuidInfo, dcp_cpuid4_eax); } static ByteSize sef_cpuid7_offset() { return byte_offset_of(CpuidInfo, sef_cpuid7_eax); } - static ByteSize sef_cpuid7_ecx1_offset() { return byte_offset_of(CpuidInfo, sef_cpuid7_ecx1_eax); } + static ByteSize sefsl1_cpuid7_offset() { return byte_offset_of(CpuidInfo, sefsl1_cpuid7_eax); } static ByteSize ext_cpuid1_offset() { return byte_offset_of(CpuidInfo, ext_cpuid1_eax); } static ByteSize ext_cpuid5_offset() { return byte_offset_of(CpuidInfo, ext_cpuid5_eax); } static ByteSize ext_cpuid7_offset() { return byte_offset_of(CpuidInfo, ext_cpuid7_eax); } @@ -596,9 +618,11 @@ class VM_Version : public Abstract_VM_Version { static ByteSize xem_xcr0_offset() { return byte_offset_of(CpuidInfo, xem_xcr0_eax); } static ByteSize ymm_save_offset() { return byte_offset_of(CpuidInfo, ymm_save); } static ByteSize zmm_save_offset() { return byte_offset_of(CpuidInfo, zmm_save); } + static ByteSize apx_save_offset() { return byte_offset_of(CpuidInfo, apx_save); } // The value used to check ymm register after signal handle static int ymm_test_value() { return 0xCAFEBABE; } + static jlong egpr_test_value() { return 0xCAFEBABECAFEBABELL; } static void get_cpu_info_wrapper(); static void set_cpuinfo_segv_addr(address pc) { _cpuinfo_segv_addr = pc; } @@ -606,9 +630,17 @@ class VM_Version : public Abstract_VM_Version { static void set_cpuinfo_cont_addr(address pc) { _cpuinfo_cont_addr = pc; } static address cpuinfo_cont_addr() { return _cpuinfo_cont_addr; } + static void set_cpuinfo_segv_addr_apx(address pc) { _cpuinfo_segv_addr_apx = pc; } + static bool is_cpuinfo_segv_addr_apx(address pc) { return _cpuinfo_segv_addr_apx == pc; } + static void set_cpuinfo_cont_addr_apx(address pc) { _cpuinfo_cont_addr_apx = pc; } + static address cpuinfo_cont_addr_apx() { return _cpuinfo_cont_addr_apx; } + + static void clear_apx_test_state(); + static void clean_cpuFeatures() { _features = 0; } static void set_avx_cpuFeatures() { _features = (CPU_SSE | CPU_SSE2 | CPU_AVX | CPU_VZEROUPPER ); } static void set_evex_cpuFeatures() { _features = (CPU_AVX512F | CPU_SSE | CPU_SSE2 | CPU_VZEROUPPER ); } + static void set_apx_cpuFeatures() { _features |= CPU_APX_F; } // Initialization static void initialize(); @@ -705,6 +737,7 @@ class VM_Version : public Abstract_VM_Version { static bool supports_avx512novl() { return (supports_evex() && !supports_avx512vl()); } static bool supports_avx512nobw() { return (supports_evex() && !supports_avx512bw()); } static bool supports_avx256only() { return (supports_avx2() && !supports_evex()); } + static bool supports_apx_f() { return (_features & CPU_APX_F) != 0; } static bool supports_avxonly() { return ((supports_avx2() || supports_avx()) && !supports_evex()); } static bool supports_sha() { return (_features & CPU_SHA) != 0; } static bool supports_fma() { return (_features & CPU_FMA) != 0 && supports_avx(); } diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 30783c809f968..773bda647e15f 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -2744,6 +2744,15 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { // Verify that OS save/restore AVX registers. return Handle_Exception(exceptionInfo, VM_Version::cpuinfo_cont_addr()); } + +#ifndef PRODUCT + if ((exception_code == EXCEPTION_ACCESS_VIOLATION) && + VM_Version::is_cpuinfo_segv_addr_apx(pc)) { + // Verify that OS save/restore APX registers. + VM_Version::clear_apx_test_state(); + return Handle_Exception(exceptionInfo, VM_Version::cpuinfo_cont_addr_apx()); + } +#endif #endif if (t != nullptr && t->is_Java_thread()) { diff --git a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp b/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp index 4e7316e06610a..17f256eadb78a 100644 --- a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp +++ b/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp @@ -416,6 +416,14 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, stub = VM_Version::cpuinfo_cont_addr(); } +#ifndef PRODUCT + if ((sig == SIGSEGV || sig == SIGBUS) && VM_Version::is_cpuinfo_segv_addr_apx(pc)) { + // Verify that OS save/restore APX registers. + stub = VM_Version::cpuinfo_cont_addr_apx(); + VM_Version::clear_apx_test_state(); + } +#endif + // We test if stub is already set (by the stack overflow code // above) so it is not overwritten by the code that follows. This // check is not required on other platforms, because on other diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index 6cdb2a5e3f66a..35e9321a2a7af 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -248,6 +248,14 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, stub = VM_Version::cpuinfo_cont_addr(); } +#ifndef PRODUCT + if ((sig == SIGSEGV) && VM_Version::is_cpuinfo_segv_addr_apx(pc)) { + // Verify that OS save/restore APX registers. + stub = VM_Version::cpuinfo_cont_addr_apx(); + VM_Version::clear_apx_test_state(); + } +#endif + if (thread->thread_state() == _thread_in_Java) { // Java thread running in Java code => find exception handler if any // a fault inside compiled code, the interpreter, or a stub diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp index 20954156b9e4a..7b30e1ecbac70 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetNMethod.cpp @@ -36,13 +36,19 @@ #include "runtime/threadWXSetters.inline.hpp" bool ShenandoahBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { + if (!is_armed(nm)) { + // Some other thread got here first and healed the oops + // and disarmed the nmethod. No need to continue. + return true; + } + ShenandoahReentrantLock* lock = ShenandoahNMethod::lock_for_nmethod(nm); assert(lock != nullptr, "Must be"); ShenandoahReentrantLocker locker(lock); if (!is_armed(nm)) { - // Some other thread got here first and healed the oops - // and disarmed the nmethod. + // Some other thread managed to complete while we were + // waiting for lock. No need to continue. return true; } diff --git a/src/hotspot/share/nmt/mallocHeader.cpp b/src/hotspot/share/nmt/mallocHeader.cpp index a125ba416b1a6..d5a7b689c2a48 100644 --- a/src/hotspot/share/nmt/mallocHeader.cpp +++ b/src/hotspot/share/nmt/mallocHeader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -64,7 +64,3 @@ void MallocHeader::print_block_on_error(outputStream* st, address bad_address) c os::print_hex_dump(st, from1, to2, 1); } } - -bool MallocHeader::get_stack(NativeCallStack& stack) const { - return MallocSiteTable::access_stack(stack, _mst_marker); -} diff --git a/src/hotspot/share/nmt/mallocHeader.hpp b/src/hotspot/share/nmt/mallocHeader.hpp index 6d847a22de3f4..9f9f7f97ea7f5 100644 --- a/src/hotspot/share/nmt/mallocHeader.hpp +++ b/src/hotspot/share/nmt/mallocHeader.hpp @@ -130,7 +130,6 @@ class MallocHeader { inline size_t size() const { return _size; } inline MEMFLAGS flags() const { return _flags; } inline uint32_t mst_marker() const { return _mst_marker; } - bool get_stack(NativeCallStack& stack) const; // Return the necessary data to deaccount the block with NMT. FreeInfo free_info() { diff --git a/src/hotspot/share/nmt/mallocSiteTable.hpp b/src/hotspot/share/nmt/mallocSiteTable.hpp index 47fe82b590a9f..ae9266f5369a0 100644 --- a/src/hotspot/share/nmt/mallocSiteTable.hpp +++ b/src/hotspot/share/nmt/mallocSiteTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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 @@ -131,8 +131,8 @@ class MallocSiteTable : AllStatic { // Access and copy a call stack from this table. Shared lock should be // acquired before access the entry. - static inline bool access_stack(NativeCallStack& stack, uint32_t marker) { - MallocSite* site = malloc_site(marker); + static inline bool access_stack(NativeCallStack& stack, const MallocHeader& header) { + MallocSite* site = malloc_site(header.mst_marker()); if (site != nullptr) { stack = *site->call_stack(); return true; diff --git a/src/hotspot/share/nmt/mallocTracker.cpp b/src/hotspot/share/nmt/mallocTracker.cpp index ab323b3bae6db..021ce5d1332b1 100644 --- a/src/hotspot/share/nmt/mallocTracker.cpp +++ b/src/hotspot/share/nmt/mallocTracker.cpp @@ -299,7 +299,7 @@ bool MallocTracker::print_pointer_information(const void* p, outputStream* st) { block->size(), NMTUtil::flag_to_enum_name(block->flags())); if (MemTracker::tracking_level() == NMT_detail) { NativeCallStack ncs; - if (block->get_stack(ncs)) { + if (MallocSiteTable::access_stack(ncs, *block)) { ncs.print_on(st); st->cr(); } diff --git a/src/hotspot/share/oops/klassVtable.hpp b/src/hotspot/share/oops/klassVtable.hpp index f986139d3f4c8..349a859a17f5e 100644 --- a/src/hotspot/share/oops/klassVtable.hpp +++ b/src/hotspot/share/oops/klassVtable.hpp @@ -54,6 +54,9 @@ class klassVtable { klassVtable(Klass* klass, void* base, int length) : _klass(klass) { _tableOffset = int((address)base - (address)klass); _length = length; +#ifndef PRODUCT + _verify_count = 0; +#endif } // accessors diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 5630ee0228d7d..8a80392d5c786 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -2197,6 +2197,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) { strcmp(call->as_CallLeaf()->_name, "bigIntegerRightShiftWorker") == 0 || strcmp(call->as_CallLeaf()->_name, "bigIntegerLeftShiftWorker") == 0 || strcmp(call->as_CallLeaf()->_name, "vectorizedMismatch") == 0 || + strcmp(call->as_CallLeaf()->_name, "stringIndexOf") == 0 || strcmp(call->as_CallLeaf()->_name, "arraysort_stub") == 0 || strcmp(call->as_CallLeaf()->_name, "array_partition_stub") == 0 || strcmp(call->as_CallLeaf()->_name, "get_class_id_intrinsic") == 0 || diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 33c2d7309d0fd..b3253a817a408 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -1205,6 +1205,9 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) { Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE); Node* tgt_count = load_array_length(tgt); + Node* result = nullptr; + bool call_opt_stub = (StubRoutines::_string_indexof_array[ae] != nullptr); + if (ae == StrIntrinsicNode::UU || ae == StrIntrinsicNode::UL) { // Divide src size by 2 if String is UTF16 encoded src_count = _gvn.transform(new RShiftINode(src_count, intcon(1))); @@ -1214,7 +1217,16 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) { tgt_count = _gvn.transform(new RShiftINode(tgt_count, intcon(1))); } - Node* result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, result_rgn, result_phi, ae); + if (call_opt_stub) { + Node* call = make_runtime_call(RC_LEAF, OptoRuntime::string_IndexOf_Type(), + StubRoutines::_string_indexof_array[ae], + "stringIndexOf", TypePtr::BOTTOM, src_start, + src_count, tgt_start, tgt_count); + result = _gvn.transform(new ProjNode(call, TypeFunc::Parms)); + } else { + result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, + result_rgn, result_phi, ae); + } if (result != nullptr) { result_phi->init_req(3, result); result_rgn->init_req(3, control()); @@ -1226,7 +1238,7 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) { return true; } -//-----------------------------inline_string_indexOf----------------------- +//-----------------------------inline_string_indexOfI----------------------- bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { if (too_many_traps(Deoptimization::Reason_intrinsic)) { return false; @@ -1234,6 +1246,7 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { if (!Matcher::match_rule_supported(Op_StrIndexOf)) { return false; } + assert(callee()->signature()->size() == 5, "String.indexOf() has 5 arguments"); Node* src = argument(0); // byte[] Node* src_count = argument(1); // char count @@ -1259,8 +1272,21 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { RegionNode* region = new RegionNode(5); Node* phi = new PhiNode(region, TypeInt::INT); + Node* result = nullptr; + + bool call_opt_stub = (StubRoutines::_string_indexof_array[ae] != nullptr); - Node* result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, region, phi, ae); + if (call_opt_stub) { + assert(arrayOopDesc::base_offset_in_bytes(T_BYTE) >= 16, "Needed for indexOf"); + Node* call = make_runtime_call(RC_LEAF, OptoRuntime::string_IndexOf_Type(), + StubRoutines::_string_indexof_array[ae], + "stringIndexOf", TypePtr::BOTTOM, src_start, + src_count, tgt_start, tgt_count); + result = _gvn.transform(new ProjNode(call, TypeFunc::Parms)); + } else { + result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, + region, phi, ae); + } if (result != nullptr) { // The result is index relative to from_index if substring was found, -1 otherwise. // Generate code which will fold into cmove. diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index d8e5cdbab0409..a6f9a7e54703d 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -1357,6 +1357,27 @@ const TypeFunc* OptoRuntime::base64_encodeBlock_Type() { const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields); return TypeFunc::make(domain, range); } + +// String IndexOf function +const TypeFunc* OptoRuntime::string_IndexOf_Type() { + int argcnt = 4; + + const Type** fields = TypeTuple::fields(argcnt); + int argp = TypeFunc::Parms; + fields[argp++] = TypePtr::NOTNULL; // haystack array + fields[argp++] = TypeInt::INT; // haystack length + fields[argp++] = TypePtr::NOTNULL; // needle array + fields[argp++] = TypeInt::INT; // needle length + assert(argp == TypeFunc::Parms + argcnt, "correct decoding"); + const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); + + // result type needed + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms + 0] = TypeInt::INT; // Index of needle in haystack + const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields); + return TypeFunc::make(domain, range); +} + // Base64 decode function const TypeFunc* OptoRuntime::base64_decodeBlock_Type() { int argcnt = 7; diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index e4cbdf2f0d0b4..9ca8ac0494339 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -297,6 +297,7 @@ class OptoRuntime : public AllStatic { static const TypeFunc* chacha20Block_Type(); static const TypeFunc* base64_encodeBlock_Type(); static const TypeFunc* base64_decodeBlock_Type(); + static const TypeFunc* string_IndexOf_Type(); static const TypeFunc* poly1305_processBlocks_Type(); static const TypeFunc* intpoly_montgomeryMult_P256_Type(); static const TypeFunc* intpoly_assign_Type(); diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 74286a4ac98fb..773f8031e154c 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -149,6 +149,8 @@ address StubRoutines::_sha3_implCompressMB = nullptr; address StubRoutines::_updateBytesCRC32 = nullptr; address StubRoutines::_crc_table_adr = nullptr; +address StubRoutines::_string_indexof_array[4] = { nullptr }; + address StubRoutines::_crc32c_table_addr = nullptr; address StubRoutines::_updateBytesCRC32C = nullptr; address StubRoutines::_updateBytesAdler32 = nullptr; diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index 65b0c0d2f26f9..762a6edf59075 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -232,6 +232,8 @@ class StubRoutines: AllStatic { static address _updateBytesCRC32; static address _crc_table_adr; + static address _string_indexof_array[4]; + static address _crc32c_table_addr; static address _updateBytesCRC32C; static address _updateBytesAdler32; diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp index 72df31e12f90a..6057d1a7710b0 100644 --- a/src/hotspot/share/utilities/ostream.cpp +++ b/src/hotspot/share/utilities/ostream.cpp @@ -979,7 +979,7 @@ void ostream_exit() { ClassListWriter::delete_classlist(); // Make sure tty works after VM exit by assigning an always-on functioning fdStream. outputStream* tmp = tty; - tty = DisplayVMOutputToStderr ? fdStream::stdout_stream() : fdStream::stderr_stream(); + tty = DisplayVMOutputToStderr ? fdStream::stderr_stream() : fdStream::stdout_stream(); if (tmp != &tty_preinit_stream && tmp != defaultStream::instance) { delete tmp; } diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 4573a6dc69004..4bc7ccab8345d 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -70,6 +70,7 @@ import java.util.Set; import java.util.stream.Collectors; +import jdk.internal.constant.ConstantUtils; import jdk.internal.javac.PreviewFeature; import jdk.internal.loader.BootLoader; import jdk.internal.loader.BuiltinClassLoader; @@ -4709,7 +4710,7 @@ public Class arrayType() { public Optional describeConstable() { Class c = isArray() ? elementType() : this; return c.isHidden() ? Optional.empty() - : Optional.of(ClassDesc.ofDescriptor(descriptorString())); + : Optional.of(ConstantUtils.classDesc(this)); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/ClassFile.java b/src/java.base/share/classes/java/lang/classfile/ClassFile.java index eb93a8c6a315f..1997ffb487ce0 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassFile.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassFile.java @@ -45,6 +45,7 @@ import java.lang.classfile.instruction.ExceptionCatch; import java.util.List; import static java.util.Objects.requireNonNull; +import static jdk.internal.constant.ConstantUtils.CD_module_info; import jdk.internal.javac.PreviewFeature; /** @@ -392,7 +393,7 @@ default byte[] buildModule(ModuleAttribute moduleAttribute) { */ default byte[] buildModule(ModuleAttribute moduleAttribute, Consumer handler) { - return build(ClassDesc.of("module-info"), clb -> { + return build(CD_module_info, clb -> { clb.withFlags(AccessFlag.MODULE); clb.with(moduleAttribute); handler.accept(clb); diff --git a/src/java.base/share/classes/java/lang/constant/ClassDesc.java b/src/java.base/share/classes/java/lang/constant/ClassDesc.java index 130a81420b68b..370d2eee507f6 100644 --- a/src/java.base/share/classes/java/lang/constant/ClassDesc.java +++ b/src/java.base/share/classes/java/lang/constant/ClassDesc.java @@ -36,7 +36,6 @@ import static jdk.internal.constant.ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS; import static jdk.internal.constant.ConstantUtils.arrayDepth; import static jdk.internal.constant.ConstantUtils.binaryToInternal; -import static jdk.internal.constant.ConstantUtils.dropFirstAndLastChar; import static jdk.internal.constant.ConstantUtils.internalToBinary; import static jdk.internal.constant.ConstantUtils.validateBinaryClassName; import static jdk.internal.constant.ConstantUtils.validateInternalClassName; @@ -165,7 +164,7 @@ static ClassDesc of(String packageName, String className) { static ClassDesc ofDescriptor(String descriptor) { // implicit null-check return (descriptor.length() == 1) - ? Wrapper.forPrimitiveType(descriptor.charAt(0)).classDescriptor() + ? Wrapper.forPrimitiveType(descriptor.charAt(0)).basicClassDescriptor() // will throw IAE on descriptor.length == 0 or if array dimensions too long : ReferenceClassDescImpl.of(descriptor); } @@ -315,7 +314,7 @@ default ClassDesc componentType() { if (isArray()) { String desc = descriptorString(); if (desc.length() == 2) { - return Wrapper.forBasicType(desc.charAt(1)).classDescriptor(); + return Wrapper.forBasicType(desc.charAt(1)).basicClassDescriptor(); } else { return ReferenceClassDescImpl.ofValidated(desc.substring(1)); } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index 5ca45e3fe529d..8f0f355a9802d 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, 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 @@ -52,6 +52,8 @@ import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; import java.lang.classfile.TypeKind; + +import jdk.internal.constant.ConstantUtils; import jdk.internal.module.Modules; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -63,6 +65,7 @@ import static java.lang.invoke.MethodType.methodType; import static java.lang.module.ModuleDescriptor.Modifier.SYNTHETIC; import static java.lang.classfile.ClassFile.*; +import static jdk.internal.constant.ConstantUtils.*; /** * This class consists exclusively of static methods that help adapt @@ -249,14 +252,14 @@ private static Class newProxyClass(Class intfc) { // the field name holding the method handle for this method String fieldName = "m" + count++; - var mt = methodType(m.getReturnType(), JLRA.getExecutableSharedParameterTypes(m), true); + var md = methodTypeDesc(m.getReturnType(), JLRA.getExecutableSharedParameterTypes(m)); var thrown = JLRA.getExecutableSharedExceptionTypes(m); var exceptionTypeDescs = thrown.length == 0 ? DEFAULT_RETHROWS : Stream.concat(DEFAULT_RETHROWS.stream(), - Arrays.stream(thrown).map(MethodHandleProxies::desc)) + Arrays.stream(thrown).map(ConstantUtils::referenceClassDesc)) .distinct().toList(); - methods.add(new MethodInfo(desc(mt), exceptionTypeDescs, fieldName)); + methods.add(new MethodInfo(md, exceptionTypeDescs, fieldName)); // find the types referenced by this method addElementType(referencedTypes, m.getReturnType()); @@ -279,7 +282,8 @@ private static Class newProxyClass(Class intfc) { int i = intfcName.lastIndexOf('.'); // jdk.MHProxy#.Interface String className = packageName + "." + (i > 0 ? intfcName.substring(i + 1) : intfcName); - byte[] template = createTemplate(loader, ClassDesc.of(className), desc(intfc), uniqueName, methods); + byte[] template = createTemplate(loader, binaryNameToDesc(className), + referenceClassDesc(intfc), uniqueName, methods); // define the dynamic module to the class loader of the interface var definer = new Lookup(intfc).makeHiddenClassDefiner(className, template, Set.of(), DUMPER); @@ -335,17 +339,17 @@ private static Class getProxyClass(Class intfc) { } } - private static final List DEFAULT_RETHROWS = List.of(desc(RuntimeException.class), desc(Error.class)); - private static final ClassDesc CD_UndeclaredThrowableException = desc(UndeclaredThrowableException.class); - private static final ClassDesc CD_IllegalAccessException = desc(IllegalAccessException.class); + private static final List DEFAULT_RETHROWS = List.of(referenceClassDesc(RuntimeException.class), referenceClassDesc(Error.class)); + private static final ClassDesc CD_UndeclaredThrowableException = referenceClassDesc(UndeclaredThrowableException.class); + private static final ClassDesc CD_IllegalAccessException = referenceClassDesc(IllegalAccessException.class); private static final MethodTypeDesc MTD_void_Throwable = MethodTypeDesc.of(CD_void, CD_Throwable); private static final MethodType MT_void_Lookup_MethodHandle_MethodHandle = methodType(void.class, Lookup.class, MethodHandle.class, MethodHandle.class); private static final MethodType MT_Object_Lookup_MethodHandle_MethodHandle = MT_void_Lookup_MethodHandle_MethodHandle.changeReturnType(Object.class); private static final MethodType MT_MethodHandle_Object = methodType(MethodHandle.class, Object.class); - private static final MethodTypeDesc MTD_void_Lookup_MethodHandle_MethodHandle = - desc(MT_void_Lookup_MethodHandle_MethodHandle); + private static final MethodTypeDesc MTD_void_Lookup_MethodHandle_MethodHandle + = methodTypeDesc(MT_void_Lookup_MethodHandle_MethodHandle); private static final MethodTypeDesc MTD_void_Lookup = MethodTypeDesc.of(CD_void, CD_MethodHandles_Lookup); private static final MethodTypeDesc MTD_MethodHandle_MethodType = MethodTypeDesc.of(CD_MethodHandle, CD_MethodType); private static final MethodTypeDesc MTD_Class = MethodTypeDesc.of(CD_Class); @@ -531,16 +535,6 @@ public static Class wrapperInstanceType(Object x) { } } - private static ClassDesc desc(Class cl) { - return cl.describeConstable().orElseThrow(() -> newInternalError("Cannot convert class " - + cl.getName() + " to a constant")); - } - - private static MethodTypeDesc desc(MethodType mt) { - return mt.describeConstable().orElseThrow(() -> newInternalError("Cannot convert method type " - + mt + " to a constant")); - } - private static final JavaLangReflectAccess JLRA = SharedSecrets.getJavaLangReflectAccess(); private static final AtomicInteger counter = new AtomicInteger(); diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java index 3499fd8352595..29896fd8f93df 100644 --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -27,6 +27,7 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.constant.ConstantUtils; import jdk.internal.misc.VM; import jdk.internal.util.ClassFileDumper; import jdk.internal.vm.annotation.Stable; @@ -1090,13 +1091,13 @@ private SimpleStringBuilderStrategy() { private static MethodHandle generate(Lookup lookup, MethodType args, String[] constants) throws Exception { String className = getClassName(lookup.lookupClass()); - byte[] classBytes = ClassFile.of().build(ClassDesc.of(className), + byte[] classBytes = ClassFile.of().build(ConstantUtils.binaryNameToDesc(className), new Consumer() { @Override public void accept(ClassBuilder clb) { clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC) .withMethodBody(METHOD_NAME, - MethodTypeDesc.ofDescriptor(args.toMethodDescriptorString()), + ConstantUtils.methodTypeDesc(args), ClassFile.ACC_FINAL | ClassFile.ACC_PRIVATE | ClassFile.ACC_STATIC, generateMethod(constants, args)); }}); diff --git a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java index 776f5df4e5acd..a484c19120662 100644 --- a/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java +++ b/src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java @@ -49,6 +49,8 @@ import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.constant.ConstantDescs; import static java.lang.constant.ConstantDescs.*; +import static jdk.internal.constant.ConstantUtils.*; + import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicConstantDesc; @@ -134,7 +136,7 @@ final class ProxyGenerator { /** * Name of proxy class */ - private ClassEntry classEntry; + private final ClassEntry classEntry; /** * Proxy interfaces @@ -160,10 +162,10 @@ final class ProxyGenerator { * A ProxyGenerator object contains the state for the ongoing * generation of a particular proxy class. */ - private ProxyGenerator(ClassLoader loader, String className, List> interfaces, + private ProxyGenerator(String className, List> interfaces, int accessFlags) { this.cp = ConstantPoolBuilder.of(); - this.classEntry = cp.classEntry(ReferenceClassDescImpl.ofValidatedBinaryName(className)); + this.classEntry = cp.classEntry(ConstantUtils.binaryNameToDesc(className)); this.interfaces = interfaces; this.accessFlags = accessFlags; this.throwableStack = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(cp.classEntry(CD_Throwable))); @@ -190,7 +192,7 @@ static byte[] generateProxyClass(ClassLoader loader, List> interfaces, int accessFlags) { Objects.requireNonNull(interfaces); - ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); + ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); final byte[] classFile = gen.generateClassFile(); if (SAVE_GENERATED_FILES) { @@ -227,18 +229,10 @@ public Void run() { private static List toClassEntries(ConstantPoolBuilder cp, List> types) { var ces = new ArrayList(types.size()); for (var t : types) - ces.add(cp.classEntry(ReferenceClassDescImpl.ofValidatedBinaryName(t.getName()))); + ces.add(cp.classEntry(ConstantUtils.binaryNameToDesc(t.getName()))); return ces; } - /** - * {@return the {@code ClassDesc} of the given type} - * @param type the {@code Class} object - */ - private static ClassDesc toClassDesc(Class type) { - return ClassDesc.ofDescriptor(type.descriptorString()); - } - /** * For a given set of proxy methods with the same signature, check * that their return types are compatible according to the Proxy @@ -325,7 +319,7 @@ private static void checkReturnTypes(List methods) { * not assignable from any of the others. */ if (uncoveredReturnTypes.size() > 1) { - ProxyMethod pm = methods.get(0); + ProxyMethod pm = methods.getFirst(); throw new IllegalArgumentException( "methods with same signature " + pm.shortSignature + @@ -501,7 +495,7 @@ private void addProxyMethod(Method m, Class fromClass) { String sig = m.toShortSignature(); List sigmethods = proxyMethods.computeIfAbsent(sig, - (f) -> new ArrayList<>(3)); + _ -> new ArrayList<>(3)); for (ProxyMethod pm : sigmethods) { if (returnType == pm.returnType) { /* @@ -531,7 +525,7 @@ private void addProxyMethod(Method m, Class fromClass) { private void addProxyMethod(ProxyMethod pm) { String sig = pm.shortSignature; List sigmethods = proxyMethods.computeIfAbsent(sig, - (f) -> new ArrayList<>(3)); + _ -> new ArrayList<>(3)); sigmethods.add(pm); } @@ -637,7 +631,6 @@ private ProxyMethod(Method method, String sig, Class[] parameterTypes, * Create a new specific ProxyMethod with a specific field name * * @param method The method for which to create a proxy - * @param methodFieldName the fieldName to generate */ private ProxyMethod(Method method) { this(method, method.toShortSignature(), @@ -650,11 +643,7 @@ private ProxyMethod(Method method) { */ private void generateMethod(ProxyGenerator pg, ClassBuilder clb) { var cp = pg.cp; - var pTypes = new ClassDesc[parameterTypes.length]; - for (int i = 0; i < pTypes.length; i++) { - pTypes[i] = toClassDesc(parameterTypes[i]); - } - MethodTypeDesc desc = MethodTypeDescImpl.ofTrusted(toClassDesc(returnType), pTypes); + var desc = methodTypeDesc(returnType, parameterTypes); int accessFlags = (method.isVarArgs()) ? ACC_VARARGS | ACC_PUBLIC | ACC_FINAL : ACC_PUBLIC | ACC_FINAL; var catchList = computeUniqueCatchList(exceptionTypes); @@ -665,7 +654,7 @@ private void generateMethod(ProxyGenerator pg, ClassBuilder clb) { .getfield(pg.handlerField) .aload(0) .ldc(DynamicConstantDesc.of(pg.bsm, - toClassDesc(fromClass), + referenceClassDesc(fromClass), method.getName(), desc)); if (parameterTypes.length > 0) { @@ -693,7 +682,7 @@ private void generateMethod(ProxyGenerator pg, ClassBuilder clb) { if (!catchList.isEmpty()) { var c1 = cob.newBoundLabel(); for (var exc : catchList) { - cob.exceptionCatch(cob.startLabel(), c1, c1, toClassDesc(exc)); + cob.exceptionCatch(cob.startLabel(), c1, c1, referenceClassDesc(exc)); } cob.athrow(); // just rethrow the exception var c2 = cob.newBoundLabel(); @@ -739,7 +728,7 @@ private void codeUnwrapReturnValue(CodeBuilder cob, Class type) { .invokevirtual(prim.unwrapMethodRef(cob.constantPool())) .return_(TypeKind.from(type).asLoadable()); } else { - cob.checkcast(toClassDesc(type)) + cob.checkcast(referenceClassDesc(type)) .areturn(); } } diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index c564b7b69f6df..3eecd2ab42f9c 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -26,7 +26,6 @@ package java.lang.runtime; import java.lang.Enum.EnumDesc; -import java.lang.classfile.ClassBuilder; import java.lang.classfile.CodeBuilder; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; @@ -50,6 +49,7 @@ import java.lang.classfile.Label; import java.lang.classfile.instruction.SwitchCase; +import jdk.internal.constant.ConstantUtils; import jdk.internal.constant.ReferenceClassDescImpl; import jdk.internal.misc.PreviewFeatures; import jdk.internal.vm.annotation.Stable; @@ -59,6 +59,9 @@ import java.util.HashMap; import java.util.Map; import static java.util.Objects.requireNonNull; +import static jdk.internal.constant.ConstantUtils.classDesc; +import static jdk.internal.constant.ConstantUtils.referenceClassDesc; + import sun.invoke.util.Wrapper; /** @@ -321,7 +324,7 @@ private static > Object convertEnumConstants(MethodHandles.Loo } return label; } else if (labelClass == String.class) { - return EnumDesc.of(enumClassTemplate.describeConstable().orElseThrow(), (String) label); + return EnumDesc.of(referenceClassDesc(enumClassTemplate), (String) label); } else { throw new IllegalArgumentException("label with illegal type found: " + labelClass + ", expected label of type either String or Class"); @@ -464,10 +467,7 @@ record Element(Label target, Label next, Object caseLabel) { } // Object o = ... // o instanceof Wrapped(float) cb.aload(SELECTOR_OBJ); - cb.instanceOf(Wrapper.forBasicType(classLabel) - .wrapperType() - .describeConstable() - .orElseThrow()); + cb.instanceOf(Wrapper.forBasicType(classLabel).wrapperClassDescriptor()); cb.ifeq(next); } else if (!unconditionalExactnessCheck(Wrapper.asPrimitiveType(selectorType), classLabel)) { // Integer i = ... or int i = ... @@ -515,9 +515,9 @@ record Element(Label target, Label next, Object caseLabel) { } TypePairs typePair = TypePairs.of(Wrapper.asPrimitiveType(selectorType), classLabel); String methodName = TypePairs.typePairToName.get(typePair); - cb.invokestatic(ExactConversionsSupport.class.describeConstable().orElseThrow(), + cb.invokestatic(referenceClassDesc(ExactConversionsSupport.class), methodName, - MethodTypeDesc.of(ConstantDescs.CD_boolean, typePair.from.describeConstable().orElseThrow())); + MethodTypeDesc.of(ConstantDescs.CD_boolean, classDesc(typePair.from))); cb.ifeq(next); } } else { @@ -553,7 +553,7 @@ record Element(Label target, Label next, Object caseLabel) { } MethodTypeDesc.of(ConstantDescs.CD_Integer, ConstantDescs.CD_int)); cb.aload(SELECTOR_OBJ); - cb.invokeinterface(BiPredicate.class.describeConstable().orElseThrow(), + cb.invokeinterface(referenceClassDesc(BiPredicate.class), "test", MethodTypeDesc.of(ConstantDescs.CD_boolean, ConstantDescs.CD_Object, @@ -601,10 +601,11 @@ record Element(Label target, Label next, Object caseLabel) { } } else { cb.loadConstant((ConstantDesc) element.caseLabel()); } - cb.invokestatic(element.caseLabel().getClass().describeConstable().orElseThrow(), + var caseLabelWrapper = Wrapper.forWrapperType(element.caseLabel().getClass()); + cb.invokestatic(caseLabelWrapper.wrapperClassDescriptor(), "valueOf", - MethodTypeDesc.of(element.caseLabel().getClass().describeConstable().orElseThrow(), - Wrapper.asPrimitiveType(element.caseLabel().getClass()).describeConstable().orElseThrow())); + MethodTypeDesc.of(caseLabelWrapper.wrapperClassDescriptor(), + caseLabelWrapper.basicClassDescriptor())); cb.aload(SELECTOR_OBJ); cb.invokevirtual(ConstantDescs.CD_Object, "equals", @@ -631,7 +632,7 @@ private static MethodHandle generateTypeSwitch(MethodHandles.Lookup caller, Clas List> enumDescs = new ArrayList<>(); List> extraClassLabels = new ArrayList<>(); - byte[] classBytes = ClassFile.of().build(ReferenceClassDescImpl.ofValidatedBinaryName(typeSwitchClassName(caller.lookupClass())), + byte[] classBytes = ClassFile.of().build(ConstantUtils.binaryNameToDesc(typeSwitchClassName(caller.lookupClass())), clb -> { clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC) .withMethodBody("typeSwitch", diff --git a/src/java.base/share/classes/java/util/Spliterators.java b/src/java.base/share/classes/java/util/Spliterators.java index c047d96ab4907..6cc9146a3cf1e 100644 --- a/src/java.base/share/classes/java/util/Spliterators.java +++ b/src/java.base/share/classes/java/util/Spliterators.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, 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 @@ -57,12 +57,9 @@ private Spliterators() {} */ @SuppressWarnings("unchecked") public static Spliterator emptySpliterator() { - return (Spliterator) EMPTY_SPLITERATOR; + return (Spliterator) EmptySpliterator.OfRef.EMPTY_SPLITERATOR; } - private static final Spliterator EMPTY_SPLITERATOR = - new EmptySpliterator.OfRef<>(); - /** * Creates an empty {@code Spliterator.OfInt} * @@ -73,12 +70,9 @@ public static Spliterator emptySpliterator() { * @return An empty spliterator */ public static Spliterator.OfInt emptyIntSpliterator() { - return EMPTY_INT_SPLITERATOR; + return EmptySpliterator.OfInt.EMPTY_INT_SPLITERATOR; } - private static final Spliterator.OfInt EMPTY_INT_SPLITERATOR = - new EmptySpliterator.OfInt(); - /** * Creates an empty {@code Spliterator.OfLong} * @@ -89,12 +83,9 @@ public static Spliterator.OfInt emptyIntSpliterator() { * @return An empty spliterator */ public static Spliterator.OfLong emptyLongSpliterator() { - return EMPTY_LONG_SPLITERATOR; + return EmptySpliterator.OfLong.EMPTY_LONG_SPLITERATOR; } - private static final Spliterator.OfLong EMPTY_LONG_SPLITERATOR = - new EmptySpliterator.OfLong(); - /** * Creates an empty {@code Spliterator.OfDouble} * @@ -105,12 +96,9 @@ public static Spliterator.OfLong emptyLongSpliterator() { * @return An empty spliterator */ public static Spliterator.OfDouble emptyDoubleSpliterator() { - return EMPTY_DOUBLE_SPLITERATOR; + return EmptySpliterator.OfDouble.EMPTY_DOUBLE_SPLITERATOR; } - private static final Spliterator.OfDouble EMPTY_DOUBLE_SPLITERATOR = - new EmptySpliterator.OfDouble(); - // Array-based spliterators /** @@ -905,28 +893,40 @@ public int characteristics() { private static final class OfRef extends EmptySpliterator, Consumer> implements Spliterator { - OfRef() { } + static final Spliterator EMPTY_SPLITERATOR = + new EmptySpliterator.OfRef<>(); + + private OfRef() { } } @SuppressWarnings("overloads") private static final class OfInt extends EmptySpliterator implements Spliterator.OfInt { - OfInt() { } + static final Spliterator.OfInt EMPTY_INT_SPLITERATOR = + new EmptySpliterator.OfInt(); + + private OfInt() { } } @SuppressWarnings("overloads") private static final class OfLong extends EmptySpliterator implements Spliterator.OfLong { - OfLong() { } + static final Spliterator.OfLong EMPTY_LONG_SPLITERATOR = + new EmptySpliterator.OfLong(); + + private OfLong() { } } @SuppressWarnings("overloads") private static final class OfDouble extends EmptySpliterator implements Spliterator.OfDouble { - OfDouble() { } + static final Spliterator.OfDouble EMPTY_DOUBLE_SPLITERATOR = + new EmptySpliterator.OfDouble(); + + private OfDouble() { } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java index 1154b038dee94..6765bdee3bdac 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -41,6 +41,7 @@ import static java.lang.constant.ConstantDescs.CD_Object; import static java.lang.classfile.ClassFile.*; import static java.util.Objects.requireNonNull; +import static jdk.internal.constant.ConstantUtils.referenceClassDesc; /** * Class hierarchy resolution framework is answering questions about classes assignability, common classes ancestor and whether the class represents an interface. @@ -245,7 +246,7 @@ public ClassHierarchyInfo getClassInfo(ClassDesc cd) { } return cl.isInterface() ? ClassHierarchyInfo.ofInterface() - : ClassHierarchyInfo.ofClass(cl.getSuperclass().describeConstable().orElseThrow()); + : ClassHierarchyInfo.ofClass(referenceClassDesc(cl.getSuperclass())); } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index ba032c7b0c25c..ddb14b2d26a5a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -26,14 +26,12 @@ package jdk.internal.classfile.impl; import java.lang.classfile.constantpool.InvokeDynamicEntry; -import java.lang.classfile.constantpool.NameAndTypeEntry; import java.lang.constant.ClassDesc; import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; import java.lang.classfile.ClassFile; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantDynamicEntry; -import java.lang.classfile.constantpool.DynamicConstantPoolEntry; import java.lang.classfile.constantpool.MemberRefEntry; import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.nio.ByteBuffer; @@ -46,6 +44,8 @@ import java.lang.classfile.Attribute; import static java.lang.classfile.ClassFile.*; +import static jdk.internal.constant.ConstantUtils.binaryNameToDesc; + import java.lang.classfile.BufWriter; import java.lang.classfile.Label; import java.lang.classfile.attribute.StackMapTableAttribute; @@ -1321,8 +1321,8 @@ Type mergeComponentFrom(Type from, ClassHierarchyImpl context) { } } - private static final ClassDesc CD_Cloneable = ClassDesc.of("java.lang.Cloneable"); - private static final ClassDesc CD_Serializable = ClassDesc.of("java.io.Serializable"); + private static final ClassDesc CD_Cloneable = binaryNameToDesc("java.lang.Cloneable"); + private static final ClassDesc CD_Serializable = binaryNameToDesc("java.io.Serializable"); private Type mergeReferenceFrom(Type from, ClassHierarchyImpl context) { if (from == NULL_TYPE) { diff --git a/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java b/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java index 7c676a44b9927..da90f373eb53a 100644 --- a/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java +++ b/src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java @@ -29,6 +29,8 @@ import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -41,12 +43,85 @@ public final class ConstantUtils { public static final ConstantDesc[] EMPTY_CONSTANTDESC = new ConstantDesc[0]; public static final ClassDesc[] EMPTY_CLASSDESC = new ClassDesc[0]; public static final int MAX_ARRAY_TYPE_DESC_DIMENSIONS = 255; + public static final ClassDesc CD_module_info = binaryNameToDesc("module-info"); private static final Set pointyNames = Set.of(ConstantDescs.INIT_NAME, ConstantDescs.CLASS_INIT_NAME); /** No instantiation */ private ConstantUtils() {} + // Note: + // Non-JDK users should create their own utilities that wrap + // {@code .describeConstable().orElseThrow()} calls; + // these xxDesc methods has undefined and unsafe exceptional + // behavior, so they are not suitable as public APIs. + + /** + * Creates a {@linkplain ClassDesc} from a pre-validated binary name + * for a class or interface type. Validated version of {@link + * ClassDesc#of(String)}. + * + * @param binaryName a binary name + */ + public static ClassDesc binaryNameToDesc(String binaryName) { + return ReferenceClassDescImpl.ofValidated("L" + binaryToInternal(binaryName) + ";"); + } + + /** + * Creates a ClassDesc from a Class object, requires that this class + * can always be described nominally, i.e. this class is not a + * hidden class or interface or an array with a hidden component + * type. + */ + public static ClassDesc classDesc(Class type) { + if (type.isPrimitive()) { + return Wrapper.forPrimitiveType(type).basicClassDescriptor(); + } + return referenceClassDesc(type); + } + + /** + * Creates a ClassDesc from a Class object representing a non-hidden + * class or interface or an array type with a non-hidden component type. + */ + public static ClassDesc referenceClassDesc(Class type) { + return ReferenceClassDescImpl.ofValidated(type.descriptorString()); + } + + /** + * Creates a MethodTypeDesc from a MethodType object, requires that + * the type can be described nominally, i.e. all of its return + * type and parameter types can be described nominally. + */ + public static MethodTypeDesc methodTypeDesc(MethodType type) { + var returnDesc = classDesc(type.returnType()); + if (type.parameterCount() == 0) { + return MethodTypeDescImpl.ofValidated(returnDesc, EMPTY_CLASSDESC); + } + var paramDescs = new ClassDesc[type.parameterCount()]; + for (int i = 0; i < type.parameterCount(); i++) { + paramDescs[i] = classDesc(type.parameterType(i)); + } + return MethodTypeDescImpl.ofValidated(returnDesc, paramDescs); + } + + /** + * Creates a MethodTypeDesc from return class and parameter + * class objects, requires that all of them can be described nominally. + * This version is mainly useful for working with Method objects. + */ + public static MethodTypeDesc methodTypeDesc(Class returnType, Class[] parameterTypes) { + var returnDesc = classDesc(returnType); + if (parameterTypes.length == 0) { + return MethodTypeDescImpl.ofValidated(returnDesc, EMPTY_CLASSDESC); + } + var paramDescs = new ClassDesc[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + paramDescs[i] = classDesc(parameterTypes[i]); + } + return MethodTypeDescImpl.ofValidated(returnDesc, paramDescs); + } + /** * Validates the correctness of a binary class name. In particular checks for the presence of * invalid characters in the name. @@ -231,7 +306,7 @@ public static List parseMethodDescriptor(String descriptor) { private static ClassDesc resolveClassDesc(String descriptor, int start, int len) { if (len == 1) { - return Wrapper.forPrimitiveType(descriptor.charAt(start)).classDescriptor(); + return Wrapper.forPrimitiveType(descriptor.charAt(start)).basicClassDescriptor(); } // Pre-verified in parseMethodDescriptor; avoid redundant verification return ReferenceClassDescImpl.ofValidated(descriptor.substring(start, start + len)); diff --git a/src/java.base/share/classes/jdk/internal/constant/ReferenceClassDescImpl.java b/src/java.base/share/classes/jdk/internal/constant/ReferenceClassDescImpl.java index b4ec9c13de580..71473b87e188f 100644 --- a/src/java.base/share/classes/jdk/internal/constant/ReferenceClassDescImpl.java +++ b/src/java.base/share/classes/jdk/internal/constant/ReferenceClassDescImpl.java @@ -66,20 +66,11 @@ public static ReferenceClassDescImpl of(String descriptor) { * @jvms 4.3.2 Field Descriptors */ public static ReferenceClassDescImpl ofValidated(String descriptor) { + assert ConstantUtils.skipOverFieldSignature(descriptor, 0, descriptor.length(), false) + == descriptor.length() : descriptor; return new ReferenceClassDescImpl(descriptor); } - /** - * Creates a {@linkplain ClassDesc} from a pre-validated descriptor string - * for a class or interface type or an array type. - * - * @param descriptor a field descriptor string for a class or interface type - * @jvms 4.3.2 Field Descriptors - */ - public static ClassDesc ofValidatedBinaryName(String typeSwitchClassName) { - return ofValidated("L" + binaryToInternal(typeSwitchClassName) + ";"); - } - @Override public String descriptorString() { return descriptor; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java index 7f5ef54bdcab1..6109f59f2d8ca 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -29,6 +29,7 @@ import java.lang.classfile.Label; import java.lang.classfile.Opcode; import java.lang.classfile.TypeKind; + import jdk.internal.foreign.AbstractMemorySegmentImpl; import jdk.internal.foreign.MemorySessionImpl; import jdk.internal.foreign.Utils; @@ -50,7 +51,6 @@ import java.io.IOException; import java.lang.constant.ClassDesc; -import java.lang.constant.Constable; import java.lang.constant.ConstantDesc; import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodTypeDesc; @@ -70,6 +70,7 @@ import static java.lang.constant.ConstantDescs.*; import static java.lang.classfile.ClassFile.*; import static java.lang.classfile.TypeKind.*; +import static jdk.internal.constant.ConstantUtils.*; public class BindingSpecializer { private static final String DUMP_CLASSES_DIR @@ -80,24 +81,24 @@ public class BindingSpecializer { // Bunch of helper constants private static final int CLASSFILE_VERSION = ClassFileFormatVersion.latest().major(); - private static final ClassDesc CD_Arena = desc(Arena.class); - private static final ClassDesc CD_MemorySegment = desc(MemorySegment.class); - private static final ClassDesc CD_MemorySegment_Scope = desc(MemorySegment.Scope.class); - private static final ClassDesc CD_SharedUtils = desc(SharedUtils.class); - private static final ClassDesc CD_AbstractMemorySegmentImpl = desc(AbstractMemorySegmentImpl.class); - private static final ClassDesc CD_MemorySessionImpl = desc(MemorySessionImpl.class); - private static final ClassDesc CD_Utils = desc(Utils.class); - private static final ClassDesc CD_SegmentAllocator = desc(SegmentAllocator.class); - private static final ClassDesc CD_ValueLayout = desc(ValueLayout.class); - private static final ClassDesc CD_ValueLayout_OfBoolean = desc(ValueLayout.OfBoolean.class); - private static final ClassDesc CD_ValueLayout_OfByte = desc(ValueLayout.OfByte.class); - private static final ClassDesc CD_ValueLayout_OfShort = desc(ValueLayout.OfShort.class); - private static final ClassDesc CD_ValueLayout_OfChar = desc(ValueLayout.OfChar.class); - private static final ClassDesc CD_ValueLayout_OfInt = desc(ValueLayout.OfInt.class); - private static final ClassDesc CD_ValueLayout_OfLong = desc(ValueLayout.OfLong.class); - private static final ClassDesc CD_ValueLayout_OfFloat = desc(ValueLayout.OfFloat.class); - private static final ClassDesc CD_ValueLayout_OfDouble = desc(ValueLayout.OfDouble.class); - private static final ClassDesc CD_AddressLayout = desc(AddressLayout.class); + private static final ClassDesc CD_Arena = referenceClassDesc(Arena.class); + private static final ClassDesc CD_MemorySegment = referenceClassDesc(MemorySegment.class); + private static final ClassDesc CD_MemorySegment_Scope = referenceClassDesc(MemorySegment.Scope.class); + private static final ClassDesc CD_SharedUtils = referenceClassDesc(SharedUtils.class); + private static final ClassDesc CD_AbstractMemorySegmentImpl = referenceClassDesc(AbstractMemorySegmentImpl.class); + private static final ClassDesc CD_MemorySessionImpl = referenceClassDesc(MemorySessionImpl.class); + private static final ClassDesc CD_Utils = referenceClassDesc(Utils.class); + private static final ClassDesc CD_SegmentAllocator = referenceClassDesc(SegmentAllocator.class); + private static final ClassDesc CD_ValueLayout = referenceClassDesc(ValueLayout.class); + private static final ClassDesc CD_ValueLayout_OfBoolean = referenceClassDesc(ValueLayout.OfBoolean.class); + private static final ClassDesc CD_ValueLayout_OfByte = referenceClassDesc(ValueLayout.OfByte.class); + private static final ClassDesc CD_ValueLayout_OfShort = referenceClassDesc(ValueLayout.OfShort.class); + private static final ClassDesc CD_ValueLayout_OfChar = referenceClassDesc(ValueLayout.OfChar.class); + private static final ClassDesc CD_ValueLayout_OfInt = referenceClassDesc(ValueLayout.OfInt.class); + private static final ClassDesc CD_ValueLayout_OfLong = referenceClassDesc(ValueLayout.OfLong.class); + private static final ClassDesc CD_ValueLayout_OfFloat = referenceClassDesc(ValueLayout.OfFloat.class); + private static final ClassDesc CD_ValueLayout_OfDouble = referenceClassDesc(ValueLayout.OfDouble.class); + private static final ClassDesc CD_AddressLayout = referenceClassDesc(AddressLayout.class); private static final MethodTypeDesc MTD_NEW_BOUNDED_ARENA = MethodTypeDesc.of(CD_Arena, CD_long); private static final MethodTypeDesc MTD_NEW_EMPTY_ARENA = MethodTypeDesc.of(CD_Arena); @@ -196,7 +197,7 @@ private static byte[] specializeHelper(MethodType leafType, MethodType callerMet clb.withSuperclass(CD_Object); clb.withVersion(CLASSFILE_VERSION, 0); - clb.withMethodBody(METHOD_NAME, desc(callerMethodType), ACC_PUBLIC | ACC_STATIC, + clb.withMethodBody(METHOD_NAME, methodTypeDesc(callerMethodType), ACC_PUBLIC | ACC_STATIC, cb -> new BindingSpecializer(cb, callerMethodType, callingSequence, abi, leafType).specialize()); }); @@ -362,7 +363,7 @@ private void specialize() { cb.loadLocal(TypeKind.from(leafArgTypes.get(i)), leafArgSlots[i]); } // call leaf MH - cb.invokevirtual(CD_MethodHandle, "invokeExact", desc(leafType)); + cb.invokevirtual(CD_MethodHandle, "invokeExact", methodTypeDesc(leafType)); // for downcalls, store the result of the leaf handle call away, until // it is requested by a VM_LOAD in the return recipe. @@ -466,9 +467,9 @@ private void doBindings(List bindings) { case Copy copy -> emitCopyBuffer(copy); case Allocate allocate -> emitAllocBuffer(allocate); case BoxAddress boxAddress -> emitBoxAddress(boxAddress); - case SegmentBase unused -> emitSegmentBase(); + case SegmentBase _ -> emitSegmentBase(); case SegmentOffset segmentOffset -> emitSegmentOffset(segmentOffset); - case Dup unused -> emitDupBinding(); + case Dup _ -> emitDupBinding(); case ShiftLeft shiftLeft -> emitShiftLeft(shiftLeft); case ShiftRight shiftRight -> emitShiftRight(shiftRight); case Cast cast -> emitCast(cast); @@ -608,7 +609,7 @@ private void emitBufferStore(BufferStore bufferStore) { ClassDesc valueLayoutType = emitLoadLayoutConstant(storeType); cb.loadConstant(offset); cb.loadLocal(storeTypeKind, valueIdx); - MethodTypeDesc descriptor = MethodTypeDesc.of(CD_void, valueLayoutType, CD_long, desc(storeType)); + MethodTypeDesc descriptor = MethodTypeDesc.of(CD_void, valueLayoutType, CD_long, classDesc(storeType)); cb.invokeinterface(CD_MemorySegment, "set", descriptor); } else { // long longValue = ((Number) value).longValue(); @@ -666,7 +667,7 @@ private void emitBufferStore(BufferStore bufferStore) { long writeOffset = offset + SharedUtils.pickChunkOffset(chunkOffset, byteWidth, chunkSize); cb.loadConstant(writeOffset); cb.loadLocal(chunkStoreTypeKind, chunkIdx); - MethodTypeDesc descriptor = MethodTypeDesc.of(CD_void, valueLayoutType, CD_long, desc(chunkStoreType)); + MethodTypeDesc descriptor = MethodTypeDesc.of(CD_void, valueLayoutType, CD_long, classDesc(chunkStoreType)); cb.invokeinterface(CD_MemorySegment, "set", descriptor); remaining -= chunkSize; @@ -697,7 +698,7 @@ private void emitVMStore(VMStore vmStore) { ClassDesc valueLayoutType = emitLoadLayoutConstant(storeType); cb.loadConstant(retBufOffset); cb.loadLocal(storeTypeKind, valueIdx); - MethodTypeDesc descriptor = MethodTypeDesc.of(CD_void, valueLayoutType, CD_long, desc(storeType)); + MethodTypeDesc descriptor = MethodTypeDesc.of(CD_void, valueLayoutType, CD_long, classDesc(storeType)); cb.invokeinterface(CD_MemorySegment, "set", descriptor); retBufOffset += abi.arch.typeSize(vmStore.storage().type()); } @@ -716,7 +717,7 @@ private void emitVMLoad(VMLoad vmLoad) { cb.loadLocal(ReferenceType, returnBufferIdx); ClassDesc valueLayoutType = emitLoadLayoutConstant(loadType); cb.loadConstant(retBufOffset); - MethodTypeDesc descriptor = MethodTypeDesc.of(desc(loadType), valueLayoutType, CD_long); + MethodTypeDesc descriptor = MethodTypeDesc.of(classDesc(loadType), valueLayoutType, CD_long); cb.invokeinterface(CD_MemorySegment, "get", descriptor); retBufOffset += abi.arch.typeSize(vmLoad.storage().type()); pushType(loadType); @@ -809,7 +810,7 @@ private void emitBufferLoad(BufferLoad bufferLoad) { if (SharedUtils.isPowerOfTwo(byteWidth)) { ClassDesc valueLayoutType = emitLoadLayoutConstant(loadType); cb.loadConstant(offset); - MethodTypeDesc descriptor = MethodTypeDesc.of(desc(loadType), valueLayoutType, CD_long); + MethodTypeDesc descriptor = MethodTypeDesc.of(classDesc(loadType), valueLayoutType, CD_long); cb.invokeinterface(CD_MemorySegment, "get", descriptor); } else { // chunked @@ -849,7 +850,7 @@ private void emitBufferLoad(BufferLoad bufferLoad) { // read from segment cb.loadLocal(ReferenceType, readAddrIdx); ClassDesc valueLayoutType = emitLoadLayoutConstant(chunkType); - MethodTypeDesc descriptor = MethodTypeDesc.of(desc(chunkType), valueLayoutType, CD_long); + MethodTypeDesc descriptor = MethodTypeDesc.of(classDesc(chunkType), valueLayoutType, CD_long); long readOffset = offset + SharedUtils.pickChunkOffset(chunkOffset, byteWidth, chunkSize); cb.loadConstant(readOffset); cb.invokeinterface(CD_MemorySegment, "get", descriptor); @@ -988,9 +989,4 @@ private void emitConstZero(TypeKind kind) { case ReferenceType -> cb.aconst_null(); } } - - @SuppressWarnings("unchecked") - private static T desc(Constable c) { - return (T) c.describeConstable().orElseThrow(); - } } diff --git a/src/java.base/share/classes/sun/invoke/util/Wrapper.java b/src/java.base/share/classes/sun/invoke/util/Wrapper.java index 5f2e9ba284115..5d0849cd93def 100644 --- a/src/java.base/share/classes/sun/invoke/util/Wrapper.java +++ b/src/java.base/share/classes/sun/invoke/util/Wrapper.java @@ -32,20 +32,31 @@ import java.lang.constant.ConstantDescs; public enum Wrapper { - // wrapperType simple primitiveType simple char emptyArray format numericClass superClass classDescriptor - BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1), 0, 0, ConstantDescs.CD_boolean), + // wrapperType simple primitiveType simple char emptyArray format numericClass superClass + // basicClassDescriptor wrapperClassDescriptor + BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0], Format.unsigned( 1), 0, 0, + ConstantDescs.CD_boolean, ConstantDescs.CD_Boolean), // These must be in the order defined for widening primitive conversions in JLS 5.1.2 // Avoid boxing integral types here to defer initialization of internal caches - BYTE ( Byte.class, "Byte", byte.class, "byte", 'B', new byte[0], Format.signed( 8), BYTE_CLASS, BYTE_SUPERCLASSES, ConstantDescs.CD_byte), - SHORT ( Short.class, "Short", short.class, "short", 'S', new short[0], Format.signed( 16), SHORT_CLASS, SHORT_SUPERCLASSES, ConstantDescs.CD_short), - CHAR (Character.class, "Character", char.class, "char", 'C', new char[0], Format.unsigned(16), CHAR_CLASS, CHAR_SUPERCLASSES, ConstantDescs.CD_char), - INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0], Format.signed( 32), INT_CLASS, INT_SUPERCLASSES, ConstantDescs.CD_int), - LONG ( Long.class, "Long", long.class, "long", 'J', new long[0], Format.signed( 64), LONG_CLASS, LONG_SUPERCLASSES, ConstantDescs.CD_long), - FLOAT ( Float.class, "Float", float.class, "float", 'F', new float[0], Format.floating(32), FLOAT_CLASS, FLOAT_SUPERCLASSES, ConstantDescs.CD_float), - DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0], Format.floating(64), DOUBLE_CLASS, DOUBLE_CLASS, ConstantDescs.CD_double), - OBJECT ( Object.class, "Object", Object.class, "Object", 'L', new Object[0], Format.other( 1), 0, 0, ConstantDescs.CD_Object), + BYTE ( Byte.class, "Byte", byte.class, "byte", 'B', new byte[0], Format.signed( 8), BYTE_CLASS, BYTE_SUPERCLASSES, + ConstantDescs.CD_byte, ConstantDescs.CD_Byte), + SHORT ( Short.class, "Short", short.class, "short", 'S', new short[0], Format.signed( 16), SHORT_CLASS, SHORT_SUPERCLASSES, + ConstantDescs.CD_short, ConstantDescs.CD_Short), + CHAR (Character.class, "Character", char.class, "char", 'C', new char[0], Format.unsigned(16), CHAR_CLASS, CHAR_SUPERCLASSES, + ConstantDescs.CD_char, ConstantDescs.CD_Character), + INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0], Format.signed( 32), INT_CLASS, INT_SUPERCLASSES, + ConstantDescs.CD_int, ConstantDescs.CD_Integer), + LONG ( Long.class, "Long", long.class, "long", 'J', new long[0], Format.signed( 64), LONG_CLASS, LONG_SUPERCLASSES, + ConstantDescs.CD_long, ConstantDescs.CD_Long), + FLOAT ( Float.class, "Float", float.class, "float", 'F', new float[0], Format.floating(32), FLOAT_CLASS, FLOAT_SUPERCLASSES, + ConstantDescs.CD_float, ConstantDescs.CD_Float), + DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0], Format.floating(64), DOUBLE_CLASS, DOUBLE_CLASS, + ConstantDescs.CD_double, ConstantDescs.CD_Double), + OBJECT ( Object.class, "Object", Object.class, "Object", 'L', new Object[0], Format.other( 1), 0, 0, + ConstantDescs.CD_Object, ConstantDescs.CD_Object), // VOID must be the last type, since it is "assignable" from any other type: - VOID ( Void.class, "Void", void.class, "void", 'V', null, Format.other( 0), 0, 0, ConstantDescs.CD_void), + VOID ( Void.class, "Void", void.class, "void", 'V', null, Format.other( 0), 0, 0, + ConstantDescs.CD_void, ConstantDescs.CD_Void), ; public static final int COUNT = 10; @@ -60,18 +71,20 @@ public enum Wrapper { private final int superClasses; private final String wrapperSimpleName; private final String primitiveSimpleName; - private final ClassDesc classDesc; - - private Wrapper(Class wtype, - String wtypeName, - Class ptype, - String ptypeName, - char tchar, - Object emptyArray, - int format, - int numericClass, - int superClasses, - ClassDesc classDesc) { + private final ClassDesc basicClassDesc; + private final ClassDesc wrapperClassDesc; + + Wrapper(Class wtype, + String wtypeName, + Class ptype, + String ptypeName, + char tchar, + Object emptyArray, + int format, + int numericClass, + int superClasses, + ClassDesc basicClassDesc, + ClassDesc wrapperClassDesc) { this.wrapperType = wtype; this.primitiveType = ptype; this.basicTypeChar = tchar; @@ -82,7 +95,8 @@ private Wrapper(Class wtype, this.superClasses = superClasses; this.wrapperSimpleName = wtypeName; this.primitiveSimpleName = ptypeName; - this.classDesc = classDesc; + this.basicClassDesc = basicClassDesc; + this.wrapperClassDesc = wrapperClassDesc; } /** For debugging, give the details of this wrapper. */ @@ -390,7 +404,10 @@ public static Wrapper forBasicType(Class type) { } /** A nominal descriptor of the wrapped type */ - public ClassDesc classDescriptor() { return classDesc; } + public ClassDesc basicClassDescriptor() { return basicClassDesc; } + + /** A nominal descriptor of the wrapper type */ + public ClassDesc wrapperClassDescriptor() { return wrapperClassDesc; } /** What is the primitive type wrapped by this wrapper? */ public Class primitiveType() { return primitiveType; } diff --git a/src/jdk.compiler/share/data/symbols/java.base-N.sym.txt b/src/jdk.compiler/share/data/symbols/java.base-N.sym.txt index 905a0db8b73c8..8fab1f56765cd 100644 --- a/src/jdk.compiler/share/data/symbols/java.base-N.sym.txt +++ b/src/jdk.compiler/share/data/symbols/java.base-N.sym.txt @@ -34,6 +34,7 @@ class name java/io/ByteArrayOutputStream method name writeTo descriptor (Ljava/io/OutputStream;)V thrownTypes java/io/IOException flags 1 class name java/io/Console +header extends java/lang/Object implements java/io/Flushable sealed true permittedSubclasses java/io/ProxyingConsole flags 21 method name format descriptor (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/Console; flags 81 method name printf descriptor (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/Console; flags 81 method name readLine descriptor (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; flags 81 @@ -63,6 +64,27 @@ method name println descriptor (Ljava/lang/Object;)Ljava/io/Console; flags 1 method name print descriptor (Ljava/lang/Object;)Ljava/io/Console; flags 1 method name readln descriptor (Ljava/lang/String;)Ljava/lang/String; flags 1 +class name java/lang/ScopedValue +header extends java/lang/Object nestMembers java/lang/ScopedValue$CallableOp,java/lang/ScopedValue$Carrier flags 31 signature Ljava/lang/Object; classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;SCOPED_VALUES;) +innerclass innerClass java/lang/ScopedValue$Carrier outerClass java/lang/ScopedValue innerClassName Carrier flags 19 +innerclass innerClass java/lang/ScopedValue$CallableOp outerClass java/lang/ScopedValue innerClassName CallableOp flags 609 +-method name callWhere descriptor (Ljava/lang/ScopedValue;Ljava/lang/Object;Ljava/util/concurrent/Callable;)Ljava/lang/Object; +-method name getWhere descriptor (Ljava/lang/ScopedValue;Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object; +method name callWhere descriptor (Ljava/lang/ScopedValue;Ljava/lang/Object;Ljava/lang/ScopedValue$CallableOp;)Ljava/lang/Object; thrownTypes java/lang/Throwable flags 9 signature (Ljava/lang/ScopedValue;TT;Ljava/lang/ScopedValue$CallableOp<+TR;TX;>;)TR;^TX; + +class name java/lang/ScopedValue$CallableOp +header extends java/lang/Object nestHost java/lang/ScopedValue flags 601 signature Ljava/lang/Object; classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;SCOPED_VALUES;) runtimeAnnotations @Ljava/lang/FunctionalInterface; +innerclass innerClass java/lang/ScopedValue$CallableOp outerClass java/lang/ScopedValue innerClassName CallableOp flags 609 +method name call descriptor ()Ljava/lang/Object; thrownTypes java/lang/Throwable flags 401 signature ()TT;^TX; + +class name java/lang/ScopedValue$Carrier +header extends java/lang/Object nestHost java/lang/ScopedValue flags 31 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;SCOPED_VALUES;) +innerclass innerClass java/lang/ScopedValue$Carrier outerClass java/lang/ScopedValue innerClassName Carrier flags 19 +innerclass innerClass java/lang/ScopedValue$CallableOp outerClass java/lang/ScopedValue innerClassName CallableOp flags 609 +-method name call descriptor (Ljava/util/concurrent/Callable;)Ljava/lang/Object; +-method name get descriptor (Ljava/util/function/Supplier;)Ljava/lang/Object; +method name call descriptor (Ljava/lang/ScopedValue$CallableOp;)Ljava/lang/Object; thrownTypes java/lang/Throwable flags 1 signature (Ljava/lang/ScopedValue$CallableOp<+TR;TX;>;)TR;^TX; + -class name java/lang/StringTemplate -class name java/lang/StringTemplate$Processor @@ -175,6 +197,12 @@ innerclass innerClass java/lang/classfile/ClassFile$ShortJumpsOption outerClass innerclass innerClass java/lang/classfile/ClassFile$StackMapsOption outerClass java/lang/classfile/ClassFile innerClassName StackMapsOption flags 4019 innerclass innerClass java/lang/classfile/ClassFile$AttributesProcessingOption outerClass java/lang/classfile/ClassFile innerClassName AttributesProcessingOption flags 4019 +class name java/lang/classfile/ClassReader +-method name thisClassPos descriptor ()I +-method name skipAttributeHolder descriptor (I)I +-method name utf8EntryByIndex descriptor (I)Ljava/lang/classfile/constantpool/Utf8Entry; +method name readEntryOrNull descriptor (ILjava/lang/Class;)Ljava/lang/classfile/constantpool/PoolEntry; flags 401 signature (ILjava/lang/Class;)TT; + class name java/lang/classfile/ClassSignature header extends java/lang/Object sealed true permittedSubclasses jdk/internal/classfile/impl/SignaturesImpl$ClassSignatureImpl flags 601 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;CLASSFILE_API;) innerclass innerClass java/lang/classfile/Signature$ClassTypeSig outerClass java/lang/classfile/Signature innerClassName ClassTypeSig flags 609 @@ -311,6 +339,12 @@ class name java/lang/classfile/TypeKind method name newarrayCode descriptor ()I flags 1 method name fromNewarrayCode descriptor (I)Ljava/lang/classfile/TypeKind; flags 9 +class name java/lang/classfile/components/ClassPrinter$ListNode +header extends java/lang/Object implements java/lang/classfile/components/ClassPrinter$Node,java/util/List nestHost java/lang/classfile/components/ClassPrinter sealed true permittedSubclasses jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl flags 601 signature Ljava/lang/Object;Ljava/lang/classfile/components/ClassPrinter$Node;Ljava/util/List; classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;CLASSFILE_API;) +innerclass innerClass java/lang/classfile/components/ClassPrinter$ListNode outerClass java/lang/classfile/components/ClassPrinter innerClassName ListNode flags 609 +innerclass innerClass java/lang/classfile/components/ClassPrinter$Node outerClass java/lang/classfile/components/ClassPrinter innerClassName Node flags 609 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName ListNodeImpl flags 9 + class name java/lang/classfile/components/ClassPrinter$Node header extends java/lang/Object nestHost java/lang/classfile/components/ClassPrinter sealed true permittedSubclasses java/lang/classfile/components/ClassPrinter$LeafNode,java/lang/classfile/components/ClassPrinter$ListNode,java/lang/classfile/components/ClassPrinter$MapNode flags 601 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;CLASSFILE_API;) innerclass innerClass java/lang/classfile/components/ClassPrinter$Node outerClass java/lang/classfile/components/ClassPrinter innerClassName Node flags 609 @@ -318,6 +352,9 @@ innerclass innerClass java/lang/classfile/components/ClassPrinter$LeafNode outer innerclass innerClass java/lang/classfile/components/ClassPrinter$ListNode outerClass java/lang/classfile/components/ClassPrinter innerClassName ListNode flags 609 innerclass innerClass java/lang/classfile/components/ClassPrinter$MapNode outerClass java/lang/classfile/components/ClassPrinter innerClassName MapNode flags 609 +class name java/lang/classfile/constantpool/ConstantPool +method name entryByIndex descriptor (ILjava/lang/Class;)Ljava/lang/classfile/constantpool/PoolEntry; flags 401 signature (ILjava/lang/Class;)TT; + class name java/lang/constant/ClassDesc header extends java/lang/Object implements java/lang/constant/ConstantDesc,java/lang/invoke/TypeDescriptor$OfField sealed true permittedSubclasses jdk/internal/constant/PrimitiveClassDescImpl,jdk/internal/constant/ReferenceClassDescImpl flags 601 signature Ljava/lang/Object;Ljava/lang/constant/ConstantDesc;Ljava/lang/invoke/TypeDescriptor$OfField; innerclass innerClass java/lang/invoke/MethodHandles$Lookup outerClass java/lang/invoke/MethodHandles innerClassName Lookup flags 19 @@ -419,6 +456,12 @@ method name setTTL descriptor (B)V thrownTypes java/io/IOException flags 1 depre method name getTTL descriptor ()B thrownTypes java/io/IOException flags 1 deprecated true runtimeAnnotations @Ljava/lang/Deprecated;(forRemoval=Ztrue,since="1.2") method name send descriptor (Ljava/net/DatagramPacket;B)V thrownTypes java/io/IOException flags 1 deprecated true runtimeAnnotations @Ljava/lang/Deprecated;(forRemoval=Ztrue,since="1.4") +class name java/net/Socket +-method name descriptor (Ljava/lang/String;IZ)V +-method name descriptor (Ljava/net/InetAddress;IZ)V +method name descriptor (Ljava/lang/String;IZ)V thrownTypes java/io/IOException flags 1 deprecated true runtimeAnnotations @Ljava/lang/Deprecated;(forRemoval=Ztrue,since="1.1") +method name descriptor (Ljava/net/InetAddress;IZ)V thrownTypes java/io/IOException flags 1 deprecated true runtimeAnnotations @Ljava/lang/Deprecated;(forRemoval=Ztrue,since="1.1") + class name java/nio/HeapByteBuffer method name hashCode descriptor ()I flags 1 @@ -461,6 +504,10 @@ method name isStrict descriptor ()Z flags 1 method name setStrict descriptor (Z)V flags 1 method name toString descriptor ()Ljava/lang/String; flags 1 +class name java/text/DecimalFormatSymbols +header extends java/lang/Object implements java/lang/Cloneable,java/io/Serializable flags 21 +innerclass innerClass java/util/Locale$Category outerClass java/util/Locale innerClassName Category flags 4019 + class name java/text/MessageFormat header extends java/text/Format nestMembers java/text/MessageFormat$Field flags 21 innerclass innerClass java/util/Locale$Category outerClass java/util/Locale innerClassName Category flags 4019 @@ -521,6 +568,12 @@ method name getTotalOut descriptor ()I flags 1 deprecated true runtimeAnnotation class name java/util/zip/ZipFile method name toString descriptor ()Ljava/lang/String; flags 1 +class name javax/security/auth/Subject +header extends java/lang/Object implements java/io/Serializable flags 31 +innerclass innerClass java/lang/ScopedValue$CallableOp outerClass java/lang/ScopedValue innerClassName CallableOp flags 609 +innerclass innerClass java/io/ObjectInputStream$GetField outerClass java/io/ObjectInputStream innerClassName GetField flags 409 +innerclass innerClass java/lang/invoke/MethodHandles$Lookup outerClass java/lang/invoke/MethodHandles innerClassName Lookup flags 19 + class name jdk/internal/classfile/impl/AbstractInstruction header extends jdk/internal/classfile/impl/AbstractElement implements java/lang/classfile/Instruction nestMembers jdk/internal/classfile/impl/AbstractInstruction$UnboundRetInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundJsrInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundNopInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundMonitorInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundLoadConstantInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundArgumentConstantInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundIntrinsicConstantInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundOperatorInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundConvertInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundStackInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundTypeCheckInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundArrayStoreInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundArrayLoadInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundNewMultidimensionalArrayInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundNewReferenceArrayInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundNewPrimitiveArrayInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundNewObjectInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundInvokeDynamicInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundInvokeInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundFieldInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundThrowInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundReturnInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundTableSwitchInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundLookupSwitchInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundBranchInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundIncrementInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundStoreInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundLoadInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundRetInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundJsrInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundLoadConstantInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundArgumentConstantInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundTypeCheckInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundNewMultidimensionalArrayInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundNewReferenceArrayInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundNewPrimitiveArrayInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundNewObjectInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundInvokeDynamicInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundInvokeInterfaceInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundInvokeInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundFieldInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundTableSwitchInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundLookupSwitchInstruction,jdk/internal/classfile/impl/AbstractInstruction$SwitchCaseImpl,jdk/internal/classfile/impl/AbstractInstruction$BoundBranchInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundIncrementInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundStoreInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundLoadInstruction,jdk/internal/classfile/impl/AbstractInstruction$BoundInstruction sealed true permittedSubclasses jdk/internal/classfile/impl/AbstractInstruction$BoundInstruction,jdk/internal/classfile/impl/AbstractInstruction$UnboundInstruction flags 421 innerclass innerClass jdk/internal/classfile/impl/AbstractInstruction$UnboundRetInstruction outerClass jdk/internal/classfile/impl/AbstractInstruction innerClassName UnboundRetInstruction flags 19 @@ -747,10 +800,10 @@ innerclass innerClass jdk/internal/classfile/impl/BoundAttribute$BoundStackMapTa method name standardAttribute descriptor (Ljava/lang/classfile/constantpool/Utf8Entry;)Ljava/lang/classfile/AttributeMapper; flags 9 signature (Ljava/lang/classfile/constantpool/Utf8Entry;)Ljava/lang/classfile/AttributeMapper<*>; class name jdk/internal/classfile/impl/ClassPrinterImpl -header extends java/lang/Object nestMembers jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl,jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl,jdk/internal/classfile/impl/ClassPrinterImpl$LeafNodeImpl flags 31 +header extends java/lang/Object nestMembers jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl,jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl$PrivateListNodeImpl,jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl,jdk/internal/classfile/impl/ClassPrinterImpl$LeafNodeImpl flags 31 innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$LeafNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName LeafNodeImpl flags 19 innerclass innerClass java/lang/classfile/components/ClassPrinter$Node outerClass java/lang/classfile/components/ClassPrinter innerClassName Node flags 609 -innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName ListNodeImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName ListNodeImpl flags 9 innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName MapNodeImpl flags 19 innerclass innerClass java/lang/classfile/components/ClassPrinter$LeafNode outerClass java/lang/classfile/components/ClassPrinter innerClassName LeafNode flags 609 innerclass innerClass java/lang/classfile/AnnotationValue$OfString outerClass java/lang/classfile/AnnotationValue innerClassName OfString flags 609 @@ -781,8 +834,63 @@ innerclass innerClass java/lang/classfile/attribute/StackMapFrameInfo$Verificati innerclass innerClass java/lang/classfile/attribute/StackMapFrameInfo$SimpleVerificationTypeInfo outerClass java/lang/classfile/attribute/StackMapFrameInfo innerClassName SimpleVerificationTypeInfo flags 4019 innerclass innerClass java/lang/classfile/attribute/StackMapFrameInfo$ObjectVerificationTypeInfo outerClass java/lang/classfile/attribute/StackMapFrameInfo innerClassName ObjectVerificationTypeInfo flags 609 innerclass innerClass java/lang/classfile/attribute/StackMapFrameInfo$UninitializedVerificationTypeInfo outerClass java/lang/classfile/attribute/StackMapFrameInfo innerClassName UninitializedVerificationTypeInfo flags 609 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl$PrivateListNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl innerClassName PrivateListNodeImpl flags 1a innerclass innerClass java/lang/invoke/MethodHandles$Lookup outerClass java/lang/invoke/MethodHandles innerClassName Lookup flags 19 +class name jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl +header extends java/util/AbstractList implements java/lang/classfile/components/ClassPrinter$ListNode nestHost jdk/internal/classfile/impl/ClassPrinterImpl sealed true permittedSubclasses jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl$PrivateListNodeImpl flags 21 signature Ljava/util/AbstractList;Ljava/lang/classfile/components/ClassPrinter$ListNode; +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName ListNodeImpl flags 9 +innerclass innerClass java/lang/classfile/components/ClassPrinter$Node outerClass java/lang/classfile/components/ClassPrinter innerClassName Node flags 609 +innerclass innerClass java/lang/classfile/components/ClassPrinter$ListNode outerClass java/lang/classfile/components/ClassPrinter innerClassName ListNode flags 609 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName MapNodeImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl$PrivateListNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl innerClassName PrivateListNodeImpl flags 1a +innerclass innerClass java/lang/invoke/MethodHandles$Lookup outerClass java/lang/invoke/MethodHandles innerClassName Lookup flags 19 +field name nodes descriptor Ljava/util/List; flags 14 signature Ljava/util/List; +method name descriptor (Ljdk/internal/classfile/impl/ClassPrinterImpl$Style;Ljava/lang/constant/ConstantDesc;Ljava/util/List;)V flags 4 signature (Ljdk/internal/classfile/impl/ClassPrinterImpl$Style;Ljava/lang/constant/ConstantDesc;Ljava/util/List;)V + +class name jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl +header extends java/lang/Object implements java/lang/classfile/components/ClassPrinter$MapNode nestHost jdk/internal/classfile/impl/ClassPrinterImpl flags 31 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName MapNodeImpl flags 19 +innerclass innerClass java/lang/classfile/components/ClassPrinter$Node outerClass java/lang/classfile/components/ClassPrinter innerClassName Node flags 609 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl$PrivateListNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl innerClassName PrivateListNodeImpl flags 1a +innerclass innerClass java/lang/classfile/components/ClassPrinter$MapNode outerClass java/lang/classfile/components/ClassPrinter innerClassName MapNode flags 609 +innerclass innerClass java/util/Map$Entry outerClass java/util/Map innerClassName Entry flags 609 +innerclass innerClass java/lang/invoke/MethodHandles$Lookup outerClass java/lang/invoke/MethodHandles innerClassName Lookup flags 19 + +class name jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl$PrivateListNodeImpl +header extends jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl nestHost jdk/internal/classfile/impl/ClassPrinterImpl flags 30 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$ListNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName ListNodeImpl flags 9 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl innerClassName MapNodeImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl$PrivateListNodeImpl outerClass jdk/internal/classfile/impl/ClassPrinterImpl$MapNodeImpl innerClassName PrivateListNodeImpl flags 1a +innerclass innerClass java/lang/classfile/components/ClassPrinter$Node outerClass java/lang/classfile/components/ClassPrinter innerClassName Node flags 609 + +class name jdk/internal/classfile/impl/ClassReaderImpl +header extends java/lang/Object implements java/lang/classfile/ClassReader flags 31 +innerclass innerClass java/lang/classfile/ClassFile$AttributeMapperOption outerClass java/lang/classfile/ClassFile innerClassName AttributeMapperOption flags 609 +innerclass innerClass jdk/internal/classfile/impl/UnboundAttribute$EmptyBootstrapAttribute outerClass jdk/internal/classfile/impl/UnboundAttribute innerClassName EmptyBootstrapAttribute flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$MethodHandleEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName MethodHandleEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$Utf8EntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName Utf8EntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$IntegerEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName IntegerEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$FloatEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName FloatEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$LongEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName LongEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$DoubleEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName DoubleEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$ClassEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName ClassEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$StringEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName StringEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$FieldRefEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName FieldRefEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$MethodRefEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName MethodRefEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$InterfaceMethodRefEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName InterfaceMethodRefEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$NameAndTypeEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName NameAndTypeEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$MethodTypeEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName MethodTypeEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$ConstantDynamicEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName ConstantDynamicEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$InvokeDynamicEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName InvokeDynamicEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$ModuleEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName ModuleEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$PackageEntryImpl outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName PackageEntryImpl flags 19 +innerclass innerClass jdk/internal/classfile/impl/AbstractPoolEntry$AbstractMemberRefEntry outerClass jdk/internal/classfile/impl/AbstractPoolEntry innerClassName AbstractMemberRefEntry flags 409 +-method name utf8EntryByIndex descriptor (I)Ljdk/internal/classfile/impl/AbstractPoolEntry$Utf8EntryImpl; +-method name utf8EntryByIndex descriptor (I)Ljava/lang/classfile/constantpool/Utf8Entry; +method name entryByIndex descriptor (ILjava/lang/Class;)Ljava/lang/classfile/constantpool/PoolEntry; flags 1 signature (ILjava/lang/Class;)TT; +method name readEntryOrNull descriptor (ILjava/lang/Class;)Ljava/lang/classfile/constantpool/PoolEntry; flags 1 signature (ILjava/lang/Class;)TT; + class name jdk/internal/classfile/impl/ClassRemapperImpl header extends java/lang/Record implements java/lang/classfile/components/ClassRemapper record true flags 31 recordcomponent name mapFunction descriptor Ljava/util/function/Function; signature Ljava/util/function/Function; @@ -893,6 +1001,22 @@ field name INSTANCE descriptor Ljdk/internal/classfile/impl/SignaturesImpl$Unbou method name values descriptor ()[Ljdk/internal/classfile/impl/SignaturesImpl$UnboundedTypeArgImpl; flags 9 method name valueOf descriptor (Ljava/lang/String;)Ljdk/internal/classfile/impl/SignaturesImpl$UnboundedTypeArgImpl; flags 9 methodParameters 8000:null +class name jdk/internal/classfile/impl/SplitConstantPool +method name entryByIndex descriptor (ILjava/lang/Class;)Ljava/lang/classfile/constantpool/PoolEntry; flags 1 signature (ILjava/lang/Class;)TT; + +class name jdk/internal/classfile/impl/StackMapDecoder$ObjectVerificationTypeInfoImpl +header extends java/lang/Record implements java/lang/classfile/attribute/StackMapFrameInfo$ObjectVerificationTypeInfo nestHost jdk/internal/classfile/impl/StackMapDecoder record true flags 31 +recordcomponent name className descriptor Ljava/lang/classfile/constantpool/ClassEntry; +innerclass innerClass jdk/internal/classfile/impl/StackMapDecoder$ObjectVerificationTypeInfoImpl outerClass jdk/internal/classfile/impl/StackMapDecoder innerClassName ObjectVerificationTypeInfoImpl flags 19 +innerclass innerClass java/lang/classfile/attribute/StackMapFrameInfo$ObjectVerificationTypeInfo outerClass java/lang/classfile/attribute/StackMapFrameInfo innerClassName ObjectVerificationTypeInfo flags 609 +-method name hashCode descriptor ()I +-method name equals descriptor (Ljava/lang/Object;)Z +method name equals descriptor (Ljava/lang/Object;)Z flags 1 +method name hashCode descriptor ()I flags 1 + +class name jdk/internal/classfile/impl/TemporaryConstantPool +method name entryByIndex descriptor (ILjava/lang/Class;)Ljava/lang/classfile/constantpool/PoolEntry; flags 1 signature (ILjava/lang/Class;)TT; + class name jdk/internal/classfile/impl/UnboundAttribute header extends jdk/internal/classfile/impl/AbstractElement implements java/lang/classfile/Attribute nestMembers jdk/internal/classfile/impl/UnboundAttribute$EmptyBootstrapAttribute,jdk/internal/classfile/impl/UnboundAttribute$AdHocAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleAttribute,jdk/internal/classfile/impl/UnboundAttribute$TypePathComponentImpl,jdk/internal/classfile/impl/UnboundAttribute$UnboundTypeAnnotation,jdk/internal/classfile/impl/UnboundAttribute$UnboundRecordComponentInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleRequiresInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleProvideInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleOpenInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleHashInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleExportInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundMethodParameterInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTypeInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundLineNumberInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundInnerClassInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundCharacterRangeInfo,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleTypeAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleTypeAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleParameterAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleParameterAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTypeTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLineNumberTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundCharacterRangeTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceDebugExtensionAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceIDAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundCompilationIDAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundNestHostAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundNestMembersAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundPermittedSubclassesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleResolutionAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModulePackagesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleHashesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleMainClassAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleTargetAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundMethodParametersAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundEnclosingMethodAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRecordAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundInnerClassesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundStackMapTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceFileAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundAnnotationDefaultAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundExceptionsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSignatureAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSyntheticAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundDeprecatedAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundConstantValueAttribute sealed true permittedSubclasses jdk/internal/classfile/impl/UnboundAttribute$UnboundConstantValueAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundDeprecatedAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSyntheticAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSignatureAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundExceptionsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundAnnotationDefaultAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceFileAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundStackMapTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundInnerClassesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRecordAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundEnclosingMethodAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundMethodParametersAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleTargetAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleMainClassAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleHashesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModulePackagesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleResolutionAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundPermittedSubclassesAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundNestMembersAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundNestHostAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundCompilationIDAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceIDAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundSourceDebugExtensionAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundCharacterRangeTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLineNumberTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundLocalVariableTypeTableAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleParameterAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleParameterAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeVisibleTypeAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundRuntimeInvisibleTypeAnnotationsAttribute,jdk/internal/classfile/impl/UnboundAttribute$UnboundModuleAttribute,jdk/internal/classfile/impl/UnboundAttribute$AdHocAttribute,jdk/internal/classfile/impl/UnboundAttribute$EmptyBootstrapAttribute flags 421 signature ;>Ljdk/internal/classfile/impl/AbstractElement;Ljava/lang/classfile/Attribute; innerclass innerClass jdk/internal/classfile/impl/UnboundAttribute$EmptyBootstrapAttribute outerClass jdk/internal/classfile/impl/UnboundAttribute innerClassName EmptyBootstrapAttribute flags 19 @@ -970,7 +1094,6 @@ innerclass innerClass java/lang/invoke/MethodHandles$Lookup outerClass java/lang innerclass innerClass java/lang/invoke/TypeDescriptor$OfField outerClass java/lang/invoke/TypeDescriptor innerClassName OfField flags 609 innerclass innerClass java/lang/invoke/TypeDescriptor$OfMethod outerClass java/lang/invoke/TypeDescriptor innerClassName OfMethod flags 609 method name ofTrusted descriptor (Ljava/lang/constant/ClassDesc;[Ljava/lang/constant/ClassDesc;)Ljdk/internal/constant/MethodTypeDescImpl; flags 9 -method name ofValidated descriptor (Ljava/lang/constant/ClassDesc;[Ljava/lang/constant/ClassDesc;)Ljdk/internal/constant/MethodTypeDescImpl; flags 9 method name ofDescriptor descriptor (Ljava/lang/String;)Ljdk/internal/constant/MethodTypeDescImpl; flags 9 method name returnType descriptor ()Ljava/lang/constant/ClassDesc; flags 1 method name parameterCount descriptor ()I flags 1 @@ -994,6 +1117,7 @@ method name changeReturnType descriptor (Ljava/lang/invoke/TypeDescriptor$OfFiel method name parameterArray descriptor ()[Ljava/lang/invoke/TypeDescriptor$OfField; flags 1041 method name returnType descriptor ()Ljava/lang/invoke/TypeDescriptor$OfField; flags 1041 method name parameterType descriptor (I)Ljava/lang/invoke/TypeDescriptor$OfField; flags 1041 methodParameters 1000:null +method name ofValidated descriptor (Ljava/lang/constant/ClassDesc;[Ljava/lang/constant/ClassDesc;)Ljdk/internal/constant/MethodTypeDescImpl; flags 89 class name jdk/internal/constant/ModuleDescImpl header extends java/lang/Record implements java/lang/constant/ModuleDesc record true flags 31 diff --git a/src/jdk.compiler/share/data/symbols/java.desktop-N.sym.txt b/src/jdk.compiler/share/data/symbols/java.desktop-N.sym.txt index 38cfc456f03fe..fdc899578cf82 100644 --- a/src/jdk.compiler/share/data/symbols/java.desktop-N.sym.txt +++ b/src/jdk.compiler/share/data/symbols/java.desktop-N.sym.txt @@ -26,6 +26,9 @@ # ### THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. ### # ########################################################## # +module name java.desktop +header exports java/applet,java/awt,java/awt/color,java/awt/desktop,java/awt/dnd,java/awt/event,java/awt/font,java/awt/geom,java/awt/im,java/awt/im/spi,java/awt/image,java/awt/image/renderable,java/awt/print,java/beans,java/beans/beancontext,javax/accessibility,javax/imageio,javax/imageio/event,javax/imageio/metadata,javax/imageio/plugins/bmp,javax/imageio/plugins/jpeg,javax/imageio/plugins/tiff,javax/imageio/spi,javax/imageio/stream,javax/print,javax/print/attribute,javax/print/attribute/standard,javax/print/event,javax/sound/midi,javax/sound/midi/spi,javax/sound/sampled,javax/sound/sampled/spi,javax/swing,javax/swing/border,javax/swing/colorchooser,javax/swing/event,javax/swing/filechooser,javax/swing/plaf,javax/swing/plaf/basic,javax/swing/plaf/metal,javax/swing/plaf/multi,javax/swing/plaf/nimbus,javax/swing/plaf/synth,javax/swing/table,javax/swing/text,javax/swing/text/html,javax/swing/text/html/parser,javax/swing/text/rtf,javax/swing/tree,javax/swing/undo extraModulePackages sun/print requires name\u0020;java.base\u0020;flags\u0020;8000,name\u0020;java.prefs\u0020;flags\u0020;0,name\u0020;java.datatransfer\u0020;flags\u0020;20,name\u0020;java.xml\u0020;flags\u0020;20 uses java/awt/im/spi/InputMethodDescriptor,javax/accessibility/AccessibilityProvider,javax/imageio/spi/ImageInputStreamSpi,javax/imageio/spi/ImageOutputStreamSpi,javax/imageio/spi/ImageReaderSpi,javax/imageio/spi/ImageTranscoderSpi,javax/imageio/spi/ImageWriterSpi,javax/print/PrintServiceLookup,javax/print/StreamPrintServiceFactory,javax/sound/midi/spi/MidiDeviceProvider,javax/sound/midi/spi/MidiFileReader,javax/sound/midi/spi/MidiFileWriter,javax/sound/midi/spi/SoundbankReader,javax/sound/sampled/spi/AudioFileReader,javax/sound/sampled/spi/AudioFileWriter,javax/sound/sampled/spi/FormatConversionProvider,javax/sound/sampled/spi/MixerProvider,sun/swing/InteropProvider provides interface\u0020;sun/datatransfer/DesktopDatatransferService\u0020;impls\u0020;sun/awt/datatransfer/DesktopDatatransferServiceImpl,interface\u0020;java/net/ContentHandlerFactory\u0020;impls\u0020;sun/awt/www/content/MultimediaContentHandlers,interface\u0020;javax/print/PrintServiceLookup\u0020;impls\u0020;sun/print/PrintServiceLookupProvider,interface\u0020;javax/print/StreamPrintServiceFactory\u0020;impls\u0020;sun/print/PSStreamPrinterFactory,interface\u0020;javax/sound/midi/spi/MidiDeviceProvider\u0020;impls\u0020;com/sun/media/sound/MidiInDeviceProvider\u005C;u002C;com/sun/media/sound/MidiOutDeviceProvider\u005C;u002C;com/sun/media/sound/RealTimeSequencerProvider\u005C;u002C;com/sun/media/sound/SoftProvider,interface\u0020;javax/sound/midi/spi/MidiFileReader\u0020;impls\u0020;com/sun/media/sound/StandardMidiFileReader,interface\u0020;javax/sound/midi/spi/MidiFileWriter\u0020;impls\u0020;com/sun/media/sound/StandardMidiFileWriter,interface\u0020;javax/sound/midi/spi/SoundbankReader\u0020;impls\u0020;com/sun/media/sound/AudioFileSoundbankReader\u005C;u002C;com/sun/media/sound/DLSSoundbankReader\u005C;u002C;com/sun/media/sound/JARSoundbankReader\u005C;u002C;com/sun/media/sound/SF2SoundbankReader,interface\u0020;javax/sound/sampled/spi/AudioFileReader\u0020;impls\u0020;com/sun/media/sound/AiffFileReader\u005C;u002C;com/sun/media/sound/AuFileReader\u005C;u002C;com/sun/media/sound/SoftMidiAudioFileReader\u005C;u002C;com/sun/media/sound/WaveFileReader\u005C;u002C;com/sun/media/sound/WaveFloatFileReader\u005C;u002C;com/sun/media/sound/WaveExtensibleFileReader,interface\u0020;javax/sound/sampled/spi/AudioFileWriter\u0020;impls\u0020;com/sun/media/sound/AiffFileWriter\u005C;u002C;com/sun/media/sound/AuFileWriter\u005C;u002C;com/sun/media/sound/WaveFileWriter\u005C;u002C;com/sun/media/sound/WaveFloatFileWriter,interface\u0020;javax/sound/sampled/spi/FormatConversionProvider\u0020;impls\u0020;com/sun/media/sound/AlawCodec\u005C;u002C;com/sun/media/sound/AudioFloatFormatConverter\u005C;u002C;com/sun/media/sound/PCMtoPCMCodec\u005C;u002C;com/sun/media/sound/UlawCodec,interface\u0020;javax/sound/sampled/spi/MixerProvider\u0020;impls\u0020;com/sun/media/sound/DirectAudioDeviceProvider\u005C;u002C;com/sun/media/sound/PortMixerProvider target linux-amd64 flags 8000 + class name java/beans/Beans -method name instantiate descriptor (Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/beans/beancontext/BeanContext;)Ljava/lang/Object; method name instantiate descriptor (Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/beans/beancontext/BeanContext;)Ljava/lang/Object; thrownTypes java/io/IOException,java/lang/ClassNotFoundException flags 9 deprecated true runtimeAnnotations @Ljava/lang/Deprecated;(forRemoval=Ztrue,since="23") @@ -93,6 +96,25 @@ innerclass innerClass java/beans/beancontext/BeanContextSupport$BCSIterator oute innerclass innerClass java/beans/beancontext/BeanContextSupport$BCSChild outerClass java/beans/beancontext/BeanContextSupport innerClassName BCSChild flags 4 innerclass innerClass java/util/Map$Entry outerClass java/util/Map innerClassName Entry flags 609 +class name javax/print/attribute/standard/OutputBin +header extends javax/print/attribute/EnumSyntax implements javax/print/attribute/PrintRequestAttribute,javax/print/attribute/PrintJobAttribute sealed true permittedSubclasses sun/print/CustomOutputBin flags 21 +field name TOP descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name MIDDLE descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name BOTTOM descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name SIDE descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name LEFT descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name RIGHT descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name CENTER descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name REAR descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name FACE_UP descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name FACE_DOWN descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +field name LARGE_CAPACITY descriptor Ljavax/print/attribute/standard/OutputBin; flags 19 +method name descriptor (I)V flags 4 +method name getStringTable descriptor ()[Ljava/lang/String; flags 4 +method name getEnumValueTable descriptor ()[Ljavax/print/attribute/EnumSyntax; flags 4 +method name getCategory descriptor ()Ljava/lang/Class; flags 11 signature ()Ljava/lang/Class<+Ljavax/print/attribute/Attribute;>; +method name getName descriptor ()Ljava/lang/String; flags 11 + class name javax/swing/JScrollBar method name setMinimumSize descriptor (Ljava/awt/Dimension;)V flags 1 method name setMaximumSize descriptor (Ljava/awt/Dimension;)V flags 1 @@ -100,3 +122,13 @@ method name setMaximumSize descriptor (Ljava/awt/Dimension;)V flags 1 class name javax/swing/plaf/synth/SynthTreeUI method name getCollapsedIcon descriptor ()Ljavax/swing/Icon; flags 1 +class name sun/print/CustomOutputBin +header extends javax/print/attribute/standard/OutputBin flags 31 +method name createOutputBin descriptor (Ljava/lang/String;Ljava/lang/String;)Lsun/print/CustomOutputBin; flags 29 +method name getChoiceName descriptor ()Ljava/lang/String; flags 1 +method name getSuperEnumTable descriptor ()[Ljavax/print/attribute/standard/OutputBin; flags 1 +method name getStringTable descriptor ()[Ljava/lang/String; flags 4 +method name getCustomName descriptor ()Ljava/lang/String; flags 1 +method name getEnumValueTable descriptor ()[Lsun/print/CustomOutputBin; flags 4 +method name getEnumValueTable descriptor ()[Ljavax/print/attribute/EnumSyntax; flags 1044 + diff --git a/src/jdk.compiler/share/data/symbols/jdk.jshell-N.sym.txt b/src/jdk.compiler/share/data/symbols/jdk.jshell-N.sym.txt index 5bbe06929441e..da0509ee6c023 100644 --- a/src/jdk.compiler/share/data/symbols/jdk.jshell-N.sym.txt +++ b/src/jdk.compiler/share/data/symbols/jdk.jshell-N.sym.txt @@ -26,6 +26,9 @@ # ### THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. ### # ########################################################## # +module name jdk.jshell +header exports jdk/jshell,jdk/jshell/execution,jdk/jshell/spi,jdk/jshell/tool requires name\u0020;java.base\u0020;flags\u0020;8000,name\u0020;java.logging\u0020;flags\u0020;0,name\u0020;jdk.compiler\u0020;flags\u0020;0,name\u0020;jdk.internal.ed\u0020;flags\u0020;0,name\u0020;jdk.internal.le\u0020;flags\u0020;0,name\u0020;jdk.internal.md\u0020;flags\u0020;0,name\u0020;jdk.internal.opt\u0020;flags\u0020;0,name\u0020;java.compiler\u0020;flags\u0020;20,name\u0020;java.prefs\u0020;flags\u0020;20,name\u0020;jdk.jdi\u0020;flags\u0020;20 uses jdk/jshell/spi/ExecutionControlProvider,jdk/internal/editor/spi/BuildInEditorProvider provides interface\u0020;javax/tools/Tool\u0020;impls\u0020;jdk/internal/jshell/tool/JShellToolProvider,interface\u0020;jdk/jshell/spi/ExecutionControlProvider\u0020;impls\u0020;jdk/jshell/execution/JdiExecutionControlProvider\u005C;u002C;jdk/jshell/execution/LocalExecutionControlProvider\u005C;u002C;jdk/jshell/execution/FailOverExecutionControlProvider,interface\u0020;jdk/internal/io/JdkConsoleProvider\u0020;impls\u0020;jdk/jshell/execution/impl/ConsoleImpl$ConsoleProviderImpl target linux-amd64 moduleMainClass jdk/internal/jshell/tool/JShellToolProvider flags 8000 classAnnotations @Ljdk/internal/javac/ParticipatesInPreview; + class name jdk/jshell/Snippet$SubKind field name MODULE_IMPORT_SUBKIND descriptor Ljdk/jshell/Snippet$SubKind; flags 4019 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;MODULE_IMPORTS;,reflective=Ztrue) diff --git a/src/jdk.hotspot.agent/test/libproc/Makefile b/src/jdk.hotspot.agent/test/libproc/Makefile index 81fadaeb55272..c7b171bc63381 100644 --- a/src/jdk.hotspot.agent/test/libproc/Makefile +++ b/src/jdk.hotspot.agent/test/libproc/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2003, 2024, 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 @@ -19,7 +19,7 @@ # 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. -# +# # all: diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/amd64/AMD64.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/amd64/AMD64.java index 9f611b0dfcef1..c76ec64278ada 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/amd64/AMD64.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/amd64/AMD64.java @@ -233,6 +233,7 @@ public enum CPUFeature implements CPUFeatureName { CET_SS, AVX512_IFMA, AVX_IFMA, + APX_F, } private final EnumSet features; diff --git a/src/utils/LogCompilation/Makefile b/src/utils/LogCompilation/Makefile index 5f9ca08384279..6ab946abc2099 100644 --- a/src/utils/LogCompilation/Makefile +++ b/src/utils/LogCompilation/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2009, 2024, 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 @@ -19,7 +19,7 @@ # 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. -# +# # PKGLIST = \ com.sun.hotspot.tools.compiler @@ -49,7 +49,7 @@ SRC_DIR = src BUILD_DIR = build OUTPUT_DIR = $(BUILD_DIR)/classes -# gnumake 3.78.1 does not accept the *s, +# gnumake 3.78.1 does not accept the *s, # so use the shell to expand them ALLFILES := $(patsubst %,$(SRC_DIR)/%,$(FILELIST)) ALLFILES := $(shell /bin/ls $(ALLFILES)) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index b6cc0489485b8..55fc22b4e759c 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -118,11 +118,6 @@ runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le runtime/cds/appcds/customLoader/HelloCustom_JFR.java 8241075 linux-all,windows-x64 runtime/Thread/TestAlwaysPreTouchStacks.java 8324781 linux-all -applications/jcstress/accessAtomic.java 8325984 generic-all -applications/jcstress/acqrel.java 8325984 generic-all -applications/jcstress/atomicity.java 8325984 generic-all -applications/jcstress/coherence.java 8325984 generic-all - applications/jcstress/copy.java 8229852 linux-all containers/docker/TestJcmd.java 8278102 linux-all diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index 25ef8250e4d45..bfa99c72b6646 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -11,7 +11,7 @@ # # A test flagged with cgroups uses cgroups. # -# Notes on "client" keywords : headful sound printer multimon +# Notes on "client" keywords : headful sound printer multimon # =========================================================== # # These keywords are there to help with test selection so that @@ -31,7 +31,7 @@ # Tests may not fail if there is none, instead just silently return. # But they also may legitimately throw an Exception depending on the test. # Also printer tests are not necessarily headful, but some are, and some are automated. -# +# # "sound". Similarly, not all sound tests require audio devices, but many do. # A test flagged with key "sound" needs audio devices on the system. # Also they are not necessarily "headful", since they don't require a display etc. @@ -99,6 +99,7 @@ requires.properties= \ vm.jvmci \ vm.jvmci.enabled \ vm.jvmti \ + vm.cpu.features \ docker.support \ release.implementor \ jdk.containerized \ diff --git a/test/jdk/com/sun/tools/attach/Agent.java b/test/jdk/com/sun/tools/attach/Agent.java index 9090ea7351969..510ff787fa76f 100644 --- a/test/jdk/com/sun/tools/attach/Agent.java +++ b/test/jdk/com/sun/tools/attach/Agent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ * the given port. */ import java.net.Socket; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.io.IOException; @@ -38,7 +39,7 @@ public static void agentmain(String args) throws IOException { int port = Integer.parseInt(args); System.out.println("Agent connecting back to Tool...."); Socket s = new Socket(); - s.connect( new InetSocketAddress(port) ); + s.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port)); System.out.println("Agent connected to Tool."); s.close(); } diff --git a/test/jdk/com/sun/tools/attach/BasicTests.java b/test/jdk/com/sun/tools/attach/BasicTests.java index 4dc7065630fad..9ae454368106e 100644 --- a/test/jdk/com/sun/tools/attach/BasicTests.java +++ b/test/jdk/com/sun/tools/attach/BasicTests.java @@ -23,6 +23,8 @@ import java.io.File; import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.List; @@ -213,7 +215,8 @@ public static void main(String args[]) throws Exception { System.out.println(" - Test: End-to-end connection with agent"); - ServerSocket ss = new ServerSocket(0); + ServerSocket ss = new ServerSocket(); + ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); int port = ss.getLocalPort(); System.out.println(" - Loading Agent.jar into target VM ..."); @@ -231,7 +234,8 @@ public static void main(String args[]) throws Exception { System.out.println(" - Test: End-to-end connection with RedefineAgent"); - ServerSocket ss2 = new ServerSocket(0); + ServerSocket ss2 = new ServerSocket(); + ss2.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); int port2 = ss2.getLocalPort(); System.out.println(" - Loading RedefineAgent.jar into target VM ..."); diff --git a/test/jdk/com/sun/tools/attach/RedefineAgent.java b/test/jdk/com/sun/tools/attach/RedefineAgent.java index af01df427628e..cc0e7d269df35 100644 --- a/test/jdk/com/sun/tools/attach/RedefineAgent.java +++ b/test/jdk/com/sun/tools/attach/RedefineAgent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2024, 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 @@ -32,6 +32,7 @@ * 6446941 java.lang.instrument: multiple agent attach fails (first agent chooses capabilities) */ import java.net.Socket; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.io.IOException; import java.util.Arrays; @@ -104,7 +105,7 @@ public static void agentmain(String args, Instrumentation inst) throws Exception int port = Integer.parseInt(args); System.out.println("RedefineAgent connecting back to Tool...."); Socket s = new Socket(); - s.connect( new InetSocketAddress(port) ); + s.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), port)); System.out.println("RedefineAgent connected to Tool."); testRedefine(inst); diff --git a/test/jdk/java/io/Console/RestoreEchoTest.java b/test/jdk/java/io/Console/RestoreEchoTest.java new file mode 100644 index 0000000000000..78d36faa53736 --- /dev/null +++ b/test/jdk/java/io/Console/RestoreEchoTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, 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. + */ + +import java.io.Console; +import java.nio.file.Files; +import java.nio.file.Paths; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import static org.junit.jupiter.api.Assertions.*; + + +/** + * @test + * @bug 8332161 + * @summary Tests JdkConsoleImpl restores the echo state after readPassword() call + * @library /test/lib + * @run junit RestoreEchoTest + */ +public class RestoreEchoTest { + + @Test + @EnabledOnOs({OS.LINUX, OS.MAC}) + public void testRestoreEcho() throws Throwable { + // check "expect" command availability + var expect = Paths.get("/usr/bin/expect"); + if (!Files.exists(expect) || !Files.isExecutable(expect)) { + Assumptions.abort("'" + expect + "' not found"); + } + + expectRunner("-echo"); + expectRunner("echo"); + } + + private static void expectRunner(String initialEcho) throws Throwable { + // invoking "expect" command + var testSrc = System.getProperty("test.src", "."); + var testClasses = System.getProperty("test.classes", "."); + var jdkDir = System.getProperty("test.jdk"); + OutputAnalyzer output = ProcessTools.executeProcess( + "expect", + "-n", + testSrc + "/restoreEcho.exp", + initialEcho, + jdkDir + "/bin/java", + "-Djdk.console=java.base", + "-classpath", testClasses, + "RestoreEchoTest"); + output.reportDiagnosticSummary(); + assertEquals(0, output.getExitValue()); + } + + public static void main(String... args) throws Throwable { + if (!"java.base".equals(System.getProperty("jdk.console"))) { + throw new RuntimeException("Test failed. jdk.console is not java.base"); + } + + Console con = System.console(); + if (con == null) { + throw new RuntimeException("Test failed. System.console() returned null"); + } + + // testing readLine() + String input = con.readLine("prompt: "); + con.printf("input is %s%n", input); + + // testing readPassword() + input = String.valueOf(con.readPassword("password prompt: ")); + con.printf("password is %s%n", input); + } +} diff --git a/test/jdk/java/io/Console/restoreEcho.exp b/test/jdk/java/io/Console/restoreEcho.exp new file mode 100644 index 0000000000000..5a194b07a7d1a --- /dev/null +++ b/test/jdk/java/io/Console/restoreEcho.exp @@ -0,0 +1,58 @@ +# +# Copyright (c) 2024, 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. +# + +set initialEcho [lindex $argv 0] +set java [lrange $argv 1 end] +set rlprompt "prompt: " +set rpprompt "password prompt: " +set rlinput "InPuT" +set rpinput "PaSsWoRd" +set rlexpected "input is $rlinput" +set rpexpected "password is $rpinput" +set stty_init $initialEcho +set timeout 10 + +proc test {prompt input echoStat expected} { + expect "$prompt" + send -- "$input\n" + if {$echoStat == "echo"} { + expect "$input" + } + expect "$expected" + + if {$expect_out(0,string) != $expected} { + send_error "Expected: $expected\n" + send_error "Received: $expect_out(0,string)" + exit 1 + } +} + +spawn sh -c "[list {*}$java] && stty -a" + +# readLine() - input is displayed depending on initialEcho value +test "$rlprompt" "$rlinput" "$initialEcho" "$rlexpected" +# readPassword() - input is not displayed +test "$rpprompt" "$rpinput" "-echo" "$rpexpected" +# See if the initialEcho is restored with `stty -a` +expect -- " $initialEcho " +expect eof diff --git a/test/jdk/java/lang/String/IndexOf.java b/test/jdk/java/lang/String/IndexOf.java new file mode 100644 index 0000000000000..baab83e19c4ff --- /dev/null +++ b/test/jdk/java/lang/String/IndexOf.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2024, Intel Corporation. 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 + * @bug 8320448 + * @summary test String indexOf() intrinsic + * @run driver IndexOf + */ + +/* + * @test + * @bug 8320448 + * @summary test String indexOf() intrinsic + * @requires vm.cpu.features ~= ".*avx2.*" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xcomp -XX:-TieredCompilation -XX:UseAVX=2 -XX:+UnlockDiagnosticVMOptions -XX:+EnableX86ECoreOpts IndexOf + */ + + public class IndexOf { + final int scope = 32*2+16+8; + final char a, aa, b, c, d; + enum Encoding {LL, UU, UL; } + final Encoding ae; + int failures; + + IndexOf(Encoding _ae) { + failures = 0; + ae = _ae; + switch (ae) { + case LL: + a = 'a'; + aa = a; + b = 'b'; + c = 'c'; + d = 'd'; + break; + case UU: + a = '\u0061'; + aa = a; + b = '\u0062'; + c = '\u1063'; + d = '\u0064'; + break; + default: //case UL: + a = 'a'; + aa = '\u1061'; + b = 'b'; + c = 'c'; + d = 'd'; + break; + } + } + + // needle =~ /ab*d/ + // badNeedle =~ /ab*db*d/ + interface Append {void append(int pos, char cc);} + String newNeedle(int size, int badPosition) { + if (size<2) {throw new RuntimeException("Fix testcase "+size);} + + StringBuilder needle = new StringBuilder(size); + Append n = (int pos, char cc) -> { + if (pos == badPosition) + needle.append(c); + else + needle.append(cc); + }; + + n.append(0, a); + for (int i=1; isize) {throw new RuntimeException("Fix testcase "+nPosition+" "+needle.length()+" "+size);} + StringBuilder haystack = new StringBuilder(size); + int i = 0; + for (; isize) {throw new RuntimeException("Fix testcase "+nPosition+" "+needle.length()+" "+size);} + StringBuilder haystack = new StringBuilder(size); + int i = 0; + for (; ihaystack_len) return -1; + if (-1 != "Hello".indexOf("HelloWorld", 3)) { + System.out.println("FAILED: if (needle_len>haystack_len) return -1"); + failures++; + } + return this; + } + + IndexOf test1() { // Test expected to find one needle + for (int nSize = 2; nSize titles = new HashMap(); + static Random rng = new Random(1999); + + public static void main(String[] args) throws Exception { + int foo = 0; + String testName = "ECoreIndexOf"; + + generator = new Random(); + long seed = generator.nextLong(); + generator.setSeed(seed); + System.out.println("Seed set to "+ seed); + + /////////////////////////// WARM-UP ////////////////////////// + + for (int i = 0; i < 20000; i++) { + char c = 65; + char c16 = 0x1ed; + StringBuffer sb = new StringBuffer("a"); + StringBuffer sb16 = new StringBuffer("\u01fe"); + + foo += indexOfKernel("\u01fe", "a"); + foo += indexOfKernel("\u01fe", "a", 0); + foo += indexOfKernel("\u01fe", "\u01ff"); + foo += indexOfKernel("\u01fe", "\u01ff", 0); + foo += indexOfKernel("a", "a"); + foo += indexOfKernel("a", "a", 0); + foo += indexOfKernel("a", "\u01ff"); + foo += indexOfKernel("a", "\u01ff", 0); + + foo += indexOfKernel("\u01fe", c); + foo += indexOfKernel("\u01fe", c, 0); + foo += indexOfKernel("\u01fe", c16); + foo += indexOfKernel("\u01fe", c16, 0); + foo += indexOfKernel("a", c); + foo += indexOfKernel("a", c, 0); + foo += indexOfKernel("a", c16); + foo += indexOfKernel("a", c16, 0); + + foo += indexOfKernel(sb16, c); + foo += indexOfKernel(sb16, c, 0); + foo += indexOfKernel(sb16, c16); + foo += indexOfKernel(sb16, c16, 0); + foo += indexOfKernel(sb, c); + foo += indexOfKernel(sb, c, 0); + foo += indexOfKernel(sb, c16); + foo += indexOfKernel(sb, c16, 0); + } + + /////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////// + + String[] decorators = {"", " (same char)"}; + Charset[] charSets = {StandardCharsets.ISO_8859_1, StandardCharsets.UTF_16}; + boolean[] truefalse = {true, false}; + + titles.put(StandardCharsets.ISO_8859_1, "L"); + titles.put(StandardCharsets.UTF_16, "U"); + + for (int xxy = 0; xxy < 2; xxy++) { // Run at least twice to ensure stub called + + for (int i = 0; i < 128; i++) { + haystack[i] = (char) i; + } + + haystack_16[0] = '\u0000'; // (char) (23 + 256); + for (int i = 1; i < 128; i++) { + haystack_16[i] = (char) (i); + } + + simpleTest(); + compareIndexOfLastIndexOf(); + compareStringStringBuffer(); + StringIndexof(); + StringIndexofChar(); + StringIndexofHuge(); + + for (String decorator : decorators) { + for (Charset csHaystack : charSets) { + for (Charset csNeedle : charSets) { + System.out.println("Testing " + titles.get(csHaystack) + titles.get(csNeedle) + decorator); + for (boolean useOffset : truefalse) { + for (boolean useBuffer : truefalse) { + exhaustive(useOffset, useBuffer, csHaystack, csNeedle); + } + } + } + } + + for (int i = 0; i < 128; i++) { + haystack[i] = (char) 'a'; + } + + for (int i = 0; i < 128; i++) { + haystack_16[i] = (char) ('a' + 256); + } + } + } + + System.out.println(testName + " complete."); + + if (failure) + throw new RuntimeException("One or more failures."); + } + + private static void report(String testName, int failCount) { + System.err.println(testName + ": " + + (failCount == 0 ? "Passed" : "Failed(" + failCount + ")")); + if (failCount > 0) + failure = true; + } + + private static String generateTestString(int min, int max) { + StringBuffer aNewString = new StringBuffer(120); + int aNewLength = getRandomIndex(min, max); + for (int y = 0; y < aNewLength; y++) { + int achar = generator.nextInt(30) + 30; + char test = (char) (achar); + aNewString.append(test); + } + return aNewString.toString(); + } + + private static String makeRndString(boolean isUtf16, int length) { + StringBuilder sb = new StringBuilder(length); + if (length > 0) { + sb.append(isUtf16 ? '\u2026' : 'b'); // ... + + for (int i = 1; i < length - 1; i++) { + sb.append((char) ('b' + rng.nextInt(26))); + } + + sb.append(rng.nextInt(3) >= 1 ? 'a' : 'b');// 66.6% of time 'a' is in string + } + return sb.toString(); + } + + private static int indexOfKernel(String haystack, String needle) { + return haystack.indexOf(needle); + } + + private static int indexOfKernel(String haystack, String needle, int offset) { + return haystack.indexOf(needle, offset); + } + + private static int indexOfKernel(StringBuffer haystack, String needle) { + return haystack.indexOf(needle); + } + + private static int indexOfKernel(StringBuffer haystack, char cneedle) { + String needle = String.valueOf(cneedle); + return haystack.indexOf(needle); + } + + private static int indexOfKernel(StringBuffer haystack, String needle, int offset) { + return haystack.indexOf(needle, offset); + } + + private static int indexOfKernel(StringBuffer haystack, char cneedle, int offset) { + String needle = String.valueOf(cneedle); + return haystack.indexOf(needle, offset); + } + + private static int indexOfKernel(String haystack, char needle) { + return haystack.indexOf(needle); + } + + private static int indexOfKernel(String haystack, char needle, int offset) { + return haystack.indexOf(needle, offset); + } + + private static void printStringBytes(byte[] bytes) { + System.err.println(" bytes.len=" + bytes.length); + for (byte b : bytes) { + System.err.print(String.format("0x%02x ", b)); + } + System.err.println(""); + } + + private static int getRandomIndex(int constraint1, int constraint2) { + int range = constraint2 - constraint1; + int x = generator.nextInt(range); + return constraint1 + x; + } + + private static int naiveFind(String haystack, String needle) { + return naiveFind(haystack, needle, 0); + } + + private static int naiveFind(String haystack, char needle) { + return naiveFind(haystack, needle, 0); + } + + private static int naiveFind(String haystack, String needle, int offset) { + int x = offset; + int len = haystack.length() - offset; + if (needle.length() == 0) + return offset; + if (needle.length() > len) + return -1; + int hsndx = 0; + int nndx = 0; + for (int xx = 0; xx < offset; xx++) { + hsndx += Character.charCount(haystack.codePointAt(hsndx)); + } + + for (x = offset; x < haystack.length() - needle.length() + 1; x++) { + if (haystack.codePointAt(hsndx) == needle.codePointAt(0)) { + nndx = Character.charCount(needle.codePointAt(0)); + int hsndx_tmp = hsndx + Character.charCount(haystack.codePointAt(hsndx)); + + while (nndx < needle.length()) { + if (haystack.codePointAt(hsndx_tmp) != needle.codePointAt(nndx)) { + break; + } + hsndx_tmp += Character.charCount(haystack.codePointAt(hsndx_tmp)); + nndx += Character.charCount(needle.codePointAt(nndx)); + } + if (nndx == needle.length()) { + return x; + } + } + hsndx += Character.charCount(haystack.codePointAt(hsndx)); + } + return -1; + } + + private static int naiveFind(String haystack, char cneedle, int offset) { + int x = offset; + int len = haystack.length() - offset; + String needle = String.valueOf(cneedle); + if (len == 0) + return -1; + int hsndx = 0; + for (int xx = 0; xx < offset; xx++) { + hsndx += Character.charCount(haystack.codePointAt(hsndx)); + } + + for (x = offset; x < haystack.length(); x++) { + if (haystack.codePointAt(hsndx) == needle.codePointAt(0)) { + return x; + } + hsndx += Character.charCount(haystack.codePointAt(hsndx)); + } + + return -1; + } + + private static void exhaustive(boolean useOffset, boolean useStringBuffer, Charset hs_charset, + Charset needleCharset) { + int result = 0; + int midresult = 0; + int endresult = 0; + int l_offset = 0; + int failCount = 0; + + String thisTest = titles.get(hs_charset) + titles.get(needleCharset) + (useOffset ? " w/offset" : "") + (useStringBuffer ? " StringBuffer" : ""); + + for (int needleSize = 0; needleSize < 128; needleSize++) { + for (int haystackSize = 0; haystackSize < 128; haystackSize++) { + for (l_offset = 0; l_offset <= haystackSize; l_offset++) { + String needle = new String(Arrays.copyOfRange( + (needleCharset == StandardCharsets.UTF_16) ? haystack_16 : haystack, l_offset, l_offset + needleSize)); + int hsSize = (haystackSize - l_offset) >= 0 ? haystackSize - l_offset : 0; + int midStart = Math.max((hsSize / 2) - (needleSize / 2), 0); + int endStart = (hsSize > needleSize) ? hsSize - needleSize : 0; + String midNeedle = new String( + Arrays.copyOfRange((needleCharset == StandardCharsets.UTF_16) ? haystack_16 : haystack, + midStart + l_offset, midStart + needleSize + l_offset)); + String endNeedle = new String( + Arrays.copyOfRange((needleCharset == StandardCharsets.UTF_16) ? haystack_16 : haystack, + endStart + l_offset, endStart + needleSize + l_offset)); + String shs = new String( + Arrays.copyOfRange((hs_charset == StandardCharsets.UTF_16) ? haystack_16 : haystack, 0, haystackSize)); + + // Truncate needles to correct lengths + + if (l_offset + needleSize > haystack.length + 1) { + needle = needle.substring(0, needleSize); + midNeedle = midNeedle.substring(0, needleSize); + endNeedle = endNeedle.substring(0, needleSize); + } + + if (!success && needleSize > 1) { + needle = needle.substring(0, needle.length() - 1) + (char) ((int) (needle.charAt(needle.length() - 2) + 1)); + midNeedle = midNeedle.substring(0, midNeedle.length() - 1) + + (char) ((int) (midNeedle.charAt(midNeedle.length() - 2) + 1)); + endNeedle = endNeedle.substring(0, endNeedle.length() - 1) + + (char) ((int) (endNeedle.charAt(endNeedle.length() - 2) + 1)); + } + + StringBuffer hs = new StringBuffer(shs.length()); + hs.append(shs); + if (!shs.equals(hs.toString())) + throw new RuntimeException("Initial equality failure"); + + if (useStringBuffer) { + result = indexOfKernel(hs, needle, l_offset); + midresult = indexOfKernel(hs, midNeedle, l_offset); + endresult = indexOfKernel(hs, endNeedle, l_offset); + } else { + result = indexOfKernel(shs, needle, l_offset); + midresult = indexOfKernel(shs, midNeedle, l_offset); + endresult = indexOfKernel(shs, endNeedle, l_offset); + } + int nResult = naiveFind(hs.toString(), needle, l_offset); + int midnResult = naiveFind(hs.toString(), midNeedle, l_offset); + int endnResult = naiveFind(hs.toString(), endNeedle, l_offset); + if (result != nResult) { + failCount++; + System.err.println("useOffset=" + useOffset + ", useStringBuffer=" + useStringBuffer); + System.err.print("Haystack="); + printStringBytes(shs.getBytes(hs_charset)); + System.err.print("Needle="); + printStringBytes(needle.getBytes(needleCharset)); + System.err.println("l_offset=" + l_offset); + System.err.println("haystackLen=" + haystackSize + " needleLen=" + needleSize + + " result=" + result + " nResult=" + nResult); + System.err.println(""); + } + // badResults = success ? ((midnResult == -1) || (midresult == -1)) : + // ((midnResult != -1) || (midresult != -1)); + if ((midresult != midnResult)) { + failCount++; + System.err.println("useOffset=" + useOffset + ", useStringBuffer=" + useStringBuffer); + System.err.print("Haystack="); + printStringBytes(shs.getBytes(hs_charset)); + System.err.print("Needle="); + printStringBytes(midNeedle.getBytes(needleCharset)); + System.err.println("l_offset=" + l_offset); + System.err.println("haystackLen=" + haystackSize + " needleLen=" + needleSize + + " midresult=" + midresult + " midnResult=" + midnResult); + System.err.println(""); + } + // badResults = success ? ((endnResult == -1) || (endresult == -1)) : + // ((endnResult != -1) || (endresult != -1)); + if ((endresult != endnResult)) { + failCount++; + System.err.println("useOffset=" + useOffset + ", useStringBuffer=" + useStringBuffer); + System.err.print("Haystack="); + printStringBytes(shs.getBytes(hs_charset)); + System.err.print("Needle="); + printStringBytes(endNeedle.getBytes(needleCharset)); + System.err.println("l_offset=" + l_offset); + System.err.println("haystackLen=" + haystackSize + " needleLen=" + needleSize + + " endresult=" + endresult + " endnResult=" + endnResult); + System.err.println(""); + } + + if (!useOffset) + l_offset = haystackSize + 100; + } + } + } + + report("Exhaustive " + thisTest, failCount); + } + + private static void PrintError(int kernel, int naive, int num, String prefix, String hs, char needle) { + PrintError(kernel, naive, num, prefix, hs, String.valueOf(needle)); + } + + private static void PrintError(int kernel, int naive, int num, String prefix, String hs, String needle) { + if (!verbose) + return; + System.err.println(prefix + ": (" + num + "): kernel=" + kernel + ", naive=" + naive); + System.err.print("Haystack="); + printStringBytes(hs.getBytes()); + System.err.print("Needle="); + printStringBytes(needle.getBytes()); + System.err.println(""); + } + + private static void simpleTest() { + int failCount = 0; + String sourceString; + StringBuffer sourceBuffer; + String targetString; + String emptyString = ""; + String allAs = new String("aaaaaaaaaaaaaaaaaaaaaaaaa"); + StringBuffer allAsBuffer = new StringBuffer(allAs); + + for (int i = 0; i < 10000; i++) { + do { + sourceString = generateTestString(99, 100); + sourceBuffer = new StringBuffer(sourceString); + targetString = generateTestString(10, 11); + } while (indexOfKernel(sourceString, targetString) != -1); + + int index1 = generator.nextInt(90) + 5; + sourceBuffer = sourceBuffer.replace(index1, index1, targetString); + + if ((indexOfKernel(sourceBuffer, targetString) != index1) || + (index1 != naiveFind(sourceBuffer.toString(), targetString, 0))) { + System.err.println("sourceBuffer.indexOf(targetString) fragment '" + targetString + "' (" + + targetString.length() + ") String = " + + sourceBuffer.toString() + " len Buffer = " + sourceBuffer.toString().length()); + System.err.println(" naive = " + naiveFind(sourceBuffer.toString(), targetString, 0) + ", IndexOf = " + + indexOfKernel(sourceBuffer, targetString)); + failCount++; + } + if ((indexOfKernel(sourceBuffer, targetString, 5) != index1) || + (index1 != naiveFind(sourceBuffer.toString(), targetString, 0))) { + System.err.println("sourceBuffer.indexOf(targetString, 5) fragment '" + targetString + "' (" + + targetString.length() + ") String = " + + sourceBuffer.toString() + " len Buffer = " + sourceBuffer.toString().length()); + System.err.println(" naive = " + naiveFind(sourceBuffer.toString(), targetString, 0) + ", IndexOf = " + + indexOfKernel(sourceBuffer, targetString, 5)); + failCount++; + } + if ((indexOfKernel(sourceBuffer, targetString, 99) == index1) || + (index1 != naiveFind(sourceBuffer.toString(), targetString, 0))) { + System.err.println("sourceBuffer.indexOf(targetString, 99) fragment '" + targetString + "' (" + + targetString.length() + ") String = " + + sourceBuffer.toString() + " len Buffer = " + sourceBuffer.toString().length()); + System.err.println(" naive = " + naiveFind(sourceBuffer.toString(), targetString, 0) + ", IndexOf = " + + indexOfKernel(sourceBuffer, targetString, 99)); + failCount++; + } + if ((indexOfKernel(sourceBuffer, emptyString, 99) != 99) || + (99 != naiveFind(sourceBuffer.toString(), emptyString, 99))) { + System.err.println("sourceBuffer.indexOf(emptyString, 99) fragment '" + emptyString + "' (" + + emptyString.length() + ") String = " + + sourceBuffer.toString() + " len Buffer = " + sourceBuffer.toString().length()); + System.err.println(" naive = " + naiveFind(sourceBuffer.toString(), emptyString, 99) + ", IndexOf = " + + indexOfKernel(sourceBuffer, emptyString, 99)); + failCount++; + } + if ((indexOfKernel(allAsBuffer.substring(1, 3), allAsBuffer.substring(5, 12)) != -1) || + (-1 != naiveFind(allAsBuffer.substring(1, 3).toString(), allAsBuffer.substring(5, 12), 0))) { + System.err.println("allAsBuffer.substring(1, 3).indexOf(allAsBuffer.substring(5, 12)) fragment '" + + allAsBuffer.substring(5, 12) + "' (" + + allAsBuffer.substring(5, 12).length() + ") String = " + + allAsBuffer.substring(1, 3) + " len Buffer = " + allAsBuffer.substring(1, 3).length()); + System.err.println( + " naive = " + naiveFind(allAsBuffer.substring(1, 3).toString(), allAsBuffer.substring(5, 12), 0) + + ", IndexOf = " + indexOfKernel(allAsBuffer.substring(1, 3), allAsBuffer.substring(5, 12))); + failCount++; + } + } + + report("Basic Test ", failCount); + } + + // Note: it is possible although highly improbable that failCount will + // be > 0 even if everthing is working ok + private static void compareIndexOfLastIndexOf() { + int failCount = 0; + String sourceString; + StringBuffer sourceBuffer; + String targetString; + + for (int i = 0; i < 10000; i++) { + do { + sourceString = generateTestString(99, 100); + sourceBuffer = new StringBuffer(sourceString); + targetString = generateTestString(10, 11); + } while (indexOfKernel(sourceString, targetString) != -1); + + int index1 = generator.nextInt(100); + sourceBuffer = sourceBuffer.replace(index1, index1, targetString); + + // extremely remote possibility of > 1 match + int matches = 0; + int index2 = -1; + while ((index2 = indexOfKernel(sourceBuffer, targetString, index2 + 1)) != -1) + matches++; + if (matches > 1) + continue; + + if (indexOfKernel(sourceBuffer, targetString) != sourceBuffer.lastIndexOf(targetString)) + failCount++; + sourceString = sourceBuffer.toString(); + if (indexOfKernel(sourceString, targetString) != sourceString.lastIndexOf(targetString)) + failCount++; + } + + report("IndexOf vs LastIndexOf ", failCount); + } + + private static void compareStringStringBuffer() { + int failCount = 0; + boolean make_new = true; + + String fragment = null; + StringBuffer testBuffer = null; + String testString = null; + int testIndex = 0; + + failCount = indexOfKernel("", ""); + + for (int x = 0; x < 1000000; x++) { + if (make_new) { + testString = generateTestString(1, 100); + int len = testString.length(); + + testBuffer = new StringBuffer(len); + testBuffer.append(testString); + if (!testString.equals(testBuffer.toString())) + throw new RuntimeException("Initial equality failure"); + + int x1 = 0; + int x2 = 1000; + while (x2 > testString.length()) { + x1 = generator.nextInt(len); + x2 = generator.nextInt(100); + x2 = x1 + x2; + } + fragment = testString.substring(x1, x2); + } + + int sAnswer = indexOfKernel(testString, fragment); + int sbAnswer = indexOfKernel(testBuffer, fragment); + + if (sAnswer != sbAnswer) { + System.err.println("(1) IndexOf fragment '" + fragment + "' (" + fragment.length() + ") len String = " + + testString.length() + " len Buffer = " + testBuffer.length()); + System.err.println(" sAnswer = " + sAnswer + ", sbAnswer = " + sbAnswer); + System.err.println(" testString = '" + testString + "'"); + System.err.println(" testBuffer = '" + testBuffer + "'"); + failCount++; + + sAnswer = indexOfKernel(testString, fragment); + sbAnswer = indexOfKernel(testBuffer, fragment); + } else { + if (sAnswer > testString.length()) { + System.err.println( + "IndexOf returned value out of range; return: " + sAnswer + " length max: " + testBuffer.length()); + } + } + + if ((fragment == "0#:02/62;+-\"\"0$25-5$#)1263") && (testBuffer.length() == 94)) { + String xx = "abc"; + String yy = "abcdefg"; + int sA = indexOfKernel(xx, yy); + } + + if (make_new) + testIndex = getRandomIndex(-100, 100); + + sAnswer = indexOfKernel(testString, fragment, testIndex); + sbAnswer = indexOfKernel(testBuffer, fragment, testIndex); + + if (sAnswer != sbAnswer) { + System.err.println("(2) IndexOf fragment '" + fragment + "' (" + fragment.length() + ") index = " + testIndex + + " len String = " + testString.length() + " len Buffer = " + testBuffer.length()); + System.err.println(" sAnswer = " + sAnswer + ", sbAnswer = " + sbAnswer); + System.err.println(" testString = '" + testString + "'"); + System.err.println(" testBuffer = '" + testBuffer + "'"); + failCount++; + make_new = true; + + sAnswer = indexOfKernel(testString, fragment, testIndex); + sbAnswer = indexOfKernel(testBuffer, fragment, testIndex); + } else { + if ((sAnswer > testString.length()) || ((sAnswer != -1) && (sAnswer < testIndex) && (fragment.length() != 0))) { + System.err.println("IndexOf returned value out of range; return: " + sAnswer + " length max: " + + testString.length() + " index: " + testIndex); + System.err.println("(3) IndexOf fragment '" + fragment + "' (" + fragment.length() + ") index = " + testIndex + + " len String = " + testString.length() + " len Buffer = " + testBuffer.length()); + } + } + + sAnswer = testString.lastIndexOf(fragment); + sbAnswer = testBuffer.lastIndexOf(fragment); + + if (sAnswer != sbAnswer) { + System.err.println("(1) lastIndexOf fragment '" + fragment + "' len String = " + testString.length() + + " len Buffer = " + testBuffer.length()); + System.err.println(" sAnswer = " + sAnswer + ", sbAnswer = " + sbAnswer); + failCount++; + + sAnswer = testString.lastIndexOf(fragment); + sbAnswer = testBuffer.lastIndexOf(fragment); + } + + if (make_new) + testIndex = getRandomIndex(-100, 100); + + sAnswer = testString.lastIndexOf(fragment, testIndex); + sbAnswer = testBuffer.lastIndexOf(fragment, testIndex); + + if (sAnswer != sbAnswer) { + System.err.println("(2) lastIndexOf fragment '" + fragment + "' index = " + testIndex + " len String = " + + testString.length() + " len Buffer = " + testBuffer.length()); + failCount++; + } + } + + report("String vs StringBuffer ", failCount); + } + + ////////////////////////////////////////////////////////////////////// + // Test routines used in benchmarks + // + // From StringIndexofHuge + private static void StringIndexofHuge() { + int stubResult = 0; + int failCount = 0; + + for (int xx = 0; xx < 2; xx++) { + int num = 1; + + String dataString = "ngdflsoscargfdgf"; + String dataString16 = "ngdfilso\u01facargfd\u01eef"; + String dataStringHuge = (("A".repeat(32) + "B".repeat(32)).repeat(16) + "X").repeat(2) + "bB"; + String dataStringHuge16 = "\u01de" + (("A".repeat(32) + "B".repeat(32)).repeat(16) + "\u01fe").repeat(2) + + "\u01eeB"; + String earlyMatchString = dataStringHuge.substring(0, 34); + String earlyMatchString16 = dataStringHuge16.substring(0, 34); + String midMatchString = dataStringHuge.substring(dataStringHuge.length() / 2 - 16, + dataStringHuge.length() / 2 + 32); + String midMatchString16 = dataStringHuge16.substring(dataStringHuge16.length() / 2 - 16, + dataStringHuge16.length() / 2 + 32); + String lateMatchString = dataStringHuge.substring(dataStringHuge.length() - 31); + String lateMatchString16 = dataStringHuge16.substring(dataStringHuge16.length() - 31); + + String searchString = "oscar"; + String searchString16 = "o\u01facar"; + String searchStringSmall = "dgf"; + String searchStringSmall16 = "d\u01eef"; + + String searchStringHuge = "capaapapapasdkajdlkajskldjaslkajdlkajskldjaslkjdlkasjdsalk"; + String searchStringHuge16 = "capaapapapasdkajdlka\u01feskldjaslkajdlkajskldjaslkjdlkasjdsalk"; + + String searchNoMatch = "XYXyxYxy".repeat(22); + String searchNoMatch16 = "\u01ab\u01ba\u01cb\u01bc\u01de\u01ed\u01fa\u01af".repeat(22); + + stubResult = indexOfKernel(dataStringHuge16, earlyMatchString); + int nResult = naiveFind(dataStringHuge16, earlyMatchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, earlyMatchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge, earlyMatchString); + nResult = naiveFind(dataStringHuge, earlyMatchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge, earlyMatchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge, midMatchString); + nResult = naiveFind(dataStringHuge, midMatchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge, midMatchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge, lateMatchString); + nResult = naiveFind(dataStringHuge, lateMatchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge, lateMatchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge, searchNoMatch); + nResult = naiveFind(dataStringHuge, searchNoMatch); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge, searchNoMatch); + failCount++; + } + num++; + stubResult = indexOfKernel(searchString, searchString); + nResult = naiveFind(searchString, searchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", searchString, searchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataString, searchString); + nResult = naiveFind(dataString, searchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataString, searchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataString, searchStringSmall); + nResult = naiveFind(dataString, searchStringSmall); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataString, searchStringSmall); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge, "B".repeat(30) + "X" + "A".repeat(30), 74); + nResult = naiveFind(dataStringHuge, "B".repeat(30) + "X" + "A".repeat(30), 74); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge, + "B".repeat(30) + "X" + "A".repeat(30)); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge, "A".repeat(32) + "F" + "B".repeat(32), 64); + nResult = naiveFind(dataStringHuge, "A".repeat(32) + "F" + "B".repeat(32), 64); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge, + "A".repeat(32) + "F" + "B".repeat(32)); + failCount++; + } + num++; + stubResult = indexOfKernel(midMatchString, dataStringHuge, 3); + nResult = naiveFind(midMatchString, dataStringHuge, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", midMatchString, dataStringHuge); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge, "A".repeat(32) + "B".repeat(30) + "bB"); + nResult = naiveFind(dataStringHuge, "A".repeat(32) + "B".repeat(30) + "bB"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge, + "A".repeat(32) + "B".repeat(30) + "bB"); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, earlyMatchString); + nResult = naiveFind(dataStringHuge16, earlyMatchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, earlyMatchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, midMatchString); + nResult = naiveFind(dataStringHuge16, midMatchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, midMatchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, lateMatchString); + nResult = naiveFind(dataStringHuge16, lateMatchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, lateMatchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, searchNoMatch); + nResult = naiveFind(dataStringHuge16, searchNoMatch); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, searchNoMatch); + failCount++; + } + num++; + stubResult = indexOfKernel(searchString16, searchString); + nResult = naiveFind(searchString16, searchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", searchString16, searchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataString16, searchString); + nResult = naiveFind(dataString16, searchString); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataString16, searchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataString16, searchStringSmall); + nResult = naiveFind(dataString16, searchStringSmall); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataString16, searchStringSmall); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, "B".repeat(30) + "X" + "A".repeat(30), 74); + nResult = naiveFind(dataStringHuge16, "B".repeat(30) + "X" + "A".repeat(30), 74); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, + "B".repeat(30) + "X" + "A".repeat(30)); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, "A".repeat(32) + "F" + "B".repeat(32), 64); + nResult = naiveFind(dataStringHuge16, "A".repeat(32) + "F" + "B".repeat(32), 64); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, + "A".repeat(32) + "F" + "B".repeat(32)); + failCount++; + } + num++; + stubResult = indexOfKernel(midMatchString16, dataStringHuge, 3); + nResult = naiveFind(midMatchString16, dataStringHuge, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", midMatchString16, dataStringHuge); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, "A".repeat(32) + "B".repeat(30) + "bB"); + nResult = naiveFind(dataStringHuge16, "A".repeat(32) + "B".repeat(30) + "bB"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, + "A".repeat(32) + "B".repeat(30) + "bB"); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, earlyMatchString16); + nResult = naiveFind(dataStringHuge16, earlyMatchString16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, earlyMatchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, midMatchString16); + nResult = naiveFind(dataStringHuge16, midMatchString16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, midMatchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, lateMatchString16); + nResult = naiveFind(dataStringHuge16, lateMatchString16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, lateMatchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, searchNoMatch16); + nResult = naiveFind(dataStringHuge16, searchNoMatch16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, searchNoMatch16); + failCount++; + } + num++; + stubResult = indexOfKernel(searchString16, searchString16); + nResult = naiveFind(searchString16, searchString16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", searchString16, searchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(dataString16, searchString16); + nResult = naiveFind(dataString16, searchString16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataString16, searchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(dataString16, searchStringSmall16); + nResult = naiveFind(dataString16, searchStringSmall16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataString16, searchStringSmall16); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, "B".repeat(30) + "X" + "A".repeat(30), 74); + nResult = naiveFind(dataStringHuge16, "B".repeat(30) + "X" + "A".repeat(30), 74); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, + "B".repeat(30) + "X" + "A".repeat(30)); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, "A".repeat(32) + "\u01ef" + "B".repeat(32), 64); + nResult = naiveFind(dataStringHuge16, "A".repeat(32) + "\u01ef" + "B".repeat(32), 64); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, + "A".repeat(32) + "\u01ef" + "B".repeat(32)); + failCount++; + } + num++; + stubResult = indexOfKernel(midMatchString16, dataStringHuge16, 3); + nResult = naiveFind(midMatchString16, dataStringHuge16, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", midMatchString16, dataStringHuge16); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringHuge16, "A".repeat(32) + "B".repeat(30) + "\u01eeB"); + nResult = naiveFind(dataStringHuge16, "A".repeat(32) + "B".repeat(30) + "\u01eeB"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofHuge", dataStringHuge16, + "A".repeat(32) + "B".repeat(30) + "\u01eeB"); + failCount++; + } + num++; + } + + report("StringIndexofHuge ", failCount); + } + + ///////////////////////////////////////////////////////////////////// + // + // From StringIndexof + private static void StringIndexof() { + int stubResult = 0; + int failCount = 0; + + for (int xx = 0; xx < 2; xx++) { + int num = 1; + + String dataString = "ngdfilsoscargfdgf"; + String searchString = "oscar"; + String dataStringBig = "2937489745890797905764956790452976742965790437698498409583479067ngdcapaapapapasdkajdlkajskldjaslkjdlkasjdsalkjas"; + String searchStringBig = "capaapapapasdkajdlkajskldjaslkjdlkasjdsalk"; + String data = "0000100101010010110101010010101110101001110110101010010101010010000010111010101010101010100010010101110111010101101010100010010100001010111111100001010101001010100001010101001010101010111010010101010101010101010101010"; + String sub = "10101010"; + String shortSub1 = "1"; + String data2 = "00001001010100a10110101010010101110101001110110101010010101010010000010111010101010101010a100010010101110111010101101010100010010a100a0010101111111000010101010010101000010101010010101010101110a10010101010101010101010101010"; + String shortSub2 = "a"; + char searchChar = 's'; + + String string16Short = "scar\u01fe1"; + String string16Medium = "capaapapapasdkajdlkajskldjaslkjdlkasjdsalksca1r\u01fescar"; + String string16Long = "2937489745890797905764956790452976742965790437698498409583479067ngdcapaapapapasdkajdlkajskldjaslkjdlkasjdsalkja1sscar\u01fescar"; + char searchChar16 = 0x1fe; + String searchString16 = "\u01fe"; + + stubResult = indexOfKernel(dataStringBig, searchChar); + int nResult = naiveFind(dataStringBig, searchChar); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", dataStringBig, searchChar); + failCount++; + } + num++; + stubResult = indexOfKernel(searchStringBig, searchChar); + nResult = naiveFind(searchStringBig, searchChar); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", searchStringBig, searchChar); + failCount++; + } + num++; + stubResult = indexOfKernel(searchString, searchChar); + nResult = naiveFind(searchString, searchChar); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", searchString, searchChar); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Long, searchChar16); + nResult = naiveFind(string16Long, searchChar16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Long, searchChar16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Medium, searchChar16); + nResult = naiveFind(string16Medium, searchChar16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Medium, searchChar16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Short, searchChar16); + nResult = naiveFind(string16Short, searchChar16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Short, searchChar16); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringBig, searchChar, 3); + nResult = naiveFind(dataStringBig, searchChar, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", dataStringBig, searchChar); + failCount++; + } + num++; + stubResult = indexOfKernel(searchStringBig, searchChar, 3); + nResult = naiveFind(searchStringBig, searchChar, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", searchStringBig, searchChar); + failCount++; + } + num++; + stubResult = indexOfKernel(searchString, searchChar, 1); + nResult = naiveFind(searchString, searchChar, 1); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", searchString, searchChar); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Long, searchChar16, 3); + nResult = naiveFind(string16Long, searchChar16, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Long, searchChar16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Medium, searchChar16, 3); + nResult = naiveFind(string16Medium, searchChar16, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Medium, searchChar16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Short, searchChar16, 2); + nResult = naiveFind(string16Short, searchChar16, 2); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Short, searchChar16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Long, shortSub1); + nResult = naiveFind(string16Long, shortSub1); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Long, shortSub1); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Medium, shortSub1); + nResult = naiveFind(string16Medium, shortSub1); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Medium, shortSub1); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Long, shortSub2); + nResult = naiveFind(string16Long, shortSub2); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Long, shortSub2); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Long, shortSub1, 3); + nResult = naiveFind(string16Long, shortSub1, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Long, shortSub1); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Medium, shortSub1, 3); + nResult = naiveFind(string16Medium, shortSub1, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Medium, shortSub1); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Short, shortSub2, 1); + nResult = naiveFind(string16Short, shortSub2, 1); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Short, shortSub2); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Long, searchString16, 3); + nResult = naiveFind(string16Long, searchString16, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Long, searchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Medium, searchString16, 3); + nResult = naiveFind(string16Medium, searchString16, 3); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Medium, searchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Short, searchString16, 2); + nResult = naiveFind(string16Short, searchString16, 2); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Short, searchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Long, searchString16); + nResult = naiveFind(string16Long, searchString16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Long, searchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Medium, searchString16); + nResult = naiveFind(string16Medium, searchString16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Medium, searchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(string16Short, searchString16); + nResult = naiveFind(string16Short, searchString16); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", string16Short, searchString16); + failCount++; + } + num++; + stubResult = indexOfKernel(dataString, searchString, 2); + nResult = naiveFind(dataString, searchString, 2); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", dataString, searchString); + failCount++; + } + num++; + stubResult = indexOfKernel(dataStringBig, searchStringBig, 2); + nResult = naiveFind(dataStringBig, searchStringBig, 2); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexof", dataStringBig, searchStringBig); + } + { + int index = 0; + int dummy = 0; + while ((index = indexOfKernel(data, sub, index)) > -1) { + nResult = naiveFind(data, sub, index); + if (index != nResult) { + PrintError(stubResult, nResult, num, "StringIndexof", data, sub); + failCount++; + } + index++; + dummy += index; + } + num++; + } + { + int dummy = 0; + int index = 0; + while ((index = indexOfKernel(data, shortSub1, index)) > -1) { + nResult = naiveFind(data, shortSub1, index); + if (index != nResult) { + PrintError(stubResult, nResult, num, "StringIndexof", data, shortSub1); + failCount++; + } + index++; + dummy += index; + } + num++; + } + { + int dummy = 0; + int index = 0; + while ((index = indexOfKernel(data2, shortSub2, index)) > -1) { + nResult = naiveFind(data2, shortSub2, index); + if (index != nResult) { + PrintError(stubResult, nResult, num, "StringIndexof", data2, shortSub2); + failCount++; + } + index++; + dummy += index; + } + num++; + } + { + String tmp = "simple-hash:SHA-1/UTF-8"; + if (!tmp.contains("SHA-1")) { + PrintError(stubResult, nResult, num, "StringIndexof", "simple-hash:SHA-1/UTF-8", "SHA-1"); + failCount++; + } + num++; + } + } + + report("StringIndexof ", failCount); + } + + ///////////////////////////////////////////////////////////////////// + // + // From StringIndexofChar + private static void StringIndexofChar() { + int stubResult = 0; + int failCount = 0; + + for (int xx = 0; xx < 2; xx++) { + stubResult = 0; + int nResult = 0; + int num = 1; + + String[] latn1_short = new String[100]; + String[] latn1_sse4 = new String[100]; + String[] latn1_avx2 = new String[100]; + String[] latn1_mixedLength = new String[100]; + String[] utf16_short = new String[100]; + String[] utf16_sse4 = new String[100]; + String[] utf16_avx2 = new String[100]; + String[] utf16_mixedLength = new String[100]; + + for (int i = 0; i < 100; i++) { + latn1_short[i] = makeRndString(false, 15); + latn1_sse4[i] = makeRndString(false, 16); + latn1_avx2[i] = makeRndString(false, 32); + utf16_short[i] = makeRndString(true, 7); + utf16_sse4[i] = makeRndString(true, 8); + utf16_avx2[i] = makeRndString(true, 16); + latn1_mixedLength[i] = makeRndString(false, rng.nextInt(65)); + utf16_mixedLength[i] = makeRndString(true, rng.nextInt(65)); + } + for (String what : latn1_mixedLength) { + stubResult = indexOfKernel(what, 'a'); + nResult = naiveFind(what, 'a'); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, 'a'); + failCount++; + } + } + num++; + for (String what : utf16_mixedLength) { + stubResult = indexOfKernel(what, 'a'); + nResult = naiveFind(what, 'a'); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, 'a'); + failCount++; + } + } + num++; + for (String what : latn1_mixedLength) { + stubResult = indexOfKernel(what, "a"); + nResult = naiveFind(what, "a"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, "a"); + failCount++; + } + } + num++; + for (String what : utf16_mixedLength) { + stubResult = indexOfKernel(what, "a"); + nResult = naiveFind(what, "a"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, "a"); + failCount++; + } + } + num++; + for (String what : latn1_short) { + stubResult = indexOfKernel(what, 'a'); + nResult = naiveFind(what, 'a'); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, 'a'); + failCount++; + } + } + num++; + for (String what : latn1_sse4) { + stubResult = indexOfKernel(what, 'a'); + nResult = naiveFind(what, 'a'); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, 'a'); + failCount++; + } + } + num++; + for (String what : latn1_avx2) { + stubResult = indexOfKernel(what, 'a'); + nResult = naiveFind(what, 'a'); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, 'a'); + failCount++; + } + } + num++; + for (String what : utf16_short) { + stubResult = indexOfKernel(what, 'a'); + nResult = naiveFind(what, 'a'); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, 'a'); + failCount++; + } + } + num++; + for (String what : utf16_sse4) { + stubResult = indexOfKernel(what, 'a'); + nResult = naiveFind(what, 'a'); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, 'a'); + failCount++; + } + } + num++; + for (String what : utf16_avx2) { + stubResult = indexOfKernel(what, 'a'); + nResult = naiveFind(what, 'a'); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, 'a'); + failCount++; + } + } + num++; + for (String what : latn1_short) { + stubResult = indexOfKernel(what, "a"); + nResult = naiveFind(what, "a"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, "a"); + failCount++; + } + } + num++; + for (String what : latn1_sse4) { + stubResult = indexOfKernel(what, "a"); + nResult = naiveFind(what, "a"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, "a"); + failCount++; + } + } + num++; + for (String what : latn1_avx2) { + stubResult = indexOfKernel(what, "a"); + nResult = naiveFind(what, "a"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, "a"); + failCount++; + } + } + num++; + for (String what : utf16_short) { + stubResult = indexOfKernel(what, "a"); + nResult = naiveFind(what, "a"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, "a"); + failCount++; + } + } + num++; + for (String what : utf16_sse4) { + stubResult = indexOfKernel(what, "a"); + nResult = naiveFind(what, "a"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, "a"); + failCount++; + } + } + num++; + for (String what : utf16_avx2) { + stubResult = indexOfKernel(what, "a"); + nResult = naiveFind(what, "a"); + if (nResult != stubResult) { + PrintError(stubResult, nResult, num, "StringIndexofChar", what, "a"); + failCount++; + } + } + num++; + } + + report("StringIndexofChar ", failCount); + } + +} diff --git a/test/jdk/java/rmi/reliability/benchmark/bench/Makefile b/test/jdk/java/rmi/reliability/benchmark/bench/Makefile index f0600d4df3065..805d62163732d 100644 --- a/test/jdk/java/rmi/reliability/benchmark/bench/Makefile +++ b/test/jdk/java/rmi/reliability/benchmark/bench/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 1999, 2000, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1999, 2024, 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 @@ -47,4 +47,3 @@ all: .classes clean: rm -f *.class .classes - diff --git a/test/jdk/java/rmi/reliability/benchmark/bench/rmi/Makefile b/test/jdk/java/rmi/reliability/benchmark/bench/rmi/Makefile index bb44d94f1547c..c142b3a2d0555 100644 --- a/test/jdk/java/rmi/reliability/benchmark/bench/rmi/Makefile +++ b/test/jdk/java/rmi/reliability/benchmark/bench/rmi/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2024, 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 @@ -72,4 +72,3 @@ altroot.clean: clean: altroot.clean rm -f *.class .classes - diff --git a/test/jdk/javax/crypto/SecretKeyFactory/evilprov/Makefile b/test/jdk/javax/crypto/SecretKeyFactory/evilprov/Makefile index 918c31f094789..800e1817b42f9 100644 --- a/test/jdk/javax/crypto/SecretKeyFactory/evilprov/Makefile +++ b/test/jdk/javax/crypto/SecretKeyFactory/evilprov/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2019, 2024, 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 @@ -26,7 +26,7 @@ JAVABIN=$(JAVA_BASE)/bin JAVAC=$(JAVABIN)/javac JAVA=$(JAVABIN)/java -JAR=$(JAVABIN)/jar +JAR=$(JAVABIN)/jar JARSIGNER=$(JAVABIN)/jarsigner # Compile-time flags and paths diff --git a/test/lib-test/jdk/test/whitebox/CPUInfoTest.java b/test/lib-test/jdk/test/whitebox/CPUInfoTest.java index 138327ec44f78..859ec772229cb 100644 --- a/test/lib-test/jdk/test/whitebox/CPUInfoTest.java +++ b/test/lib-test/jdk/test/whitebox/CPUInfoTest.java @@ -65,7 +65,8 @@ public class CPUInfoTest { "avx512_vbmi2", "avx512_vbmi", "rdtscp", "rdpid", "hv", "fsrm", "avx512_bitalg", "gfni", "f16c", "pku", "ospke", "cet_ibt", - "cet_ss", "avx512_ifma", "serialize", "avx_ifma" + "cet_ss", "avx512_ifma", "serialize", "avx_ifma", + "apx_f" ); // @formatter:on // Checkstyle: resume diff --git a/test/micro/org/openjdk/bench/java/lang/StringIndexOfHuge.java b/test/micro/org/openjdk/bench/java/lang/StringIndexOfHuge.java new file mode 100644 index 0000000000000..cac86bcc17f8c --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/StringIndexOfHuge.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class StringIndexOfHuge { + + private String dataString; + private String dataString16; + private String dataStringHuge; + private String dataStringHuge16; + private String earlyMatchString; + private String earlyMatchString16; + private String midMatchString; + private String midMatchString16; + private String lateMatchString; + private String lateMatchString16; + + private String searchString; + private String searchString16; + private String searchStringSmall; + private String searchStringSmall16; + + private String searchStringHuge; + private String searchStringHuge16; + + private String searchNoMatch; + private String searchNoMatch16; + + private String Amdahl_1; + private String Amdahl_2; + private String Amdahl_3; + private String Amdahl_4; + private String Amdahl_5; + private String Amdahl_6; + + @Setup + public void setup() { + dataString = "ngdflsoscargfdgf"; + dataString16 = "ngdfilso\u01facargfd\u01eef"; + dataStringHuge = (("A".repeat(32) + "B".repeat(32)).repeat(16) + "X").repeat(2) + "bB"; + dataStringHuge16 = "\u01de" + (("A".repeat(32) + "B".repeat(32)).repeat(16) + "\u01fe").repeat(2) + "\u01eeB"; + earlyMatchString = dataStringHuge.substring(0, 34); + earlyMatchString16 = dataStringHuge16.substring(0, 34); + midMatchString = dataStringHuge.substring(dataStringHuge.length() / 2 - 16, dataStringHuge.length() / 2 + 17); + midMatchString16 = dataStringHuge16.substring(dataStringHuge16.length() / 2 - 16, dataStringHuge16.length() / 2 + 17); + lateMatchString = dataStringHuge.substring(dataStringHuge.length() - 31); + lateMatchString16 = dataStringHuge16.substring(dataStringHuge16.length() - 31); + + searchString = "oscar"; + searchString16 = "o\u01facar"; + searchStringSmall = "dgf"; + searchStringSmall16 = "d\u01eef"; + + searchStringHuge = "capaapapapasdkajdlkajskldjaslkajdlkajskldjaslkjdlkasjdsalk"; + searchStringHuge16 = "capaapapapasdkajdlka\u01feskldjaslkajdlkajskldjaslkjdlkasjdsalk"; + + searchNoMatch = "XYXyxYxy".repeat(22); + searchNoMatch16 = "\u01ab\u01ba\u01cb\u01bc\u01de\u01ed\u01fa\u01af".repeat(22); + + Amdahl_1 = "B".repeat(30) + "X" + "A".repeat(30); + Amdahl_2 = "A".repeat(32) + "F" + "B".repeat(32); + Amdahl_3 = "A".repeat(32) + "B".repeat(32) + "XbB"; + Amdahl_4 = "B".repeat(30) + "\u01ef" + "A".repeat(30); + Amdahl_5 = "A".repeat(32) + "\u01ef" + "B".repeat(32); + Amdahl_6 = "A".repeat(32) + "B".repeat(32) + "\u01fe\u01eeB"; + } + + + /** IndexOf Micros */ + @Benchmark + public int searchHugeEarlyMatch() { + return dataStringHuge.indexOf(earlyMatchString); + } + + @Benchmark + public int searchHugeMiddleMatch() { + return dataStringHuge.indexOf(midMatchString); + } + + @Benchmark + public int searchHugeLateMatch() { + return dataStringHuge.indexOf(lateMatchString); + } + + @Benchmark + public int searchHugeNoMatch() { + return dataStringHuge.indexOf(searchNoMatch); + } + + @Benchmark + public int searchSmallEarlyMatch() { + return searchString.indexOf(searchString); + } + + @Benchmark + public int searchSmallMidMatch() { + return dataString.indexOf(searchString); + } + + @Benchmark + public int searchSmallLateMatch() { + return dataString.indexOf(searchStringSmall); + } + + @Benchmark + public int searchHugeLargeSubstring() { + return dataStringHuge.indexOf(Amdahl_1, 74); + } + + @Benchmark + public int searchHugeLargeSubstringNoMatch() { + return dataStringHuge.indexOf(Amdahl_2, 64); + } + + @Benchmark + public int searchSubstringLongerThanString() { + return midMatchString.indexOf(dataStringHuge, 3); + } + + @Benchmark + public int searchHugeWorstCase() { + return dataStringHuge.indexOf(Amdahl_3); + } + + @Benchmark + public int search16HugeEarlyMatch() { + return dataStringHuge16.indexOf(earlyMatchString); + } + + @Benchmark + public int search16HugeMiddleMatch() { + return dataStringHuge16.indexOf(midMatchString); + } + + @Benchmark + public int search16HugeLateMatch() { + return dataStringHuge16.indexOf(lateMatchString); + } + + @Benchmark + public int search16HugeNoMatch() { + return dataStringHuge16.indexOf(searchNoMatch); + } + + @Benchmark + public int search16SmallEarlyMatch() { + return searchString16.indexOf(searchString); + } + + @Benchmark + public int search16SmallMidMatch() { + return dataString16.indexOf(searchString); + } + + @Benchmark + public int search16SmallLateMatch() { + return dataString16.indexOf(searchStringSmall); + } + + @Benchmark + public int search16HugeLargeSubstring() { + return dataStringHuge16.indexOf(Amdahl_1, 74); + } + + @Benchmark + public int search16HugeLargeSubstringNoMatch() { + return dataStringHuge16.indexOf(Amdahl_2, 64); + } + + @Benchmark + public int search16SubstringLongerThanString() { + return midMatchString16.indexOf(dataStringHuge, 3); + } + + @Benchmark + public int search16HugeWorstCase() { + return dataStringHuge16.indexOf(Amdahl_3); + } + + @Benchmark + public int search16HugeEarlyMatch16() { + return dataStringHuge16.indexOf(earlyMatchString16); + } + + @Benchmark + public int search16HugeMiddleMatch16() { + return dataStringHuge16.indexOf(midMatchString16); + } + + @Benchmark + public int search16HugeLateMatch16() { + return dataStringHuge16.indexOf(lateMatchString16); + } + + @Benchmark + public int search16HugeNoMatch16() { + return dataStringHuge16.indexOf(searchNoMatch16); + } + + @Benchmark + public int search16SmallEarlyMatch16() { + return searchString16.indexOf(searchString16); + } + + @Benchmark + public int search16SmallMidMatch16() { + return dataString16.indexOf(searchString16); + } + + @Benchmark + public int search16SmallLateMatch16() { + return dataString16.indexOf(searchStringSmall16); + } + + @Benchmark + public int search16HugeLargeSubstring16() { + return dataStringHuge16.indexOf(Amdahl_4, 74); + } + + @Benchmark + public int search16HugeLargeSubstringNoMatch16() { + return dataStringHuge16.indexOf(Amdahl_5, 64); + } + + @Benchmark + public int search16SubstringLongerThanString16() { + return midMatchString16.indexOf(dataStringHuge16, 3); + } + + @Benchmark + public int search16HugeWorstCase16() { + return dataStringHuge16.indexOf(Amdahl_6); + } +}