diff --git a/make/data/ubsan/ubsan_default_options.c b/make/data/ubsan/ubsan_default_options.c index 05e4722e45a..5615436e39f 100644 --- a/make/data/ubsan/ubsan_default_options.c +++ b/make/data/ubsan/ubsan_default_options.c @@ -62,5 +62,8 @@ // thread so it is easier to track down. You can override these options by setting the environment // variable UBSAN_OPTIONS. ATTRIBUTE_DEFAULT_VISIBILITY ATTRIBUTE_USED const char* __ubsan_default_options() { - return "halt_on_error=1,print_stacktrace=1" _LLVM_SYMBOLIZER(LLVM_SYMBOLIZER); + return "halt_on_error=1," + "handle_segv=0," + "handle_sigbus=0," + "print_stacktrace=1" _LLVM_SYMBOLIZER(LLVM_SYMBOLIZER); } diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64_trig.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64_trig.cpp index d4bc983511f..1a3a0300e95 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64_trig.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64_trig.cpp @@ -543,7 +543,7 @@ void MacroAssembler::generate__ieee754_rem_pio2(address npio2_hw, // } // // /* compute n */ -// z = scalbnA(z,q0); /* actual value of z */ +// z = scalbn(z,q0); /* actual value of z */ // z -= 8.0*floor(z*0.125); /* trim off integer >= 8 */ // n = (int) z; // z -= (double)n; @@ -576,7 +576,7 @@ void MacroAssembler::generate__ieee754_rem_pio2(address npio2_hw, // } // if(ih==2) { // z = one - z; -// if(carry!=0) z -= scalbnA(one,q0); +// if(carry!=0) z -= scalbn(one,q0); // } // } // @@ -602,7 +602,7 @@ void MacroAssembler::generate__ieee754_rem_pio2(address npio2_hw, // jz -= 1; q0 -= 24; // while(iq[jz]==0) { jz--; q0-=24;} // } else { /* break z into 24-bit if necessary */ -// z = scalbnA(z,-q0); +// z = scalbn(z,-q0); // if(z>=two24B) { // fw = (double)((int)(twon24*z)); // iq[jz] = (int)(z-two24B*fw); @@ -612,7 +612,7 @@ void MacroAssembler::generate__ieee754_rem_pio2(address npio2_hw, // } // // /* convert integer "bit" chunk to floating-point value */ -// fw = scalbnA(one,q0); +// fw = scalbn(one,q0); // for(i=jz;i>=0;i--) { // q[i] = fw*(double)iq[i]; fw*=twon24; // } @@ -925,7 +925,7 @@ void MacroAssembler::generate__kernel_rem_pio2(address two_over_pi, address pio2 fmovd(v25, 1.0); fsubd(v18, v25, v18); // z = one - z; cbzw(rscratch2, IH_HANDLED); - fsubd(v18, v18, v30); // z -= scalbnA(one,q0); + fsubd(v18, v18, v30); // z -= scalbn(one,q0); } } bind(IH_HANDLED); @@ -1026,7 +1026,7 @@ void MacroAssembler::generate__kernel_rem_pio2(address two_over_pi, address pio2 bind(Z_ZERO_CHECK_DONE); // convert integer "bit" chunk to floating-point value // v17 = twon24 - // update v30, which was scalbnA(1.0, ); + // update v30, which was scalbn(1.0, ); addw(tmp2, rscratch1, 1023); // biased exponent lsl(tmp2, tmp2, 52); // put at correct position mov(i, jz); diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 897b06e94df..f116125767a 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -8257,6 +8257,14 @@ void Assembler::vmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src) { emit_int16(0x5F, (0xC0 | encode)); } +void Assembler::eminmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x53, (0xC0 | encode), imm8); +} + void Assembler::vminsh(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -8771,12 +8779,68 @@ void Assembler::vmaxps(XMMRegister dst, XMMRegister nds, XMMRegister src, int ve emit_int16(0x5F, (0xC0 | encode)); } +void Assembler::evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x52, (0xC0 | encode), imm8); +} + +void Assembler::evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_NObit); + if (merge) { + attributes.reset_is_clear_context(); + } + vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int8(0x52); + emit_operand(dst, src, 0); + emit_int8(imm8); +} + void Assembler::maxpd(XMMRegister dst, XMMRegister src) { InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int16(0x5F, (0xC0 | encode)); } +void Assembler::evminmaxpd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false,/* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x52, (0xC0 | encode), imm8); +} + +void Assembler::evminmaxpd(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_NObit); + if (merge) { + attributes.reset_is_clear_context(); + } + vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int8(0x52); + emit_operand(dst, src, 0); + emit_int8(imm8); +} + void Assembler::vmaxpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(vector_len >= AVX_512bit ? VM_Version::supports_evex() : VM_Version::supports_avx(), ""); InstructionAttr attributes(vector_len, /* vex_w */true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); @@ -13119,6 +13183,14 @@ void Assembler::vminss(XMMRegister dst, XMMRegister nds, XMMRegister src) { emit_int16(0x5D, (0xC0 | encode)); } +void Assembler::eminmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x53, (0xC0 | encode), imm8); +} + void Assembler::vminsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -13127,6 +13199,14 @@ void Assembler::vminsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { emit_int16(0x5D, (0xC0 | encode)); } +void Assembler::eminmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x53, (0xC0 | encode), imm8); +} + void Assembler::vcmppd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len) { assert(VM_Version::supports_avx(), ""); assert(vector_len <= AVX_256bit, ""); @@ -16526,6 +16606,34 @@ void Assembler::evminph(XMMRegister dst, XMMRegister nds, Address src, int vecto emit_operand(dst, src, 0); } +void Assembler::evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false,/* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x52, (0xC0 | encode), imm8); +} + +void Assembler::evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes); + emit_int8(0x52); + emit_operand(dst, src, 0); + emit_int8(imm8); +} + void Assembler::evmaxph(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); assert(vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl(), ""); diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index b1959e23722..45c24f8c832 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -441,6 +441,17 @@ class InstructionAttr; // See fxsave and xsave(EVEX enabled) documentation for layout const int FPUStateSizeInWords = 2688 / wordSize; + +// AVX10 new minmax instruction control mask encoding. +// +// imm8[4] = 0 (please refer to Table 11.1 of section 11.2 of AVX10 manual[1] for details) +// imm8[3:2] (sign control) = 01 (select sign, please refer to Table 11.5 of section 11.2 of AVX10 manual[1] for details) +// imm8[1:0] = 00 (min) / 01 (max) +// +// [1] https://www.intel.com/content/www/us/en/content-details/856721/intel-advanced-vector-extensions-10-2-intel-avx10-2-architecture-specification.html?wapkw=AVX10 +const int AVX10_MINMAX_MAX_COMPARE_SIGN = 0x5; +const int AVX10_MINMAX_MIN_COMPARE_SIGN = 0x4; + // The Intel x86/Amd64 Assembler: Pure assembler doing NO optimizations on the instruction // level (e.g. mov rax, 0 is not translated into xor rax, rax!); i.e., what you write // is what you get. The Assembler is generating code into a CodeBuffer. @@ -2745,6 +2756,17 @@ private: void minpd(XMMRegister dst, XMMRegister src); void vminpd(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); + // AVX10.2 floating point minmax instructions + void eminmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); + void eminmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); + void eminmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); + void evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len); + void evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len); + void evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len); + void evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len); + void evminmaxpd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len); + void evminmaxpd(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len); + // Maximum of packed integers void pmaxsb(XMMRegister dst, XMMRegister src); void vpmaxsb(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index d9a9ef0de3b..6d24c145a50 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -1230,6 +1230,21 @@ void C2_MacroAssembler::evminmax_fp(int opcode, BasicType elem_bt, } } +void C2_MacroAssembler::vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2, int vlen_enc) { + assert(opc == Op_MinV || opc == Op_MinReductionV || + opc == Op_MaxV || opc == Op_MaxReductionV, "sanity"); + + int imm8 = (opc == Op_MinV || opc == Op_MinReductionV) ? AVX10_MINMAX_MIN_COMPARE_SIGN + : AVX10_MINMAX_MAX_COMPARE_SIGN; + if (elem_bt == T_FLOAT) { + evminmaxps(dst, mask, src1, src2, true, imm8, vlen_enc); + } else { + assert(elem_bt == T_DOUBLE, ""); + evminmaxpd(dst, mask, src1, src2, true, imm8, vlen_enc); + } +} + // Float/Double signum void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero, XMMRegister one) { assert(opcode == Op_SignumF || opcode == Op_SignumD, "sanity"); @@ -2537,12 +2552,21 @@ void C2_MacroAssembler::reduceFloatMinMax(int opcode, int vlen, bool is_dst_vali } else { // i = [0,1] vpermilps(wtmp, wsrc, permconst[i], vlen_enc); } - vminmax_fp(opcode, T_FLOAT, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); + + if (VM_Version::supports_avx10_2()) { + vminmax_fp(opcode, T_FLOAT, wdst, k0, wtmp, wsrc, vlen_enc); + } else { + vminmax_fp(opcode, T_FLOAT, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); + } wsrc = wdst; vlen_enc = Assembler::AVX_128bit; } if (is_dst_valid) { - vminmax_fp(opcode, T_FLOAT, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); + if (VM_Version::supports_avx10_2()) { + vminmax_fp(opcode, T_FLOAT, dst, k0, wdst, dst, Assembler::AVX_128bit); + } else { + vminmax_fp(opcode, T_FLOAT, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); + } } } @@ -2568,12 +2592,23 @@ void C2_MacroAssembler::reduceDoubleMinMax(int opcode, int vlen, bool is_dst_val assert(i == 0, "%d", i); vpermilpd(wtmp, wsrc, 1, vlen_enc); } - vminmax_fp(opcode, T_DOUBLE, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); + + if (VM_Version::supports_avx10_2()) { + vminmax_fp(opcode, T_DOUBLE, wdst, k0, wtmp, wsrc, vlen_enc); + } else { + vminmax_fp(opcode, T_DOUBLE, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); + } + wsrc = wdst; vlen_enc = Assembler::AVX_128bit; } + if (is_dst_valid) { - vminmax_fp(opcode, T_DOUBLE, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); + if (VM_Version::supports_avx10_2()) { + vminmax_fp(opcode, T_DOUBLE, dst, k0, wdst, dst, Assembler::AVX_128bit); + } else { + vminmax_fp(opcode, T_DOUBLE, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); + } } } diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index 713eb73d68f..ee6fecb9f88 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -72,6 +72,9 @@ public: XMMRegister tmp, XMMRegister atmp, XMMRegister btmp, int vlen_enc); + void vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2, int vlen_enc); + void vpuminmaxq(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc); void evminmax_fp(int opcode, BasicType elem_bt, diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index c401863d7cd..c8bf289e9d4 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -8841,6 +8841,10 @@ void MacroAssembler::evpmins(BasicType type, XMMRegister dst, KRegister mask, XM evpminsd(dst, mask, nds, src, merge, vector_len); break; case T_LONG: evpminsq(dst, mask, nds, src, merge, vector_len); break; + case T_FLOAT: + evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break; + case T_DOUBLE: + evminmaxpd(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break; default: fatal("Unexpected type argument %s", type2name(type)); break; } @@ -8856,6 +8860,10 @@ void MacroAssembler::evpmaxs(BasicType type, XMMRegister dst, KRegister mask, XM evpmaxsd(dst, mask, nds, src, merge, vector_len); break; case T_LONG: evpmaxsq(dst, mask, nds, src, merge, vector_len); break; + case T_FLOAT: + evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break; + case T_DOUBLE: + evminmaxpd(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break; default: fatal("Unexpected type argument %s", type2name(type)); break; } @@ -8871,6 +8879,10 @@ void MacroAssembler::evpmins(BasicType type, XMMRegister dst, KRegister mask, XM evpminsd(dst, mask, nds, src, merge, vector_len); break; case T_LONG: evpminsq(dst, mask, nds, src, merge, vector_len); break; + case T_FLOAT: + evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break; + case T_DOUBLE: + evminmaxpd(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break; default: fatal("Unexpected type argument %s", type2name(type)); break; } @@ -8886,6 +8898,10 @@ void MacroAssembler::evpmaxs(BasicType type, XMMRegister dst, KRegister mask, XM evpmaxsd(dst, mask, nds, src, merge, vector_len); break; case T_LONG: evpmaxsq(dst, mask, nds, src, merge, vector_len); break; + case T_FLOAT: + evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break; + case T_DOUBLE: + evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break; default: fatal("Unexpected type argument %s", type2name(type)); break; } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp index da60a9be276..6faa2081fb2 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_cbrt.cpp @@ -46,6 +46,12 @@ // /******************************************************************************/ +/* Represents 0x7FFFFFFFFFFFFFFF double precision in lower 64 bits*/ +ATTRIBUTE_ALIGNED(16) static const juint _ABS_MASK[] = +{ + 4294967295, 2147483647, 0, 0 +}; + ATTRIBUTE_ALIGNED(4) static const juint _SIG_MASK[] = { 0, 1032192 @@ -188,10 +194,10 @@ address StubGenerator::generate_libmCbrt() { StubCodeMark mark(this, stub_id); address start = __ pc(); - Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1, L_2TAG_PACKET_3_0_1; - Label L_2TAG_PACKET_4_0_1, L_2TAG_PACKET_5_0_1, L_2TAG_PACKET_6_0_1; + Label L_2TAG_PACKET_0_0_1, L_2TAG_PACKET_1_0_1, L_2TAG_PACKET_2_0_1; Label B1_1, B1_2, B1_4; + address ABS_MASK = (address)_ABS_MASK; address SIG_MASK = (address)_SIG_MASK; address EXP_MASK = (address)_EXP_MASK; address EXP_MSK2 = (address)_EXP_MSK2; @@ -208,8 +214,12 @@ address StubGenerator::generate_libmCbrt() { __ enter(); // required for proper stackwalking of RuntimeStub frame __ bind(B1_1); - __ subq(rsp, 24); - __ movsd(Address(rsp), xmm0); + __ ucomisd(xmm0, ExternalAddress(ZERON), r11 /*rscratch*/); + __ jcc(Assembler::equal, L_2TAG_PACKET_1_0_1); // Branch only if x is +/- zero or NaN + __ movq(xmm1, xmm0); + __ andpd(xmm1, ExternalAddress(ABS_MASK), r11 /*rscratch*/); + __ ucomisd(xmm1, ExternalAddress(INF), r11 /*rscratch*/); + __ jcc(Assembler::equal, B1_4); // Branch only if x is +/- INF __ bind(B1_2); __ movq(xmm7, xmm0); @@ -228,8 +238,6 @@ address StubGenerator::generate_libmCbrt() { __ andl(rdx, rax); __ cmpl(rdx, 0); __ jcc(Assembler::equal, L_2TAG_PACKET_0_0_1); // Branch only if |x| is denormalized - __ cmpl(rdx, 524032); - __ jcc(Assembler::equal, L_2TAG_PACKET_1_0_1); // Branch only if |x| is INF or NaN __ shrl(rdx, 8); __ shrq(r9, 8); __ andpd(xmm2, xmm0); @@ -297,8 +305,6 @@ address StubGenerator::generate_libmCbrt() { __ andl(rdx, rax); __ shrl(rdx, 8); __ shrq(r9, 8); - __ cmpl(rdx, 0); - __ jcc(Assembler::equal, L_2TAG_PACKET_3_0_1); // Branch only if |x| is zero __ andpd(xmm2, xmm0); __ andpd(xmm0, xmm5); __ orpd(xmm3, xmm2); @@ -322,41 +328,10 @@ address StubGenerator::generate_libmCbrt() { __ psllq(xmm7, 52); __ jmp(L_2TAG_PACKET_2_0_1); - __ bind(L_2TAG_PACKET_3_0_1); - __ cmpq(r9, 0); - __ jcc(Assembler::notEqual, L_2TAG_PACKET_4_0_1); // Branch only if x is negative zero - __ xorpd(xmm0, xmm0); - __ jmp(B1_4); - - __ bind(L_2TAG_PACKET_4_0_1); - __ movsd(xmm0, ExternalAddress(ZERON), r11 /*rscratch*/); - __ jmp(B1_4); - __ bind(L_2TAG_PACKET_1_0_1); - __ movl(rax, Address(rsp, 4)); - __ movl(rdx, Address(rsp)); - __ movl(rcx, rax); - __ andl(rcx, 2147483647); - __ cmpl(rcx, 2146435072); - __ jcc(Assembler::above, L_2TAG_PACKET_5_0_1); // Branch only if |x| is NaN - __ cmpl(rdx, 0); - __ jcc(Assembler::notEqual, L_2TAG_PACKET_5_0_1); // Branch only if |x| is NaN - __ cmpl(rax, 2146435072); - __ jcc(Assembler::notEqual, L_2TAG_PACKET_6_0_1); // Branch only if x is negative INF - __ movsd(xmm0, ExternalAddress(INF), r11 /*rscratch*/); - __ jmp(B1_4); - - __ bind(L_2TAG_PACKET_6_0_1); - __ movsd(xmm0, ExternalAddress(NEG_INF), r11 /*rscratch*/); - __ jmp(B1_4); - - __ bind(L_2TAG_PACKET_5_0_1); - __ movsd(xmm0, Address(rsp)); __ addsd(xmm0, xmm0); - __ movq(Address(rsp, 8), xmm0); __ bind(B1_4); - __ addq(rsp, 24); __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index a281331cb29..c0a55917a94 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2024,7 +2024,7 @@ bool Matcher::match_rule_supported_vector_masked(int opcode, int vlen, BasicType if (is_subword_type(bt) && !VM_Version::supports_avx512bw()) { return false; // Implementation limitation } - if (is_floating_point_type(bt)) { + if (is_floating_point_type(bt) && !VM_Version::supports_avx10_2()) { return false; // Implementation limitation } return true; @@ -5293,9 +5293,9 @@ instruct mul_reduction64B(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legV //--------------------Min/Max Float Reduction -------------------- // Float Min Reduction -instruct minmax_reduction2F(legRegF dst, immF src1, legVec src2, legVec tmp, - legVec atmp, legVec btmp, legVec xmm_1, rFlagsReg cr) %{ - predicate(Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && +instruct minmax_reduction2F(legRegF dst, immF src1, legVec src2, legVec tmp, legVec atmp, + legVec btmp, legVec xmm_1, rFlagsReg cr) %{ + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) || (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) && Matcher::vector_length(n->in(2)) == 2); @@ -5316,7 +5316,7 @@ instruct minmax_reduction2F(legRegF dst, immF src1, legVec src2, legVec tmp, instruct minmax_reductionF(legRegF dst, immF src1, legVec src2, legVec tmp, legVec atmp, legVec btmp, legVec xmm_0, legVec xmm_1, rFlagsReg cr) %{ - predicate(Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) || (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) && Matcher::vector_length(n->in(2)) >= 4); @@ -5335,9 +5335,9 @@ instruct minmax_reductionF(legRegF dst, immF src1, legVec src2, legVec tmp, legV ins_pipe( pipe_slow ); %} -instruct minmax_reduction2F_av(legRegF dst, legVec src, legVec tmp, - legVec atmp, legVec btmp, legVec xmm_1, rFlagsReg cr) %{ - predicate(Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && +instruct minmax_reduction2F_av(legRegF dst, legVec src, legVec tmp, legVec atmp, + legVec btmp, legVec xmm_1, rFlagsReg cr) %{ + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && Matcher::vector_length(n->in(2)) == 2); match(Set dst (MinReductionV dst src)); match(Set dst (MaxReductionV dst src)); @@ -5355,9 +5355,9 @@ instruct minmax_reduction2F_av(legRegF dst, legVec src, legVec tmp, %} -instruct minmax_reductionF_av(legRegF dst, legVec src, legVec tmp, - legVec atmp, legVec btmp, legVec xmm_0, legVec xmm_1, rFlagsReg cr) %{ - predicate(Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && +instruct minmax_reductionF_av(legRegF dst, legVec src, legVec tmp, legVec atmp, legVec btmp, + legVec xmm_0, legVec xmm_1, rFlagsReg cr) %{ + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && Matcher::vector_length(n->in(2)) >= 4); match(Set dst (MinReductionV dst src)); match(Set dst (MaxReductionV dst src)); @@ -5374,12 +5374,78 @@ instruct minmax_reductionF_av(legRegF dst, legVec src, legVec tmp, ins_pipe( pipe_slow ); %} +instruct minmax_reduction2F_avx10(regF dst, immF src1, vec src2, vec xtmp1) %{ + predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && + ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) || + (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) && + Matcher::vector_length(n->in(2)) == 2); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP dst, TEMP xtmp1); + format %{ "vector_minmax_reduction $dst, $src1, $src2 \t; using $xtmp1 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ reduceFloatMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, + xnoreg, xnoreg, xnoreg, $xtmp1$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmax_reductionF_avx10(regF dst, immF src1, vec src2, vec xtmp1, vec xtmp2) %{ + predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && + ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) || + (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) && + Matcher::vector_length(n->in(2)) >= 4); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP dst, TEMP xtmp1, TEMP xtmp2); + format %{ "vector_minmax_reduction $dst, $src1, $src2 \t; using $xtmp1 and $xtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ reduceFloatMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, xnoreg, xnoreg, + xnoreg, $xtmp1$$XMMRegister, $xtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmax_reduction2F_avx10_av(regF dst, vec src, vec xtmp1) %{ + predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && + Matcher::vector_length(n->in(2)) == 2); + match(Set dst (MinReductionV dst src)); + match(Set dst (MaxReductionV dst src)); + effect(TEMP dst, TEMP xtmp1); + format %{ "vector_minmax2F_reduction $dst, $src \t; using $xtmp1 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src); + __ reduceFloatMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, xnoreg, xnoreg, xnoreg, + $xtmp1$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmax_reductionF_avx10_av(regF dst, vec src, vec xtmp1, vec xtmp2) %{ + predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT && + Matcher::vector_length(n->in(2)) >= 4); + match(Set dst (MinReductionV dst src)); + match(Set dst (MaxReductionV dst src)); + effect(TEMP dst, TEMP xtmp1, TEMP xtmp2); + format %{ "vector_minmax2F_reduction $dst, $src \t; using $xtmp1 and $xtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src); + __ reduceFloatMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, xnoreg, xnoreg, xnoreg, + $xtmp1$$XMMRegister, $xtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} //--------------------Min Double Reduction -------------------- -instruct minmax_reduction2D(legRegD dst, immD src1, legVec src2, - legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, // TEMPs - rFlagsReg cr) %{ - predicate(Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && +instruct minmax_reduction2D(legRegD dst, immD src1, legVec src2, legVec tmp1, legVec tmp2, + legVec tmp3, legVec tmp4, rFlagsReg cr) %{ + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) || (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) && Matcher::vector_length(n->in(2)) == 2); @@ -5398,10 +5464,9 @@ instruct minmax_reduction2D(legRegD dst, immD src1, legVec src2, ins_pipe( pipe_slow ); %} -instruct minmax_reductionD(legRegD dst, immD src1, legVec src2, - legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, legVec tmp5, // TEMPs - rFlagsReg cr) %{ - predicate(Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && +instruct minmax_reductionD(legRegD dst, immD src1, legVec src2, legVec tmp1, legVec tmp2, + legVec tmp3, legVec tmp4, legVec tmp5, rFlagsReg cr) %{ + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) || (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) && Matcher::vector_length(n->in(2)) >= 4); @@ -5421,10 +5486,9 @@ instruct minmax_reductionD(legRegD dst, immD src1, legVec src2, %} -instruct minmax_reduction2D_av(legRegD dst, legVec src, - legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, // TEMPs - rFlagsReg cr) %{ - predicate(Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && +instruct minmax_reduction2D_av(legRegD dst, legVec src, legVec tmp1, legVec tmp2, + legVec tmp3, legVec tmp4, rFlagsReg cr) %{ + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && Matcher::vector_length(n->in(2)) == 2); match(Set dst (MinReductionV dst src)); match(Set dst (MaxReductionV dst src)); @@ -5441,10 +5505,9 @@ instruct minmax_reduction2D_av(legRegD dst, legVec src, ins_pipe( pipe_slow ); %} -instruct minmax_reductionD_av(legRegD dst, legVec src, - legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, legVec tmp5, // TEMPs - rFlagsReg cr) %{ - predicate(Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && +instruct minmax_reductionD_av(legRegD dst, legVec src, legVec tmp1, legVec tmp2, legVec tmp3, + legVec tmp4, legVec tmp5, rFlagsReg cr) %{ + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && Matcher::vector_length(n->in(2)) >= 4); match(Set dst (MinReductionV dst src)); match(Set dst (MaxReductionV dst src)); @@ -5461,6 +5524,75 @@ instruct minmax_reductionD_av(legRegD dst, legVec src, ins_pipe( pipe_slow ); %} +instruct minmax_reduction2D_avx10(regD dst, immD src1, vec src2, vec xtmp1) %{ + predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && + ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) || + (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) && + Matcher::vector_length(n->in(2)) == 2); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP dst, TEMP xtmp1); + format %{ "vector_minmax2D_reduction $dst, $src1, $src2 ; using $xtmp1 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ reduceDoubleMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, xnoreg, + xnoreg, xnoreg, $xtmp1$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmax_reductionD_avx10(regD dst, immD src1, vec src2, vec xtmp1, vec xtmp2) %{ + predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && + ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) || + (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) && + Matcher::vector_length(n->in(2)) >= 4); + match(Set dst (MinReductionV src1 src2)); + match(Set dst (MaxReductionV src1 src2)); + effect(TEMP dst, TEMP xtmp1, TEMP xtmp2); + format %{ "vector_minmaxD_reduction $dst, $src1, $src2 ; using $xtmp1 and $xtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src2); + __ reduceDoubleMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, xnoreg, xnoreg, + xnoreg, $xtmp1$$XMMRegister, $xtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + + +instruct minmax_reduction2D_av_avx10(regD dst, vec src, vec xtmp1) %{ + predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && + Matcher::vector_length(n->in(2)) == 2); + match(Set dst (MinReductionV dst src)); + match(Set dst (MaxReductionV dst src)); + effect(TEMP dst, TEMP xtmp1); + format %{ "vector_minmax2D_reduction $dst, $src ; using $xtmp1 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src); + __ reduceDoubleMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, + xnoreg, xnoreg, xnoreg, $xtmp1$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmax_reductionD_av_avx10(regD dst, vec src, vec xtmp1, vec xtmp2) %{ + predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE && + Matcher::vector_length(n->in(2)) >= 4); + match(Set dst (MinReductionV dst src)); + match(Set dst (MaxReductionV dst src)); + effect(TEMP dst, TEMP xtmp1, TEMP xtmp2); + format %{ "vector_minmaxD_reduction $dst, $src ; using $xtmp1 and $xtmp2 as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int vlen = Matcher::vector_length(this, $src); + __ reduceDoubleMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, + xnoreg, xnoreg, xnoreg, $xtmp1$$XMMRegister, $xtmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + // ====================VECTOR ARITHMETIC======================================= // --------------------------------- ADD -------------------------------------- @@ -6347,9 +6479,25 @@ instruct vminmaxL_reg_evex(vec dst, vec src1, vec src2) %{ ins_pipe( pipe_slow ); %} +// Float/Double vector Min/Max +instruct minmaxFP_avx10_reg(vec dst, vec a, vec b) %{ + predicate(VM_Version::supports_avx10_2() && + is_floating_point_type(Matcher::vector_element_basic_type(n))); // T_FLOAT, T_DOUBLE + match(Set dst (MinV a b)); + match(Set dst (MaxV a b)); + format %{ "vector_minmaxFP $dst, $a, $b" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + int opcode = this->ideal_Opcode(); + BasicType elem_bt = Matcher::vector_element_basic_type(this); + __ vminmax_fp(opcode, elem_bt, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + // Float/Double vector Min/Max instruct minmaxFP_reg(legVec dst, legVec a, legVec b, legVec tmp, legVec atmp, legVec btmp) %{ - predicate(Matcher::vector_length_in_bytes(n) <= 32 && + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_length_in_bytes(n) <= 32 && is_floating_point_type(Matcher::vector_element_basic_type(n)) && // T_FLOAT, T_DOUBLE UseAVX > 0); match(Set dst (MinV a b)); @@ -6370,8 +6518,8 @@ instruct minmaxFP_reg(legVec dst, legVec a, legVec b, legVec tmp, legVec atmp, l ins_pipe( pipe_slow ); %} -instruct evminmaxFP_reg_eavx(vec dst, vec a, vec b, vec atmp, vec btmp, kReg ktmp) %{ - predicate(Matcher::vector_length_in_bytes(n) == 64 && +instruct evminmaxFP_reg_evex(vec dst, vec a, vec b, vec atmp, vec btmp, kReg ktmp) %{ + predicate(!VM_Version::supports_avx10_2() && Matcher::vector_length_in_bytes(n) == 64 && is_floating_point_type(Matcher::vector_element_basic_type(n))); // T_FLOAT, T_DOUBLE match(Set dst (MinV a b)); match(Set dst (MaxV a b)); @@ -10686,8 +10834,22 @@ instruct scalar_binOps_HF_reg(regF dst, regF src1, regF src2) ins_pipe(pipe_slow); %} +instruct scalar_minmax_HF_avx10_reg(regF dst, regF src1, regF src2) +%{ + predicate(VM_Version::supports_avx10_2()); + match(Set dst (MaxHF src1 src2)); + match(Set dst (MinHF src1 src2)); + format %{ "scalar_min_max_fp16 $dst, $src1, $src2" %} + ins_encode %{ + int function = this->ideal_Opcode() == Op_MinHF ? AVX10_MINMAX_MIN_COMPARE_SIGN : AVX10_MINMAX_MAX_COMPARE_SIGN; + __ eminmaxsh($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, function); + %} + ins_pipe( pipe_slow ); +%} + instruct scalar_minmax_HF_reg(regF dst, regF src1, regF src2, kReg ktmp, regF xtmp1, regF xtmp2) %{ + predicate(!VM_Version::supports_avx10_2()); match(Set dst (MaxHF src1 src2)); match(Set dst (MinHF src1 src2)); effect(TEMP_DEF dst, TEMP ktmp, TEMP xtmp1, TEMP xtmp2); @@ -10787,8 +10949,37 @@ instruct vector_fma_HF_mem(vec dst, memory src1, vec src2) ins_pipe( pipe_slow ); %} +instruct vector_minmax_HF_avx10_mem(vec dst, vec src1, memory src2) +%{ + predicate(VM_Version::supports_avx10_2()); + match(Set dst (MinVHF src1 (VectorReinterpret (LoadVector src2)))); + match(Set dst (MaxVHF src1 (VectorReinterpret (LoadVector src2)))); + format %{ "vector_min_max_fp16_mem $dst, $src1, $src2" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_MINMAX_MIN_COMPARE_SIGN : AVX10_MINMAX_MAX_COMPARE_SIGN; + __ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$Address, true, function, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + +instruct vector_minmax_HF_avx10_reg(vec dst, vec src1, vec src2) +%{ + predicate(VM_Version::supports_avx10_2()); + match(Set dst (MinVHF src1 src2)); + match(Set dst (MaxVHF src1 src2)); + format %{ "vector_min_max_fp16 $dst, $src1, $src2" %} + ins_encode %{ + int vlen_enc = vector_length_encoding(this); + int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_MINMAX_MIN_COMPARE_SIGN : AVX10_MINMAX_MAX_COMPARE_SIGN; + __ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$XMMRegister, true, function, vlen_enc); + %} + ins_pipe( pipe_slow ); +%} + instruct vector_minmax_HF_reg(vec dst, vec src1, vec src2, kReg ktmp, vec xtmp1, vec xtmp2) %{ + predicate(!VM_Version::supports_avx10_2()); match(Set dst (MinVHF src1 src2)); match(Set dst (MaxVHF src1 src2)); effect(TEMP_DEF dst, TEMP ktmp, TEMP xtmp1, TEMP xtmp2); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 3839813c4e3..5b5292fbde2 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -4450,9 +4450,20 @@ instruct loadD(regD dst, memory mem) ins_pipe(pipe_slow); // XXX %} +// max = java.lang.Math.max(float a, float b) +instruct maxF_avx10_reg(regF dst, regF a, regF b) %{ + predicate(VM_Version::supports_avx10_2()); + match(Set dst (MaxF a b)); + format %{ "maxF $dst, $a, $b" %} + ins_encode %{ + __ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MAX_COMPARE_SIGN); + %} + ins_pipe( pipe_slow ); +%} + // max = java.lang.Math.max(float a, float b) instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{ - predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n)); + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); match(Set dst (MaxF a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); format %{ "maxF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} @@ -4463,7 +4474,7 @@ instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, %} instruct maxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{ - predicate(UseAVX > 0 && VLoopReductions::is_reduction(n)); + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); match(Set dst (MaxF a b)); effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); @@ -4475,9 +4486,20 @@ instruct maxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRe ins_pipe( pipe_slow ); %} +// max = java.lang.Math.max(double a, double b) +instruct maxD_avx10_reg(regD dst, regD a, regD b) %{ + predicate(VM_Version::supports_avx10_2()); + match(Set dst (MaxD a b)); + format %{ "maxD $dst, $a, $b" %} + ins_encode %{ + __ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MAX_COMPARE_SIGN); + %} + ins_pipe( pipe_slow ); +%} + // max = java.lang.Math.max(double a, double b) instruct maxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{ - predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n)); + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); match(Set dst (MaxD a b)); effect(USE a, USE b, TEMP atmp, TEMP btmp, TEMP tmp); format %{ "maxD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} @@ -4488,7 +4510,7 @@ instruct maxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, %} instruct maxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{ - predicate(UseAVX > 0 && VLoopReductions::is_reduction(n)); + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); match(Set dst (MaxD a b)); effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); @@ -4500,9 +4522,20 @@ instruct maxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRe ins_pipe( pipe_slow ); %} +// max = java.lang.Math.min(float a, float b) +instruct minF_avx10_reg(regF dst, regF a, regF b) %{ + predicate(VM_Version::supports_avx10_2()); + match(Set dst (MinF a b)); + format %{ "minF $dst, $a, $b" %} + ins_encode %{ + __ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MIN_COMPARE_SIGN); + %} + ins_pipe( pipe_slow ); +%} + // min = java.lang.Math.min(float a, float b) instruct minF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{ - predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n)); + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); match(Set dst (MinF a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); format %{ "minF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} @@ -4513,7 +4546,7 @@ instruct minF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, %} instruct minF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{ - predicate(UseAVX > 0 && VLoopReductions::is_reduction(n)); + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); match(Set dst (MinF a b)); effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); @@ -4525,9 +4558,20 @@ instruct minF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRe ins_pipe( pipe_slow ); %} +// max = java.lang.Math.min(double a, double b) +instruct minD_avx10_reg(regD dst, regD a, regD b) %{ + predicate(VM_Version::supports_avx10_2()); + match(Set dst (MinD a b)); + format %{ "minD $dst, $a, $b" %} + ins_encode %{ + __ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MIN_COMPARE_SIGN); + %} + ins_pipe( pipe_slow ); +%} + // min = java.lang.Math.min(double a, double b) instruct minD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{ - predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n)); + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); match(Set dst (MinD a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); format %{ "minD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} @@ -4538,7 +4582,7 @@ instruct minD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, %} instruct minD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{ - predicate(UseAVX > 0 && VLoopReductions::is_reduction(n)); + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); match(Set dst (MinD a b)); effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); @@ -6379,7 +6423,7 @@ instruct cmovI_rReg_rReg_memU_ndd(rRegI dst, cmpOpU cop, rFlagsRegU cr, rRegI sr ins_pipe(pipe_cmov_mem); %} -instruct cmovI_rReg_rReg_memUCF_ndd(rRegI dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegI src1, memory src2) +instruct cmovI_rReg_rReg_memUCF_ndd(rRegI dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegI src1, memory src2) %{ predicate(UseAPX); match(Set dst (CMoveI (Binary cop cr) (Binary src1 (LoadI src2)))); @@ -6762,7 +6806,7 @@ instruct cmovL_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{ %} %} -instruct cmovL_regUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1, rRegL src2) +instruct cmovL_regUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1, rRegL src2) %{ predicate(UseAPX); match(Set dst (CMoveL (Binary cop cr) (Binary src1 src2))); @@ -6869,7 +6913,7 @@ instruct cmovL_rReg_rReg_memU_ndd(rRegL dst, cmpOpU cop, rFlagsRegU cr, rRegL sr ins_pipe(pipe_cmov_mem); %} -instruct cmovL_rReg_rReg_memUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1, memory src2) +instruct cmovL_rReg_rReg_memUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1, memory src2) %{ predicate(UseAPX); match(Set dst (CMoveL (Binary cop cr) (Binary src1 (LoadL src2)))); diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 9c6218aee16..17186fb9f3d 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -879,17 +879,6 @@ void os::free_thread(OSThread* osthread) { //////////////////////////////////////////////////////////////////////////////// // time support -double os::elapsedVTime() { - struct rusage usage; - int retval = getrusage(RUSAGE_THREAD, &usage); - if (retval == 0) { - return usage.ru_utime.tv_sec + usage.ru_stime.tv_sec + (usage.ru_utime.tv_usec + usage.ru_stime.tv_usec) / (1000.0 * 1000); - } else { - // better than nothing, but not much - return elapsedTime(); - } -} - // We use mread_real_time here. // On AIX: If the CPU has a time register, the result will be RTC_POWER and // it has to be converted to real time. AIX documentations suggests to do @@ -2441,7 +2430,7 @@ static bool thread_cpu_time_unchecked(Thread* thread, jlong* p_sys_time, jlong* dummy, &dummy_size) == 0) { tid = pinfo.__pi_tid; } else { - tty->print_cr("pthread_getthrds_np failed."); + tty->print_cr("pthread_getthrds_np failed, errno: %d.", errno); error = true; } @@ -2452,7 +2441,7 @@ static bool thread_cpu_time_unchecked(Thread* thread, jlong* p_sys_time, jlong* sys_time = thrdentry.ti_ru.ru_stime.tv_sec * 1000000000LL + thrdentry.ti_ru.ru_stime.tv_usec * 1000LL; user_time = thrdentry.ti_ru.ru_utime.tv_sec * 1000000000LL + thrdentry.ti_ru.ru_utime.tv_usec * 1000LL; } else { - tty->print_cr("pthread_getthrds_np failed."); + tty->print_cr("getthrds64 failed, errno: %d.", errno); error = true; } } diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 6f7d9a6de37..4b74e7c00f3 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -782,10 +782,6 @@ void os::free_thread(OSThread* osthread) { //////////////////////////////////////////////////////////////////////////////// // time support -double os::elapsedVTime() { - // better than nothing, but not much - return elapsedTime(); -} #ifdef __APPLE__ void os::Bsd::clock_init() { diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 1a23c956f35..b747fe4d88f 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -1487,16 +1487,6 @@ void os::Linux::capture_initial_stack(size_t max_size) { //////////////////////////////////////////////////////////////////////////////// // time support -double os::elapsedVTime() { - struct rusage usage; - int retval = getrusage(RUSAGE_THREAD, &usage); - if (retval == 0) { - return (double) (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) + (double) (usage.ru_utime.tv_usec + usage.ru_stime.tv_usec) / (1000 * 1000); - } else { - // better than nothing, but not much - return elapsedTime(); - } -} void os::Linux::fast_thread_clock_init() { clockid_t clockid; diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 303e44eadcb..1444a4f1882 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -1599,8 +1599,6 @@ jlong os::elapsed_frequency() { return NANOSECS_PER_SEC; // nanosecond resolution } -bool os::supports_vtime() { return true; } - // Return the real, user, and system times in seconds from an // arbitrary fixed point in the past. bool os::getTimesSecs(double* process_real_time, diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 24969683a1f..c1311579c5c 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -1194,21 +1194,6 @@ FILETIME java_to_windows_time(jlong l) { return result; } -bool os::supports_vtime() { return true; } - -double os::elapsedVTime() { - FILETIME created; - FILETIME exited; - FILETIME kernel; - FILETIME user; - if (GetThreadTimes(GetCurrentThread(), &created, &exited, &kernel, &user) != 0) { - // the resolution of windows_to_java_time() should be sufficient (ms) - return (double) (windows_to_java_time(kernel) + windows_to_java_time(user)) / MILLIUNITS; - } else { - return elapsedTime(); - } -} - jlong os::javaTimeMillis() { FILETIME wt; GetSystemTimeAsFileTime(&wt); diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index d8999774a53..adcc4bfb50a 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -30,6 +30,7 @@ #include "cds/dumpTimeClassInfo.inline.hpp" #include "cds/heapShared.hpp" #include "cds/lambdaProxyClassDictionary.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/systemDictionaryShared.hpp" #include "logging/log.hpp" #include "memory/metaspaceClosure.hpp" @@ -188,7 +189,11 @@ void AOTArtifactFinder::end_scanning_for_oops() { void AOTArtifactFinder::add_aot_inited_class(InstanceKlass* ik) { if (CDSConfig::is_initing_classes_at_dump_time()) { - assert(ik->is_initialized(), "must be"); + if (RegeneratedClasses::is_regenerated_object(ik)) { + precond(RegeneratedClasses::get_original_object(ik)->is_initialized()); + } else { + precond(ik->is_initialized()); + } add_cached_instance_class(ik); bool created; @@ -243,6 +248,11 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { add_cached_instance_class(intf); } + InstanceKlass* nest_host = ik->nest_host_or_null(); + if (nest_host != nullptr) { + add_cached_instance_class(nest_host); + } + if (CDSConfig::is_dumping_final_static_archive() && ik->defined_by_other_loaders()) { // The following are not appliable to unregistered classes return; diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 46a118c42e9..db18a8ee1b5 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -26,6 +26,7 @@ #include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/heapShared.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" @@ -103,6 +104,10 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { return false; } + if (RegeneratedClasses::is_regenerated_object(ik)) { + ik = RegeneratedClasses::get_original_object(ik); + } + if (!ik->is_initialized() && !ik->is_being_initialized()) { return false; } diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 5f197f70ecc..fca9d970cd4 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -575,6 +575,9 @@ ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref if (ref->msotype() == MetaspaceObj::ClassType) { Klass* klass = (Klass*)ref->obj(); assert(klass->is_klass(), "must be"); + if (RegeneratedClasses::has_been_regenerated(klass)) { + klass = RegeneratedClasses::get_regenerated_object(klass); + } if (is_excluded(klass)) { ResourceMark rm; log_debug(cds, dynamic)("Skipping class (excluded): %s", klass->external_name()); diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index ee1e334e84b..db93027e348 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.cpp +++ b/src/hotspot/share/cds/archiveHeapWriter.cpp @@ -27,6 +27,7 @@ #include "cds/cdsConfig.hpp" #include "cds/filemap.hpp" #include "cds/heapShared.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/javaClasses.hpp" #include "classfile/modules.hpp" #include "classfile/systemDictionary.hpp" @@ -543,6 +544,10 @@ template void ArchiveHeapWriter::relocate_field_in_buffer(T* field_ oop source_referent = load_source_oop_from_buffer(field_addr_in_buffer); if (source_referent != nullptr) { if (java_lang_Class::is_instance(source_referent)) { + Klass* k = java_lang_Class::as_Klass(source_referent); + if (RegeneratedClasses::has_been_regenerated(k)) { + source_referent = RegeneratedClasses::get_regenerated_object(k)->java_mirror(); + } // When the source object points to a "real" mirror, the buffered object should point // to the "scratch" mirror, which has all unarchivable fields scrubbed (to be reinstated // at run time). @@ -754,6 +759,11 @@ void ArchiveHeapWriter::compute_ptrmap(ArchiveHeapInfo* heap_info) { Metadata** buffered_field_addr = requested_addr_to_buffered_addr(requested_field_addr); Metadata* native_ptr = *buffered_field_addr; guarantee(native_ptr != nullptr, "sanity"); + + if (RegeneratedClasses::has_been_regenerated(native_ptr)) { + native_ptr = RegeneratedClasses::get_regenerated_object(native_ptr); + } + guarantee(ArchiveBuilder::current()->has_been_buffered((address)native_ptr), "Metadata %p should have been archived", native_ptr); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index c19c0776465..ad0374c04eb 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -817,9 +817,6 @@ bool CDSConfig::is_dumping_regenerated_lambdaform_invokers() { // that point to the lambda form invokers in the base archive. Such pointers will // be invalid if lambda form invokers are regenerated in the dynamic archive. return false; - } else if (CDSConfig::is_dumping_method_handles()) { - // Work around JDK-8310831, as some methods in lambda form holder classes may not get generated. - return false; } else { return is_dumping_archive(); } diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp index e51dd26ff06..730902207f0 100644 --- a/src/hotspot/share/cds/cds_globals.hpp +++ b/src/hotspot/share/cds/cds_globals.hpp @@ -147,7 +147,7 @@ product(bool, AOTVerifyTrainingData, trueInDebug, DIAGNOSTIC, \ "Verify archived training data") \ \ - product(bool, AOTCompileEagerly, false, DIAGNOSTIC, \ + product(bool, AOTCompileEagerly, false, EXPERIMENTAL, \ "Compile methods as soon as possible") \ \ /* AOT Code flags */ \ diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 06cbaf1dbe7..df045b40583 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -36,6 +36,7 @@ #include "cds/cdsHeapVerifier.hpp" #include "cds/heapShared.hpp" #include "cds/metaspaceShared.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderExt.hpp" #include "classfile/javaClasses.inline.hpp" @@ -337,6 +338,9 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra } else if (java_lang_invoke_ResolvedMethodName::is_instance(obj)) { Method* m = java_lang_invoke_ResolvedMethodName::vmtarget(obj); if (m != nullptr) { + if (RegeneratedClasses::has_been_regenerated(m)) { + m = RegeneratedClasses::get_regenerated_object(m); + } InstanceKlass* method_holder = m->method_holder(); AOTArtifactFinder::add_cached_class(method_holder); } @@ -506,10 +510,17 @@ void HeapShared::copy_and_rescan_aot_inited_mirror(InstanceKlass* ik) { ik->set_is_runtime_setup_required(); } - oop orig_mirror = ik->java_mirror(); - oop m = scratch_java_mirror(ik); - assert(ik->is_initialized(), "must be"); + oop orig_mirror; + if (RegeneratedClasses::is_regenerated_object(ik)) { + InstanceKlass* orig_ik = RegeneratedClasses::get_original_object(ik); + precond(orig_ik->is_initialized()); + orig_mirror = orig_ik->java_mirror(); + } else { + precond(ik->is_initialized()); + orig_mirror = ik->java_mirror(); + } + oop m = scratch_java_mirror(ik); int nfields = 0; for (JavaFieldStream fs(ik); !fs.done(); fs.next()) { if (fs.access_flags().is_static()) { @@ -1520,6 +1531,13 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap p2i(scratch_java_mirror(orig_obj))); } + if (java_lang_Class::is_instance(orig_obj)) { + Klass* k = java_lang_Class::as_Klass(orig_obj); + if (RegeneratedClasses::has_been_regenerated(k)) { + orig_obj = RegeneratedClasses::get_regenerated_object(k)->java_mirror(); + } + } + if (CDSConfig::is_initing_classes_at_dump_time()) { if (java_lang_Class::is_instance(orig_obj)) { orig_obj = scratch_java_mirror(orig_obj); diff --git a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp index d6a51c87513..ab91e76e923 100644 --- a/src/hotspot/share/cds/lambdaFormInvokers.cpp +++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp @@ -224,6 +224,7 @@ void LambdaFormInvokers::regenerate_class(char* class_name, ClassFileStream& st, result->set_is_generated_shared_class(); if (!klass->is_shared()) { + log_info(aot, lambda)("regenerate_class excluding klass %s %s", class_name, klass->name()->as_C_string()); SystemDictionaryShared::set_excluded(InstanceKlass::cast(klass)); // exclude the existing class from dump } log_info(aot, lambda)("Regenerated class %s, old: " INTPTR_FORMAT " new: " INTPTR_FORMAT, diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 6c6b64a0cb2..6e0385fc99c 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -837,11 +837,10 @@ void MetaspaceShared::preload_and_dump(TRAPS) { struct stat st; if (os::stat(AOTCache, &st) != 0) { tty->print_cr("AOTCache creation failed: %s", AOTCache); - vm_exit(0); } else { tty->print_cr("AOTCache creation is complete: %s " INT64_FORMAT " bytes", AOTCache, (int64_t)(st.st_size)); - vm_exit(0); } + vm_direct_exit(0); } } } diff --git a/src/hotspot/share/cds/regeneratedClasses.cpp b/src/hotspot/share/cds/regeneratedClasses.cpp index 38bf1a11952..b36f360b82a 100644 --- a/src/hotspot/share/cds/regeneratedClasses.cpp +++ b/src/hotspot/share/cds/regeneratedClasses.cpp @@ -24,6 +24,7 @@ #include "cds/archiveBuilder.hpp" #include "cds/regeneratedClasses.hpp" +#include "classfile/vmSymbols.hpp" #include "memory/universe.hpp" #include "oops/instanceKlass.hpp" #include "oops/method.hpp" @@ -34,7 +35,8 @@ #include "utilities/resourceHash.hpp" using RegeneratedObjTable = ResourceHashtable; -static RegeneratedObjTable* _renegerated_objs = nullptr; // InstanceKlass* and Method* +static RegeneratedObjTable* _regenerated_objs = nullptr; // InstanceKlass* and Method* orig_obj -> regen_obj +static RegeneratedObjTable* _original_objs = nullptr; // InstanceKlass* and Method* regen_obj -> orig_obj static GrowableArrayCHeap* _regenerated_mirrors = nullptr; // The regenerated Klass is not added to any class loader, so we need @@ -46,40 +48,71 @@ void RegeneratedClasses::add_class(InstanceKlass* orig_klass, InstanceKlass* reg } _regenerated_mirrors->append(OopHandle(Universe::vm_global(), regen_klass->java_mirror())); - if (_renegerated_objs == nullptr) { - _renegerated_objs = new (mtClass)RegeneratedObjTable(); + if (_regenerated_objs == nullptr) { + _regenerated_objs = new (mtClass)RegeneratedObjTable(); + } + if (_original_objs == nullptr) { + _original_objs = new (mtClass)RegeneratedObjTable(); } - _renegerated_objs->put((address)orig_klass, (address)regen_klass); + _regenerated_objs->put((address)orig_klass, (address)regen_klass); + _original_objs->put((address)regen_klass, (address)orig_klass); Array* methods = orig_klass->methods(); for (int i = 0; i < methods->length(); i++) { Method* orig_m = methods->at(i); Method* regen_m = regen_klass->find_method(orig_m->name(), orig_m->signature()); if (regen_m == nullptr) { ResourceMark rm; - log_warning(aot)("Method in original class is missing from regenerated class: " INTPTR_FORMAT " %s", - p2i(orig_m), orig_m->external_name()); + if (orig_m->name() != vmSymbols::object_initializer_name()) { + // JLI Holder classes are never instantiated, they don't need to have constructors. + // Not printing the warning if the method is a constructor. + log_warning(aot)("Method in original class is missing from regenerated class: " INTPTR_FORMAT " %s", + p2i(orig_m), orig_m->external_name()); + } } else { - _renegerated_objs->put((address)orig_m, (address)regen_m); + _regenerated_objs->put((address)orig_m, (address)regen_m); + _original_objs->put((address)regen_m, (address)orig_m); } } } bool RegeneratedClasses::has_been_regenerated(address orig_obj) { - if (_renegerated_objs == nullptr) { + if (_regenerated_objs == nullptr) { return false; } else { - return _renegerated_objs->get(orig_obj) != nullptr; + return _regenerated_objs->get(orig_obj) != nullptr; } } +address RegeneratedClasses::get_regenerated_object(address orig_obj) { + assert(_regenerated_objs != nullptr, "must be"); + address* p =_regenerated_objs->get(orig_obj); + assert(p != nullptr, "must be"); + return *p; +} + +bool RegeneratedClasses::is_regenerated_object(address regen_obj) { + if (_original_objs == nullptr) { + return false; + } else { + return _original_objs->get(regen_obj) != nullptr; + } +} + +address RegeneratedClasses::get_original_object(address regen_obj) { + assert(_original_objs != nullptr, "must be"); + address* p =_original_objs->get(regen_obj); + assert(p != nullptr, "must be"); + return *p; +} + void RegeneratedClasses::record_regenerated_objects() { assert_locked_or_safepoint(DumpTimeTable_lock); - if (_renegerated_objs != nullptr) { + if (_regenerated_objs != nullptr) { auto doit = [&] (address orig_obj, address regen_obj) { ArchiveBuilder::current()->record_regenerated_object(orig_obj, regen_obj); }; - _renegerated_objs->iterate_all(doit); + _regenerated_objs->iterate_all(doit); } } @@ -92,7 +125,7 @@ void RegeneratedClasses::cleanup() { delete _regenerated_mirrors; _regenerated_mirrors = nullptr; } - if (_renegerated_objs != nullptr) { - delete _renegerated_objs; + if (_regenerated_objs != nullptr) { + delete _regenerated_objs; } } diff --git a/src/hotspot/share/cds/regeneratedClasses.hpp b/src/hotspot/share/cds/regeneratedClasses.hpp index a1edaffe529..080d84a2154 100644 --- a/src/hotspot/share/cds/regeneratedClasses.hpp +++ b/src/hotspot/share/cds/regeneratedClasses.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -43,7 +43,26 @@ class RegeneratedClasses : public AllStatic { static void add_class(InstanceKlass* orig_klass, InstanceKlass* regen_klass); static void cleanup(); static bool has_been_regenerated(address orig_obj); + static address get_regenerated_object(address orig_obj); // orig_obj -> regen_obj static void record_regenerated_objects(); + + // Handy functions to avoid type casts + template static bool has_been_regenerated(T orig_obj) { + return has_been_regenerated((address)orig_obj); + } + template static T get_regenerated_object(T orig_obj) { + return (T)get_regenerated_object((address)orig_obj); + } + + static bool is_regenerated_object(address regen_obj); + static address get_original_object(address regen_obj); // regen_obj -> orig_obj + + template static bool is_regenerated_object(T regen_obj) { + return is_regenerated_object((address)regen_obj); + } + template static T get_original_object(T regen_obj) { + return (T)get_original_object((address)regen_obj); + } }; #endif // SHARE_CDS_REGENERATEDCLASSES_HPP diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index edec019bd70..cec1ae364d5 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -5176,7 +5176,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, assert(module_entry != nullptr, "module_entry should always be set"); // Obtain java.lang.Module - Handle module_handle(THREAD, module_entry->module()); + Handle module_handle(THREAD, module_entry->module_oop()); // Allocate mirror and initialize static fields java_lang_Class::create_mirror(ik, diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index 94ded38f5da..ac84a24267f 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -208,7 +208,7 @@ void ClassLoaderDataShared::clear_archived_oops() { oop ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data() { assert(CDSConfig::is_using_full_module_graph(), "must be"); _archived_boot_loader_data.restore(null_class_loader_data(), false, true); - return _archived_javabase_moduleEntry->module(); + return _archived_javabase_moduleEntry->module_oop(); } void ClassLoaderDataShared::restore_java_platform_loader_from_archive(ClassLoaderData* loader_data) { diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 0e8467289ad..2dcfc43898c 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1036,15 +1036,15 @@ void java_lang_Class::set_mirror_module_field(JavaThread* current, Klass* k, Han // If java.base was already defined then patch this particular class with java.base. if (javabase_was_defined) { ModuleEntry *javabase_entry = ModuleEntryTable::javabase_moduleEntry(); - assert(javabase_entry != nullptr && javabase_entry->module() != nullptr, + assert(javabase_entry != nullptr && javabase_entry->module_oop() != nullptr, "Setting class module field, " JAVA_BASE_NAME " should be defined"); - Handle javabase_handle(current, javabase_entry->module()); + Handle javabase_handle(current, javabase_entry->module_oop()); set_module(mirror(), javabase_handle()); } } else { assert(Universe::is_module_initialized() || (ModuleEntryTable::javabase_defined() && - (module() == ModuleEntryTable::javabase_moduleEntry()->module())), + (module() == ModuleEntryTable::javabase_moduleEntry()->module_oop())), "Incorrect java.lang.Module specification while creating mirror"); set_module(mirror(), module()); } @@ -1899,7 +1899,7 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { return nullptr; } - class GetStackTraceClosure : public HandshakeClosure { + class GetStackTraceHandshakeClosure : public HandshakeClosure { public: const Handle _java_thread; int _depth; @@ -1907,11 +1907,11 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { GrowableArray* _methods; GrowableArray* _bcis; - GetStackTraceClosure(Handle java_thread) : - HandshakeClosure("GetStackTraceClosure"), _java_thread(java_thread), _depth(0), _retry_handshake(false), + GetStackTraceHandshakeClosure(Handle java_thread) : + HandshakeClosure("GetStackTraceHandshakeClosure"), _java_thread(java_thread), _depth(0), _retry_handshake(false), _methods(nullptr), _bcis(nullptr) { } - ~GetStackTraceClosure() { + ~GetStackTraceHandshakeClosure() { delete _methods; delete _bcis; } @@ -1977,13 +1977,13 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { // Handshake with target ResourceMark rm(THREAD); HandleMark hm(THREAD); - GetStackTraceClosure gstc(Handle(THREAD, java_thread)); + GetStackTraceHandshakeClosure gsthc(Handle(THREAD, java_thread)); do { - Handshake::execute(&gstc, &tlh, thread); - } while (gstc.read_reset_retry()); + Handshake::execute(&gsthc, &tlh, thread); + } while (gsthc.read_reset_retry()); // Stop if no stack trace is found. - if (gstc._depth == 0) { + if (gsthc._depth == 0) { return nullptr; } @@ -1993,12 +1993,12 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { if (k->should_be_initialized()) { k->initialize(CHECK_NULL); } - objArrayHandle trace = oopFactory::new_objArray_handle(k, gstc._depth, CHECK_NULL); + objArrayHandle trace = oopFactory::new_objArray_handle(k, gsthc._depth, CHECK_NULL); - for (int i = 0; i < gstc._depth; i++) { - methodHandle method(THREAD, gstc._methods->at(i)); + for (int i = 0; i < gsthc._depth; i++) { + methodHandle method(THREAD, gsthc._methods->at(i)); oop element = java_lang_StackTraceElement::create(method, - gstc._bcis->at(i), + gsthc._bcis->at(i), CHECK_NULL); trace->obj_at_put(i, element); } diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 208c4efe035..65d7183dbea 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -49,7 +49,7 @@ ModuleEntry* ModuleEntryTable::_javabase_module = nullptr; -oop ModuleEntry::module() const { return _module.resolve(); } +oop ModuleEntry::module_oop() const { return _module_handle.resolve(); } void ModuleEntry::set_location(Symbol* location) { // _location symbol's refcounts are managed by ModuleEntry, @@ -284,7 +284,7 @@ ModuleEntry::ModuleEntry(Handle module_handle, } if (!module_handle.is_null()) { - _module = loader_data->add_handle(module_handle); + _module_handle = loader_data->add_handle(module_handle); } set_version(version); @@ -401,7 +401,7 @@ ModuleEntry* ModuleEntry::allocate_archived_entry() const { memcpy((void*)archived_entry, (void*)this, sizeof(ModuleEntry)); if (CDSConfig::is_dumping_full_module_graph()) { - archived_entry->_archived_module_index = HeapShared::append_root(module()); + archived_entry->_archived_module_index = HeapShared::append_root(module_oop()); } else { archived_entry->_archived_module_index = -1; } @@ -422,7 +422,7 @@ ModuleEntry* ModuleEntry::allocate_archived_entry() const { // Clear handles and restore at run time. Handles cannot be archived. OopHandle null_handle; - archived_entry->_module = null_handle; + archived_entry->_module_handle = null_handle; // For verify_archived_module_entries() DEBUG_ONLY(_num_inited_module_entries++); @@ -526,7 +526,7 @@ void ModuleEntry::restore_archived_oops(ClassLoaderData* loader_data) { assert(CDSConfig::is_using_archive(), "runtime only"); Handle module_handle(Thread::current(), HeapShared::get_root(_archived_module_index, /*clear=*/true)); assert(module_handle.not_null(), "huh"); - set_module(loader_data->add_handle(module_handle)); + set_module_handle(loader_data->add_handle(module_handle)); // This was cleared to zero during dump time -- we didn't save the value // because it may be affected by archive relocation. @@ -662,7 +662,7 @@ void ModuleEntryTable::finalize_javabase(Handle module_handle, Symbol* version, jb_module->set_location(location); // Once java.base's ModuleEntry _module field is set with the known // java.lang.Module, java.base is considered "defined" to the VM. - jb_module->set_module(boot_loader_data->add_handle(module_handle)); + jb_module->set_module_handle(boot_loader_data->add_handle(module_handle)); // Store pointer to the ModuleEntry for java.base in the java.lang.Module object. java_lang_Module::set_module_entry(module_handle(), jb_module); @@ -700,7 +700,7 @@ void ModuleEntryTable::patch_javabase_entries(JavaThread* current, Handle module // We allow -XX:ArchiveHeapTestClass to archive additional classes // into the CDS heap, but these must be in the unnamed module. ModuleEntry* unnamed_module = ClassLoaderData::the_null_class_loader_data()->unnamed_module(); - Handle unnamed_module_handle(current, unnamed_module->module()); + Handle unnamed_module_handle(current, unnamed_module->module_oop()); java_lang_Class::fixup_module_field(k, unnamed_module_handle); } else #endif @@ -745,7 +745,7 @@ void ModuleEntry::print(outputStream* st) { st->print_cr("entry " PTR_FORMAT " name %s module " PTR_FORMAT " loader %s version %s location %s strict %s", p2i(this), name_as_C_string(), - p2i(module()), + p2i(module_oop()), loader_data()->loader_name_and_id(), version() != nullptr ? version()->as_C_string() : "nullptr", location() != nullptr ? location()->as_C_string() : "nullptr", diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp index c2d07a78512..e66999c3cd9 100644 --- a/src/hotspot/share/classfile/moduleEntry.hpp +++ b/src/hotspot/share/classfile/moduleEntry.hpp @@ -53,7 +53,7 @@ class ModuleClosure; // A ModuleEntry describes a module that has been defined by a call to JVM_DefineModule. // It contains: // - Symbol* containing the module's name. -// - pointer to the java.lang.Module for this module. +// - pointer to the java.lang.Module: the representation of this module as a Java object // - pointer to the java.security.ProtectionDomain shared by classes defined to this module. // - ClassLoaderData*, class loader of this module. // - a growable array containing other module entries that this module can read. @@ -63,7 +63,7 @@ class ModuleClosure; // data structure. This lock must be taken on all accesses to either table. class ModuleEntry : public CHeapObj { private: - OopHandle _module; // java.lang.Module + OopHandle _module_handle; // java.lang.Module OopHandle _shared_pd; // java.security.ProtectionDomain, cached // for shared classes from this module Symbol* _name; // name of this module @@ -96,9 +96,9 @@ public: ~ModuleEntry(); Symbol* name() const { return _name; } - oop module() const; - OopHandle module_handle() const { return _module; } - void set_module(OopHandle j) { _module = j; } + oop module_oop() const; + OopHandle module_handle() const { return _module_handle; } + void set_module_handle(OopHandle j) { _module_handle = j; } // The shared ProtectionDomain reference is set once the VM loads a shared class // originated from the current Module. The referenced ProtectionDomain object is @@ -262,7 +262,7 @@ public: } static bool javabase_defined() { return ((_javabase_module != nullptr) && - (_javabase_module->module() != nullptr)); } + (_javabase_module->module_oop() != nullptr)); } static void finalize_javabase(Handle module_handle, Symbol* version, Symbol* location); static void patch_javabase_entries(JavaThread* current, Handle module_handle); diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index 4b146abc06b..a2206c842bd 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -773,7 +773,7 @@ void Modules::set_bootloader_unnamed_module(Handle module, TRAPS) { ClassLoaderData* boot_loader_data = ClassLoaderData::the_null_class_loader_data(); ModuleEntry* unnamed_module = boot_loader_data->unnamed_module(); assert(unnamed_module != nullptr, "boot loader's unnamed ModuleEntry not defined"); - unnamed_module->set_module(boot_loader_data->add_handle(module)); + unnamed_module->set_module_handle(boot_loader_data->add_handle(module)); // Store pointer to the ModuleEntry in the unnamed module's java.lang.Module object. java_lang_Module::set_module_entry(module(), unnamed_module); } @@ -954,8 +954,8 @@ oop Modules::get_named_module(Handle h_loader, const char* package_name) { get_package_entry_by_name(package_sym, h_loader); const ModuleEntry* const module_entry = (pkg_entry != nullptr ? pkg_entry->module() : nullptr); - if (module_entry != nullptr && module_entry->module() != nullptr && module_entry->is_named()) { - return module_entry->module(); + if (module_entry != nullptr && module_entry->module_oop() != nullptr && module_entry->is_named()) { + return module_entry->module_oop(); } return nullptr; } diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index a9d05996ebf..957ecd8ebe8 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -32,6 +32,7 @@ #include "classfile/javaClasses.inline.hpp" #include "classfile/stringTable.hpp" #include "classfile/vmClasses.hpp" +#include "compiler/compileBroker.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageSet.hpp" @@ -115,6 +116,7 @@ OopStorage* StringTable::_oop_storage; static size_t _current_size = 0; static volatile size_t _items_count = 0; +DEBUG_ONLY(static bool _disable_interning_during_cds_dump = false); volatile bool _alt_hash = false; @@ -346,6 +348,10 @@ bool StringTable::has_work() { return Atomic::load_acquire(&_has_work); } +size_t StringTable::items_count_acquire() { + return Atomic::load_acquire(&_items_count); +} + void StringTable::trigger_concurrent_work() { // Avoid churn on ServiceThread if (!has_work()) { @@ -504,6 +510,9 @@ oop StringTable::intern(const char* utf8_string, TRAPS) { } oop StringTable::intern(const StringWrapper& name, TRAPS) { + assert(!Atomic::load_acquire(&_disable_interning_during_cds_dump), + "All threads that may intern strings should have been stopped before CDS starts copying the interned string table"); + // shared table always uses java_lang_String::hash_code unsigned int hash = hash_wrapped_string(name); oop found_string = lookup_shared(name, hash); @@ -793,7 +802,7 @@ void StringTable::verify() { } // Verification and comp -class VerifyCompStrings : StackObj { +class StringTable::VerifyCompStrings : StackObj { static unsigned string_hash(oop const& str) { return java_lang_String::hash_code_noupdate(str); } @@ -805,7 +814,7 @@ class VerifyCompStrings : StackObj { string_hash, string_equals> _table; public: size_t _errors; - VerifyCompStrings() : _table(unsigned(_items_count / 8) + 1, 0 /* do not resize */), _errors(0) {} + VerifyCompStrings() : _table(unsigned(items_count_acquire() / 8) + 1, 0 /* do not resize */), _errors(0) {} bool operator()(WeakHandle* val) { oop s = val->resolve(); if (s == nullptr) { @@ -939,20 +948,31 @@ oop StringTable::lookup_shared(const jchar* name, int len) { return _shared_table.lookup(wrapped_name, java_lang_String::hash_code(name, len), 0); } -// This is called BEFORE we enter the CDS safepoint. We can allocate heap objects. -// This should be called when we know no more strings will be added (which will be easy -// to guarantee because CDS runs with a single Java thread. See JDK-8253495.) +// This is called BEFORE we enter the CDS safepoint. We can still allocate Java object arrays to +// be used by the shared strings table. void StringTable::allocate_shared_strings_array(TRAPS) { if (!CDSConfig::is_dumping_heap()) { return; } - assert(CDSConfig::allow_only_single_java_thread(), "No more interned strings can be added"); - if (_items_count > (size_t)max_jint) { - fatal("Too many strings to be archived: %zu", _items_count); + CompileBroker::wait_for_no_active_tasks(); + + precond(CDSConfig::allow_only_single_java_thread()); + + // At this point, no more strings will be added: + // - There's only a single Java thread (this thread). It no longer executes Java bytecodes + // so JIT compilation will eventually stop. + // - CompileBroker has no more active tasks, so all JIT requests have been processed. + + // This flag will be cleared after intern table dumping has completed, so we can run the + // compiler again (for future AOT method compilation, etc). + DEBUG_ONLY(Atomic::release_store(&_disable_interning_during_cds_dump, true)); + + if (items_count_acquire() > (size_t)max_jint) { + fatal("Too many strings to be archived: %zu", items_count_acquire()); } - int total = (int)_items_count; + int total = (int)items_count_acquire(); size_t single_array_size = objArrayOopDesc::object_size(total); log_info(aot)("allocated string table for %d strings", total); @@ -972,7 +992,7 @@ void StringTable::allocate_shared_strings_array(TRAPS) { // This can only happen if you have an extremely large number of classes that // refer to more than 16384 * 16384 = 26M interned strings! Not a practical concern // but bail out for safety. - log_error(aot)("Too many strings to be archived: %zu", _items_count); + log_error(aot)("Too many strings to be archived: %zu", items_count_acquire()); MetaspaceShared::unrecoverable_writing_error(); } @@ -1070,7 +1090,7 @@ oop StringTable::init_shared_strings_array() { void StringTable::write_shared_table() { _shared_table.reset(); - CompactHashtableWriter writer((int)_items_count, ArchiveBuilder::string_stats()); + CompactHashtableWriter writer((int)items_count_acquire(), ArchiveBuilder::string_stats()); int index = 0; auto copy_into_shared_table = [&] (WeakHandle* val) { @@ -1084,6 +1104,8 @@ void StringTable::write_shared_table() { }; _local_table->do_safepoint_scan(copy_into_shared_table); writer.dump(&_shared_table, "string"); + + DEBUG_ONLY(Atomic::release_store(&_disable_interning_during_cds_dump, false)); } void StringTable::set_shared_strings_array_index(int root_index) { diff --git a/src/hotspot/share/classfile/stringTable.hpp b/src/hotspot/share/classfile/stringTable.hpp index bf7bb9e2cd9..9194d0b8002 100644 --- a/src/hotspot/share/classfile/stringTable.hpp +++ b/src/hotspot/share/classfile/stringTable.hpp @@ -40,7 +40,7 @@ class StringTableConfig; class StringTable : AllStatic { friend class StringTableConfig; - + class VerifyCompStrings; static volatile bool _has_work; // Set if one bucket is out of balance due to hash algorithm deficiency @@ -74,6 +74,7 @@ private: static void item_added(); static void item_removed(); + static size_t items_count_acquire(); static oop intern(const StringWrapper& name, TRAPS); static oop do_intern(const StringWrapper& name, uintx hash, TRAPS); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 8bd09a0d947..31ff7777bf9 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -348,6 +348,13 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) { } } + InstanceKlass* nest_host = k->nest_host_or_null(); + if (nest_host != nullptr && nest_host != k && check_for_exclusion(nest_host, nullptr)) { + ResourceMark rm; + aot_log_warning(aot)("Skipping %s: nest_host class %s is excluded", k->name()->as_C_string(), nest_host->name()->as_C_string()); + return true; + } + return false; // false == k should NOT be excluded } diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index da0be2403fa..08cd05a64ed 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -1483,6 +1483,9 @@ AOTCodeAddressTable::~AOTCodeAddressTable() { if (_extrs_addr != nullptr) { FREE_C_HEAP_ARRAY(address, _extrs_addr); } + if (_stubs_addr != nullptr) { + FREE_C_HEAP_ARRAY(address, _stubs_addr); + } if (_shared_blobs_addr != nullptr) { FREE_C_HEAP_ARRAY(address, _shared_blobs_addr); } diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 1595f19e905..fb96a580764 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -136,6 +136,7 @@ private: public: AOTCodeAddressTable() : _extrs_addr(nullptr), + _stubs_addr(nullptr), _shared_blobs_addr(nullptr), _C1_blobs_addr(nullptr), _extrs_length(0), diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 5bb37c198d0..9dd0ca1c5a9 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -206,6 +206,8 @@ void CodeBlob::purge() { if (_mutable_data != blob_end()) { os::free(_mutable_data); _mutable_data = blob_end(); // Valid not null address + _mutable_data_size = 0; + _relocation_size = 0; } if (_oop_maps != nullptr) { delete _oop_maps; diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 6a5cc0f4a40..f7c86ce58fa 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -882,6 +882,7 @@ void CodeCache::do_unloading(bool unloading_occurred) { void CodeCache::verify_clean_inline_caches() { #ifdef ASSERT + if (!VerifyInlineCaches) return; NMethodIterator iter(NMethodIterator::not_unloading); while(iter.next()) { nmethod* nm = iter.method(); diff --git a/src/hotspot/share/code/compiledIC.hpp b/src/hotspot/share/code/compiledIC.hpp index 37ca090fa9c..624c1b428de 100644 --- a/src/hotspot/share/code/compiledIC.hpp +++ b/src/hotspot/share/code/compiledIC.hpp @@ -192,13 +192,13 @@ private: static inline CompiledDirectCall* before(address return_addr) { CompiledDirectCall* st = new CompiledDirectCall(nativeCall_before(return_addr)); - st->verify(); + if (VerifyInlineCaches) st->verify(); return st; } static inline CompiledDirectCall* at(address native_call) { CompiledDirectCall* st = new CompiledDirectCall(nativeCall_at(native_call)); - st->verify(); + if (VerifyInlineCaches) st->verify(); return st; } diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index cfd31b8104a..1a843786519 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1935,6 +1935,14 @@ bool nmethod::is_maybe_on_stack() { void nmethod::inc_decompile_count() { if (!is_compiled_by_c2() && !is_compiled_by_jvmci()) return; // Could be gated by ProfileTraps, but do not bother... +#if INCLUDE_JVMCI + // Deoptimization count is used by the CompileBroker to reason about compilations + // it requests so do not pollute the count for deoptimizations in non-default (i.e. + // non-CompilerBroker) compilations. + if (is_jvmci_hosted()) { + return; + } +#endif Method* m = method(); if (m == nullptr) return; MethodData* mdo = m->method_data(); @@ -2156,6 +2164,7 @@ void nmethod::purge(bool unregister_nmethod) { } CodeCache::unregister_old_nmethod(this); + JVMCI_ONLY( _metadata_size = 0; ) CodeBlob::purge(); } @@ -3463,6 +3472,9 @@ void nmethod::decode2(outputStream* ost) const { if (use_compressed_format && ! compressed_with_comments) { const_cast(this)->print_constant_pool(st); + st->bol(); + st->cr(); + st->print_cr("Loading hsdis library failed, undisassembled code is shown in MachCode section"); //---< Open the output (Marker for post-mortem disassembler) >--- st->print_cr("[MachCode]"); const char* header = nullptr; @@ -3497,6 +3509,9 @@ void nmethod::decode2(outputStream* ost) const { if (compressed_with_comments) { const_cast(this)->print_constant_pool(st); + st->bol(); + st->cr(); + st->print_cr("Loading hsdis library failed, undisassembled code is shown in MachCode section"); //---< Open the output (Marker for post-mortem disassembler) >--- st->print_cr("[MachCode]"); while ((p < end) && (p != nullptr)) { @@ -4050,4 +4065,8 @@ const char* nmethod::jvmci_name() { } return nullptr; } + +bool nmethod::is_jvmci_hosted() const { + return jvmci_nmethod_data() != nullptr && !jvmci_nmethod_data()->is_default(); +} #endif diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 55e63c33e1a..4b63f37ed3f 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -913,6 +913,10 @@ public: JVMCINMethodData* jvmci_nmethod_data() const { return jvmci_data_size() == 0 ? nullptr : (JVMCINMethodData*) jvmci_data_begin(); } + + // Returns true if a JVMCI compiled method is non-default, + // i.e., not triggered by CompilerBroker + bool is_jvmci_hosted() const; #endif void oops_do(OopClosure* f) { oops_do(f, false); } diff --git a/src/hotspot/share/compiler/abstractDisassembler.cpp b/src/hotspot/share/compiler/abstractDisassembler.cpp index 32f37e7b513..df7781e93d5 100644 --- a/src/hotspot/share/compiler/abstractDisassembler.cpp +++ b/src/hotspot/share/compiler/abstractDisassembler.cpp @@ -350,6 +350,9 @@ void AbstractDisassembler::decode_abstract(address start, address end, outputStr outputStream* st = (ost == nullptr) ? tty : ost; + st->bol(); + st->cr(); + st->print_cr("Loading hsdis library failed, undisassembled code is shown in MachCode section"); //---< Open the output (Marker for post-mortem disassembler) >--- st->bol(); st->print_cr("[MachCode]"); diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index b5f02b2d9a2..f8711e8785a 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -1750,6 +1750,10 @@ void CompileBroker::wait_for_completion(CompileTask* task) { } } +void CompileBroker::wait_for_no_active_tasks() { + CompileTask::wait_for_no_active_tasks(); +} + /** * Initialize compiler thread(s) + compiler object(s). The postcondition * of this function is that the compiler runtimes are initialized and that diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp index 9a0e77ce4ba..e3dfe66f1ad 100644 --- a/src/hotspot/share/compiler/compileBroker.hpp +++ b/src/hotspot/share/compiler/compileBroker.hpp @@ -383,6 +383,9 @@ public: static bool is_compilation_disabled_forever() { return _should_compile_new_jobs == shutdown_compilation; } + + static void wait_for_no_active_tasks(); + static void handle_full_code_cache(CodeBlobType code_blob_type); // Ensures that warning is only printed once. static bool should_print_compiler_warning() { diff --git a/src/hotspot/share/compiler/compileTask.cpp b/src/hotspot/share/compiler/compileTask.cpp index b955a250fae..b13b13b897e 100644 --- a/src/hotspot/share/compiler/compileTask.cpp +++ b/src/hotspot/share/compiler/compileTask.cpp @@ -37,12 +37,13 @@ #include "runtime/mutexLocker.hpp" CompileTask* CompileTask::_task_free_list = nullptr; +int CompileTask::_active_tasks = 0; /** * Allocate a CompileTask, from the free list if possible. */ CompileTask* CompileTask::allocate() { - MutexLocker locker(CompileTaskAlloc_lock); + MonitorLocker locker(CompileTaskAlloc_lock); CompileTask* task = nullptr; if (_task_free_list != nullptr) { @@ -56,6 +57,7 @@ CompileTask* CompileTask::allocate() { } assert(task->is_free(), "Task must be free."); task->set_is_free(false); + _active_tasks++; return task; } @@ -63,7 +65,7 @@ CompileTask* CompileTask::allocate() { * Add a task to the free list. */ void CompileTask::free(CompileTask* task) { - MutexLocker locker(CompileTaskAlloc_lock); + MonitorLocker locker(CompileTaskAlloc_lock); if (!task->is_free()) { if ((task->_method_holder != nullptr && JNIHandles::is_weak_global_handle(task->_method_holder))) { JNIHandles::destroy_weak_global(task->_method_holder); @@ -79,6 +81,17 @@ void CompileTask::free(CompileTask* task) { task->set_is_free(true); task->set_next(_task_free_list); _task_free_list = task; + _active_tasks--; + if (_active_tasks == 0) { + locker.notify_all(); + } + } +} + +void CompileTask::wait_for_no_active_tasks() { + MonitorLocker locker(CompileTaskAlloc_lock); + while (_active_tasks > 0) { + locker.wait(); } } diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp index 166f6497f2b..168d607d803 100644 --- a/src/hotspot/share/compiler/compileTask.hpp +++ b/src/hotspot/share/compiler/compileTask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, 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 @@ -83,6 +83,7 @@ class CompileTask : public CHeapObj { private: static CompileTask* _task_free_list; + static int _active_tasks; int _compile_id; Method* _method; jobject _method_holder; @@ -123,6 +124,7 @@ class CompileTask : public CHeapObj { static CompileTask* allocate(); static void free(CompileTask* task); + static void wait_for_no_active_tasks(); int compile_id() const { return _compile_id; } Method* method() const { return _method; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index b6c18420b82..a7f147611a6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1269,6 +1269,9 @@ jint G1CollectedHeap::initialize_service_thread() { jint G1CollectedHeap::initialize() { + if (!os::is_thread_cpu_time_supported()) { + vm_exit_during_initialization("G1 requires cpu time gathering support"); + } // Necessary to satisfy locking discipline assertions. MutexLocker x(Heap_lock); @@ -2234,7 +2237,7 @@ void G1CollectedHeap::gc_epilogue(bool full) { _free_arena_memory_task->notify_new_stats(&_young_gen_card_set_stats, &_collection_set_candidates_card_set_stats); - update_parallel_gc_threads_cpu_time(); + update_perf_counter_cpu_time(); } uint G1CollectedHeap::uncommit_regions(uint region_limit) { @@ -2318,10 +2321,10 @@ void G1CollectedHeap::verify_region_attr_remset_is_tracked() { } #endif -void G1CollectedHeap::update_parallel_gc_threads_cpu_time() { +void G1CollectedHeap::update_perf_counter_cpu_time() { assert(Thread::current()->is_VM_thread(), "Must be called from VM thread to avoid races"); - if (!UsePerfData || !os::is_thread_cpu_time_supported()) { + if (!UsePerfData) { return; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index ad440577f2d..5305ef475b6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -262,7 +262,7 @@ public: void set_collection_set_candidates_stats(G1MonotonicArenaMemoryStats& stats); void set_young_gen_card_set_stats(const G1MonotonicArenaMemoryStats& stats); - void update_parallel_gc_threads_cpu_time(); + void update_perf_counter_cpu_time(); private: // Return true if an explicit GC should start a concurrent cycle instead diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 25a9b80093c..bbc5a95633d 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -71,6 +71,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" #include "runtime/orderAccess.hpp" +#include "runtime/os.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/threads.hpp" #include "utilities/align.hpp" @@ -507,8 +508,6 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _remark_weak_ref_times(), _cleanup_times(), - _accum_task_vtime(nullptr), - _concurrent_workers(nullptr), _num_concurrent_workers(0), _max_concurrent_workers(0), @@ -542,7 +541,6 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, } _tasks = NEW_C_HEAP_ARRAY(G1CMTask*, _max_num_tasks, mtGC); - _accum_task_vtime = NEW_C_HEAP_ARRAY(double, _max_num_tasks, mtGC); // so that the assertion in MarkingTaskQueue::task_queue doesn't fail _num_active_tasks = _max_num_tasks; @@ -552,8 +550,6 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _task_queues->register_queue(i, task_queue); _tasks[i] = new G1CMTask(i, this, task_queue, _region_mark_stats); - - _accum_task_vtime[i] = 0.0; } reset_at_marking_complete(); @@ -980,30 +976,23 @@ public: void work(uint worker_id) { ResourceMark rm; - double start_vtime = os::elapsedVTime(); + SuspendibleThreadSetJoiner sts_join; - { - SuspendibleThreadSetJoiner sts_join; + assert(worker_id < _cm->active_tasks(), "invariant"); - assert(worker_id < _cm->active_tasks(), "invariant"); + G1CMTask* task = _cm->task(worker_id); + task->record_start_time(); + if (!_cm->has_aborted()) { + do { + task->do_marking_step(G1ConcMarkStepDurationMillis, + true /* do_termination */, + false /* is_serial*/); - G1CMTask* task = _cm->task(worker_id); - task->record_start_time(); - if (!_cm->has_aborted()) { - do { - task->do_marking_step(G1ConcMarkStepDurationMillis, - true /* do_termination */, - false /* is_serial*/); - - _cm->do_yield_check(); - } while (!_cm->has_aborted() && task->has_aborted()); - } - task->record_end_time(); - guarantee(!task->has_aborted() || _cm->has_aborted(), "invariant"); + _cm->do_yield_check(); + } while (!_cm->has_aborted() && task->has_aborted()); } - - double end_vtime = os::elapsedVTime(); - _cm->update_accum_task_vtime(worker_id, end_vtime - start_vtime); + task->record_end_time(); + guarantee(!task->has_aborted() || _cm->has_aborted(), "invariant"); } G1CMConcurrentMarkingTask(G1ConcurrentMark* cm) : @@ -1496,7 +1485,7 @@ void G1ConcurrentMark::remark() { _remark_weak_ref_times.add((now - mark_work_end) * 1000.0); _remark_times.add((now - start) * 1000.0); - _g1h->update_parallel_gc_threads_cpu_time(); + _g1h->update_perf_counter_cpu_time(); policy->record_concurrent_mark_remark_end(); } @@ -1704,25 +1693,12 @@ void G1ConcurrentMark::weak_refs_work() { // Prefer to grow the stack until the max capacity. _global_mark_stack.set_should_grow(); - // We need at least one active thread. If reference processing - // is not multi-threaded we use the current (VMThread) thread, - // otherwise we use the workers from the G1CollectedHeap and - // we utilize all the worker threads we can. - uint active_workers = (ParallelRefProcEnabled ? _g1h->workers()->active_workers() : 1U); - active_workers = clamp(active_workers, 1u, _max_num_tasks); - - // Set the degree of MT processing here. If the discovery was done MT, - // the number of threads involved during discovery could differ from - // the number of active workers. This is OK as long as the discovered - // Reference lists are balanced (see balance_all_queues() and balance_queues()). - rp->set_active_mt_degree(active_workers); - // Parallel processing task executor. G1CMRefProcProxyTask task(rp->max_num_queues(), *_g1h, *this); ReferenceProcessorPhaseTimes pt(_gc_timer_cm, rp->max_num_queues()); // Process the weak references. - const ReferenceProcessorStats& stats = rp->process_discovered_references(task, pt); + const ReferenceProcessorStats& stats = rp->process_discovered_references(task, _g1h->workers(), pt); _gc_tracer_cm->report_gc_reference_stats(stats); pt.print_all_references(); @@ -1732,8 +1708,6 @@ void G1ConcurrentMark::weak_refs_work() { assert(has_overflown() || _global_mark_stack.is_empty(), "Mark stack should be empty (unless it has overflown)"); - - assert(rp->num_queues() == active_workers, "why not"); } if (has_overflown()) { @@ -2090,6 +2064,23 @@ void G1ConcurrentMark::abort_marking_threads() { _second_overflow_barrier_sync.abort(); } +double G1ConcurrentMark::worker_threads_cpu_time_s() { + class CountCpuTimeThreadClosure : public ThreadClosure { + public: + jlong _total_cpu_time; + + CountCpuTimeThreadClosure() : ThreadClosure(), _total_cpu_time(0) { } + + void do_thread(Thread* t) { + _total_cpu_time += os::thread_cpu_time(t); + } + } cl; + + threads_do(&cl); + + return (double)cl._total_cpu_time / NANOSECS_PER_SEC; +} + static void print_ms_time_info(const char* prefix, const char* name, NumberSeq& ns) { log_trace(gc, marking)("%s%5d %12s: total time = %8.2f s (avg = %8.2f ms).", @@ -2119,7 +2110,7 @@ void G1ConcurrentMark::print_summary_info() { log.trace(" Total stop_world time = %8.2f s.", (_remark_times.sum() + _cleanup_times.sum())/1000.0); log.trace(" Total concurrent time = %8.2f s (%8.2f s marking).", - cm_thread()->vtime_accum(), cm_thread()->vtime_mark_accum()); + cm_thread()->total_mark_cpu_time_s(), cm_thread()->worker_threads_cpu_time_s()); } void G1ConcurrentMark::threads_do(ThreadClosure* tc) const { @@ -2263,8 +2254,6 @@ bool G1CMTask::regular_clock_call() { return false; } - double curr_time_ms = os::elapsedVTime() * 1000.0; - // (4) We check whether we should yield. If we have to, then we abort. if (SuspendibleThreadSet::should_yield()) { // We should yield. To do this we abort the task. The caller is @@ -2274,7 +2263,7 @@ bool G1CMTask::regular_clock_call() { // (5) We check whether we've reached our time quota. If we have, // then we abort. - double elapsed_time_ms = curr_time_ms - _start_time_ms; + double elapsed_time_ms = (double)(os::current_thread_cpu_time() - _start_cpu_time_ns) / NANOSECS_PER_MILLISEC; if (elapsed_time_ms > _time_target_ms) { _has_timed_out = true; return false; @@ -2789,9 +2778,9 @@ void G1CMTask::handle_abort(bool is_serial, double elapsed_time_ms) { phase has visited reach a given limit. Additional invocations to the method clock have been planted in a few other strategic places too. The initial reason for the clock method was to avoid calling - vtime too regularly, as it is quite expensive. So, once it was in - place, it was natural to piggy-back all the other conditions on it - too and not constantly check them throughout the code. + cpu time gathering too regularly, as it is quite expensive. So, + once it was in place, it was natural to piggy-back all the other + conditions on it too and not constantly check them throughout the code. If do_termination is true then do_marking_step will enter its termination protocol. @@ -2814,7 +2803,7 @@ void G1CMTask::do_marking_step(double time_target_ms, bool is_serial) { assert(time_target_ms >= 1.0, "minimum granularity is 1ms"); - _start_time_ms = os::elapsedVTime() * 1000.0; + _start_cpu_time_ns = os::current_thread_cpu_time(); // If do_stealing is true then do_marking_step will attempt to // steal work from the other G1CMTasks. It only makes sense to @@ -2908,8 +2897,8 @@ void G1CMTask::do_marking_step(double time_target_ms, // closure which was statically allocated in this frame doesn't // escape it by accident. set_cm_oop_closure(nullptr); - double end_time_ms = os::elapsedVTime() * 1000.0; - double elapsed_time_ms = end_time_ms - _start_time_ms; + jlong end_cpu_time_ns = os::current_thread_cpu_time(); + double elapsed_time_ms = (double)(end_cpu_time_ns - _start_cpu_time_ns) / NANOSECS_PER_MILLISEC; // Update the step history. _step_times_ms.add(elapsed_time_ms); @@ -2932,7 +2921,7 @@ G1CMTask::G1CMTask(uint worker_id, _mark_stats_cache(mark_stats, G1RegionMarkStatsCache::RegionMarkStatsCacheSize), _calls(0), _time_target_ms(0.0), - _start_time_ms(0.0), + _start_cpu_time_ns(0), _cm_oop_closure(nullptr), _curr_region(nullptr), _finger(nullptr), diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index a4c2e94b2b1..3c3416ebcad 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -446,8 +446,6 @@ class G1ConcurrentMark : public CHeapObj { NumberSeq _remark_weak_ref_times; NumberSeq _cleanup_times; - double* _accum_task_vtime; // Accumulated task vtime - WorkerThreads* _concurrent_workers; uint _num_concurrent_workers; // The number of marking worker threads we're using uint _max_concurrent_workers; // Maximum number of marking worker threads @@ -612,16 +610,8 @@ public: // running. void abort_marking_threads(); - void update_accum_task_vtime(uint i, double vtime) { - _accum_task_vtime[i] += vtime; - } - - double all_task_accum_vtime() { - double ret = 0.0; - for (uint i = 0; i < _max_num_tasks; ++i) - ret += _accum_task_vtime[i]; - return ret; - } + // Total cpu time spent in mark worker threads in seconds. + double worker_threads_cpu_time_s(); // Attempts to steal an object from the task queues of other tasks bool try_stealing(uint worker_id, G1TaskQueueEntry& task_entry); @@ -753,8 +743,8 @@ private: // When the virtual timer reaches this time, the marking step should exit double _time_target_ms; - // Start time of the current marking step - double _start_time_ms; + // Start cpu time of the current marking step + jlong _start_cpu_time_ns; // Oop closure used for iterations over oops G1CMOopClosure* _cm_oop_closure; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index 83d547966ed..c05e7cc4be4 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -47,8 +47,6 @@ G1ConcurrentMarkThread::G1ConcurrentMarkThread(G1ConcurrentMark* cm) : ConcurrentGCThread(), - _vtime_start(0.0), - _vtime_accum(0.0), _cm(cm), _state(Idle) { @@ -113,8 +111,6 @@ class G1ConcPhaseTimer : public GCTraceConcTimeImplhas_aborted()); - _vtime_accum = (os::elapsedVTime() - _vtime_start); - - update_threads_cpu_time(); + update_perf_counter_cpu_time(); } _cm->root_regions()->cancel_scan(); } @@ -171,7 +165,7 @@ bool G1ConcurrentMarkThread::phase_clear_cld_claimed_marks() { bool G1ConcurrentMarkThread::phase_scan_root_regions() { G1ConcPhaseTimer p(_cm, "Concurrent Scan Root Regions"); _cm->scan_root_regions(); - update_threads_cpu_time(); + update_perf_counter_cpu_time(); return _cm->has_aborted(); } @@ -231,7 +225,7 @@ bool G1ConcurrentMarkThread::subphase_delay_to_keep_mmu_before_remark() { bool G1ConcurrentMarkThread::subphase_remark() { ConcurrentGCBreakpoints::at("BEFORE MARKING COMPLETED"); - update_threads_cpu_time(); + update_perf_counter_cpu_time(); VM_G1PauseRemark op; VMThread::execute(&op); return _cm->has_aborted(); @@ -241,7 +235,7 @@ bool G1ConcurrentMarkThread::phase_rebuild_and_scrub() { ConcurrentGCBreakpoints::at("AFTER REBUILD STARTED"); G1ConcPhaseTimer p(_cm, "Concurrent Rebuild Remembered Sets and Scrub Regions"); _cm->rebuild_and_scrub(); - update_threads_cpu_time(); + update_perf_counter_cpu_time(); return _cm->has_aborted(); } @@ -342,8 +336,8 @@ void G1ConcurrentMarkThread::concurrent_cycle_end(bool mark_cycle_completed) { ConcurrentGCBreakpoints::notify_active_to_idle(); } -void G1ConcurrentMarkThread::update_threads_cpu_time() { - if (!UsePerfData || !os::is_thread_cpu_time_supported()) { +void G1ConcurrentMarkThread::update_perf_counter_cpu_time() { + if (!UsePerfData) { return; } ThreadTotalCPUTimeClosure tttc(CPUTimeGroups::CPUTimeType::gc_conc_mark); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp index 55655ac2c14..5f9ec4ef404 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, 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 @@ -35,9 +35,6 @@ class G1Policy; class G1ConcurrentMarkThread: public ConcurrentGCThread { friend class VMStructs; - double _vtime_start; // Initial virtual time. - double _vtime_accum; // Accumulated virtual time. - G1ConcurrentMark* _cm; enum ServiceState : uint { @@ -88,10 +85,11 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { // Constructor G1ConcurrentMarkThread(G1ConcurrentMark* cm); - // Total virtual time so far for this thread and concurrent marking tasks. - double vtime_accum(); - // Marking virtual time so far this thread and concurrent marking tasks. - double vtime_mark_accum(); + // Total cpu time used by all marking related threads (i.e. this thread and the + // marking worker threads) in seconds. + double total_mark_cpu_time_s(); + // Cpu time used by all marking worker threads in seconds. + double worker_threads_cpu_time_s(); G1ConcurrentMark* cm() { return _cm; } @@ -110,7 +108,7 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { bool in_undo_mark() const; // Update the perf data counter for concurrent mark. - void update_threads_cpu_time(); + void update_perf_counter_cpu_time(); }; #endif // SHARE_GC_G1_G1CONCURRENTMARKTHREAD_HPP diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp index 52ebf1c6e37..254eaf62bb2 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, 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,15 +28,16 @@ #include "gc/g1/g1ConcurrentMarkThread.hpp" #include "gc/g1/g1ConcurrentMark.hpp" +#include "runtime/os.hpp" // Total virtual time so far. -inline double G1ConcurrentMarkThread::vtime_accum() { - return _vtime_accum + _cm->all_task_accum_vtime(); +inline double G1ConcurrentMarkThread::total_mark_cpu_time_s() { + return os::thread_cpu_time(this) + worker_threads_cpu_time_s(); } // Marking virtual time so far -inline double G1ConcurrentMarkThread::vtime_mark_accum() { - return _cm->all_task_accum_vtime(); +inline double G1ConcurrentMarkThread::worker_threads_cpu_time_s() { + return _cm->worker_threads_cpu_time_s(); } inline void G1ConcurrentMarkThread::set_idle() { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp index da1f85eba73..2fa19d46093 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp @@ -40,8 +40,6 @@ G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) : ConcurrentGCThread(), - _vtime_start(0.0), - _vtime_accum(0.0), _notifier(Mutex::nosafepoint, FormatBuffer<>("G1 Refine#%d", worker_id), true), _requested_active(false), _refinement_stats(), @@ -53,8 +51,6 @@ G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint } void G1ConcurrentRefineThread::run_service() { - _vtime_start = os::elapsedVTime(); - while (wait_for_completed_buffers()) { SuspendibleThreadSetJoiner sts_join; G1ConcurrentRefineStats active_stats_start = _refinement_stats; @@ -74,7 +70,7 @@ void G1ConcurrentRefineThread::run_service() { } } report_inactive("Deactivated", _refinement_stats - active_stats_start); - track_usage(); + update_perf_counter_cpu_time(); } log_debug(gc, refine)("Stopping %d", _worker_id); @@ -128,12 +124,17 @@ void G1ConcurrentRefineThread::stop_service() { activate(); } +jlong G1ConcurrentRefineThread::cpu_time() { + return os::thread_cpu_time(this); +} + // The (single) primary thread drives the controller for the refinement threads. class G1PrimaryConcurrentRefineThread final : public G1ConcurrentRefineThread { bool wait_for_completed_buffers() override; bool maybe_deactivate() override; void do_refinement_step() override; - void track_usage() override; + // Updates jstat cpu usage for all refinement threads. + void update_perf_counter_cpu_time() override; public: G1PrimaryConcurrentRefineThread(G1ConcurrentRefine* cr) : @@ -179,10 +180,8 @@ void G1PrimaryConcurrentRefineThread::do_refinement_step() { } } -void G1PrimaryConcurrentRefineThread::track_usage() { - G1ConcurrentRefineThread::track_usage(); - // The primary thread is responsible for updating the CPU time for all workers. - if (UsePerfData && os::is_thread_cpu_time_supported()) { +void G1PrimaryConcurrentRefineThread::update_perf_counter_cpu_time() { + if (UsePerfData) { ThreadTotalCPUTimeClosure tttc(CPUTimeGroups::CPUTimeType::gc_conc_refine); cr()->threads_do(&tttc); } @@ -191,6 +190,7 @@ void G1PrimaryConcurrentRefineThread::track_usage() { class G1SecondaryConcurrentRefineThread final : public G1ConcurrentRefineThread { bool wait_for_completed_buffers() override; void do_refinement_step() override; + void update_perf_counter_cpu_time() override { /* Nothing to do. The primary thread does all the work. */ } public: G1SecondaryConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) : diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp index 0711b61b194..b1e34e4b78d 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, 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 @@ -39,9 +39,6 @@ class G1ConcurrentRefineThread: public ConcurrentGCThread { friend class VMStructs; friend class G1CollectedHeap; - double _vtime_start; // Initial virtual time. - double _vtime_accum; // Accumulated virtual time. - Monitor _notifier; bool _requested_active; @@ -71,15 +68,8 @@ protected: // precondition: this is the current thread. virtual void do_refinement_step() = 0; - // Update concurrent refine threads stats. - // If we are in Primary thread, we additionally update CPU time tracking. - virtual void track_usage() { - if (os::supports_vtime()) { - _vtime_accum = (os::elapsedVTime() - _vtime_start); - } else { - _vtime_accum = 0.0; - } - }; + // Update concurrent refine threads cpu time stats. + virtual void update_perf_counter_cpu_time() = 0; // Helper for do_refinement_step implementations. Try to perform some // refinement work, limited by stop_at. Returns true if any refinement work @@ -113,8 +103,8 @@ public: return &_refinement_stats; } - // Total virtual time so far. - double vtime_accum() { return _vtime_accum; } + // Total cpu time spent in this thread so far. + jlong cpu_time(); }; #endif // SHARE_GC_G1_G1CONCURRENTREFINETHREAD_HPP diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 637995c7e52..4992df8e214 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -299,18 +299,14 @@ void G1FullCollector::phase1_mark_live_objects() { } { - uint old_active_mt_degree = reference_processor()->num_queues(); - reference_processor()->set_active_mt_degree(workers()); GCTraceTime(Debug, gc, phases) debug("Phase 1: Reference Processing", scope()->timer()); // Process reference objects found during marking. ReferenceProcessorPhaseTimes pt(scope()->timer(), reference_processor()->max_num_queues()); G1FullGCRefProcProxyTask task(*this, reference_processor()->max_num_queues()); - const ReferenceProcessorStats& stats = reference_processor()->process_discovered_references(task, pt); + const ReferenceProcessorStats& stats = reference_processor()->process_discovered_references(task, _heap->workers(), pt); scope()->tracer()->report_gc_reference_stats(stats); pt.print_all_references(); assert(marker(0)->oop_stack()->is_empty(), "Should be no oops on the stack"); - - reference_processor()->set_active_mt_degree(old_active_mt_degree); } { diff --git a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp index 38f874d5359..ec876d020ec 100644 --- a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp @@ -44,7 +44,7 @@ void G1RemSetSummary::update() { CollectData(G1RemSetSummary * summary) : _summary(summary), _counter(0) {} virtual void do_thread(Thread* t) { G1ConcurrentRefineThread* crt = static_cast(t); - _summary->set_rs_thread_vtime(_counter, crt->vtime_accum()); + _summary->set_refine_thread_cpu_time(_counter, crt->cpu_time()); _counter++; } } collector(this); @@ -53,23 +53,23 @@ void G1RemSetSummary::update() { g1h->concurrent_refine()->threads_do(&collector); } -void G1RemSetSummary::set_rs_thread_vtime(uint thread, double value) { - assert(_rs_threads_vtimes != nullptr, "just checking"); - assert(thread < _num_vtimes, "just checking"); - _rs_threads_vtimes[thread] = value; +void G1RemSetSummary::set_refine_thread_cpu_time(uint thread, jlong value) { + assert(_refine_threads_cpu_times != nullptr, "just checking"); + assert(thread < _num_refine_threads, "just checking"); + _refine_threads_cpu_times[thread] = value; } -double G1RemSetSummary::rs_thread_vtime(uint thread) const { - assert(_rs_threads_vtimes != nullptr, "just checking"); - assert(thread < _num_vtimes, "just checking"); - return _rs_threads_vtimes[thread]; +jlong G1RemSetSummary::refine_thread_cpu_time(uint thread) const { + assert(_refine_threads_cpu_times != nullptr, "just checking"); + assert(thread < _num_refine_threads, "just checking"); + return _refine_threads_cpu_times[thread]; } G1RemSetSummary::G1RemSetSummary(bool should_update) : - _num_vtimes(G1ConcRefinementThreads), - _rs_threads_vtimes(NEW_C_HEAP_ARRAY(double, _num_vtimes, mtGC)) { + _num_refine_threads(G1ConcRefinementThreads), + _refine_threads_cpu_times(NEW_C_HEAP_ARRAY(jlong, _num_refine_threads, mtGC)) { - memset(_rs_threads_vtimes, 0, sizeof(double) * _num_vtimes); + memset(_refine_threads_cpu_times, 0, sizeof(jlong) * _num_refine_threads); if (should_update) { update(); @@ -77,26 +77,26 @@ G1RemSetSummary::G1RemSetSummary(bool should_update) : } G1RemSetSummary::~G1RemSetSummary() { - FREE_C_HEAP_ARRAY(double, _rs_threads_vtimes); + FREE_C_HEAP_ARRAY(jlong, _refine_threads_cpu_times); } void G1RemSetSummary::set(G1RemSetSummary* other) { assert(other != nullptr, "just checking"); - assert(_num_vtimes == other->_num_vtimes, "just checking"); + assert(_num_refine_threads == other->_num_refine_threads, "just checking"); - memcpy(_rs_threads_vtimes, other->_rs_threads_vtimes, sizeof(double) * _num_vtimes); + memcpy(_refine_threads_cpu_times, other->_refine_threads_cpu_times, sizeof(jlong) * _num_refine_threads); } void G1RemSetSummary::subtract_from(G1RemSetSummary* other) { assert(other != nullptr, "just checking"); - assert(_num_vtimes == other->_num_vtimes, "just checking"); + assert(_num_refine_threads == other->_num_refine_threads, "just checking"); - for (uint i = 0; i < _num_vtimes; i++) { - set_rs_thread_vtime(i, other->rs_thread_vtime(i) - rs_thread_vtime(i)); + for (uint i = 0; i < _num_refine_threads; i++) { + set_refine_thread_cpu_time(i, other->refine_thread_cpu_time(i) - refine_thread_cpu_time(i)); } } -class RegionTypeCounter { +class G1PerRegionTypeRemSetCounters { private: const char* _name; @@ -130,7 +130,7 @@ private: public: - RegionTypeCounter(const char* name) : _name(name), _rs_unused_mem_size(0), _rs_mem_size(0), _cards_occupied(0), + G1PerRegionTypeRemSetCounters(const char* name) : _name(name), _rs_unused_mem_size(0), _rs_mem_size(0), _cards_occupied(0), _amount(0), _amount_tracked(0), _code_root_mem_size(0), _code_root_elems(0) { } void add(size_t rs_unused_mem_size, size_t rs_mem_size, size_t cards_occupied, @@ -180,13 +180,12 @@ public: }; -class HRRSStatsIter: public G1HeapRegionClosure { -private: - RegionTypeCounter _young; - RegionTypeCounter _humongous; - RegionTypeCounter _free; - RegionTypeCounter _old; - RegionTypeCounter _all; +class G1HeapRegionStatsClosure: public G1HeapRegionClosure { + G1PerRegionTypeRemSetCounters _young; + G1PerRegionTypeRemSetCounters _humongous; + G1PerRegionTypeRemSetCounters _free; + G1PerRegionTypeRemSetCounters _old; + G1PerRegionTypeRemSetCounters _all; size_t _max_rs_mem_sz; G1HeapRegion* _max_rs_mem_sz_region; @@ -214,7 +213,7 @@ private: G1HeapRegion* max_code_root_mem_sz_region() const { return _max_code_root_mem_sz_region; } public: - HRRSStatsIter() : _young("Young"), _humongous("Humongous"), + G1HeapRegionStatsClosure() : _young("Young"), _humongous("Humongous"), _free("Free"), _old("Old"), _all("All"), _max_rs_mem_sz(0), _max_rs_mem_sz_region(nullptr), _max_code_root_mem_sz(0), _max_code_root_mem_sz_region(nullptr), @@ -249,7 +248,7 @@ public: } size_t code_root_elems = hrrs->code_roots_list_length(); - RegionTypeCounter* current = nullptr; + G1PerRegionTypeRemSetCounters* current = nullptr; if (r->is_free()) { current = &_free; } else if (r->is_young()) { @@ -290,7 +289,7 @@ public: } - RegionTypeCounter* current = &_old; + G1PerRegionTypeRemSetCounters* current = &_old; for (G1CSetCandidateGroup* group : g1h->policy()->candidates()->from_marking_groups()) { if (group->length() > 1) { G1CardSet* group_card_set = group->card_set(); @@ -311,7 +310,7 @@ public: } void print_summary_on(outputStream* out) { - RegionTypeCounter* counters[] = { &_young, &_humongous, &_free, &_old, nullptr }; + G1PerRegionTypeRemSetCounters* counters[] = { &_young, &_humongous, &_free, &_old, nullptr }; out->print_cr(" Current rem set statistics"); out->print_cr(" Total per region rem sets sizes = %zu" @@ -319,13 +318,13 @@ public: total_rs_mem_sz(), max_rs_mem_sz(), total_rs_unused_mem_sz()); - for (RegionTypeCounter** current = &counters[0]; *current != nullptr; current++) { + for (G1PerRegionTypeRemSetCounters** current = &counters[0]; *current != nullptr; current++) { (*current)->print_rs_mem_info_on(out, total_rs_mem_sz()); } out->print_cr(" %zu occupied cards represented.", total_cards_occupied()); - for (RegionTypeCounter** current = &counters[0]; *current != nullptr; current++) { + for (G1PerRegionTypeRemSetCounters** current = &counters[0]; *current != nullptr; current++) { (*current)->print_cards_occupied_info_on(out, total_cards_occupied()); } @@ -360,13 +359,13 @@ public: proper_unit_for_byte_size(total_code_root_mem_sz()), byte_size_in_proper_unit(max_code_root_rem_set->code_roots_mem_size()), proper_unit_for_byte_size(max_code_root_rem_set->code_roots_mem_size())); - for (RegionTypeCounter** current = &counters[0]; *current != nullptr; current++) { + for (G1PerRegionTypeRemSetCounters** current = &counters[0]; *current != nullptr; current++) { (*current)->print_code_root_mem_info_on(out, total_code_root_mem_sz()); } out->print_cr(" %zu code roots represented.", total_code_root_elems()); - for (RegionTypeCounter** current = &counters[0]; *current != nullptr; current++) { + for (G1PerRegionTypeRemSetCounters** current = &counters[0]; *current != nullptr; current++) { (*current)->print_code_root_elems_info_on(out, total_code_root_elems()); } @@ -383,12 +382,12 @@ void G1RemSetSummary::print_on(outputStream* out, bool show_thread_times) { if (show_thread_times) { out->print_cr(" Concurrent refinement threads times (s)"); out->print(" "); - for (uint i = 0; i < _num_vtimes; i++) { - out->print(" %5.2f", rs_thread_vtime(i)); + for (uint i = 0; i < _num_refine_threads; i++) { + out->print(" %5.2f", (double)refine_thread_cpu_time(i) / NANOSECS_PER_SEC); } out->cr(); } - HRRSStatsIter blk; + G1HeapRegionStatsClosure blk; G1CollectedHeap::heap()->heap_region_iterate(&blk); blk.do_cset_groups(); blk.print_summary_on(out); diff --git a/src/hotspot/share/gc/g1/g1RemSetSummary.hpp b/src/hotspot/share/gc/g1/g1RemSetSummary.hpp index f3bb7d3adcc..373f38952c8 100644 --- a/src/hotspot/share/gc/g1/g1RemSetSummary.hpp +++ b/src/hotspot/share/gc/g1/g1RemSetSummary.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, 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 @@ -31,15 +31,14 @@ class G1RemSet; -// A G1RemSetSummary manages statistical information about the G1RemSet - +// A G1RemSetSummary manages statistical information about the remembered set. class G1RemSetSummary { - size_t _num_vtimes; - double* _rs_threads_vtimes; + size_t _num_refine_threads; + jlong* _refine_threads_cpu_times; - void set_rs_thread_vtime(uint thread, double value); + void set_refine_thread_cpu_time(uint thread, jlong value); - // update this summary with current data from various places + // Update this summary with current data from various places. void update(); public: @@ -47,14 +46,14 @@ public: ~G1RemSetSummary(); - // set the counters in this summary to the values of the others + // Set the counters in this summary to the values of the others. void set(G1RemSetSummary* other); - // subtract all counters from the other summary, and set them in the current + // Subtract all counters from the other summary, and set them in the current. void subtract_from(G1RemSetSummary* other); void print_on(outputStream* out, bool show_thread_times); - double rs_thread_vtime(uint thread) const; + jlong refine_thread_cpu_time(uint thread) const; }; #endif // SHARE_GC_G1_G1REMSETSUMMARY_HPP diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.cpp b/src/hotspot/share/gc/g1/g1ServiceThread.cpp index 22675ec2a64..3c96123d14f 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.cpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.cpp @@ -119,7 +119,7 @@ G1ServiceTask* G1ServiceThread::wait_for_task() { void G1ServiceThread::run_task(G1ServiceTask* task) { jlong start = os::elapsed_counter(); - double vstart = os::elapsedVTime(); + jlong start_cpu_time_ns = os::thread_cpu_time(this); assert(task->time() <= start, "task run early: " JLONG_FORMAT " > " JLONG_FORMAT, @@ -130,12 +130,12 @@ void G1ServiceThread::run_task(G1ServiceTask* task) { task->execute(); - update_thread_cpu_time(); + update_perf_counter_cpu_time(); log_debug(gc, task)("G1 Service Thread (%s) (run: %1.3fms) (cpu: %1.3fms)", task->name(), TimeHelper::counter_to_millis(os::elapsed_counter() - start), - (os::elapsedVTime() - vstart) * MILLIUNITS); + (double)(os::thread_cpu_time(this) - start_cpu_time_ns) / NANOSECS_PER_MILLISEC); } void G1ServiceThread::run_service() { @@ -153,8 +153,8 @@ void G1ServiceThread::stop_service() { ml.notify(); } -void G1ServiceThread::update_thread_cpu_time() { - if (UsePerfData && os::is_thread_cpu_time_supported()) { +void G1ServiceThread::update_perf_counter_cpu_time() { + if (UsePerfData) { ThreadTotalCPUTimeClosure tttc(CPUTimeGroups::CPUTimeType::gc_service); tttc.do_thread(this); } diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.hpp b/src/hotspot/share/gc/g1/g1ServiceThread.hpp index cfa7abb6552..4ed9c241562 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.hpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.hpp @@ -121,7 +121,7 @@ class G1ServiceThread: public ConcurrentGCThread { void schedule(G1ServiceTask* task, jlong delay, bool notify); // Update the perf data counter for service thread. - void update_thread_cpu_time(); + void update_perf_counter_cpu_time(); public: G1ServiceThread(); diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index b56d9991acb..435c001799f 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -971,12 +971,9 @@ void G1YoungCollector::process_discovered_references(G1ParScanThreadStateSet* pe ReferenceProcessor* rp = ref_processor_stw(); assert(rp->discovery_enabled(), "should have been enabled"); - uint no_of_gc_workers = workers()->active_workers(); - rp->set_active_mt_degree(no_of_gc_workers); - G1STWRefProcProxyTask task(rp->max_num_queues(), *_g1h, *per_thread_states, *task_queues()); ReferenceProcessorPhaseTimes& pt = *phase_times()->ref_phase_times(); - ReferenceProcessorStats stats = rp->process_discovered_references(task, pt); + ReferenceProcessorStats stats = rp->process_discovered_references(task, _g1h->workers(), pt); gc_tracer_stw()->report_gc_reference_stats(stats); diff --git a/src/hotspot/share/gc/parallel/parallel_globals.hpp b/src/hotspot/share/gc/parallel/parallel_globals.hpp index e3b9660b069..83b378d5bbe 100644 --- a/src/hotspot/share/gc/parallel/parallel_globals.hpp +++ b/src/hotspot/share/gc/parallel/parallel_globals.hpp @@ -41,7 +41,7 @@ "for a system GC") \ \ product(bool, PSChunkLargeArrays, true, \ - "Process large arrays in chunks") + "(Deprecated) Process large arrays in chunks") // end of GC_PARALLEL_FLAGS diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index 79a0b898a6b..50f22a5ef9a 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -1310,9 +1310,8 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) { ReferenceProcessorStats stats; ReferenceProcessorPhaseTimes pt(&_gc_timer, ref_processor()->max_num_queues()); - ref_processor()->set_active_mt_degree(active_gc_threads); ParallelCompactRefProcProxyTask task(ref_processor()->max_num_queues()); - stats = ref_processor()->process_discovered_references(task, pt); + stats = ref_processor()->process_discovered_references(task, &ParallelScavengeHeap::heap()->workers(), pt); gc_tracer->report_gc_reference_stats(stats); pt.print_all_references(); diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index 7729b3e2006..9816655603c 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -294,7 +294,7 @@ public: } PSThreadRootsTaskClosure closure(worker_id); - Threads::possibly_parallel_threads_do(true /* is_par */, &closure); + Threads::possibly_parallel_threads_do(_active_workers > 1 /* is_par */, &closure); // Scavenge OopStorages { @@ -410,12 +410,11 @@ bool PSScavenge::invoke(bool clear_soft_refs) { { GCTraceTime(Debug, gc, phases) tm("Reference Processing", &_gc_timer); - reference_processor()->set_active_mt_degree(active_workers); ReferenceProcessorStats stats; ReferenceProcessorPhaseTimes pt(&_gc_timer, reference_processor()->max_num_queues()); ParallelScavengeRefProcProxyTask task(reference_processor()->max_num_queues()); - stats = reference_processor()->process_discovered_references(task, pt); + stats = reference_processor()->process_discovered_references(task, &ParallelScavengeHeap::heap()->workers(), pt); _gc_tracer.report_gc_reference_stats(stats); pt.print_all_references(); diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index cb36207ff47..4f45821a889 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -631,7 +631,7 @@ bool DefNewGeneration::collect(bool clear_all_soft_refs) { ReferenceProcessor* rp = ref_processor(); ReferenceProcessorPhaseTimes pt(_gc_timer, rp->max_num_queues()); SerialGCRefProcProxyTask task(is_alive, keep_alive, evacuate_followers); - const ReferenceProcessorStats& stats = rp->process_discovered_references(task, pt); + const ReferenceProcessorStats& stats = rp->process_discovered_references(task, nullptr, pt); _gc_tracer->report_gc_reference_stats(stats); _gc_tracer->report_tenuring_threshold(tenuring_threshold()); pt.print_all_references(); diff --git a/src/hotspot/share/gc/serial/serialFullGC.cpp b/src/hotspot/share/gc/serial/serialFullGC.cpp index 15468704547..da5b8ba53a0 100644 --- a/src/hotspot/share/gc/serial/serialFullGC.cpp +++ b/src/hotspot/share/gc/serial/serialFullGC.cpp @@ -498,7 +498,7 @@ void SerialFullGC::phase1_mark(bool clear_all_softrefs) { ReferenceProcessorPhaseTimes pt(_gc_timer, ref_processor()->max_num_queues()); SerialGCRefProcProxyTask task(is_alive, keep_alive, follow_stack_closure); - const ReferenceProcessorStats& stats = ref_processor()->process_discovered_references(task, pt); + const ReferenceProcessorStats& stats = ref_processor()->process_discovered_references(task, nullptr, pt); pt.print_all_references(); gc_tracer()->report_gc_reference_stats(stats); } diff --git a/src/hotspot/share/gc/shared/referenceProcessor.cpp b/src/hotspot/share/gc/shared/referenceProcessor.cpp index 0ee4ac134c2..e5d1bd4bec1 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.cpp @@ -179,6 +179,7 @@ void ReferenceProcessor::verify_total_count_zero(DiscoveredList lists[], const c #endif ReferenceProcessorStats ReferenceProcessor::process_discovered_references(RefProcProxyTask& proxy_task, + WorkerThreads* workers, ReferenceProcessorPhaseTimes& phase_times) { double start_time = os::elapsedTime(); @@ -197,17 +198,17 @@ ReferenceProcessorStats ReferenceProcessor::process_discovered_references(RefPro { RefProcTotalPhaseTimesTracker tt(SoftWeakFinalRefsPhase, &phase_times); - process_soft_weak_final_refs(proxy_task, phase_times); + process_soft_weak_final_refs(proxy_task, workers, phase_times); } { RefProcTotalPhaseTimesTracker tt(KeepAliveFinalRefsPhase, &phase_times); - process_final_keep_alive(proxy_task, phase_times); + process_final_keep_alive(proxy_task, workers, phase_times); } { RefProcTotalPhaseTimesTracker tt(PhantomRefsPhase, &phase_times); - process_phantom_refs(proxy_task, phase_times); + process_phantom_refs(proxy_task, workers, phase_times); } phase_times.set_total_time_ms((os::elapsedTime() - start_time) * 1000); @@ -619,8 +620,7 @@ void ReferenceProcessor::maybe_balance_queues(DiscoveredList refs_lists[]) { // Move entries from all queues[0, 1, ..., _max_num_q-1] to // queues[0, 1, ..., _num_q-1] because only the first _num_q // corresponding to the active workers will be processed. -void ReferenceProcessor::balance_queues(DiscoveredList ref_lists[]) -{ +void ReferenceProcessor::balance_queues(DiscoveredList ref_lists[]) { // calculate total length size_t total_refs = 0; log_develop_trace(gc, ref)("Balance ref_lists "); @@ -633,60 +633,60 @@ void ReferenceProcessor::balance_queues(DiscoveredList ref_lists[]) size_t avg_refs = total_refs / _num_queues + 1; uint to_idx = 0; for (uint from_idx = 0; from_idx < _max_num_queues; from_idx++) { - bool move_all = false; + size_t from_len = ref_lists[from_idx].length(); + + size_t remaining_to_move; if (from_idx >= _num_queues) { - move_all = ref_lists[from_idx].length() > 0; + // Move all + remaining_to_move = from_len; + } else { + // Move those above avg_refs + remaining_to_move = from_len > avg_refs + ? from_len - avg_refs + : 0; } - while ((ref_lists[from_idx].length() > avg_refs) || - move_all) { + + while (remaining_to_move > 0) { assert(to_idx < _num_queues, "Sanity Check!"); - if (ref_lists[to_idx].length() < avg_refs) { - // move superfluous refs - size_t refs_to_move; - // Move all the Ref's if the from queue will not be processed. - if (move_all) { - refs_to_move = MIN2(ref_lists[from_idx].length(), - avg_refs - ref_lists[to_idx].length()); - } else { - refs_to_move = MIN2(ref_lists[from_idx].length() - avg_refs, - avg_refs - ref_lists[to_idx].length()); - } - assert(refs_to_move > 0, "otherwise the code below will fail"); - - oop move_head = ref_lists[from_idx].head(); - oop move_tail = move_head; - oop new_head = move_head; - // find an element to split the list on - for (size_t j = 0; j < refs_to_move; ++j) { - move_tail = new_head; - new_head = java_lang_ref_Reference::discovered(new_head); - } - - // Add the chain to the to list. - if (ref_lists[to_idx].head() == nullptr) { - // to list is empty. Make a loop at the end. - java_lang_ref_Reference::set_discovered_raw(move_tail, move_tail); - } else { - java_lang_ref_Reference::set_discovered_raw(move_tail, ref_lists[to_idx].head()); - } - ref_lists[to_idx].set_head(move_head); - ref_lists[to_idx].inc_length(refs_to_move); - - // Remove the chain from the from list. - if (move_tail == new_head) { - // We found the end of the from list. - ref_lists[from_idx].set_head(nullptr); - } else { - ref_lists[from_idx].set_head(new_head); - } - ref_lists[from_idx].dec_length(refs_to_move); - if (ref_lists[from_idx].length() == 0) { - break; - } - } else { - to_idx = (to_idx + 1) % _num_queues; + size_t to_len = ref_lists[to_idx].length(); + if (to_len >= avg_refs) { + // this list is full enough; move on to next + to_idx++; + continue; } + size_t refs_to_move = MIN2(remaining_to_move, avg_refs - to_len); + assert(refs_to_move > 0, "otherwise the code below will fail"); + + oop move_head = ref_lists[from_idx].head(); + oop move_tail = move_head; + oop new_head = move_head; + // find an element to split the list on + for (size_t j = 0; j < refs_to_move; ++j) { + move_tail = new_head; + new_head = java_lang_ref_Reference::discovered(new_head); + } + + // Add the chain to the to list. + if (ref_lists[to_idx].head() == nullptr) { + // to list is empty. Make a loop at the end. + java_lang_ref_Reference::set_discovered_raw(move_tail, move_tail); + } else { + java_lang_ref_Reference::set_discovered_raw(move_tail, ref_lists[to_idx].head()); + } + ref_lists[to_idx].set_head(move_head); + ref_lists[to_idx].inc_length(refs_to_move); + + // Remove the chain from the from list. + if (move_tail == new_head) { + // We found the end of the from list. + ref_lists[from_idx].set_head(nullptr); + } else { + ref_lists[from_idx].set_head(new_head); + } + ref_lists[from_idx].dec_length(refs_to_move); + + remaining_to_move -= refs_to_move; } } #ifdef ASSERT @@ -699,7 +699,7 @@ void ReferenceProcessor::balance_queues(DiscoveredList ref_lists[]) #endif } -void ReferenceProcessor::run_task(RefProcTask& task, RefProcProxyTask& proxy_task, bool marks_oops_alive) { +void ReferenceProcessor::run_task(RefProcTask& task, RefProcProxyTask& proxy_task, WorkerThreads* workers, bool marks_oops_alive) { log_debug(gc, ref)("ReferenceProcessor::execute queues: %d, %s, marks_oops_alive: %s", num_queues(), processing_is_mt() ? "RefProcThreadModel::Multi" : "RefProcThreadModel::Single", @@ -707,7 +707,6 @@ void ReferenceProcessor::run_task(RefProcTask& task, RefProcProxyTask& proxy_tas proxy_task.prepare_run_task(task, num_queues(), processing_is_mt() ? RefProcThreadModel::Multi : RefProcThreadModel::Single, marks_oops_alive); if (processing_is_mt()) { - WorkerThreads* workers = Universe::heap()->safepoint_workers(); assert(workers != nullptr, "can not dispatch multi threaded without workers"); assert(workers->active_workers() >= num_queues(), "Ergonomically chosen workers(%u) should be less than or equal to active workers(%u)", @@ -720,7 +719,12 @@ void ReferenceProcessor::run_task(RefProcTask& task, RefProcProxyTask& proxy_tas } } +static uint num_active_workers(WorkerThreads* workers) { + return workers != nullptr ? workers->active_workers() : 1; +} + void ReferenceProcessor::process_soft_weak_final_refs(RefProcProxyTask& proxy_task, + WorkerThreads* workers, ReferenceProcessorPhaseTimes& phase_times) { size_t const num_soft_refs = phase_times.ref_discovered(REF_SOFT); @@ -733,7 +737,7 @@ void ReferenceProcessor::process_soft_weak_final_refs(RefProcProxyTask& proxy_ta return; } - RefProcMTDegreeAdjuster a(this, SoftWeakFinalRefsPhase, num_total_refs); + RefProcMTDegreeAdjuster a(this, SoftWeakFinalRefsPhase, num_active_workers(workers), num_total_refs); if (processing_is_mt()) { RefProcBalanceQueuesTimeTracker tt(SoftWeakFinalRefsPhase, &phase_times); @@ -747,7 +751,7 @@ void ReferenceProcessor::process_soft_weak_final_refs(RefProcProxyTask& proxy_ta log_reflist("SoftWeakFinalRefsPhase Final before", _discoveredFinalRefs, _max_num_queues); RefProcSoftWeakFinalPhaseTask phase_task(*this, &phase_times); - run_task(phase_task, proxy_task, false); + run_task(phase_task, proxy_task, workers, false); verify_total_count_zero(_discoveredSoftRefs, "SoftReference"); verify_total_count_zero(_discoveredWeakRefs, "WeakReference"); @@ -755,6 +759,7 @@ void ReferenceProcessor::process_soft_weak_final_refs(RefProcProxyTask& proxy_ta } void ReferenceProcessor::process_final_keep_alive(RefProcProxyTask& proxy_task, + WorkerThreads* workers, ReferenceProcessorPhaseTimes& phase_times) { size_t const num_final_refs = phase_times.ref_discovered(REF_FINAL); @@ -764,7 +769,7 @@ void ReferenceProcessor::process_final_keep_alive(RefProcProxyTask& proxy_task, return; } - RefProcMTDegreeAdjuster a(this, KeepAliveFinalRefsPhase, num_final_refs); + RefProcMTDegreeAdjuster a(this, KeepAliveFinalRefsPhase, num_active_workers(workers), num_final_refs); if (processing_is_mt()) { RefProcBalanceQueuesTimeTracker tt(KeepAliveFinalRefsPhase, &phase_times); @@ -773,12 +778,13 @@ void ReferenceProcessor::process_final_keep_alive(RefProcProxyTask& proxy_task, // Traverse referents of final references and keep them and followers alive. RefProcKeepAliveFinalPhaseTask phase_task(*this, &phase_times); - run_task(phase_task, proxy_task, true); + run_task(phase_task, proxy_task, workers, true); verify_total_count_zero(_discoveredFinalRefs, "FinalReference"); } void ReferenceProcessor::process_phantom_refs(RefProcProxyTask& proxy_task, + WorkerThreads* workers, ReferenceProcessorPhaseTimes& phase_times) { size_t const num_phantom_refs = phase_times.ref_discovered(REF_PHANTOM); @@ -788,7 +794,7 @@ void ReferenceProcessor::process_phantom_refs(RefProcProxyTask& proxy_task, return; } - RefProcMTDegreeAdjuster a(this, PhantomRefsPhase, num_phantom_refs); + RefProcMTDegreeAdjuster a(this, PhantomRefsPhase, num_active_workers(workers), num_phantom_refs); if (processing_is_mt()) { RefProcBalanceQueuesTimeTracker tt(PhantomRefsPhase, &phase_times); @@ -798,7 +804,7 @@ void ReferenceProcessor::process_phantom_refs(RefProcProxyTask& proxy_task, log_reflist("PhantomRefsPhase Phantom before", _discoveredPhantomRefs, _max_num_queues); RefProcPhantomPhaseTask phase_task(*this, &phase_times); - run_task(phase_task, proxy_task, false); + run_task(phase_task, proxy_task, workers, false); verify_total_count_zero(_discoveredPhantomRefs, "PhantomReference"); } @@ -1137,10 +1143,11 @@ bool RefProcMTDegreeAdjuster::use_max_threads(RefProcPhases phase) const { RefProcMTDegreeAdjuster::RefProcMTDegreeAdjuster(ReferenceProcessor* rp, RefProcPhases phase, + uint num_active_workers, size_t ref_count): _rp(rp), _saved_num_queues(_rp->num_queues()) { - uint workers = ergo_proc_thread_count(ref_count, _rp->num_queues(), phase); + uint workers = ergo_proc_thread_count(ref_count, num_active_workers, phase); _rp->set_active_mt_degree(workers); } diff --git a/src/hotspot/share/gc/shared/referenceProcessor.hpp b/src/hotspot/share/gc/shared/referenceProcessor.hpp index 4be8f3f6f16..a29ee7fca86 100644 --- a/src/hotspot/share/gc/shared/referenceProcessor.hpp +++ b/src/hotspot/share/gc/shared/referenceProcessor.hpp @@ -185,6 +185,7 @@ public: class ReferenceProcessor : public ReferenceDiscoverer { friend class RefProcTask; friend class RefProcKeepAliveFinalPhaseTask; + friend class RefProcMTDegreeAdjuster; public: // Names of sub-phases of reference processing. Indicates the type of the reference // processed and the associated phase number at the end. @@ -253,19 +254,22 @@ private: DiscoveredList* _discoveredFinalRefs; DiscoveredList* _discoveredPhantomRefs; - void run_task(RefProcTask& task, RefProcProxyTask& proxy_task, bool marks_oops_alive); + void run_task(RefProcTask& task, RefProcProxyTask& proxy_task, WorkerThreads* threads, bool marks_oops_alive); // Drop Soft/Weak/Final references with a null or live referent, and clear // and enqueue non-Final references. void process_soft_weak_final_refs(RefProcProxyTask& proxy_task, + WorkerThreads* workers, ReferenceProcessorPhaseTimes& phase_times); // Keep alive followers of Final references, and enqueue. void process_final_keep_alive(RefProcProxyTask& proxy_task, + WorkerThreads* workers, ReferenceProcessorPhaseTimes& phase_times); // Drop and keep alive live Phantom references, or clear and enqueue if dead. void process_phantom_refs(RefProcProxyTask& proxy_task, + WorkerThreads* workers, ReferenceProcessorPhaseTimes& phase_times); // Work methods used by the process_* methods. All methods return the number of @@ -292,12 +296,14 @@ private: _always_clear_soft_ref_policy : _default_soft_ref_policy; _current_soft_ref_policy->setup(); // snapshot the policy threshold } + + void set_active_mt_degree(uint v); + public: static int number_of_subclasses_of_ref() { return (REF_PHANTOM - REF_NONE); } uint num_queues() const { return _num_queues; } uint max_num_queues() const { return _max_num_queues; } - void set_active_mt_degree(uint v); void start_discovery(bool always_clear) { enable_discovery(); @@ -416,6 +422,7 @@ public: // Process references found during GC (called by the garbage collector) ReferenceProcessorStats process_discovered_references(RefProcProxyTask& proxy_task, + WorkerThreads* workers, ReferenceProcessorPhaseTimes& phase_times); // If a discovery is in process that is being superseded, abandon it: all @@ -589,6 +596,7 @@ class RefProcMTDegreeAdjuster : public StackObj { public: RefProcMTDegreeAdjuster(ReferenceProcessor* rp, RefProcPhases phase, + uint num_active_workers, size_t ref_count); ~RefProcMTDegreeAdjuster(); }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index fda97c4836e..2f264cae70f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -1145,22 +1145,22 @@ void ShenandoahConcurrentGC::op_update_refs() { ShenandoahHeap::heap()->update_heap_references(true /*concurrent*/); } -class ShenandoahUpdateThreadClosure : public HandshakeClosure { +class ShenandoahUpdateThreadHandshakeClosure : public HandshakeClosure { private: // This closure runs when thread is stopped for handshake, which means // we can use non-concurrent closure here, as long as it only updates // locations modified by the thread itself, i.e. stack locations. ShenandoahNonConcUpdateRefsClosure _cl; public: - ShenandoahUpdateThreadClosure(); + ShenandoahUpdateThreadHandshakeClosure(); void do_thread(Thread* thread); }; -ShenandoahUpdateThreadClosure::ShenandoahUpdateThreadClosure() : +ShenandoahUpdateThreadHandshakeClosure::ShenandoahUpdateThreadHandshakeClosure() : HandshakeClosure("Shenandoah Update Thread Roots") { } -void ShenandoahUpdateThreadClosure::do_thread(Thread* thread) { +void ShenandoahUpdateThreadHandshakeClosure::do_thread(Thread* thread) { if (thread->is_Java_thread()) { JavaThread* jt = JavaThread::cast(thread); ResourceMark rm; @@ -1169,7 +1169,7 @@ void ShenandoahUpdateThreadClosure::do_thread(Thread* thread) { } void ShenandoahConcurrentGC::op_update_thread_roots() { - ShenandoahUpdateThreadClosure cl; + ShenandoahUpdateThreadHandshakeClosure cl; Handshake::execute(&cl); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 55dfb2e8de4..50881a50778 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1245,9 +1245,9 @@ public: } }; -class ShenandoahGCStatePropagator : public HandshakeClosure { +class ShenandoahGCStatePropagatorHandshakeClosure : public HandshakeClosure { public: - explicit ShenandoahGCStatePropagator(char gc_state) : + explicit ShenandoahGCStatePropagatorHandshakeClosure(char gc_state) : HandshakeClosure("Shenandoah GC State Change"), _gc_state(gc_state) {} @@ -1258,9 +1258,9 @@ private: char _gc_state; }; -class ShenandoahPrepareForUpdateRefs : public HandshakeClosure { +class ShenandoahPrepareForUpdateRefsHandshakeClosure : public HandshakeClosure { public: - explicit ShenandoahPrepareForUpdateRefs(char gc_state) : + explicit ShenandoahPrepareForUpdateRefsHandshakeClosure(char gc_state) : HandshakeClosure("Shenandoah Prepare for Update Refs"), _retire(ResizeTLAB), _propagator(gc_state) {} @@ -1272,7 +1272,7 @@ public: } private: ShenandoahRetireGCLABClosure _retire; - ShenandoahGCStatePropagator _propagator; + ShenandoahGCStatePropagatorHandshakeClosure _propagator; }; void ShenandoahHeap::evacuate_collection_set(bool concurrent) { @@ -1295,7 +1295,7 @@ void ShenandoahHeap::concurrent_prepare_for_update_refs() { } // This will propagate the gc state and retire gclabs and plabs for threads that require it. - ShenandoahPrepareForUpdateRefs prepare_for_update_refs(_gc_state.raw_value()); + ShenandoahPrepareForUpdateRefsHandshakeClosure prepare_for_update_refs(_gc_state.raw_value()); // The handshake won't touch worker threads (or control thread, or VM thread), so do those separately. Threads::non_java_threads_do(&prepare_for_update_refs); @@ -1327,7 +1327,7 @@ void ShenandoahHeap::concurrent_final_roots(HandshakeClosure* handshake_closure) set_gc_state_concurrent(WEAK_ROOTS, false); } - ShenandoahGCStatePropagator propagator(_gc_state.raw_value()); + ShenandoahGCStatePropagatorHandshakeClosure propagator(_gc_state.raw_value()); Threads::non_java_threads_do(&propagator); if (handshake_closure == nullptr) { Handshake::execute(&propagator); @@ -2020,14 +2020,14 @@ void ShenandoahHeap::parallel_heap_region_iterate(ShenandoahHeapRegionClosure* b } } -class ShenandoahRendezvousClosure : public HandshakeClosure { +class ShenandoahRendezvousHandshakeClosure : public HandshakeClosure { public: - inline ShenandoahRendezvousClosure(const char* name) : HandshakeClosure(name) {} + inline ShenandoahRendezvousHandshakeClosure(const char* name) : HandshakeClosure(name) {} inline void do_thread(Thread* thread) {} }; void ShenandoahHeap::rendezvous_threads(const char* name) { - ShenandoahRendezvousClosure cl(name); + ShenandoahRendezvousHandshakeClosure cl(name); Handshake::execute(&cl); } @@ -2069,7 +2069,7 @@ void ShenandoahHeap::prepare_update_heap_references() { void ShenandoahHeap::propagate_gc_state_to_all_threads() { assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must be at Shenandoah safepoint"); if (_gc_state_changed) { - ShenandoahGCStatePropagator propagator(_gc_state.raw_value()); + ShenandoahGCStatePropagatorHandshakeClosure propagator(_gc_state.raw_value()); Threads::threads_do(&propagator); _gc_state_changed = false; } diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp index d1646da7604..482b4ddd75f 100644 --- a/src/hotspot/share/gc/z/zMark.cpp +++ b/src/hotspot/share/gc/z/zMark.cpp @@ -532,13 +532,13 @@ bool ZMark::try_steal(ZMarkContext* context) { return try_steal_local(context) || try_steal_global(context); } -class ZMarkFlushStacksClosure : public HandshakeClosure { +class ZMarkFlushStacksHandshakeClosure : public HandshakeClosure { private: ZMark* const _mark; bool _flushed; public: - ZMarkFlushStacksClosure(ZMark* mark) + ZMarkFlushStacksHandshakeClosure(ZMark* mark) : HandshakeClosure("ZMarkFlushStacks"), _mark(mark), _flushed(false) {} @@ -585,7 +585,7 @@ public: }; bool ZMark::flush() { - ZMarkFlushStacksClosure cl(this); + ZMarkFlushStacksHandshakeClosure cl(this); VM_ZMarkFlushOperation vm_cl(&cl); Handshake::execute(&cl); VMThread::execute(&vm_cl); @@ -956,7 +956,7 @@ bool ZMark::try_end() { } // Try end marking - ZMarkFlushStacksClosure cl(this); + ZMarkFlushStacksHandshakeClosure cl(this); Threads::non_java_threads_do(&cl); // Check if non-java threads have any pending marking diff --git a/src/hotspot/share/jfr/jni/jfrUpcalls.cpp b/src/hotspot/share/jfr/jni/jfrUpcalls.cpp index 2e814cef875..a139dadc26d 100644 --- a/src/hotspot/share/jfr/jni/jfrUpcalls.cpp +++ b/src/hotspot/share/jfr/jni/jfrUpcalls.cpp @@ -237,7 +237,7 @@ ClassFileStream* JfrUpcalls::on_method_trace(InstanceKlass* ik, const ClassFileS ModuleEntry* module_entry = ik->module(); oop module = nullptr; if (module_entry != nullptr) { - module = module_entry->module(); + module = module_entry->module_oop(); } instanceHandle module_handle(THREAD, (instanceOop)module); diff --git a/src/hotspot/share/jvmci/jvmciCompiler.cpp b/src/hotspot/share/jvmci/jvmciCompiler.cpp index 924add42f99..659297973e5 100644 --- a/src/hotspot/share/jvmci/jvmciCompiler.cpp +++ b/src/hotspot/share/jvmci/jvmciCompiler.cpp @@ -144,7 +144,7 @@ bool JVMCICompiler::force_comp_at_level_simple(const methodHandle& method) { if (excludeModules.not_null()) { ModuleEntry* moduleEntry = method->method_holder()->module(); for (int i = 0; i < excludeModules->length(); i++) { - if (excludeModules->obj_at(i) == moduleEntry->module()) { + if (excludeModules->obj_at(i) == moduleEntry->module_oop()) { return true; } } diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index 24ea4936822..ad848998823 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -746,6 +746,7 @@ JVM_END void JVMCINMethodData::initialize(int nmethod_mirror_index, int nmethod_entry_patch_offset, const char* nmethod_mirror_name, + bool is_default, FailedSpeculation** failed_speculations) { _failed_speculations = failed_speculations; @@ -753,16 +754,17 @@ void JVMCINMethodData::initialize(int nmethod_mirror_index, guarantee(nmethod_entry_patch_offset != -1, "missing entry barrier"); _nmethod_entry_patch_offset = nmethod_entry_patch_offset; if (nmethod_mirror_name != nullptr) { - _has_name = true; + _properties.bits._has_name = 1; char* dest = (char*) name(); strcpy(dest, nmethod_mirror_name); } else { - _has_name = false; + _properties.bits._has_name = 0; } + _properties.bits._is_default = is_default; } void JVMCINMethodData::copy(JVMCINMethodData* data) { - initialize(data->_nmethod_mirror_index, data->_nmethod_entry_patch_offset, data->name(), data->_failed_speculations); + initialize(data->_nmethod_mirror_index, data->_nmethod_entry_patch_offset, data->name(), data->_properties.bits._is_default, data->_failed_speculations); } void JVMCINMethodData::add_failed_speculation(nmethod* nm, jlong speculation) { @@ -2130,7 +2132,7 @@ JVMCI::CodeInstallResult JVMCIRuntime::register_method(JVMCIEnv* JVMCIENV, JVMCICompileState* compile_state = JVMCIENV->compile_state(); bool failing_dep_is_call_site; result = validate_compile_task_dependencies(dependencies, compile_state, &failure_detail, failing_dep_is_call_site); - if (result != JVMCI::ok) { + if (install_default && result != JVMCI::ok) { // While not a true deoptimization, it is a preemptive decompile. MethodData* mdp = method()->method_data(); if (mdp != nullptr && !failing_dep_is_call_site) { @@ -2151,6 +2153,7 @@ JVMCI::CodeInstallResult JVMCIRuntime::register_method(JVMCIEnv* JVMCIENV, JVMCINMethodData* data = JVMCINMethodData::create(nmethod_mirror_index, nmethod_entry_patch_offset, nmethod_mirror_name, + install_default, failed_speculations); nm = nmethod::new_nmethod(method, compile_id, diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp index 95c7d32f928..2bb223d8376 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.hpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, 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 @@ -48,9 +48,19 @@ class MetadataHandles; class JVMCINMethodData : public ResourceObj { friend class JVMCIVMStructs; - // Is HotSpotNmethod.name non-null? If so, the value is - // embedded in the end of this object. - bool _has_name; + union JVMCINMethodProperties { + uint8_t value; + struct { + // Is HotSpotNmethod.name non-null? If so, the value is + // embedded in the end of this object. + uint8_t _has_name : 1, + // HotSpotNmethod.isDefault (e.g., compilation scheduled by CompileBroker) + _is_default : 1, + : 6; + } bits; + }; + + JVMCINMethodProperties _properties; // Index for the HotSpotNmethod mirror in the nmethod's oops table. // This is -1 if there is no mirror in the oops table. @@ -76,6 +86,7 @@ class JVMCINMethodData : public ResourceObj { void initialize(int nmethod_mirror_index, int nmethod_entry_patch_offset, const char* nmethod_mirror_name, + bool is_default, FailedSpeculation** failed_speculations); void* operator new(size_t size, const char* nmethod_mirror_name) { @@ -88,11 +99,13 @@ public: static JVMCINMethodData* create(int nmethod_mirror_index, int nmethod_entry_patch_offset, const char* nmethod_mirror_name, + bool is_default, FailedSpeculation** failed_speculations) { JVMCINMethodData* result = new (nmethod_mirror_name) JVMCINMethodData(); result->initialize(nmethod_mirror_index, nmethod_entry_patch_offset, nmethod_mirror_name, + is_default, failed_speculations); return result; } @@ -117,7 +130,7 @@ public: void add_failed_speculation(nmethod* nm, jlong speculation); // Gets the JVMCI name of the nmethod (which may be null). - const char* name() { return _has_name ? (char*)(((address) this) + sizeof(JVMCINMethodData)) : nullptr; } + const char* name() { return has_name() ? (char*)(((address) this) + sizeof(JVMCINMethodData)) : nullptr; } // Clears the HotSpotNmethod.address field in the mirror. If nm // is dead, the HotSpotNmethod.entryPoint field is also cleared. @@ -132,6 +145,14 @@ public: int nmethod_entry_patch_offset() { return _nmethod_entry_patch_offset; } + + bool has_name() { + return _properties.bits._has_name; + } + + bool is_default() { + return _properties.bits._is_default; + } }; // A top level class that represents an initialized JVMCI runtime. diff --git a/src/hotspot/share/jvmci/jvmci_globals.cpp b/src/hotspot/share/jvmci/jvmci_globals.cpp index d5a23a59982..f68515e3be9 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.cpp +++ b/src/hotspot/share/jvmci/jvmci_globals.cpp @@ -106,7 +106,7 @@ bool JVMCIGlobals::check_jvmci_flags_are_consistent() { } if (BootstrapJVMCI && (TieredStopAtLevel < CompLevel_full_optimization)) { jio_fprintf(defaultStream::error_stream(), - "-XX:+BootstrapJVMCI is not compatible with -XX:TieredStopAtLevel=%d\n", TieredStopAtLevel); + "-XX:+BootstrapJVMCI is not compatible with -XX:TieredStopAtLevel=%zd\n", TieredStopAtLevel); return false; } } diff --git a/src/hotspot/share/memory/memoryReserver.cpp b/src/hotspot/share/memory/memoryReserver.cpp index 457818139cd..a6c1be5b33c 100644 --- a/src/hotspot/share/memory/memoryReserver.cpp +++ b/src/hotspot/share/memory/memoryReserver.cpp @@ -22,7 +22,6 @@ * */ -#include "jvm.h" #include "logging/log.hpp" #include "memory/memoryReserver.hpp" #include "oops/compressedOops.hpp" @@ -65,11 +64,9 @@ static void log_on_large_pages_failure(char* req_addr, size_t bytes) { // Compressed oops logging. log_debug(gc, heap, coops)("Reserve regular memory without large pages"); // JVM style warning that we did not succeed in using large pages. - char msg[128]; - jio_snprintf(msg, sizeof(msg), "Failed to reserve and commit memory using large pages. " - "req_addr: " PTR_FORMAT " bytes: %zu", - req_addr, bytes); - warning("%s", msg); + warning("Failed to reserve and commit memory using large pages. " + "req_addr: " PTR_FORMAT " bytes: %zu", + p2i(req_addr), bytes); } } diff --git a/src/hotspot/share/oops/arrayKlass.cpp b/src/hotspot/share/oops/arrayKlass.cpp index 5f6f6ac674d..73e3a5e4c15 100644 --- a/src/hotspot/share/oops/arrayKlass.cpp +++ b/src/hotspot/share/oops/arrayKlass.cpp @@ -120,8 +120,8 @@ void ArrayKlass::complete_create_array_klass(ArrayKlass* k, Klass* super_klass, // java.base is defined. assert((module_entry != nullptr) || ((module_entry == nullptr) && !ModuleEntryTable::javabase_defined()), "module entry not available post " JAVA_BASE_NAME " definition"); - oop module = (module_entry != nullptr) ? module_entry->module() : (oop)nullptr; - java_lang_Class::create_mirror(k, Handle(THREAD, k->class_loader()), Handle(THREAD, module), Handle(), Handle(), CHECK); + oop module_oop = (module_entry != nullptr) ? module_entry->module_oop() : (oop)nullptr; + java_lang_Class::create_mirror(k, Handle(THREAD, k->class_loader()), Handle(THREAD, module_oop), Handle(), Handle(), CHECK); } ArrayKlass* ArrayKlass::array_klass(int n, TRAPS) { diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 2512d8d869d..131e8f145f1 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -444,6 +444,9 @@ public: assert(_nest_host != nullptr, "must be"); return _nest_host; } + InstanceKlass* nest_host_or_null() { + return _nest_host; + } // Used to construct informative IllegalAccessError messages at a higher level, // if there was an issue resolving or validating the nest host. // Returns null if there was no error. diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index aa31f586aab..958e1f61351 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -896,7 +896,7 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec module_entry = ModuleEntryTable::javabase_moduleEntry(); } // Obtain java.lang.Module, if available - Handle module_handle(THREAD, ((module_entry != nullptr) ? module_entry->module() : (oop)nullptr)); + Handle module_handle(THREAD, ((module_entry != nullptr) ? module_entry->module_oop() : (oop)nullptr)); if (this->has_archived_mirror_index()) { ResourceMark rm(THREAD); diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index ca76114af31..5bfb9045e1c 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -58,6 +58,9 @@ product(bool, StressMacroExpansion, false, DIAGNOSTIC, \ "Randomize macro node expansion order") \ \ + product(bool, StressMacroElimination, false, DIAGNOSTIC, \ + "Randomize macro node elimination order") \ + \ product(bool, StressUnstableIfTraps, false, DIAGNOSTIC, \ "Randomly take unstable if traps") \ \ diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 4c5f382ceee..e524249a6cf 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -736,8 +736,9 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, } if (StressLCM || StressGCM || StressIGVN || StressCCP || - StressIncrementalInlining || StressMacroExpansion || StressUnstableIfTraps || StressBailout || - StressLoopPeeling) { + StressIncrementalInlining || StressMacroExpansion || + StressMacroElimination || StressUnstableIfTraps || + StressBailout || StressLoopPeeling) { initialize_stress_seed(directive); } @@ -2421,6 +2422,7 @@ void Compile::Optimize() { PhaseMacroExpand mexp(igvn); mexp.eliminate_macro_nodes(); if (failing()) return; + print_method(PHASE_AFTER_MACRO_ELIMINATION, 2); igvn.set_delay_transform(false); igvn.optimize(); @@ -2520,6 +2522,18 @@ void Compile::Optimize() { TracePhase tp(_t_macroExpand); print_method(PHASE_BEFORE_MACRO_EXPANSION, 3); PhaseMacroExpand mex(igvn); + // Do not allow new macro nodes once we start to eliminate and expand + C->reset_allow_macro_nodes(); + // Last attempt to eliminate macro nodes before expand + mex.eliminate_macro_nodes(); + if (failing()) { + return; + } + mex.eliminate_opaque_looplimit_macro_nodes(); + if (failing()) { + return; + } + print_method(PHASE_AFTER_MACRO_ELIMINATION, 2); if (mex.expand_macro_nodes()) { assert(failing(), "must bail out w/ explicit message"); return; diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index b3ac0a4a1b0..d4e76950503 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -825,6 +825,29 @@ const Type* DivHFNode::Value(PhaseGVN* phase) const { if (t1->base() == Type::HalfFloatCon && t2->base() == Type::HalfFloatCon) { + // IEEE 754 floating point comparison treats 0.0 and -0.0 as equals. + + // Division of a zero by a zero results in NaN. + if (t1->getf() == 0.0f && t2->getf() == 0.0f) { + return TypeH::make(NAN); + } + + // As per C++ standard section 7.6.5 (expr.mul), behavior is undefined only if + // the second operand is 0.0. In all other situations, we can expect a standard-compliant + // C++ compiler to generate code following IEEE 754 semantics. + if (t2->getf() == 0.0) { + // If either operand is NaN, the result is NaN + if (g_isnan(t1->getf())) { + return TypeH::make(NAN); + } else { + // Division of a nonzero finite value by a zero results in a signed infinity. Also, + // division of an infinity by a finite value results in a signed infinity. + bool res_sign_neg = (jint_cast(t1->getf()) < 0) ^ (jint_cast(t2->getf()) < 0); + const TypeF* res = res_sign_neg ? TypeF::NEG_INF : TypeF::POS_INF; + return TypeH::make(res->getf()); + } + } + return TypeH::make(t1->getf() / t2->getf()); } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 8f21ee13e79..d7914f04c1f 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2365,6 +2365,10 @@ void PhaseMacroExpand::refine_strip_mined_loop_macro_nodes() { void PhaseMacroExpand::eliminate_macro_nodes() { if (C->macro_count() == 0) return; + + if (StressMacroElimination) { + C->shuffle_macro_nodes(); + } NOT_PRODUCT(int membar_before = count_MemBar(C);) // Before elimination may re-mark (change to Nested or NonEscObj) @@ -2404,6 +2408,9 @@ void PhaseMacroExpand::eliminate_macro_nodes() { } assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count"); progress = progress || success; + if (success) { + C->print_method(PHASE_AFTER_MACRO_ELIMINATION_STEP, 5, n); + } } } // Next, attempt to eliminate allocations @@ -2452,6 +2459,9 @@ void PhaseMacroExpand::eliminate_macro_nodes() { } assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count"); progress = progress || success; + if (success) { + C->print_method(PHASE_AFTER_MACRO_ELIMINATION_STEP, 5, n); + } } } #ifndef PRODUCT @@ -2462,19 +2472,11 @@ void PhaseMacroExpand::eliminate_macro_nodes() { #endif } -//------------------------------expand_macro_nodes---------------------- -// Returns true if a failure occurred. -bool PhaseMacroExpand::expand_macro_nodes() { - refine_strip_mined_loop_macro_nodes(); - // Do not allow new macro nodes once we started to expand - C->reset_allow_macro_nodes(); - if (StressMacroExpansion) { - C->shuffle_macro_nodes(); +void PhaseMacroExpand::eliminate_opaque_looplimit_macro_nodes() { + if (C->macro_count() == 0) { + return; } - // Last attempt to eliminate macro nodes. - eliminate_macro_nodes(); - if (C->failing()) return true; - + refine_strip_mined_loop_macro_nodes(); // Eliminate Opaque and LoopLimit nodes. Do it after all loop optimizations. bool progress = true; while (progress) { @@ -2536,10 +2538,18 @@ bool PhaseMacroExpand::expand_macro_nodes() { assert(!success || (C->macro_count() == (old_macro_count - 1)), "elimination must have deleted one node from macro list"); progress = progress || success; if (success) { - C->print_method(PHASE_AFTER_MACRO_EXPANSION_STEP, 5, n); + C->print_method(PHASE_AFTER_MACRO_ELIMINATION_STEP, 5, n); } } } +} + +//------------------------------expand_macro_nodes---------------------- +// Returns true if a failure occurred. +bool PhaseMacroExpand::expand_macro_nodes() { + if (StressMacroExpansion) { + C->shuffle_macro_nodes(); + } // Clean up the graph so we're less likely to hit the maximum node // limit diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index 7f27688a57a..6b8c95e2d69 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -203,6 +203,7 @@ public: void refine_strip_mined_loop_macro_nodes(); void eliminate_macro_nodes(); bool expand_macro_nodes(); + void eliminate_opaque_looplimit_macro_nodes(); SafePointScalarObjectNode* create_scalarized_object_description(AllocateNode *alloc, SafePointNode* sfpt); static bool can_eliminate_allocation(PhaseIterGVN *igvn, AllocateNode *alloc, GrowableArray *safepoints); diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 1ba8e145e7d..5b530f81330 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -3121,6 +3121,7 @@ Node *PhaseCCP::transform_once( Node *n ) { hash_delete(n); // changing bottom type may force a rehash n->raise_bottom_type(t); _worklist.push(n); // n re-enters the hash table via the worklist + add_users_to_worklist(n); // if ideal or identity optimizations depend on the input type, users need to be notified } // TEMPORARY fix to ensure that 2nd GVN pass eliminates null checks diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp index 517f0aa72c7..2bb810c55cc 100644 --- a/src/hotspot/share/opto/phasetype.hpp +++ b/src/hotspot/share/opto/phasetype.hpp @@ -91,6 +91,8 @@ flags(PHASEIDEALLOOP_ITERATIONS, "PhaseIdealLoop iterations") \ flags(AFTER_LOOP_OPTS, "After Loop Optimizations") \ flags(AFTER_MERGE_STORES, "After Merge Stores") \ + flags(AFTER_MACRO_ELIMINATION_STEP, "After Macro Elimination Step") \ + flags(AFTER_MACRO_ELIMINATION, "After Macro Elimination") \ flags(BEFORE_MACRO_EXPANSION , "Before Macro Expansion") \ flags(AFTER_MACRO_EXPANSION_STEP, "After Macro Expansion Step") \ flags(AFTER_MACRO_EXPANSION, "After Macro Expansion") \ diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 1389bf6d602..13d89b396fa 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2966,7 +2966,7 @@ JVM_ENTRY(jobject, JVM_CreateThreadSnapshot(JNIEnv* env, jobject jthread)) oop snapshot = ThreadSnapshotFactory::get_thread_snapshot(jthread, THREAD); return JNIHandles::make_local(THREAD, snapshot); #else - return nullptr; + THROW_NULL(vmSymbols::java_lang_UnsupportedOperationException()); #endif JVM_END diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 13822f73f77..10614f445d3 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -943,12 +943,12 @@ class JvmtiClassFileLoadHookPoster : public StackObj { ModuleEntry* module_entry = InstanceKlass::cast(klass)->module(); assert(module_entry != nullptr, "module_entry should always be set"); if (module_entry->is_named() && - module_entry->module() != nullptr && + module_entry->module_oop() != nullptr && !module_entry->has_default_read_edges()) { if (!module_entry->set_has_default_read_edges()) { // We won a potential race. // Add read edges to the unnamed modules of the bootstrap and app class loaders - Handle class_module(_thread, module_entry->module()); // Obtain j.l.r.Module + Handle class_module(_thread, module_entry->module_oop()); // Obtain j.l.r.Module JvmtiExport::add_default_read_edges(class_module, _thread); } } diff --git a/src/hotspot/share/prims/jvmtiImpl.cpp b/src/hotspot/share/prims/jvmtiImpl.cpp index 0059636099e..d20de5f44a3 100644 --- a/src/hotspot/share/prims/jvmtiImpl.cpp +++ b/src/hotspot/share/prims/jvmtiImpl.cpp @@ -120,6 +120,7 @@ address JvmtiBreakpoint::getBcp() const { } void JvmtiBreakpoint::each_method_version_do(method_action meth_act) { + assert(!_method->is_old(), "the breakpoint method shouldn't be old"); ((Method*)_method->*meth_act)(_bci); // add/remove breakpoint to/from versions of the method that are EMCP. @@ -183,8 +184,16 @@ void JvmtiBreakpoint::print_on(outputStream* out) const { // // Modify the Breakpoints data structure at a safepoint // +// The caller of VM_ChangeBreakpoints operation should ensure that +// _bp.method is preserved until VM_ChangeBreakpoints is processed. void VM_ChangeBreakpoints::doit() { + if (_bp->method()->is_old()) { + // The bp->_method became old because VMOp with class redefinition happened for this class + // after JvmtiBreakpoint was created but before JVM_ChangeBreakpoints started. + // All class breakpoints are cleared during redefinition, so don't set/clear this breakpoint. + return; + } switch (_operation) { case SET_BREAKPOINT: _breakpoints->set_at_safepoint(*_bp); @@ -249,6 +258,9 @@ int JvmtiBreakpoints::set(JvmtiBreakpoint& bp) { if (find(bp) != -1) { return JVMTI_ERROR_DUPLICATE; } + + // Ensure that bp._method is not deallocated before VM_ChangeBreakpoints::doit(). + methodHandle mh(Thread::current(), bp.method()); VM_ChangeBreakpoints set_breakpoint(VM_ChangeBreakpoints::SET_BREAKPOINT, &bp); VMThread::execute(&set_breakpoint); return JVMTI_ERROR_NONE; @@ -259,6 +271,8 @@ int JvmtiBreakpoints::clear(JvmtiBreakpoint& bp) { return JVMTI_ERROR_NOT_FOUND; } + // Ensure that bp._method is not deallocated before VM_ChangeBreakpoints::doit(). + methodHandle mh(Thread::current(), bp.method()); VM_ChangeBreakpoints clear_breakpoint(VM_ChangeBreakpoints::CLEAR_BREAKPOINT, &bp); VMThread::execute(&clear_breakpoint); return JVMTI_ERROR_NONE; diff --git a/src/hotspot/share/prims/scopedMemoryAccess.cpp b/src/hotspot/share/prims/scopedMemoryAccess.cpp index 8ef5b96f37d..c1d1b8cd8c0 100644 --- a/src/hotspot/share/prims/scopedMemoryAccess.cpp +++ b/src/hotspot/share/prims/scopedMemoryAccess.cpp @@ -105,15 +105,15 @@ static frame get_last_frame(JavaThread* jt) { return last_frame; } -class ScopedAsyncExceptionHandshake : public AsyncExceptionHandshake { +class ScopedAsyncExceptionHandshakeClosure : public AsyncExceptionHandshakeClosure { OopHandle _session; public: - ScopedAsyncExceptionHandshake(OopHandle& session, OopHandle& error) - : AsyncExceptionHandshake(error), + ScopedAsyncExceptionHandshakeClosure(OopHandle& session, OopHandle& error) + : AsyncExceptionHandshakeClosure(error), _session(session) {} - ~ScopedAsyncExceptionHandshake() { + ~ScopedAsyncExceptionHandshakeClosure() { _session.release(Universe::vm_global()); } @@ -122,17 +122,17 @@ public: bool ignored; if (is_accessing_session(jt, _session.resolve(), ignored)) { // Throw exception to unwind out from the scoped access - AsyncExceptionHandshake::do_thread(thread); + AsyncExceptionHandshakeClosure::do_thread(thread); } } }; -class CloseScopedMemoryClosure : public HandshakeClosure { +class CloseScopedMemoryHandshakeClosure : public HandshakeClosure { jobject _session; jobject _error; public: - CloseScopedMemoryClosure(jobject session, jobject error) + CloseScopedMemoryHandshakeClosure(jobject session, jobject error) : HandshakeClosure("CloseScopedMemory") , _session(session) , _error(error) {} @@ -159,7 +159,7 @@ public: // the scoped access. OopHandle session(Universe::vm_global(), JNIHandles::resolve(_session)); OopHandle error(Universe::vm_global(), JNIHandles::resolve(_error)); - jt->install_async_exception(new ScopedAsyncExceptionHandshake(session, error)); + jt->install_async_exception(new ScopedAsyncExceptionHandshakeClosure(session, error)); } else if (!in_scoped) { frame last_frame = get_last_frame(jt); if (last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) { @@ -213,7 +213,7 @@ public: * closed (deopt), this method returns false, signalling that the session cannot be closed safely. */ JVM_ENTRY(void, ScopedMemoryAccess_closeScope(JNIEnv *env, jobject receiver, jobject session, jobject error)) - CloseScopedMemoryClosure cl(session, error); + CloseScopedMemoryHandshakeClosure cl(session, error); Handshake::execute(&cl); JVM_END diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 60ebba36f92..9beb50fe78b 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -2246,7 +2246,7 @@ WB_END #endif // INCLUDE_CDS WB_ENTRY(jboolean, WB_HandshakeReadMonitors(JNIEnv* env, jobject wb, jobject thread_handle)) - class ReadMonitorsClosure : public HandshakeClosure { + class ReadMonitorsHandshakeClosure : public HandshakeClosure { jboolean _executed; void do_thread(Thread* th) { @@ -2281,24 +2281,24 @@ WB_ENTRY(jboolean, WB_HandshakeReadMonitors(JNIEnv* env, jobject wb, jobject thr } public: - ReadMonitorsClosure() : HandshakeClosure("WB_HandshakeReadMonitors"), _executed(false) {} + ReadMonitorsHandshakeClosure() : HandshakeClosure("WB_HandshakeReadMonitors"), _executed(false) {} jboolean executed() const { return _executed; } }; - ReadMonitorsClosure rmc; + ReadMonitorsHandshakeClosure rmhc; if (thread_handle != nullptr) { ThreadsListHandle tlh; JavaThread* target = nullptr; bool is_alive = tlh.cv_internal_thread_to_JavaThread(thread_handle, &target, nullptr); if (is_alive) { - Handshake::execute(&rmc, &tlh, target); + Handshake::execute(&rmhc, &tlh, target); } } - return rmc.executed(); + return rmhc.executed(); WB_END WB_ENTRY(jint, WB_HandshakeWalkStack(JNIEnv* env, jobject wb, jobject thread_handle, jboolean all_threads)) - class TraceSelfClosure : public HandshakeClosure { + class TraceSelfHandshakeClosure : public HandshakeClosure { jint _num_threads_completed; void do_thread(Thread* th) { @@ -2312,27 +2312,27 @@ WB_ENTRY(jint, WB_HandshakeWalkStack(JNIEnv* env, jobject wb, jobject thread_han } public: - TraceSelfClosure(Thread* thread) : HandshakeClosure("WB_TraceSelf"), _num_threads_completed(0) {} + TraceSelfHandshakeClosure(Thread* thread) : HandshakeClosure("WB_TraceSelf"), _num_threads_completed(0) {} jint num_threads_completed() const { return _num_threads_completed; } }; - TraceSelfClosure tsc(Thread::current()); + TraceSelfHandshakeClosure tshc(Thread::current()); if (all_threads) { - Handshake::execute(&tsc); + Handshake::execute(&tshc); } else if (thread_handle != nullptr) { ThreadsListHandle tlh; JavaThread* target = nullptr; bool is_alive = tlh.cv_internal_thread_to_JavaThread(thread_handle, &target, nullptr); if (is_alive) { - Handshake::execute(&tsc, &tlh, target); + Handshake::execute(&tshc, &tlh, target); } } - return tsc.num_threads_completed(); + return tshc.num_threads_completed(); WB_END WB_ENTRY(void, WB_AsyncHandshakeWalkStack(JNIEnv* env, jobject wb, jobject thread_handle)) - class TraceSelfClosure : public AsyncHandshakeClosure { + class TraceSelfHandshakeClosure : public AsyncHandshakeClosure { JavaThread* _self; void do_thread(Thread* th) { assert(th->is_Java_thread(), "sanity"); @@ -2347,15 +2347,15 @@ WB_ENTRY(void, WB_AsyncHandshakeWalkStack(JNIEnv* env, jobject wb, jobject threa } public: - TraceSelfClosure(JavaThread* self_target) : AsyncHandshakeClosure("WB_TraceSelf"), _self(self_target) {} + TraceSelfHandshakeClosure(JavaThread* self_target) : AsyncHandshakeClosure("WB_TraceSelf"), _self(self_target) {} }; if (thread_handle != nullptr) { ThreadsListHandle tlh; JavaThread* target = nullptr; bool is_alive = tlh.cv_internal_thread_to_JavaThread(thread_handle, &target, nullptr); if (is_alive) { - TraceSelfClosure* tsc = new TraceSelfClosure(target); - Handshake::execute(tsc, target); + TraceSelfHandshakeClosure* tshc = new TraceSelfHandshakeClosure(target); + Handshake::execute(tshc, target); } } WB_END diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 07a26916256..d2a1c31282f 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -534,6 +534,7 @@ static SpecialFlag const special_jvm_flags[] = { #endif { "ParallelRefProcEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "ParallelRefProcBalancingEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "PSChunkLargeArrays", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() }, diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index ceb09811f73..b392fecab29 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -1049,9 +1049,9 @@ JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_m return bt; JRT_END -class DeoptimizeMarkedClosure : public HandshakeClosure { +class DeoptimizeMarkedHandshakeClosure : public HandshakeClosure { public: - DeoptimizeMarkedClosure() : HandshakeClosure("Deoptimize") {} + DeoptimizeMarkedHandshakeClosure() : HandshakeClosure("Deoptimize") {} void do_thread(Thread* thread) { JavaThread* jt = JavaThread::cast(thread); jt->deoptimize_marked_methods(); @@ -1064,7 +1064,7 @@ void Deoptimization::deoptimize_all_marked() { // Make the dependent methods not entrant CodeCache::make_marked_nmethods_deoptimized(); - DeoptimizeMarkedClosure deopt; + DeoptimizeMarkedHandshakeClosure deopt; if (SafepointSynchronize::is_at_safepoint()) { Threads::java_threads_do(&deopt); } else { @@ -2363,6 +2363,14 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr ShouldNotReachHere(); } +#if INCLUDE_JVMCI + // Deoptimization count is used by the CompileBroker to reason about compilations + // it requests so do not pollute the count for deoptimizations in non-default (i.e. + // non-CompilerBroker) compilations. + if (nm->is_jvmci_hosted()) { + update_trap_state = false; + } +#endif // Setting +ProfileTraps fixes the following, on all platforms: // The result is infinite heroic-opt-uncommon-trap/deopt/recompile cycles, since the // recompile relies on a MethodData* to record heroic opt failures. @@ -2473,7 +2481,6 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr trap_mdo->inc_tenure_traps(); } } - if (inc_recompile_count) { trap_mdo->inc_overflow_recompile_count(); if ((uint)trap_mdo->overflow_recompile_count() > diff --git a/src/hotspot/share/runtime/escapeBarrier.cpp b/src/hotspot/share/runtime/escapeBarrier.cpp index 8f9a8b59dd6..2005527bb8b 100644 --- a/src/hotspot/share/runtime/escapeBarrier.cpp +++ b/src/hotspot/share/runtime/escapeBarrier.cpp @@ -165,9 +165,9 @@ bool EscapeBarrier::deoptimize_objects_all_threads() { bool EscapeBarrier::_deoptimizing_objects_for_all_threads = false; bool EscapeBarrier::_self_deoptimization_in_progress = false; -class EscapeBarrierSuspendHandshake : public HandshakeClosure { +class EscapeBarrierSuspendHandshakeClosure : public HandshakeClosure { public: - EscapeBarrierSuspendHandshake(const char* name) : + EscapeBarrierSuspendHandshakeClosure(const char* name) : HandshakeClosure(name) { } void do_thread(Thread* th) { } }; @@ -196,7 +196,7 @@ void EscapeBarrier::sync_and_suspend_one() { } // Use a handshake to synchronize with the target thread. - EscapeBarrierSuspendHandshake sh("EscapeBarrierSuspendOne"); + EscapeBarrierSuspendHandshakeClosure sh("EscapeBarrierSuspendOne"); Handshake::execute(&sh, _deoptee_thread); assert(!_deoptee_thread->has_last_Java_frame() || _deoptee_thread->frame_anchor()->walkable(), "stack should be walkable now"); @@ -242,7 +242,7 @@ void EscapeBarrier::sync_and_suspend_all() { } // Use a handshake to synchronize with the other threads. - EscapeBarrierSuspendHandshake sh("EscapeBarrierSuspendAll"); + EscapeBarrierSuspendHandshakeClosure sh("EscapeBarrierSuspendAll"); Handshake::execute(&sh); #ifdef ASSERT for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index b61c3724dba..89af5ca0c98 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -293,6 +293,9 @@ const int ObjectAlignmentInBytes = 8; product(bool, UseInlineCaches, true, \ "Use Inline Caches for virtual calls ") \ \ + develop(bool, VerifyInlineCaches, true, \ + "Verify Inline Caches") \ + \ product(bool, InlineArrayCopy, true, DIAGNOSTIC, \ "Inline arraycopy native that is known to be part of " \ "base library DLL") \ diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index 2c827b61602..c55803242de 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -705,7 +705,7 @@ void HandshakeState::handle_unsafe_access_error() { // back to Java until resumed we cannot create the exception // object yet. Add a new unsafe access error operation to // the end of the queue and try again in the next attempt. - Handshake::execute(new UnsafeAccessErrorHandshake(), _handshakee); + Handshake::execute(new UnsafeAccessErrorHandshakeClosure(), _handshakee); log_info(handshake)("JavaThread " INTPTR_FORMAT " skipping unsafe access processing due to suspend.", p2i(_handshakee)); return; } diff --git a/src/hotspot/share/runtime/handshake.hpp b/src/hotspot/share/runtime/handshake.hpp index ca9cef76f5d..c6f3aad08db 100644 --- a/src/hotspot/share/runtime/handshake.hpp +++ b/src/hotspot/share/runtime/handshake.hpp @@ -35,7 +35,7 @@ class HandshakeOperation; class AsyncHandshakeOperation; class JavaThread; -class UnsafeAccessErrorHandshake; +class UnsafeAccessErrorHandshakeClosure; class ThreadsListHandle; // A handshake closure is a callback that is executed for a JavaThread @@ -86,7 +86,7 @@ class JvmtiRawMonitor; // operation is only done by either VMThread/Handshaker on behalf of the // JavaThread or by the target JavaThread itself. class HandshakeState { - friend UnsafeAccessErrorHandshake; + friend UnsafeAccessErrorHandshakeClosure; friend JavaThread; // This a back reference to the JavaThread, // the target for all operation in the queue. diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index aa0b5dca3e4..8544ec16884 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -471,6 +471,7 @@ void before_exit(JavaThread* thread, bool halt) { NativeHeapTrimmer::cleanup(); + Universe::heap()->print_tracing_info(); // Stop concurrent GC threads Universe::heap()->stop(); @@ -513,7 +514,6 @@ void before_exit(JavaThread* thread, bool halt) { os::terminate_signal_thread(); print_statistics(); - Universe::heap()->print_tracing_info(); { MutexLocker ml(BeforeExit_lock); _before_exit_status = BEFORE_EXIT_DONE; diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index abae0e10b37..b84cf6e9011 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -1124,16 +1124,16 @@ void JavaThread::handle_async_exception(oop java_throwable) { } } -void JavaThread::install_async_exception(AsyncExceptionHandshake* aeh) { +void JavaThread::install_async_exception(AsyncExceptionHandshakeClosure* aehc) { // Do not throw asynchronous exceptions against the compiler thread // or if the thread is already exiting. if (!can_call_java() || is_exiting()) { - delete aeh; + delete aehc; return; } - oop exception = aeh->exception(); - Handshake::execute(aeh, this); // Install asynchronous handshake + oop exception = aehc->exception(); + Handshake::execute(aehc, this); // Install asynchronous handshake ResourceMark rm; if (log_is_enabled(Info, exceptions)) { @@ -1151,25 +1151,25 @@ void JavaThread::install_async_exception(AsyncExceptionHandshake* aeh) { } } -class InstallAsyncExceptionHandshake : public HandshakeClosure { - AsyncExceptionHandshake* _aeh; +class InstallAsyncExceptionHandshakeClosure : public HandshakeClosure { + AsyncExceptionHandshakeClosure* _aehc; public: - InstallAsyncExceptionHandshake(AsyncExceptionHandshake* aeh) : - HandshakeClosure("InstallAsyncException"), _aeh(aeh) {} - ~InstallAsyncExceptionHandshake() { - // If InstallAsyncExceptionHandshake was never executed we need to clean up _aeh. - delete _aeh; + InstallAsyncExceptionHandshakeClosure(AsyncExceptionHandshakeClosure* aehc) : + HandshakeClosure("InstallAsyncException"), _aehc(aehc) {} + ~InstallAsyncExceptionHandshakeClosure() { + // If InstallAsyncExceptionHandshakeClosure was never executed we need to clean up _aehc. + delete _aehc; } void do_thread(Thread* thr) { JavaThread* target = JavaThread::cast(thr); - target->install_async_exception(_aeh); - _aeh = nullptr; + target->install_async_exception(_aehc); + _aehc = nullptr; } }; void JavaThread::send_async_exception(JavaThread* target, oop java_throwable) { OopHandle e(Universe::vm_global(), java_throwable); - InstallAsyncExceptionHandshake iaeh(new AsyncExceptionHandshake(e)); + InstallAsyncExceptionHandshakeClosure iaeh(new AsyncExceptionHandshakeClosure(e)); Handshake::execute(&iaeh, target); } diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index af46492622d..fac263d9048 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -53,7 +53,7 @@ #include "utilities/ticks.hpp" #endif -class AsyncExceptionHandshake; +class AsyncExceptionHandshakeClosure; class DeoptResourceMark; class InternalOOMEMark; class JNIHandleBlock; @@ -233,13 +233,13 @@ class JavaThread: public Thread { // Asynchronous exception support private: - friend class InstallAsyncExceptionHandshake; - friend class AsyncExceptionHandshake; + friend class InstallAsyncExceptionHandshakeClosure; + friend class AsyncExceptionHandshakeClosure; friend class HandshakeState; void handle_async_exception(oop java_throwable); public: - void install_async_exception(AsyncExceptionHandshake* aec = nullptr); + void install_async_exception(AsyncExceptionHandshakeClosure* aec = nullptr); bool has_async_exception_condition(); inline void set_pending_unsafe_access_error(); static void send_async_exception(JavaThread* jt, oop java_throwable); @@ -1164,7 +1164,7 @@ public: // Used by the interpreter in fullspeed mode for frame pop, method // entry, method exit and single stepping support. This field is // only set to non-zero at a safepoint or using a direct handshake - // (see EnterInterpOnlyModeClosure). + // (see EnterInterpOnlyModeHandshakeClosure). // It can be set to zero asynchronously to this threads execution (i.e., without // safepoint/handshake or a lock) so we have to be very careful. // Accesses by other threads are synchronized using JvmtiThreadState_lock though. diff --git a/src/hotspot/share/runtime/javaThread.inline.hpp b/src/hotspot/share/runtime/javaThread.inline.hpp index 136a9d84151..be76407f511 100644 --- a/src/hotspot/share/runtime/javaThread.inline.hpp +++ b/src/hotspot/share/runtime/javaThread.inline.hpp @@ -73,13 +73,13 @@ inline bool JavaThread::clear_carrier_thread_suspended() { } #endif -class AsyncExceptionHandshake : public AsyncHandshakeClosure { +class AsyncExceptionHandshakeClosure : public AsyncHandshakeClosure { OopHandle _exception; public: - AsyncExceptionHandshake(OopHandle& o, const char* name = "AsyncExceptionHandshake") + AsyncExceptionHandshakeClosure(OopHandle& o, const char* name = "AsyncExceptionHandshakeClosure") : AsyncHandshakeClosure(name), _exception(o) { } - ~AsyncExceptionHandshake() { + ~AsyncExceptionHandshakeClosure() { Thread* current = Thread::current(); // Can get here from the VMThread via install_async_exception() bail out. if (current->is_Java_thread()) { @@ -103,9 +103,9 @@ class AsyncExceptionHandshake : public AsyncHandshakeClosure { bool is_async_exception() { return true; } }; -class UnsafeAccessErrorHandshake : public AsyncHandshakeClosure { +class UnsafeAccessErrorHandshakeClosure : public AsyncHandshakeClosure { public: - UnsafeAccessErrorHandshake() : AsyncHandshakeClosure("UnsafeAccessErrorHandshake") {} + UnsafeAccessErrorHandshakeClosure() : AsyncHandshakeClosure("UnsafeAccessErrorHandshakeClosure") {} void do_thread(Thread* thr) { JavaThread* self = JavaThread::cast(thr); assert(self == JavaThread::current(), "must be"); @@ -117,7 +117,7 @@ class UnsafeAccessErrorHandshake : public AsyncHandshakeClosure { inline void JavaThread::set_pending_unsafe_access_error() { if (!has_async_exception_condition()) { - Handshake::execute(new UnsafeAccessErrorHandshake(), this); + Handshake::execute(new UnsafeAccessErrorHandshakeClosure(), this); } } diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index 0a6f472c8f0..3550cda3590 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -84,7 +84,7 @@ Monitor* CompileTaskWait_lock = nullptr; Monitor* MethodCompileQueue_lock = nullptr; Monitor* CompileThread_lock = nullptr; Monitor* Compilation_lock = nullptr; -Mutex* CompileTaskAlloc_lock = nullptr; +Monitor* CompileTaskAlloc_lock = nullptr; Mutex* CompileStatistics_lock = nullptr; Mutex* DirectivesStack_lock = nullptr; Monitor* Terminator_lock = nullptr; @@ -346,7 +346,7 @@ void mutex_init() { MUTEX_DEFL(G1RareEvent_lock , PaddedMutex , Threads_lock, true); } - MUTEX_DEFL(CompileTaskAlloc_lock , PaddedMutex , MethodCompileQueue_lock); + MUTEX_DEFL(CompileTaskAlloc_lock , PaddedMonitor, MethodCompileQueue_lock); MUTEX_DEFL(CompileTaskWait_lock , PaddedMonitor, MethodCompileQueue_lock); #if INCLUDE_PARALLELGC diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 640747c3fe9..9c48f5341a2 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -86,7 +86,7 @@ extern Monitor* CompileThread_lock; // a lock held by compile threa extern Monitor* Compilation_lock; // a lock used to pause compilation extern Mutex* TrainingData_lock; // a lock used when accessing training records extern Monitor* TrainingReplayQueue_lock; // a lock held when class are added/removed to the training replay queue -extern Mutex* CompileTaskAlloc_lock; // a lock held when CompileTasks are allocated +extern Monitor* CompileTaskAlloc_lock; // a lock held when CompileTasks are allocated extern Monitor* CompileTaskWait_lock; // a lock held when CompileTasks are waited/notified extern Mutex* CompileStatistics_lock; // a lock held when updating compilation statistics extern Mutex* DirectivesStack_lock; // a lock held when mutating the dirstack and ref counting directives diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index ee1f0a3b081..a344c1d66c1 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -1042,7 +1042,7 @@ void os::print_hex_dump(outputStream* st, const_address start, const_address end } print_hex_location(st, p, unitsize, ascii_form); p += unitsize; - logical_p += unitsize; + logical_p = (const_address) ((uintptr_t)logical_p + unitsize); cols++; if (cols >= cols_per_line) { if (print_ascii && !ascii_form.is_empty()) { diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index b26ec280e72..a2ff2ad9eca 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -293,13 +293,6 @@ class os: AllStatic { static jlong elapsed_counter(); static jlong elapsed_frequency(); - // The "virtual time" of a thread is the amount of time a thread has - // actually run. The first function indicates whether the OS supports - // this functionality for the current thread, and if so the second - // returns the elapsed virtual time for the current thread. - static bool supports_vtime(); - static double elapsedVTime(); - // Return current local time in a string (YYYY-MM-DD HH:MM:SS). // It is MT safe, but not async-safe, as reading time zone // information may require a lock on some platforms. diff --git a/src/hotspot/share/runtime/reflection.cpp b/src/hotspot/share/runtime/reflection.cpp index 2a0af9d2d09..a7b468c57a3 100644 --- a/src/hotspot/share/runtime/reflection.cpp +++ b/src/hotspot/share/runtime/reflection.cpp @@ -551,9 +551,9 @@ char* Reflection::verify_class_access_msg(const Klass* current_class, current_class_name, module_from_name, new_class_name, module_to_name, module_from_name, module_to_name); } else { - oop jlm = module_to->module(); - assert(jlm != nullptr, "Null jlm in module_to ModuleEntry"); - intptr_t identity_hash = jlm->identity_hash(); + oop module_oop = module_to->module_oop(); + assert(module_oop != nullptr, "should have been initialized"); + intptr_t identity_hash = module_oop->identity_hash(); size_t len = 160 + strlen(current_class_name) + 2*strlen(module_from_name) + strlen(new_class_name) + 2*sizeof(uintx); msg = NEW_RESOURCE_ARRAY(char, len); @@ -578,9 +578,9 @@ char* Reflection::verify_class_access_msg(const Klass* current_class, current_class_name, module_from_name, new_class_name, module_to_name, module_to_name, package_name, module_from_name); } else { - oop jlm = module_from->module(); - assert(jlm != nullptr, "Null jlm in module_from ModuleEntry"); - intptr_t identity_hash = jlm->identity_hash(); + oop module_oop = module_from->module_oop(); + assert(module_oop != nullptr, "should have been initialized"); + intptr_t identity_hash = module_oop->identity_hash(); size_t len = 170 + strlen(current_class_name) + strlen(new_class_name) + 2*strlen(module_to_name) + strlen(package_name) + 2*sizeof(uintx); msg = NEW_RESOURCE_ARRAY(char, len); diff --git a/src/hotspot/share/runtime/sharedRuntimeMath.hpp b/src/hotspot/share/runtime/sharedRuntimeMath.hpp index 91dda2a4fe8..01ba4d93335 100644 --- a/src/hotspot/share/runtime/sharedRuntimeMath.hpp +++ b/src/hotspot/share/runtime/sharedRuntimeMath.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -67,64 +67,9 @@ static inline void set_low(double* d, int low) { *d = conv.d; } -static double copysignA(double x, double y) { - DoubleIntConv convX; - convX.d = x; - convX.split.hi = (convX.split.hi & 0x7fffffff) | (high(y) & 0x80000000); - return convX.d; -} - -/* - * ==================================================== - * Copyright (c) 1998 Oracle and/or its affiliates. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -/* - * scalbn (double x, int n) - * scalbn(x,n) returns x* 2**n computed by exponent - * manipulation rather than by actually performing an - * exponentiation or a multiplication. - */ - static const double two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ -twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ hugeX = 1.0e+300, tiny = 1.0e-300; -static double scalbnA(double x, int n) { - int k,hx,lx; - hx = high(x); - lx = low(x); - k = (hx&0x7ff00000)>>20; /* extract exponent */ - if (k==0) { /* 0 or subnormal x */ - if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */ - x *= two54; - hx = high(x); - k = ((hx&0x7ff00000)>>20) - 54; - if (n< -50000) return tiny*x; /*underflow*/ - } - if (k==0x7ff) return x+x; /* NaN or Inf */ - k = k+n; - if (k > 0x7fe) return hugeX*copysignA(hugeX,x); /* overflow */ - if (k > 0) { /* normal result */ - set_high(&x, (hx&0x800fffff)|(k<<20)); - return x; - } - if (k <= -54) { - if (n > 50000) /* in case integer overflow in n+k */ - return hugeX*copysignA(hugeX,x); /*overflow*/ - else return tiny*copysignA(tiny,x); /*underflow*/ - } - k += 54; /* subnormal result */ - set_high(&x, (hx&0x800fffff)|(k<<20)); - return x*twom54; -} - #endif // SHARE_RUNTIME_SHAREDRUNTIMEMATH_HPP diff --git a/src/hotspot/share/runtime/sharedRuntimeTrans.cpp b/src/hotspot/share/runtime/sharedRuntimeTrans.cpp index ee44151e433..a06bd724bb7 100644 --- a/src/hotspot/share/runtime/sharedRuntimeTrans.cpp +++ b/src/hotspot/share/runtime/sharedRuntimeTrans.cpp @@ -657,7 +657,7 @@ static double __ieee754_pow(double x, double y) { z = one-(r-z); j = high(z); j += (n<<20); - if((j>>20)<=0) z = scalbnA(z,n); /* subnormal output */ + if((j>>20)<=0) z = scalbn(z,n); /* subnormal output */ else set_high(&z, high(z) + (n<<20)); return s*z; } diff --git a/src/hotspot/share/runtime/sharedRuntimeTrig.cpp b/src/hotspot/share/runtime/sharedRuntimeTrig.cpp index 86958ea2bd2..985198efd5d 100644 --- a/src/hotspot/share/runtime/sharedRuntimeTrig.cpp +++ b/src/hotspot/share/runtime/sharedRuntimeTrig.cpp @@ -200,7 +200,7 @@ recompute: } /* compute n */ - z = scalbnA(z,q0); /* actual value of z */ + z = scalbn(z,q0); /* actual value of z */ z -= 8.0*floor(z*0.125); /* trim off integer >= 8 */ n = (int) z; z -= (double)n; @@ -233,7 +233,7 @@ recompute: } if(ih==2) { z = one - z; - if(carry!=0) z -= scalbnA(one,q0); + if(carry!=0) z -= scalbn(one,q0); } } @@ -259,7 +259,7 @@ recompute: jz -= 1; q0 -= 24; while(iq[jz]==0) { jz--; q0-=24;} } else { /* break z into 24-bit if necessary */ - z = scalbnA(z,-q0); + z = scalbn(z,-q0); if(z>=two24B) { fw = (double)((int)(twon24*z)); iq[jz] = (int)(z-two24B*fw); @@ -269,7 +269,7 @@ recompute: } /* convert integer "bit" chunk to floating-point value */ - fw = scalbnA(one,q0); + fw = scalbn(one,q0); for(i=jz;i>=0;i--) { q[i] = fw*(double)iq[i]; fw*=twon24; } diff --git a/src/hotspot/share/runtime/suspendResumeManager.cpp b/src/hotspot/share/runtime/suspendResumeManager.cpp index fd14f73f553..2e75d763cb3 100644 --- a/src/hotspot/share/runtime/suspendResumeManager.cpp +++ b/src/hotspot/share/runtime/suspendResumeManager.cpp @@ -35,9 +35,9 @@ // This is the closure that prevents a suspended JavaThread from // escaping the suspend request. -class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure { +class ThreadSelfSuspensionHandshakeClosure : public AsyncHandshakeClosure { public: - ThreadSelfSuspensionHandshake() : AsyncHandshakeClosure("ThreadSelfSuspensionHandshake") {} + ThreadSelfSuspensionHandshakeClosure() : AsyncHandshakeClosure("ThreadSelfSuspensionHandshakeClosure") {} void do_thread(Thread* thr) { JavaThread* current = JavaThread::cast(thr); assert(current == Thread::current(), "Must be self executed."); @@ -52,11 +52,11 @@ public: }; // This is the closure that synchronously honors the suspend request. -class SuspendThreadHandshake : public HandshakeClosure { +class SuspendThreadHandshakeClosure : public HandshakeClosure { bool _register_vthread_SR; bool _did_suspend; public: - SuspendThreadHandshake(bool register_vthread_SR) : HandshakeClosure("SuspendThread"), + SuspendThreadHandshakeClosure(bool register_vthread_SR) : HandshakeClosure("SuspendThread"), _register_vthread_SR(register_vthread_SR), _did_suspend(false) { } void do_thread(Thread* thr) { @@ -93,7 +93,7 @@ bool SuspendResumeManager::suspend(bool register_vthread_SR) { do_owner_suspend(); return true; } else { - SuspendThreadHandshake st(register_vthread_SR); + SuspendThreadHandshakeClosure st(register_vthread_SR); Handshake::execute(&st, _target); return st.did_suspend(); } @@ -150,7 +150,7 @@ bool SuspendResumeManager::suspend_with_handshake(bool register_vthread_SR) { set_suspended(true, register_vthread_SR); set_async_suspend_handshake(true); log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended, arming ThreadSuspension", p2i(_target)); - ThreadSelfSuspensionHandshake* ts = new ThreadSelfSuspensionHandshake(); + ThreadSelfSuspensionHandshakeClosure* ts = new ThreadSelfSuspensionHandshakeClosure(); Handshake::execute(ts, _target); return true; } diff --git a/src/hotspot/share/runtime/suspendResumeManager.hpp b/src/hotspot/share/runtime/suspendResumeManager.hpp index fed3b34055e..01735cb3bf8 100644 --- a/src/hotspot/share/runtime/suspendResumeManager.hpp +++ b/src/hotspot/share/runtime/suspendResumeManager.hpp @@ -25,12 +25,12 @@ #ifndef SHARE_RUNTIME_SUSPENDRESUMEMANAGER_HPP #define SHARE_RUNTIME_SUSPENDRESUMEMANAGER_HPP -class SuspendThreadHandshake; -class ThreadSelfSuspensionHandshake; +class SuspendThreadHandshakeClosure; +class ThreadSelfSuspensionHandshakeClosure; class SuspendResumeManager { - friend SuspendThreadHandshake; - friend ThreadSelfSuspensionHandshake; + friend SuspendThreadHandshakeClosure; + friend ThreadSelfSuspensionHandshakeClosure; friend JavaThread; JavaThread* _target; diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index 503b7833351..221e7dd71ec 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -1664,12 +1664,12 @@ size_t ObjectSynchronizer::deflate_monitor_list(ObjectMonitorDeflationSafepointe return deflated_count; } -class HandshakeForDeflation : public HandshakeClosure { +class DeflationHandshakeClosure : public HandshakeClosure { public: - HandshakeForDeflation() : HandshakeClosure("HandshakeForDeflation") {} + DeflationHandshakeClosure() : HandshakeClosure("DeflationHandshakeClosure") {} void do_thread(Thread* thread) { - log_trace(monitorinflation)("HandshakeForDeflation::do_thread: thread=" + log_trace(monitorinflation)("DeflationHandshakeClosure::do_thread: thread=" INTPTR_FORMAT, p2i(thread)); if (thread->is_Java_thread()) { // Clear OM cache @@ -1834,8 +1834,8 @@ size_t ObjectSynchronizer::deflate_idle_monitors() { // A JavaThread needs to handshake in order to safely free the // ObjectMonitors that were deflated in this cycle. - HandshakeForDeflation hfd_hc; - Handshake::execute(&hfd_hc); + DeflationHandshakeClosure dhc; + Handshake::execute(&dhc); // Also, we sync and desync GC threads around the handshake, so that they can // safely read the mark-word and look-through to the object-monitor, without // being afraid that the object-monitor is going away. diff --git a/src/hotspot/share/runtime/vmThread.cpp b/src/hotspot/share/runtime/vmThread.cpp index 0ff5e5d227b..2a31929c34b 100644 --- a/src/hotspot/share/runtime/vmThread.cpp +++ b/src/hotspot/share/runtime/vmThread.cpp @@ -297,9 +297,9 @@ void VMThread::evaluate_operation(VM_Operation* op) { } } -class HandshakeALotClosure : public HandshakeClosure { +class ALotOfHandshakeClosure : public HandshakeClosure { public: - HandshakeALotClosure() : HandshakeClosure("HandshakeALot") {} + ALotOfHandshakeClosure() : HandshakeClosure("ALotOfHandshakeClosure") {} void do_thread(Thread* thread) { #ifdef ASSERT JavaThread::cast(thread)->verify_states_for_handshake(); @@ -453,8 +453,8 @@ void VMThread::wait_for_operation() { if (handshake_or_safepoint_alot()) { if (HandshakeALot) { MutexUnlocker mul(VMOperation_lock); - HandshakeALotClosure hal_cl; - Handshake::execute(&hal_cl); + ALotOfHandshakeClosure aohc; + Handshake::execute(&aohc); } // When we unlocked above someone might have setup a new op. if (_next_vm_operation != nullptr) { diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 8e0c955bff8..f30b0c170a6 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -1124,7 +1124,7 @@ ThreadsListEnumerator::ThreadsListEnumerator(Thread* cur_thread, // jdk.internal.vm.ThreadSnapshot support #if INCLUDE_JVMTI -class GetThreadSnapshotClosure: public HandshakeClosure { +class GetThreadSnapshotHandshakeClosure: public HandshakeClosure { private: static OopStorage* oop_storage() { assert(_thread_service_storage != nullptr, "sanity"); @@ -1180,14 +1180,14 @@ public: GrowableArray* _locks; Blocker _blocker; - GetThreadSnapshotClosure(Handle thread_h, JavaThread* java_thread): - HandshakeClosure("GetThreadSnapshotClosure"), + GetThreadSnapshotHandshakeClosure(Handle thread_h, JavaThread* java_thread): + HandshakeClosure("GetThreadSnapshotHandshakeClosure"), _thread_h(thread_h), _java_thread(java_thread), _frame_count(0), _methods(nullptr), _bcis(nullptr), _thread_status(), _thread_name(nullptr), _locks(nullptr), _blocker() { } - virtual ~GetThreadSnapshotClosure() { + virtual ~GetThreadSnapshotHandshakeClosure() { delete _methods; delete _bcis; _thread_name.release(oop_storage()); @@ -1439,7 +1439,17 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { ResourceMark rm(THREAD); HandleMark hm(THREAD); - Handle thread_h(THREAD, JNIHandles::resolve(jthread)); + + JavaThread* java_thread = nullptr; + oop thread_oop; + bool has_javathread = tlh.cv_internal_thread_to_JavaThread(jthread, &java_thread, &thread_oop); + assert((has_javathread && thread_oop != nullptr) || !has_javathread, "Missing Thread oop"); + Handle thread_h(THREAD, thread_oop); + bool is_virtual = java_lang_VirtualThread::is_instance(thread_h()); // Deals with null + + if (!has_javathread && !is_virtual) { + return nullptr; // thread terminated so not of interest + } // wrapper to auto delete JvmtiVTMSTransitionDisabler class TransitionDisabler { @@ -1460,8 +1470,6 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { } } transition_disabler; - JavaThread* java_thread = nullptr; - bool is_virtual = java_lang_VirtualThread::is_instance(thread_h()); Handle carrier_thread; if (is_virtual) { // 1st need to disable mount/unmount transitions @@ -1476,7 +1484,7 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { } // Handshake with target - GetThreadSnapshotClosure cl(thread_h, java_thread); + GetThreadSnapshotHandshakeClosure cl(thread_h, java_thread); if (java_thread == nullptr) { // unmounted vthread, execute on the current thread cl.do_thread(nullptr); @@ -1508,7 +1516,7 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { if (cl._locks != nullptr && cl._locks->length() > 0) { locks = oopFactory::new_objArray_handle(lock_klass, cl._locks->length(), CHECK_NULL); for (int n = 0; n < cl._locks->length(); n++) { - GetThreadSnapshotClosure::OwnedLock* lock_info = cl._locks->adr_at(n); + GetThreadSnapshotHandshakeClosure::OwnedLock* lock_info = cl._locks->adr_at(n); Handle lock = jdk_internal_vm_ThreadLock::create(lock_klass, lock_info->_frame_depth, lock_info->_type, lock_info->_obj, CHECK_NULL); diff --git a/src/java.base/aix/classes/sun/nio/fs/AixFileSystemProvider.java b/src/java.base/aix/classes/sun/nio/fs/AixFileSystemProvider.java index 5c5c7a1865a..fabd2086fa5 100644 --- a/src/java.base/aix/classes/sun/nio/fs/AixFileSystemProvider.java +++ b/src/java.base/aix/classes/sun/nio/fs/AixFileSystemProvider.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 SAP SE. All rights reserved. + * Copyright (c) 2013, 2025 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 @@ -52,6 +52,15 @@ class AixFileSystemProvider extends UnixFileSystemProvider { return new AixFileStore(path); } + private static boolean supportsUserDefinedFileAttributeView(UnixPath file) { + try { + FileStore store = new AixFileStore(file); + return store.supportsFileAttributeView(UserDefinedFileAttributeView.class); + } catch (IOException e) { + return false; + } + } + @Override @SuppressWarnings("unchecked") public V getFileAttributeView(Path obj, @@ -59,8 +68,10 @@ class AixFileSystemProvider extends UnixFileSystemProvider { LinkOption... options) { if (type == UserDefinedFileAttributeView.class) { - return (V) new AixUserDefinedFileAttributeView(UnixPath.toUnixPath(obj), - Util.followLinks(options)); + UnixPath file = UnixPath.toUnixPath(obj); + return supportsUserDefinedFileAttributeView(file) ? + (V) new AixUserDefinedFileAttributeView(file, Util.followLinks(options)) + : null; } return super.getFileAttributeView(obj, type, options); } @@ -71,8 +82,10 @@ class AixFileSystemProvider extends UnixFileSystemProvider { LinkOption... options) { if (name.equals("user")) { - return new AixUserDefinedFileAttributeView(UnixPath.toUnixPath(obj), - Util.followLinks(options)); + UnixPath file = UnixPath.toUnixPath(obj); + return supportsUserDefinedFileAttributeView(file) ? + new AixUserDefinedFileAttributeView(file, Util.followLinks(options)) + : null; } return super.getFileAttributeView(obj, name, options); } diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java index b7f03c1b0af..fd5627e2a00 100644 --- a/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/src/java.base/share/classes/java/util/regex/Pattern.java @@ -4154,7 +4154,7 @@ loop: for(int x=0, offset=0; x dumpThreads(c, writer)); } - private static void dumpThread(Thread thread, TextWriter writer) { + private static boolean dumpThread(Thread thread, TextWriter writer) { ThreadSnapshot snapshot = ThreadSnapshot.of(thread); + if (snapshot == null) { + return false; // thread terminated + } Instant now = Instant.now(); Thread.State state = snapshot.threadState(); writer.println("#" + thread.threadId() + " \"" + snapshot.threadName() @@ -217,6 +220,7 @@ public class ThreadDumper { depth++; } writer.println(); + return true; } /** @@ -284,8 +288,9 @@ public class ThreadDumper { Iterator threads = container.threads().iterator(); while (threads.hasNext()) { Thread thread = threads.next(); - dumpThread(thread, jsonWriter); - threadCount++; + if (dumpThread(thread, jsonWriter)) { + threadCount++; + } } jsonWriter.endArray(); // threads @@ -303,11 +308,15 @@ public class ThreadDumper { /** * Write a thread to the given JSON writer. + * @return true if the thread dump was written, false otherwise * @throws UncheckedIOException if an I/O error occurs */ - private static void dumpThread(Thread thread, JsonWriter jsonWriter) { + private static boolean dumpThread(Thread thread, JsonWriter jsonWriter) { Instant now = Instant.now(); ThreadSnapshot snapshot = ThreadSnapshot.of(thread); + if (snapshot == null) { + return false; // thread terminated + } Thread.State state = snapshot.threadState(); StackTraceElement[] stackTrace = snapshot.stackTrace(); @@ -369,6 +378,7 @@ public class ThreadDumper { } jsonWriter.endObject(); + return true; } /** diff --git a/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java b/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java index e0dd4bbc508..4fcbaf24d2e 100644 --- a/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java +++ b/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java @@ -52,12 +52,14 @@ class ThreadSnapshot { /** * Take a snapshot of a Thread to get all information about the thread. + * Return null if a ThreadSnapshot is not created, for example if the + * thread has terminated. * @throws UnsupportedOperationException if not supported by VM */ static ThreadSnapshot of(Thread thread) { ThreadSnapshot snapshot = create(thread); if (snapshot == null) { - throw new UnsupportedOperationException(); + return null; // thread terminated } if (snapshot.stackTrace == null) { snapshot.stackTrace = EMPTY_STACK; diff --git a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java index 2df6d26ff31..98e4693e917 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, 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 @@ -46,7 +46,7 @@ final class KeyShareExtension { new CHKeyShareProducer(); static final ExtensionConsumer chOnLoadConsumer = new CHKeyShareConsumer(); - static final HandshakeAbsence chOnTradAbsence = + static final HandshakeAbsence chOnTradeAbsence = new CHKeyShareOnTradeAbsence(); static final SSLStringizer chStringizer = new CHKeyShareStringizer(); diff --git a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java index 76bb64a66c3..819fdd589cb 100644 --- a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java @@ -58,7 +58,7 @@ final class PreSharedKeyExtension { new CHPreSharedKeyOnLoadAbsence(); static final HandshakeConsumer chOnTradeConsumer = new CHPreSharedKeyUpdate(); - static final HandshakeAbsence chOnTradAbsence = + static final HandshakeAbsence chOnTradeAbsence = new CHPreSharedKeyOnTradeAbsence(); static final SSLStringizer chStringizer = new CHPreSharedKeyStringizer(); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index b28ef763796..c7175ea7fdc 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, 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 @@ -142,7 +142,7 @@ enum SSLExtension implements SSLStringizer { SupportedGroupsExtension.chOnLoadConsumer, null, null, - SupportedGroupsExtension.chOnTradAbsence, + SupportedGroupsExtension.chOnTradeAbsence, SupportedGroupsExtension.sgsStringizer), EE_SUPPORTED_GROUPS (0x000A, "supported_groups", SSLHandshake.ENCRYPTED_EXTENSIONS, @@ -433,7 +433,7 @@ enum SSLExtension implements SSLStringizer { KeyShareExtension.chOnLoadConsumer, null, null, - KeyShareExtension.chOnTradAbsence, + KeyShareExtension.chOnTradeAbsence, KeyShareExtension.chStringizer), SH_KEY_SHARE (0x0033, "key_share", SSLHandshake.SERVER_HELLO, @@ -486,7 +486,7 @@ enum SSLExtension implements SSLStringizer { PreSharedKeyExtension.chOnLoadConsumer, PreSharedKeyExtension.chOnLoadAbsence, PreSharedKeyExtension.chOnTradeConsumer, - PreSharedKeyExtension.chOnTradAbsence, + PreSharedKeyExtension.chOnTradeAbsence, PreSharedKeyExtension.chStringizer), SH_PRE_SHARED_KEY (0x0029, "pre_shared_key", SSLHandshake.SERVER_HELLO, diff --git a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java index d6e1391d09b..57e5f8c9093 100644 --- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, 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,7 +47,7 @@ final class SupportedGroupsExtension { new CHSupportedGroupsProducer(); static final ExtensionConsumer chOnLoadConsumer = new CHSupportedGroupsConsumer(); - static final HandshakeAbsence chOnTradAbsence = + static final HandshakeAbsence chOnTradeAbsence = new CHSupportedGroupsOnTradeAbsence(); static final SSLStringizer sgsStringizer = new SupportedGroupsStringizer(); diff --git a/src/java.base/windows/native/libnio/ch/Net.c b/src/java.base/windows/native/libnio/ch/Net.c index 105cb9cf743..814f502c48a 100644 --- a/src/java.base/windows/native/libnio/ch/Net.c +++ b/src/java.base/windows/native/libnio/ch/Net.c @@ -214,6 +214,13 @@ Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean prefe JNIEXPORT void JNICALL Java_sun_nio_ch_Net_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog) { + /* + * Use SOMAXCONN_HINT when backlog larger than 200. It will adjust the value + * to be within the range (200, 65535). + */ + if (backlog > 200) { + backlog = SOMAXCONN_HINT(backlog); + } if (listen(fdval(env,fdo), backlog) == SOCKET_ERROR) { NET_ThrowNew(env, WSAGetLastError(), "listen"); } diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java index f9779afdf47..ea72dbd2b17 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaKeyBindings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, 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 @@ -423,10 +423,14 @@ public class AquaKeyBindings { "KP_UP", "selectPrevious", "shift UP", "selectPreviousExtendSelection", "shift KP_UP", "selectPreviousExtendSelection", + "shift ctrl UP", "selectPreviousExtendSelection", + "shift ctrl KP_UP", "selectPreviousExtendSelection", "DOWN", "selectNext", "KP_DOWN", "selectNext", "shift DOWN", "selectNextExtendSelection", "shift KP_DOWN", "selectNextExtendSelection", + "shift ctrl DOWN", "selectNextExtendSelection", + "shift ctrl KP_DOWN", "selectNextExtendSelection", "RIGHT", "aquaExpandNode", "KP_RIGHT", "aquaExpandNode", "LEFT", "aquaCollapseNode", diff --git a/src/java.desktop/share/classes/java/awt/GraphicsDevice.java b/src/java.desktop/share/classes/java/awt/GraphicsDevice.java index ed331e4915a..63b9543014e 100644 --- a/src/java.desktop/share/classes/java/awt/GraphicsDevice.java +++ b/src/java.desktop/share/classes/java/awt/GraphicsDevice.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -27,7 +27,6 @@ package java.awt; import java.awt.image.ColorModel; -import sun.awt.AppContext; import sun.awt.SunToolkit; /** @@ -75,13 +74,7 @@ import sun.awt.SunToolkit; */ public abstract class GraphicsDevice { - private Window fullScreenWindow; - private AppContext fullScreenAppContext; // tracks which AppContext - // created the FS window - // this lock is used for making synchronous changes to the AppContext's - // current full screen window - private final Object fsAppContextLock = new Object(); - + private volatile Window fullScreenWindow; private Rectangle windowedModeBounds; /** @@ -303,15 +296,7 @@ public abstract class GraphicsDevice { fullScreenWindow.setBounds(windowedModeBounds); } // Set the full screen window - synchronized (fsAppContextLock) { - // Associate fullscreen window with current AppContext - if (w == null) { - fullScreenAppContext = null; - } else { - fullScreenAppContext = AppContext.getAppContext(); - } - fullScreenWindow = w; - } + fullScreenWindow = w; if (fullScreenWindow != null) { windowedModeBounds = fullScreenWindow.getBounds(); // Note that we use the graphics configuration of the device, @@ -319,7 +304,7 @@ public abstract class GraphicsDevice { // this device. final GraphicsConfiguration gc = getDefaultConfiguration(); final Rectangle screenBounds = gc.getBounds(); - if (SunToolkit.isDispatchThreadForAppContext(fullScreenWindow)) { + if (EventQueue.isDispatchThread()) { // Update graphics configuration here directly and do not wait // asynchronous notification from the peer. Note that // setBounds() will reset a GC, if it was set incorrectly. @@ -342,15 +327,7 @@ public abstract class GraphicsDevice { * @since 1.4 */ public Window getFullScreenWindow() { - Window returnWindow = null; - synchronized (fsAppContextLock) { - // Only return a handle to the current fs window if we are in the - // same AppContext that set the fs window - if (fullScreenAppContext == AppContext.getAppContext()) { - returnWindow = fullScreenWindow; - } - } - return returnWindow; + return fullScreenWindow; } /** diff --git a/src/java.desktop/share/classes/javax/swing/border/LineBorder.java b/src/java.desktop/share/classes/javax/swing/border/LineBorder.java index 9116731eaf0..7fcb644a261 100644 --- a/src/java.desktop/share/classes/javax/swing/border/LineBorder.java +++ b/src/java.desktop/share/classes/javax/swing/border/LineBorder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -37,6 +37,8 @@ import java.beans.ConstructorProperties; import com.sun.java.swing.SwingUtilities3; +import static sun.java2d.pipe.Region.clipRound; + /** * A class which implements a line border of arbitrary thickness * and of a single color. @@ -161,7 +163,7 @@ public class LineBorder extends AbstractBorder Shape outer; Shape inner; - int offs = this.thickness * (int) scaleFactor; + int offs = clipRound(this.thickness * scaleFactor); int size = offs + offs; if (this.roundedCorners) { float arc = .2f * offs; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java index 52037ba497f..088e8c66b04 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java @@ -41,7 +41,7 @@ import jdk.internal.net.http.common.Alpns; import jdk.internal.net.http.common.SSLTube; import jdk.internal.net.http.common.Log; import jdk.internal.net.http.common.Utils; -import static jdk.internal.net.http.common.Utils.ServerName; +import sun.net.util.IPAddressUtil; /** * Asynchronous version of SSLConnection. @@ -63,6 +63,10 @@ import static jdk.internal.net.http.common.Utils.ServerName; */ abstract class AbstractAsyncSSLConnection extends HttpConnection { + + private record ServerName(String name, boolean isLiteral) { + } + protected final SSLEngine engine; protected final SSLParameters sslParameters; private final List sniServerNames; @@ -71,17 +75,19 @@ abstract class AbstractAsyncSSLConnection extends HttpConnection private static final boolean disableHostnameVerification = Utils.isHostnameVerificationDisabled(); - AbstractAsyncSSLConnection(InetSocketAddress addr, + AbstractAsyncSSLConnection(Origin originServer, + InetSocketAddress addr, HttpClientImpl client, - ServerName serverName, int port, String[] alpn, String label) { - super(addr, client, label); + super(originServer, addr, client, label); + assert originServer != null : "origin server is null"; + final ServerName serverName = getServerName(originServer); this.sniServerNames = formSNIServerNames(serverName, client); SSLContext context = client.theSSLContext(); sslParameters = createSSLParameters(client, this.sniServerNames, alpn); Log.logParams(sslParameters); - engine = createEngine(context, serverName.name(), port, sslParameters); + engine = createEngine(context, serverName.name(), originServer.port(), sslParameters); } abstract SSLTube getConnectionFlow(); @@ -187,6 +193,23 @@ abstract class AbstractAsyncSSLConnection extends HttpConnection return engine; } + /** + * Analyse the given {@linkplain Origin origin server} and determine + * if the origin server's host is a literal or not, returning the server's + * address in String form. + */ + private static ServerName getServerName(final Origin originServer) { + final String host = originServer.host(); + byte[] literal = IPAddressUtil.textToNumericFormatV4(host); + if (literal == null) { + // not IPv4 literal. Check IPv6 + literal = IPAddressUtil.textToNumericFormatV6(host); + return new ServerName(host, literal != null); + } else { + return new ServerName(host, true); + } + } + @Override final boolean isSecure() { return true; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java index 56477e9604e..c02cd145cf8 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLConnection.java @@ -42,12 +42,13 @@ class AsyncSSLConnection extends AbstractAsyncSSLConnection { final PlainHttpPublisher writePublisher; private volatile SSLTube flow; - AsyncSSLConnection(InetSocketAddress addr, + AsyncSSLConnection(Origin originServer, + InetSocketAddress addr, HttpClientImpl client, String[] alpn, String label) { - super(addr, client, Utils.getServerName(addr), addr.getPort(), alpn, label); - plainConnection = new PlainHttpConnection(addr, client, label); + super(originServer, addr, client, alpn, label); + plainConnection = new PlainHttpConnection(originServer, addr, client, label); writePublisher = new PlainHttpPublisher(); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java index 1210f4dd62b..a81d8de328e 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/AsyncSSLTunnelConnection.java @@ -43,15 +43,17 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection { final PlainHttpPublisher writePublisher; volatile SSLTube flow; - AsyncSSLTunnelConnection(InetSocketAddress addr, + AsyncSSLTunnelConnection(Origin originServer, + InetSocketAddress addr, HttpClientImpl client, String[] alpn, InetSocketAddress proxy, ProxyHeaders proxyHeaders, String label) { - super(addr, client, Utils.getServerName(addr), addr.getPort(), alpn, label); - this.plainConnection = new PlainTunnelingConnection(addr, proxy, client, proxyHeaders, label); + super(originServer, addr, client, alpn, label); + this.plainConnection = new PlainTunnelingConnection(originServer, addr, proxy, client, + proxyHeaders, label); this.writePublisher = new PlainHttpPublisher(); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java index dd8f6652290..07cfc4dbdf6 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpConnection.java @@ -100,7 +100,11 @@ abstract class HttpConnection implements Closeable { */ private final String label; - HttpConnection(InetSocketAddress address, HttpClientImpl client, String label) { + private final Origin originServer; + + HttpConnection(Origin originServer, InetSocketAddress address, HttpClientImpl client, + String label) { + this.originServer = originServer; this.address = address; this.client = client; trailingOperations = new TrailingOperations(); @@ -244,6 +248,14 @@ abstract class HttpConnection implements Closeable { return false; } + /** + * {@return the {@link Origin} server against which this connection communicates. + * Returns {@code null} if the connection is a plain connection to a proxy} + */ + final Origin getOriginServer() { + return this.originServer; + } + interface HttpPublisher extends FlowTube.TubePublisher { void enqueue(List buffers) throws IOException; void enqueueUnordered(List buffers) throws IOException; @@ -334,13 +346,20 @@ abstract class HttpConnection implements Closeable { String[] alpn, HttpRequestImpl request, HttpClientImpl client) { - String label = nextLabel(); + final String label = nextLabel(); + final Origin originServer; + try { + originServer = Origin.from(request.uri()); + } catch (IllegalArgumentException iae) { + // should never happen + throw new AssertionError("failed to determine origin server from request URI", iae); + } if (proxy != null) - return new AsyncSSLTunnelConnection(addr, client, alpn, proxy, + return new AsyncSSLTunnelConnection(originServer, addr, client, alpn, proxy, proxyTunnelHeaders(request), label); else - return new AsyncSSLConnection(addr, client, alpn, label); + return new AsyncSSLConnection(originServer, addr, client, alpn, label); } /** @@ -414,14 +433,21 @@ abstract class HttpConnection implements Closeable { InetSocketAddress proxy, HttpRequestImpl request, HttpClientImpl client) { - String label = nextLabel(); + final String label = nextLabel(); + final Origin originServer; + try { + originServer = Origin.from(request.uri()); + } catch (IllegalArgumentException iae) { + // should never happen + throw new AssertionError("failed to determine origin server from request URI", iae); + } if (request.isWebSocket() && proxy != null) - return new PlainTunnelingConnection(addr, proxy, client, + return new PlainTunnelingConnection(originServer, addr, proxy, client, proxyTunnelHeaders(request), label); if (proxy == null) - return new PlainHttpConnection(addr, client, label); + return new PlainHttpConnection(originServer, addr, client, label); else return new PlainProxyConnection(proxy, client, label); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Origin.java b/src/java.net.http/share/classes/jdk/internal/net/http/Origin.java new file mode 100644 index 00000000000..adbee565297 --- /dev/null +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Origin.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2025, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 jdk.internal.net.http; + +import java.net.URI; +import java.util.Locale; +import java.util.Objects; + +import sun.net.util.IPAddressUtil; + +/** + * Represents an origin server to which a HTTP request is targeted. + * + * @param scheme The scheme of the origin (for example: https). Unlike the application layer + * protocol (which can be a finer grained protocol like h2, h3 etc...), + * this is actually a scheme. Only {@code http} and {@code https} literals are + * supported. Cannot be null. + * @param host The host of the origin, cannot be null. If the host is an IPv6 address, + * then it must not be enclosed in square brackets ({@code '['} and {@code ']'}). + * If the host is a DNS hostname, then it must be passed as a lower case String. + * @param port The port of the origin. Must be greater than 0. + */ +public record Origin(String scheme, String host, int port) { + public Origin { + Objects.requireNonNull(scheme); + Objects.requireNonNull(host); + if (!isValidScheme(scheme)) { + throw new IllegalArgumentException("Unsupported scheme: " + scheme); + } + if (host.startsWith("[") && host.endsWith("]")) { + throw new IllegalArgumentException("Invalid host: " + host); + } + // expect DNS hostname to be passed as lower case + if (isDNSHostName(host) && !host.toLowerCase(Locale.ROOT).equals(host)) { + throw new IllegalArgumentException("non-lowercase hostname: " + host); + } + if (port <= 0) { + throw new IllegalArgumentException("Invalid port: " + port); + } + } + + @Override + public String toString() { + return scheme + "://" + toAuthority(host, port); + } + + /** + * {@return Creates and returns an Origin from an URI} + * + * @param uri The URI of the origin + * @throws IllegalArgumentException if a Origin cannot be constructed from + * the given {@code uri} + */ + public static Origin from(final URI uri) throws IllegalArgumentException { + Objects.requireNonNull(uri); + final String scheme = uri.getScheme(); + if (scheme == null) { + throw new IllegalArgumentException("missing scheme in URI"); + } + final String lcaseScheme = scheme.toLowerCase(Locale.ROOT); + if (!isValidScheme(lcaseScheme)) { + throw new IllegalArgumentException("Unsupported scheme: " + scheme); + } + final String host = uri.getHost(); + if (host == null) { + throw new IllegalArgumentException("missing host in URI"); + } + String effectiveHost; + if (host.startsWith("[") && host.endsWith("]")) { + // strip the square brackets from IPv6 host + effectiveHost = host.substring(1, host.length() - 1); + } else { + effectiveHost = host; + } + assert !effectiveHost.isEmpty() : "unexpected URI host: " + host; + // If the host is a DNS hostname, then convert the host to lower case. + // The DNS hostname is expected to be ASCII characters and is case-insensitive. + // + // Its usage in areas like SNI too match this expectation - RFC-6066, section 3: + // "HostName" contains the fully qualified DNS hostname of the server, + // as understood by the client. The hostname is represented as a byte + // string using ASCII encoding without a trailing dot. ... DNS hostnames + // are case-insensitive. + if (isDNSHostName(effectiveHost)) { + effectiveHost = effectiveHost.toLowerCase(Locale.ROOT); + } + int port = uri.getPort(); + if (port == -1) { + port = switch (lcaseScheme) { + case "http" -> 80; + case "https" -> 443; + // we have already verified that this is a valid scheme, so this + // should never happen + default -> throw new AssertionError("Unsupported scheme: " + scheme); + }; + } + return new Origin(lcaseScheme, effectiveHost, port); + } + + static String toAuthority(final String host, final int port) { + assert port > 0 : "invalid port: " + port; + // borrowed from code in java.net.URI + final boolean needBrackets = host.indexOf(':') >= 0 + && !host.startsWith("[") + && !host.endsWith("]"); + if (needBrackets) { + return "[" + host + "]:" + port; + } + return host + ":" + port; + } + + private static boolean isValidScheme(final String scheme) { + // only "http" and "https" literals allowed + return "http".equals(scheme) || "https".equals(scheme); + } + + private static boolean isDNSHostName(final String host) { + final boolean isLiteral = IPAddressUtil.isIPv4LiteralAddress(host) + || IPAddressUtil.isIPv6LiteralAddress(host); + + return !isLiteral; + } +} diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java index 190df8a00ba..ff97841d325 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/PlainHttpConnection.java @@ -310,8 +310,9 @@ class PlainHttpConnection extends HttpConnection { return tube; } - PlainHttpConnection(InetSocketAddress addr, HttpClientImpl client, String label) { - super(addr, client, label); + PlainHttpConnection(Origin originServer, InetSocketAddress addr, HttpClientImpl client, + String label) { + super(originServer, addr, client, label); try { this.chan = SocketChannel.open(); chan.configureBlocking(false); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/PlainProxyConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/PlainProxyConnection.java index c11fd489177..c4b1b14a4d2 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/PlainProxyConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/PlainProxyConnection.java @@ -30,7 +30,9 @@ import java.net.InetSocketAddress; class PlainProxyConnection extends PlainHttpConnection { PlainProxyConnection(InetSocketAddress proxy, HttpClientImpl client, String label) { - super(proxy, client, label); + // we don't track the origin server for a plain proxy connection, since it + // can be used to serve requests against several different origin servers. + super(null, proxy, client, label); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java b/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java index 7a8eb9c79c5..147d5938fe5 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/PlainTunnelingConnection.java @@ -51,15 +51,16 @@ final class PlainTunnelingConnection extends HttpConnection { final InetSocketAddress proxyAddr; private volatile boolean connected; - protected PlainTunnelingConnection(InetSocketAddress addr, + protected PlainTunnelingConnection(Origin originServer, + InetSocketAddress addr, InetSocketAddress proxy, HttpClientImpl client, ProxyHeaders proxyHeaders, String label) { - super(addr, client, label); + super(originServer, addr, client, label); this.proxyAddr = proxy; this.proxyHeaders = proxyHeaders; - delegate = new PlainHttpConnection(proxy, client, label); + delegate = new PlainHttpConnection(originServer, proxy, client, label); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java index d035a8c8da1..8aefa0ee5ba 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, 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 @@ -38,7 +38,6 @@ import java.io.UncheckedIOException; import java.lang.System.Logger.Level; import java.net.ConnectException; import java.net.InetSocketAddress; -import java.net.URI; import java.net.http.HttpHeaders; import java.net.http.HttpTimeoutException; import java.nio.ByteBuffer; @@ -73,12 +72,10 @@ import jdk.internal.net.http.common.DebugLogger.LoggerConfig; import jdk.internal.net.http.HttpRequestImpl; import sun.net.NetProperties; -import sun.net.util.IPAddressUtil; import sun.net.www.HeaderParser; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.US_ASCII; -import static java.util.stream.Collectors.joining; import static java.net.Authenticator.RequestorType.PROXY; import static java.net.Authenticator.RequestorType.SERVER; @@ -487,39 +484,6 @@ public final class Utils { return !token.isEmpty(); } - public record ServerName (String name, boolean isLiteral) { - } - - /** - * Analyse the given address and determine if it is literal or not, - * returning the address in String form. - */ - public static ServerName getServerName(InetSocketAddress addr) { - String host = addr.getHostString(); - byte[] literal = IPAddressUtil.textToNumericFormatV4(host); - if (literal == null) { - // not IPv4 literal. Check IPv6 - literal = IPAddressUtil.textToNumericFormatV6(host); - return new ServerName(host, literal != null); - } else { - return new ServerName(host, true); - } - } - - private static boolean isLoopbackLiteral(byte[] bytes) { - if (bytes.length == 4) { - return bytes[0] == 127; - } else if (bytes.length == 16) { - for (int i=0; i<14; i++) - if (bytes[i] != 0) - return false; - if (bytes[15] != 1) - return false; - return true; - } else - throw new InternalError(); - } - /* * Validates an RFC 7230 field-value. * @@ -895,33 +859,6 @@ public final class Utils { return DebugLogger.createHttpLogger(dbgTag, config); } - /** - * Return the host string from a HttpRequestImpl - * - * @param request - * @return - */ - public static String hostString(HttpRequestImpl request) { - URI uri = request.uri(); - int port = uri.getPort(); - String host = uri.getHost(); - - boolean defaultPort; - if (port == -1) { - defaultPort = true; - } else if (uri.getScheme().equalsIgnoreCase("https")) { - defaultPort = port == 443; - } else { - defaultPort = port == 80; - } - - if (defaultPort) { - return host; - } else { - return host + ":" + port; - } - } - /** * Get a logger for debug HPACK traces.The logger should only be used * with levels whose severity is {@code <= DEBUG}. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index 14c5420c7c0..e40a2fbfcea 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -756,6 +756,11 @@ public class Gen extends JCTree.Visitor { } CondItem result = genCond(tree.expr, markBranches); code.endScopes(limit); + //make sure variables defined in the let expression are not included + //in the defined variables for jumps that go outside of this let + //expression: + undefineVariablesInChain(result.falseJumps, limit); + undefineVariablesInChain(result.trueJumps, limit); return result; } else { CondItem result = genExpr(_tree, syms.booleanType).mkCond(); @@ -763,6 +768,13 @@ public class Gen extends JCTree.Visitor { return result; } } + //where: + private void undefineVariablesInChain(Chain toClear, int limit) { + while (toClear != null) { + toClear.state.defined.excludeFrom(limit); + toClear = toClear.next; + } + } public Code getCode() { return code; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index b9579332308..522248ce93f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -31,7 +31,6 @@ import java.util.Arrays; import java.util.Comparator; import java.util.EnumMap; import java.util.EnumSet; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; diff --git a/src/utils/IdealGraphVisualizer/README.md b/src/utils/IdealGraphVisualizer/README.md index 195878a5b99..7f48702c769 100644 --- a/src/utils/IdealGraphVisualizer/README.md +++ b/src/utils/IdealGraphVisualizer/README.md @@ -33,7 +33,7 @@ Ideal graphs are dumped at the following points: * `N=2`: additionally, after every major phase * `N=3`: additionally, after every minor phase * `N=4`: additionally, after every loop optimization -* `N=5`: additionally, after every effective IGVN and every macro expansion step (slow) +* `N=5`: additionally, after every effective IGVN, macro elimination, and macro expansion step (slow) * `N=6`: additionally, after parsing every bytecode (very slow) By default the JVM expects that it will connect to a visualizer on the local diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index aa262aaf575..82c205d5b09 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -82,6 +82,8 @@ compiler/c2/TestVerifyConstraintCasts.java 8355574 generic-all compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 +compiler/onSpinWait/TestOnSpinWaitAArch64.java 8360936 linux-aarch64,macosx-aarch64 + ############################################################################# # :hotspot_gc diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 9071dfa2fbf..1857978ebc0 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -79,6 +79,7 @@ requires.properties= \ vm.rtm.cpu \ vm.rtm.compiler \ vm.cds \ + vm.cds.default.archive.available \ vm.cds.custom.loaders \ vm.cds.supports.aot.class.linking \ vm.cds.supports.aot.code.caching \ diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index c6609e248d3..77f49cc2c47 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -416,6 +416,7 @@ hotspot_appcds_dynamic = \ -runtime/cds/appcds/aotClassLinking \ -runtime/cds/appcds/aotCode \ -runtime/cds/appcds/aotFlags \ + -runtime/cds/appcds/aotProfile \ -runtime/cds/appcds/applications \ -runtime/cds/appcds/cacheObject \ -runtime/cds/appcds/complexURI \ @@ -510,14 +511,17 @@ hotspot_cds_epsilongc = \ runtime/cds/appcds/jigsaw \ runtime/cds/appcds/loaderConstraints -# Run CDS tests with -XX:+AOTClassLinking. This should include most CDS tests, except for +# Run "old" CDS tests with -XX:+AOTClassLinking. This should include most CDS tests, except for # those that rely on redefining classes that are already archived. +# Note that appcds/aotXXX directories are excluded -- those tests already specifically +# test AOT class linking, so there's no need to run them again with -XX:+AOTClassLinking. hotspot_aot_classlinking = \ runtime/cds \ -runtime/cds/appcds/aotCache \ -runtime/cds/appcds/aotClassLinking \ -runtime/cds/appcds/aotCode \ -runtime/cds/appcds/aotFlags \ + -runtime/cds/appcds/aotProfile \ -runtime/cds/appcds/BadBSM.java \ -runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \ -runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \ diff --git a/test/hotspot/jtreg/compiler/arguments/TestCompilerCounts.java b/test/hotspot/jtreg/compiler/arguments/TestCompilerCounts.java index 420dc2b3414..870daa7f0a5 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestCompilerCounts.java +++ b/test/hotspot/jtreg/compiler/arguments/TestCompilerCounts.java @@ -1,5 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * Copyright (c) 2025, 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 @@ -27,7 +28,18 @@ * @bug 8356000 * @requires vm.flagless * @requires vm.bits == "64" - * @run driver compiler.arguments.TestCompilerCounts + * @requires vm.debug + * @run driver compiler.arguments.TestCompilerCounts debug + */ + +/* + * @test + * @library /test/lib / + * @bug 8356000 + * @requires vm.flagless + * @requires vm.bits == "64" + * @requires !vm.debug + * @run driver compiler.arguments.TestCompilerCounts product */ package compiler.arguments; @@ -55,6 +67,8 @@ public class TestCompilerCounts { static final int MAX_LINEAR_CPUS = Math.min(16, MAX_CPUS); public static void main(String[] args) throws Throwable { + final boolean debug = args[0].startsWith("debug"); + // CICompilerCount=0 is incorrect in default modes. fail("-XX:CICompilerCount=0"); @@ -133,31 +147,51 @@ public class TestCompilerCounts { pass(0, opt, "-XX:TieredStopAtLevel=0"); // Non-tiered modes - int nonTieredCount = heuristicCount(cpus, false); - pass(nonTieredCount, opt, "-XX:TieredStopAtLevel=1"); - pass(nonTieredCount, opt, "-XX:TieredStopAtLevel=2"); - pass(nonTieredCount, opt, "-XX:TieredStopAtLevel=3"); - pass(nonTieredCount, opt, "-XX:-TieredCompilation"); + int c1OnlyCount = heuristicCount(cpus, Compilation.C1Only, debug); + pass(c1OnlyCount, opt, "-XX:TieredStopAtLevel=1", "-XX:NonNMethodCodeHeapSize=" + NonNMethodCodeHeapSize); + pass(c1OnlyCount, opt, "-XX:TieredStopAtLevel=2", "-XX:NonNMethodCodeHeapSize=" + NonNMethodCodeHeapSize); + pass(c1OnlyCount, opt, "-XX:TieredStopAtLevel=3", "-XX:NonNMethodCodeHeapSize=" + NonNMethodCodeHeapSize); + int c2OnlyCount = heuristicCount(cpus, Compilation.C2Only, debug); + pass(c2OnlyCount, opt, "-XX:-TieredCompilation", "-XX:NonNMethodCodeHeapSize=" + NonNMethodCodeHeapSize); // Tiered modes - int tieredCount = heuristicCount(cpus, true); - pass(tieredCount, opt); - pass(tieredCount, opt, "-XX:TieredStopAtLevel=4"); + int tieredCount = heuristicCount(cpus, Compilation.Tiered, debug); + pass(tieredCount, opt, "-XX:NonNMethodCodeHeapSize=" + NonNMethodCodeHeapSize); + pass(tieredCount, opt, "-XX:TieredStopAtLevel=4", "-XX:NonNMethodCodeHeapSize=" + NonNMethodCodeHeapSize); // Also check that heuristics did not set up more threads than CPUs available - Asserts.assertTrue(nonTieredCount <= cpus, - "Non-tiered count is larger than number of CPUs: " + nonTieredCount + " > " + cpus); + Asserts.assertTrue(c1OnlyCount <= cpus, + "Non-tiered count is larger than number of CPUs: " + c1OnlyCount + " > " + cpus); Asserts.assertTrue(tieredCount <= cpus, "Tiered count is larger than number of CPUs: " + tieredCount + " > " + cpus); } } + enum Compilation { + C1Only, + C2Only, + Tiered, + } + + // Buffer sizes for calculating the maximum number of compiler threads. + static final int NonNMethodCodeHeapSize = 5 * 1024 * 1024; + static final int CodeCacheMinimumUseSpace = 400 * 1024; + static final int C1BufSize = 64 * 1024 * 8 + (64 * 1024 * 8 / 10); + static final int C2BufSize = 6544; + static final int TieredBufSize = C1BufSize / 3 + 2 * C2BufSize / 3; + // Direct translation from CompilationPolicy::initialize: - public static int heuristicCount(int cpus, boolean tiered) { + public static int heuristicCount(int cpus, Compilation comp, boolean debug) { int log_cpu = log2(cpus); int loglog_cpu = log2(Math.max(log_cpu, 1)); - int min_count = tiered ? 2 : 1; - return Math.max(log_cpu * loglog_cpu * 3 / 2, min_count); + int min_count = comp == Compilation.C1Only || comp == Compilation.C2Only ? 1 : 2; + int count = Math.max(log_cpu * loglog_cpu * 3 / 2, min_count); + int max_count = (NonNMethodCodeHeapSize - (debug ? 3 : 1) * CodeCacheMinimumUseSpace) / switch (comp) { + case C1Only -> C1BufSize; + case C2Only -> C2BufSize; + case Tiered -> TieredBufSize; + }; + return Math.max(Math.min(count, max_count), min_count); } public static int log2(int v) { @@ -173,5 +207,4 @@ public class TestCompilerCounts { OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotHaveExitValue(0); } - } diff --git a/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java b/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java index ccf4822e676..534ec9d2d97 100644 --- a/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java +++ b/test/hotspot/jtreg/compiler/arguments/TestStressOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -24,7 +24,7 @@ /* * @test * @key stress randomness - * @bug 8252219 8256535 8317349 8319879 8335334 + * @bug 8252219 8256535 8317349 8319879 8335334 8325478 * @requires vm.compiler2.enabled * @summary Tests that different combinations of stress options and * -XX:StressSeed=N are accepted. @@ -56,6 +56,10 @@ * compiler.arguments.TestStressOptions * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressUnstableIfTraps -XX:StressSeed=42 * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressMacroElimination + * compiler.arguments.TestStressOptions + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+StressMacroElimination -XX:StressSeed=42 + * compiler.arguments.TestStressOptions */ package compiler.arguments; diff --git a/test/hotspot/jtreg/compiler/c2/TestModControlFoldedAfterCCP.java b/test/hotspot/jtreg/compiler/c2/TestModControlFoldedAfterCCP.java new file mode 100644 index 00000000000..1ac3281ca3f --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestModControlFoldedAfterCCP.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8359602 + * @summary The control input of a ModI node should be discarded if it is + * possible to prove that the divisor can never be 0. + * VerifyIterativeGVN checks that this optimization was applied + * @run main/othervm -XX:CompileCommand=quiet -XX:-TieredCompilation + * -XX:+UnlockDiagnosticVMOptions -Xcomp -XX:+IgnoreUnrecognizedVMOptions + * -XX:CompileCommand=compileonly,compiler.c2.TestModControlFoldedAfterCCP::test + * -XX:VerifyIterativeGVN=1110 compiler.c2.TestModControlFoldedAfterCCP + * @run main compiler.c2.TestModControlFoldedAfterCCP + * + */ + +package compiler.c2; + +public class TestModControlFoldedAfterCCP { + static void test() { + int i22, i24 = -1191, i28; + int iArr1[] = new int[1]; + for (int i = 1;i < 100; i++) { + for (int j = 4; j > i; j--) { + i22 = i24; + + // divisor is either -1191 or -13957 + iArr1[0] = 5 % i22; + } + for (i28 = i; i28 < 2; ++i28) { + i24 = -13957; + } + } + } + + public static void main(String[] args) { + test(); + } +} \ No newline at end of file diff --git a/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java b/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java index bbcafb53460..9542e48e54e 100644 --- a/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java +++ b/test/hotspot/jtreg/compiler/debug/TestGenerateStressSeed.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -30,7 +30,7 @@ import jdk.test.lib.process.ProcessTools; /* * @test * @key stress randomness - * @bug 8252219 8256535 + * @bug 8252219 8256535 8325478 * @requires vm.compiler2.enabled * @summary Tests that using a stress option without -XX:StressSeed=N generates * and logs a random seed. @@ -40,6 +40,7 @@ import jdk.test.lib.process.ProcessTools; * @run driver compiler.debug.TestGenerateStressSeed StressIGVN * @run driver compiler.debug.TestGenerateStressSeed StressCCP * @run driver compiler.debug.TestGenerateStressSeed StressMacroExpansion + * @run driver compiler.debug.TestGenerateStressSeed StressMacroElimination */ public class TestGenerateStressSeed { diff --git a/test/hotspot/jtreg/compiler/debug/TestStress.java b/test/hotspot/jtreg/compiler/debug/TestStress.java index 6678d09e649..2046488ac40 100644 --- a/test/hotspot/jtreg/compiler/debug/TestStress.java +++ b/test/hotspot/jtreg/compiler/debug/TestStress.java @@ -30,11 +30,11 @@ import jdk.test.lib.Asserts; /* * @test * @key stress randomness - * @bug 8252219 8256535 8317349 + * @bug 8252219 8256535 8317349 8325478 * @requires vm.debug == true & vm.compiler2.enabled * @requires vm.flagless * @summary Tests that stress compilations with the same seed yield the same - * IGVN, CCP, and macro expansion traces. + * IGVN, CCP, macro elimination, and macro expansion traces. * @library /test/lib / * @run driver compiler.debug.TestStress */ @@ -69,6 +69,12 @@ public class TestStress { stressSeed); } + static String macroEliminationTrace(int stressSeed) throws Exception { + return phaseTrace("StressMacroElimination", + "CompileCommand=PrintIdealPhase,*::*,AFTER_MACRO_ELIMINATION_STEP", + stressSeed); + } + static void sum(int n) { int acc = 0; for (int i = 0; i < n; i++) acc += i; @@ -84,6 +90,8 @@ public class TestStress { "got different CCP traces for the same seed"); Asserts.assertEQ(macroExpansionTrace(s), macroExpansionTrace(s), "got different macro expansion traces for the same seed"); + Asserts.assertEQ(macroEliminationTrace(s), macroEliminationTrace(s), + "got different macro elimination traces for the same seed"); } } else if (args.length > 0) { sum(Integer.parseInt(args[0])); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java b/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java index b0e5f2fda5c..ad45b1d0856 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java @@ -99,6 +99,8 @@ public enum CompilePhase { PHASEIDEALLOOP_ITERATIONS( "PhaseIdealLoop Iterations"), AFTER_LOOP_OPTS( "After Loop Optimizations"), AFTER_MERGE_STORES( "After Merge Stores"), + AFTER_MACRO_ELIMINATION_STEP( "After Macro Elimination Step"), + AFTER_MACRO_ELIMINATION( "After Macro Elimination"), BEFORE_MACRO_EXPANSION( "Before Macro Expansion"), AFTER_MACRO_EXPANSION_STEP( "After Macro Expansion Step"), AFTER_MACRO_EXPANSION( "After Macro Expansion"), diff --git a/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java b/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java index d90618b51f2..4dd3b63c84a 100644 --- a/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java +++ b/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java @@ -26,6 +26,7 @@ * @test id=nocoops_nocoh * @summary Test Loading of default archives in all configurations * @requires vm.cds + * @requires vm.cds.default.archive.available * @requires vm.cds.write.archived.java.heap * @requires vm.bits == 64 * @library /test/lib @@ -38,6 +39,7 @@ * @test id=nocoops_coh * @summary Test Loading of default archives in all configurations (requires --enable-cds-archive-coh) * @requires vm.cds + * @requires vm.cds.default.archive.available * @requires vm.cds.write.archived.java.heap * @requires vm.bits == 64 * @library /test/lib @@ -50,6 +52,7 @@ * @test id=coops_nocoh * @summary Test Loading of default archives in all configurations * @requires vm.cds + * @requires vm.cds.default.archive.available * @requires vm.cds.write.archived.java.heap * @requires vm.bits == 64 * @library /test/lib @@ -62,6 +65,7 @@ * @test id=coops_coh * @summary Test Loading of default archives in all configurations (requires --enable-cds-archive-coh) * @requires vm.cds + * @requires vm.cds.default.archive.available * @requires vm.cds.write.archived.java.heap * @requires vm.bits == 64 * @library /test/lib diff --git a/test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java b/test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java index 21a43fcf9cb..b433458a059 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java @@ -67,6 +67,8 @@ public class ArchiveRelocationTest { String logArg = "-Xlog:cds=debug,cds+reloc=debug,aot+heap"; String unlockArg = "-XX:+UnlockDiagnosticVMOptions"; String nmtArg = "-XX:NativeMemoryTracking=detail"; + String relocMsg1 = "ArchiveRelocationMode == 1: always map archive(s) at an alternative address"; + String relocMsg2 = "Try to map archive(s) at an alternative address"; OutputAnalyzer out = TestCommon.dump(appJar, TestCommon.list(mainClass), @@ -76,8 +78,10 @@ public class ArchiveRelocationTest { TestCommon.run("-cp", appJar, unlockArg, runRelocArg, logArg, mainClass) .assertNormalExit(output -> { if (run_reloc) { - output.shouldContain("ArchiveRelocationMode == 1: always map archive(s) at an alternative address") - .shouldContain("Try to map archive(s) at an alternative address"); + if (!output.contains(relocMsg1) && !output.contains(relocMsg2)) { + throw new RuntimeException("Relocation messages \"" + relocMsg1 + + "\" and \"" + relocMsg2 + "\" are missing from the output"); + } } else { output.shouldContain("ArchiveRelocationMode: 0"); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/TestDumpClassListSource.java b/test/hotspot/jtreg/runtime/cds/appcds/TestDumpClassListSource.java index 28e92ac3700..1650cddee3f 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/TestDumpClassListSource.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/TestDumpClassListSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ * @summary test dynamic dump meanwhile output loaded class list * @bug 8279009 8275084 * @requires vm.cds + * @requires vm.cds.default.archive.available * @requires vm.cds.custom.loaders * @requires vm.flagless * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds diff --git a/test/hotspot/jtreg/runtime/cds/appcds/TransformInterfaceOfLambda.java b/test/hotspot/jtreg/runtime/cds/appcds/TransformInterfaceOfLambda.java index 8bbc713fa6f..88620fc039f 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/TransformInterfaceOfLambda.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/TransformInterfaceOfLambda.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ * @summary Transforming an interface of an archived lambda proxy class should not * crash the VM. The lambda proxy class should be regenerated during runtime. * @requires vm.cds + * @requires vm.cds.default.archive.available * @requires vm.jvmti * @requires vm.flagless * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompileEagerly.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompileEagerly.java new file mode 100644 index 00000000000..c740ecd96f5 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompileEagerly.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +/* + * @test + * @bug 8359436 + * @summary Sanity-check that eager compilation flags are accepted + * @requires vm.cds + * @requires vm.flagless + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @build Hello + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello + * @run driver AOTCompileEagerly + */ + +import java.io.File; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class AOTCompileEagerly { + static final String appJar = ClassFileInstaller.getJarPath("hello.jar"); + static final String aotConfigFile = "hello.aotconfig"; + static final String aotCacheFile = "hello.aot"; + static final String helloClass = "Hello"; + + public static void main(String[] args) throws Exception { + ProcessBuilder pb; + OutputAnalyzer out; + + //---------------------------------------------------------------------- + System.out.println("Training Run"); + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:AOTMode=record", + "-XX:AOTConfiguration=" + aotConfigFile, + "-cp", appJar, helloClass); + + out = CDSTestUtils.executeAndLog(pb, "train"); + out.shouldHaveExitValue(0); + + //---------------------------------------------------------------------- + System.out.println("Assembly Phase"); + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:AOTMode=create", + "-XX:AOTConfiguration=" + aotConfigFile, + "-XX:AOTCache=" + aotCacheFile, + "-cp", appJar); + out = CDSTestUtils.executeAndLog(pb, "asm"); + out.shouldHaveExitValue(0); + + //---------------------------------------------------------------------- + System.out.println("Production Run with AOTCache defaults"); + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:AOTCache=" + aotCacheFile, + "-cp", appJar, helloClass); + out = CDSTestUtils.executeAndLog(pb, "prod-default"); + out.shouldHaveExitValue(0); + + //---------------------------------------------------------------------- + System.out.println("Production Run with AOTCache and eager compilation explicitly ON"); + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:AOTCache=" + aotCacheFile, + "-XX:+UnlockExperimentalVMOptions", + "-XX:+AOTCompileEagerly", + "-cp", appJar, helloClass); + out = CDSTestUtils.executeAndLog(pb, "prod-eager-on"); + out.shouldHaveExitValue(0); + + //---------------------------------------------------------------------- + System.out.println("Production Run with AOTCache and eager compilation explicitly OFF"); + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:AOTCache=" + aotCacheFile, + "-XX:+UnlockExperimentalVMOptions", + "-XX:-AOTCompileEagerly", + "-cp", appJar, helloClass); + out = CDSTestUtils.executeAndLog(pb, "prod-eager-off"); + out.shouldHaveExitValue(0); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java index c808cd95bc7..7c68e4eb783 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java @@ -34,6 +34,8 @@ * TestApp$Foo * TestApp$Foo$Bar * TestApp$Foo$ShouldBeExcluded + * TestApp$Foo$ShouldBeExcludedChild + * TestApp$Foo$Taz * TestApp$MyInvocationHandler * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar * CustyWithLoop @@ -61,7 +63,7 @@ public class ExcludedClasses { public static void main(String[] args) throws Exception { Tester tester = new Tester(); - tester.runAOTWorkflow(); + tester.runAOTWorkflow("AOT", "--two-step-training"); } static class Tester extends CDSAppTester { @@ -77,7 +79,11 @@ public class ExcludedClasses { @Override public String[] vmArgs(RunMode runMode) { return new String[] { - "-Xlog:cds+resolve=trace", + "-Xlog:aot=debug", + "-Xlog:aot+class=debug", + "-Xlog:aot+resolve=trace", + "-Xlog:aot+verification=trace", + "-Xlog:class+load", }; } @@ -90,8 +96,16 @@ public class ExcludedClasses { @Override public void checkExecution(OutputAnalyzer out, RunMode runMode) { - if (isDumping(runMode)) { - out.shouldNotMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I"); + if (runMode == RunMode.TRAINING) { + out.shouldMatch("aot,resolve.*reverted field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I"); + } else if (runMode == RunMode.ASSEMBLY) { + out.shouldNotMatch("aot,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I"); + out.shouldMatch("aot,resolve.*archived method.*TestApp.Foo java/lang/Integer.intValue:[(][)]I => java/lang/Integer"); + } else if (runMode == RunMode.PRODUCTION) { + out.shouldContain("check_verification_constraint: TestApp$Foo$Taz: TestApp$Foo$ShouldBeExcludedChild must be subclass of TestApp$Foo$ShouldBeExcluded"); + out.shouldContain("jdk.jfr.Event source: jrt:/jdk.jfr"); + out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcluded source: .*/app.jar"); + out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcludedChild source: .*/app.jar"); } } } @@ -102,8 +116,8 @@ class TestApp { static volatile Object custArrayInstance; public static void main(String args[]) throws Exception { - // In new workflow, classes from custom loaders are passed from the preimage - // to the final image. See ClassPrelinker::record_unregistered_klasses(). + // In AOT workflow, classes from custom loaders are passed from the preimage + // to the final image. See FinalImageRecipes::record_all_classes(). custInstance = initFromCustomLoader(); custArrayInstance = Array.newInstance(custInstance.getClass(), 0); System.out.println(custArrayInstance); @@ -157,6 +171,7 @@ class TestApp { lambdaHotSpot(); s.hotSpot2(); b.hotSpot3(); + Taz.hotSpot4(); // In JDK mainline, generated proxy classes are excluded from the AOT cache. // In Leyden/premain, generated proxy classes included. The following code should @@ -165,7 +180,7 @@ class TestApp { counter += i.intValue(); if (custInstance != null) { - // Classes loaded by custom loaders are included included in the AOT cache + // Classes loaded by custom loaders are included in the AOT cache // but their array classes are excluded. counter += custInstance.equals(null) ? 1 : 2; } @@ -216,6 +231,16 @@ class TestApp { f(); } } + int func() { + return 1; + } + } + + static class ShouldBeExcludedChild extends ShouldBeExcluded { + @Override + int func() { + return 2; + } } static class Bar { @@ -234,6 +259,29 @@ class TestApp { } } } + + static class Taz { + static ShouldBeExcluded m() { + // When verifying this method, we need to check the constraint that + // ShouldBeExcluded must be a supertype of ShouldBeExcludedChild. This information + // is checked by SystemDictionaryShared::check_verification_constraints() when the Taz + // class is linked during the production run. + // + // Because ShouldBeExcluded is excluded from the AOT archive, it must be loaded + // dynamically from app.jar inside SystemDictionaryShared::check_verification_constraints(). + // This must happen after the app class loader has been fully restored from the AOT cache. + return new ShouldBeExcludedChild(); + } + static void hotSpot4() { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 20) { + for (int i = 0; i < 50000; i++) { + counter += i; + } + f(); + } + } + } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestHostOldInf.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestHostOldInf.java index 428e9c83df1..99ab68bda49 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestHostOldInf.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestHostOldInf.java @@ -82,7 +82,7 @@ public class NestHostOldInf extends DynamicArchiveTestBase { output.shouldHaveExitValue(0) .shouldMatch(".class.load. OldInf source:.*oldclassapp.jar") .shouldMatch(".class.load. ChildOldInf source:.*oldclassapp.jar") - .shouldContain("ChildOldInf$InnerChild source: shared objects file (top)") + .shouldMatch(".class.load. ChildOldInf[$]InnerChild source:.*oldclassapp.jar") .shouldMatch(".class.load. ChildOldInf[$]InnerChild[$][$]Lambda.*/0x.*source:.ChildOldInf"); }); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchiveNoDefaultArchive.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchiveNoDefaultArchive.java index 4806f571dc6..6c46fd07550 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchiveNoDefaultArchive.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/TestAutoCreateSharedArchiveNoDefaultArchive.java @@ -27,6 +27,7 @@ * @summary Test -XX:+AutoCreateSharedArchive on a copied JDK without default shared archive * @bug 8261455 * @requires vm.cds + * @requires vm.cds.default.archive.available * @requires vm.flagless * @comment This test doesn't work on Windows because it depends on symlinks * @requires os.family != "windows" diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/ClassVersionAfterRedefine.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/ClassVersionAfterRedefine.java index cc601147714..7e571f7703b 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/ClassVersionAfterRedefine.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/ClassVersionAfterRedefine.java @@ -32,68 +32,27 @@ * @run main/othervm -javaagent:redefineagent.jar ClassVersionAfterRedefine */ -import java.io.InputStream; import java.lang.reflect.Method; import static jdk.test.lib.Asserts.assertTrue; public class ClassVersionAfterRedefine extends ClassLoader { - private static String myName = ClassVersionAfterRedefine.class.getName(); - - private static byte[] getBytecodes(String name) throws Exception { - InputStream is = ClassVersionAfterRedefine.class.getResourceAsStream(name + ".class"); - byte[] buf = is.readAllBytes(); - System.out.println("sizeof(" + name + ".class) == " + buf.length); - return buf; - } - - private static int getStringIndex(String needle, byte[] buf) { - return getStringIndex(needle, buf, 0); - } - - private static int getStringIndex(String needle, byte[] buf, int offset) { - outer: - for (int i = offset; i < buf.length - offset - needle.length(); i++) { - for (int j = 0; j < needle.length(); j++) { - if (buf[i + j] != (byte)needle.charAt(j)) continue outer; - } - return i; - } - return 0; - } - - private static void replaceString(byte[] buf, String name, int index) { - for (int i = index; i < index + name.length(); i++) { - buf[i] = (byte)name.charAt(i - index); - } - } - - private static void replaceAllStrings(byte[] buf, String oldString, String newString) throws Exception { - assertTrue(oldString.length() == newString.length(), "must have same length"); - int index = -1; - while ((index = getStringIndex(oldString, buf, index + 1)) != 0) { - replaceString(buf, newString, index); - } - } - public static void main(String[] s) throws Exception { - byte[] buf = getBytecodes("TestClassOld"); - // Poor man's renaming of class "TestClassOld" to "TestClassXXX" - replaceAllStrings(buf, "TestClassOld", "TestClassXXX"); ClassVersionAfterRedefine cvar = new ClassVersionAfterRedefine(); + + byte[] buf = RedefineClassHelper.replaceClassName(cvar, "TestClassOld", "TestClassXXX"); Class old = cvar.defineClass(null, buf, 0, buf.length); Method foo = old.getMethod("foo"); Object result = foo.invoke(null); assertTrue("java-lang-String".equals(result)); System.out.println(old.getSimpleName() + ".foo() = " + result); - buf = getBytecodes("TestClassNew"); // Rename class "TestClassNew" to "TestClassXXX" so we can use it for // redefining the original version of "TestClassXXX" (i.e. "TestClassOld"). - replaceAllStrings(buf, "TestClassNew", "TestClassXXX"); - // Now redine the original version of "TestClassXXX" (i.e. "TestClassOld"). + buf = RedefineClassHelper.replaceClassName(cvar, "TestClassNew", "TestClassXXX"); + // Now redefine the original version of "TestClassXXX" (i.e. "TestClassOld"). RedefineClassHelper.redefineClass(old, buf); result = foo.invoke(null); assertTrue("java.lang.String".equals(result)); diff --git a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Compiler.java b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Compiler.java index b8f2919e594..487d2e304d9 100644 --- a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Compiler.java +++ b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Compiler.java @@ -170,17 +170,22 @@ public class Compiler { @Override public final void run() { + // Make sure method is not compiled at any level before starting + // progressive compilations. No deopt in-between tiers is needed, + // as long as we increase the compilation levels one by one. + WHITE_BOX.deoptimizeMethod(method); + int compLevel = Utils.INITIAL_COMP_LEVEL; if (Utils.TIERED_COMPILATION) { for (int i = compLevel; i <= Utils.TIERED_STOP_AT_LEVEL; ++i) { - WHITE_BOX.deoptimizeMethod(method); compileAtLevel(i); } } else { compileAtLevel(compLevel); } - // Make the method eligible for sweeping sooner + // Ditch all the compiled versions of the code, make the method + // eligible for sweeping sooner. WHITE_BOX.deoptimizeMethod(method); } diff --git a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java index 573b70faabe..d721c7d63c5 100644 --- a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java +++ b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, 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 @@ -308,6 +308,8 @@ public class CtwRunner { // Do not pay extra stack trace generation cost for normally thrown exceptions "-XX:-StackTraceInThrowable", "-XX:+IgnoreUnrecognizedVMOptions", + // Do not pay extra for verifying inline caches during nmethod cleanups + "-XX:-VerifyInlineCaches", // Do not pay extra zapping cost for explicit GC invocations "-XX:-ZapUnusedHeapArea", // Stress* are c2-specific stress flags, so IgnoreUnrecognizedVMOptions is needed @@ -316,6 +318,7 @@ public class CtwRunner { "-XX:+StressIGVN", "-XX:+StressCCP", "-XX:+StressMacroExpansion", + "-XX:+StressMacroElimination", "-XX:+StressIncrementalInlining", // StressSeed is uint "-XX:StressSeed=" + rng.nextInt(Integer.MAX_VALUE), diff --git a/test/hotspot/jtreg/testlibrary/jittester/conf/exclude.methods.lst b/test/hotspot/jtreg/testlibrary/jittester/conf/exclude.methods.lst index befd0ed66db..f8db44b1463 100644 --- a/test/hotspot/jtreg/testlibrary/jittester/conf/exclude.methods.lst +++ b/test/hotspot/jtreg/testlibrary/jittester/conf/exclude.methods.lst @@ -31,3 +31,6 @@ java/lang/System::nanoTime() java/lang/annotation/IncompleteAnnotationException::IncompleteAnnotationException(Ljava/lang/Class;Ljava/lang/String;) java/util/AbstractSet::toString() java/util/HashSet::toString() + +#Unstable methods +*::hashCode diff --git a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/MethodTemplate.java b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/MethodTemplate.java new file mode 100644 index 00000000000..5f136dc3e05 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/MethodTemplate.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2025, 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 jdk.test.lib.jittester; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.Asserts; + +import static java.util.function.Predicate.not; + + +/** + * A wrapper for string method templates, similar to the CompileCommand patterns. + */ +public final class MethodTemplate { + + /** + * String that can have wildcard symbols on its ends, allowing it to match a family of strings. + * For example, "abc*" matches "abc123", and so on. + */ + public static class WildcardString { + private final String pattern; + private final boolean frontWildcarded; + private final boolean tailWildcarded; + + /** + * Creates a WildcardString from given string. + * @param pattern string pattern, like "some*" + */ + public WildcardString(String pattern) { + // check for the leading '*' + frontWildcarded = pattern.charAt(0) == '*'; + pattern = frontWildcarded ? pattern.substring(1) : pattern; + + // check for the trailing '*' + tailWildcarded = pattern.length() > 0 && pattern.charAt(pattern.length() - 1) == '*'; + pattern = tailWildcarded ? pattern.substring(0, pattern.length() - 1) : pattern; + + this.pattern = pattern; + } + + /** + * Returns true it this WildcardString matches given other string. + * @param other the string that this WildcardString should be matched against + * @return true in case of a match. + */ + public boolean matches(String other) { + boolean result = pattern.equals(other); + result |= frontWildcarded ? other.endsWith(pattern) : result; + result |= tailWildcarded ? other.startsWith(pattern) : result; + result |= tailWildcarded && frontWildcarded ? other.contains(pattern) : result; + return result; + } + } + + private static final Pattern METHOD_PATTERN = Pattern.compile(generateMethodPattern()); + + private final WildcardString klassName; + private final WildcardString methodName; + private final Optional>> signature; + + private MethodTemplate(String klassName, String methodName, Optional>> signature) { + this.klassName = new WildcardString(klassName); + this.methodName = new WildcardString(methodName); + this.signature = signature; + } + + private static String generateMethodPattern() { + // Sample valid template(s): java/lang/String::indexOf(Ljava/lang/String;I) + // java/lang/::*(Ljava/lang/String;I) + // *String::indexOf(*) + // java/lang/*::indexOf + + String primitiveType = "[ZBSCIJFD]"; // Simply a letter, like 'I' + String referenceType = "L[\\w/$]+;"; // Like 'Ljava/lang/String;' + String primOrRefType = + "\\[?" + primitiveType + // Bracket is optional: '[Z', or 'Z' + "|" + + "\\[?" + referenceType; // Bracket is optional: '[LSomeObject;' or 'LSomeObject;' + String argTypesOrWildcard = "(" + // Method argument(s) Ljava/lang/String;Z... + "(" + primOrRefType + ")*" + + ")|\\*"; // .. or a wildcard: + + return + "(?[\\w/$]*\\*?)" + // Class name, like 'java/lang/String' + "::" + // Simply '::' + "(?\\*?[\\w$]+\\*?)" + // method name, 'indexOf'' + "(\\((?" + // Method argument(s) in brackets: + argTypesOrWildcard + // (Ljava/lang/String;Z) or '*' or nothing + ")\\))?"; + } + + /** + * Returns true iff none of the given MethodTemplates matches the given Executable. + * + * @param templates the collection of templates to check + * @param method the executable to match the colletions templates + * @return true if none of the given templates matches the method, false otherwise + */ + public static boolean noneMatches(Collection templates, Executable method) { + for (MethodTemplate template : templates) { + if (template.matches(method)) { + return false; + } + } + return true; + } + + /** + * Returns true if this MethodTemplate matches the given Executable. + * + * @param other the Executable to try to match to + * @return whether the other matches this MethodTemplate + */ + public boolean matches(Executable other) { + boolean result = klassName.matches(other.getDeclaringClass().getName()); + + result &= (other instanceof Constructor) + ? result + : methodName.matches(other.getName()); + + return result && + signature.map(Arrays.asList(other.getParameterTypes())::equals) + .orElse(true); + } + + /** + * Parses the given string and returs a MethodTemplate. + * + * @param methodStr the string to parse + * @return created MethodTemplate + */ + public static MethodTemplate parse(String methodStr) { + Matcher matcher = METHOD_PATTERN.matcher(methodStr); + String msg = String.format("Format of the methods exclude input file is incorrect," + + " methodStr \"%s\" has wrong format", methodStr); + Asserts.assertTrue(matcher.matches(), msg); + + String klassName = matcher.group("klassName").replaceAll("/", "\\."); + String methodName = matcher.group("methodName"); + Optional>> signature = Optional.ofNullable(matcher.group("argTypes")) + .filter(not("*"::equals)) + .map(MethodTemplate::parseSignature); + return new MethodTemplate(klassName, methodName, signature); + } + + private static List> parseSignature(String signature) { + List> sigClasses = new ArrayList<>(); + char typeChar; + boolean isArray; + String klassName; + StringBuilder sb; + StringBuilder arrayDim; + try (StringReader str = new StringReader(signature)) { + int symbol = str.read(); + while (symbol != -1) { + typeChar = (char) symbol; + arrayDim = new StringBuilder(); + Class primArrayClass = null; + if (typeChar == '[') { + isArray = true; + arrayDim.append('['); + symbol = str.read(); + while (symbol == '[') { + arrayDim.append('['); + symbol = str.read(); + } + typeChar = (char) symbol; + if (typeChar != 'L') { + primArrayClass = Class.forName(arrayDim.toString() + typeChar); + } + } else { + isArray = false; + } + switch (typeChar) { + case 'Z': + sigClasses.add(isArray ? primArrayClass : boolean.class); + break; + case 'I': + sigClasses.add(isArray ? primArrayClass : int.class); + break; + case 'J': + sigClasses.add(isArray ? primArrayClass : long.class); + break; + case 'F': + sigClasses.add(isArray ? primArrayClass : float.class); + break; + case 'D': + sigClasses.add(isArray ? primArrayClass : double.class); + break; + case 'B': + sigClasses.add(isArray ? primArrayClass : byte.class); + break; + case 'S': + sigClasses.add(isArray ? primArrayClass : short.class); + break; + case 'C': + sigClasses.add(isArray ? primArrayClass : char.class); + break; + case 'L': + sb = new StringBuilder(); + symbol = str.read(); + while (symbol != ';') { + sb.append((char) symbol); + symbol = str.read(); + } + klassName = sb.toString().replaceAll("/", "\\."); + if (isArray) { + klassName = arrayDim.toString() + "L" + klassName + ";"; + } + Class klass = Class.forName(klassName); + sigClasses.add(klass); + break; + default: + throw new Error("Unknown type " + typeChar); + } + symbol = str.read(); + } + } catch (IOException | ClassNotFoundException ex) { + throw new Error("Unexpected exception while parsing exclude methods file", ex); + } + return sigClasses; + } + +} diff --git a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/TypesParser.java b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/TypesParser.java index bfe5a3224b6..ba956692af7 100644 --- a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/TypesParser.java +++ b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/TypesParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, 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 @@ -23,9 +23,7 @@ package jdk.test.lib.jittester; -import java.io.File; import java.io.IOException; -import java.io.StringReader; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -35,22 +33,31 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; import java.util.List; -import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import jdk.test.lib.Asserts; import jdk.test.lib.jittester.functions.FunctionInfo; import jdk.test.lib.jittester.types.TypeArray; import jdk.test.lib.jittester.types.TypeKlass; +import static java.util.function.Predicate.not; + /** * Class used for parsing included classes file and excluded methods file */ public class TypesParser { + private List methodsToExclude; + private static final HashMap, Type> TYPE_CACHE = new HashMap<>(); + private static String trimComment(String source) { + int commentStart = source.indexOf('#'); + return commentStart == -1 ? source : source.substring(0, commentStart); + } + /** * Parses included classes file and excluded methods file to TypeList and SymbolTable. * This routine takes all classes named in the classes file and puts them to the TypeList, @@ -62,27 +69,21 @@ public class TypesParser { public static void parseTypesAndMethods(String klassesFileName, String exMethodsFileName) { Asserts.assertNotNull(klassesFileName, "Classes input file name is null"); Asserts.assertFalse(klassesFileName.isEmpty(), "Classes input file name is empty"); - List> klasses = parseKlasses(klassesFileName); - Set methodsToExclude; - if (exMethodsFileName != null && !exMethodsFileName.isEmpty()) { - methodsToExclude = parseMethods(exMethodsFileName); - } else { - methodsToExclude = new HashSet<>(); - } - klasses.stream().forEach(klass -> { - TypeKlass typeKlass = (TypeKlass) getType(klass); - if (TypeList.isReferenceType(typeKlass)) { - return; - } - TypeList.add(typeKlass); - Set methods = new HashSet<>(); - methods.addAll(Arrays.asList(klass.getMethods())); - methods.addAll(Arrays.asList(klass.getConstructors())); - methods.removeAll(methodsToExclude); - methods.stream().forEach(method -> { - if (method.isSynthetic()) { - return; - } + TypesParser theParser = new TypesParser(); + theParser.initMethodsToExclude(exMethodsFileName); + parseKlasses(klassesFileName) + .stream() + .filter(klass -> !TypeList.isReferenceType(getTypeKlass(klass))) + .forEach(theParser::processKlass); + } + + private void processKlass(Class klass) { + TypeKlass typeKlass = getTypeKlass(klass); + TypeList.add(typeKlass); + Stream.concat(Arrays.stream(klass.getMethods()), Arrays.stream(klass.getConstructors())) + .filter(not(Executable::isSynthetic)) + .filter(method -> MethodTemplate.noneMatches(methodsToExclude, method)) + .forEach(method -> { String name = method.getName(); boolean isConstructor = false; Type returnType; @@ -106,10 +107,8 @@ public class TypesParser { paramList.add(new VariableInfo("arg" + argNum, typeKlass, paramType, VariableInfo.LOCAL | VariableInfo.INITIALIZED)); } - typeKlass.addSymbol(new FunctionInfo(name, typeKlass, returnType, 1, flags, - paramList)); + typeKlass.addSymbol(new FunctionInfo(name, typeKlass, returnType, 1, flags, paramList)); }); - }); } private static Type getType(Class klass) { @@ -155,6 +154,10 @@ public class TypesParser { return type; } + private static TypeKlass getTypeKlass(Class klass) { + return (TypeKlass) getType(klass); + } + private static int getArrayClassDimension(Class klass) { if (!klass.isArray()) { return 0; @@ -234,133 +237,24 @@ public class TypesParser { return klassesList; } - private static Set parseMethods(String methodsFileName) { - Asserts.assertNotNull(methodsFileName, "Methods exclude input file name is null"); - Asserts.assertFalse(methodsFileName.isEmpty(), "Methods exclude input file name is empty"); - LinkedList methodNamesList = new LinkedList<>(); - Path klassesFilePath = Paths.get(methodsFileName); - try { - Files.lines(klassesFilePath).forEach(line -> { - line = line.trim(); - if (line.isEmpty()) { - return; - } - String msg = String.format("Format of the methods exclude input file \"%s\" is incorrect," - + " line \"%s\" has wrong format", methodsFileName, line); - Asserts.assertTrue(line.matches("\\w[\\w/$]*::[\\w$]+\\((\\[?[ZBSCIJFD]|\\[?L[\\w/$]+;)*\\)"), msg); - methodNamesList.add(line.substring(0, line.length() - 1)); - }); - } catch (IOException ex) { - throw new Error("Error reading exclude method file", ex); - } - Set methodsList = new HashSet<>(); - methodNamesList.forEach(methodName -> { - String[] klassAndNameAndSig = methodName.split("::"); - String klassName = klassAndNameAndSig[0].replaceAll("/", "\\."); - String[] nameAndSig = klassAndNameAndSig[1].split("[\\(\\)]"); - String name = nameAndSig[0]; - String signature = ""; - if (nameAndSig.length > 1) { - signature = nameAndSig[1]; - } - Class klass = null; - List> signatureTypes = null; + private void initMethodsToExclude(String methodsFileName) { + if (methodsFileName != null && !methodsFileName.isEmpty()) { + Path methodsFilePath = Paths.get(methodsFileName); try { - klass = Class.forName(klassName); - signatureTypes = parseSignature(signature); - } catch (ClassNotFoundException ex) { - throw new Error("Unexpected exception while parsing exclude methods file", ex); - } - try { - Executable method; - if (name.equals(klass.getSimpleName())) { - method = klass.getConstructor(signatureTypes.toArray(new Class[0])); - } else { - method = klass.getMethod(name, signatureTypes.toArray(new Class[0])); - } - methodsList.add(method); - } catch (NoSuchMethodException | SecurityException ex) { - throw new Error("Unexpected exception while parsing exclude methods file", ex); - } - }); - return methodsList; - } + methodsToExclude = Files.lines(methodsFilePath) + // Cleaning nonimportant parts + .map(TypesParser::trimComment) + .map(String::trim) + .filter(not(String::isEmpty)) - private static List> parseSignature(String signature) throws ClassNotFoundException { - LinkedList> sigClasses = new LinkedList<>(); - char typeChar; - boolean isArray; - String klassName; - StringBuilder sb; - StringBuilder arrayDim; - try (StringReader str = new StringReader(signature)) { - int symbol = str.read(); - while (symbol != -1){ - typeChar = (char) symbol; - arrayDim = new StringBuilder(); - Class primArrayClass = null; - if (typeChar == '[') { - isArray = true; - arrayDim.append('['); - symbol = str.read(); - while (symbol == '['){ - arrayDim.append('['); - symbol = str.read(); - } - typeChar = (char) symbol; - if (typeChar != 'L') { - primArrayClass = Class.forName(arrayDim.toString() + typeChar); - } - } else { - isArray = false; - } - switch (typeChar) { - case 'Z': - sigClasses.add(isArray ? primArrayClass : boolean.class); - break; - case 'I': - sigClasses.add(isArray ? primArrayClass : int.class); - break; - case 'J': - sigClasses.add(isArray ? primArrayClass : long.class); - break; - case 'F': - sigClasses.add(isArray ? primArrayClass : float.class); - break; - case 'D': - sigClasses.add(isArray ? primArrayClass : double.class); - break; - case 'B': - sigClasses.add(isArray ? primArrayClass : byte.class); - break; - case 'S': - sigClasses.add(isArray ? primArrayClass : short.class); - break; - case 'C': - sigClasses.add(isArray ? primArrayClass : char.class); - break; - case 'L': - sb = new StringBuilder(); - symbol = str.read(); - while (symbol != ';') { - sb.append((char) symbol); - symbol = str.read(); - } - klassName = sb.toString().replaceAll("/", "\\."); - if (isArray) { - klassName = arrayDim.toString() + "L" + klassName + ";"; - } - Class klass = Class.forName(klassName); - sigClasses.add(klass); - break; - default: - throw new Error("Unknown type " + typeChar); - } - symbol = str.read(); + // Actual parsing + .map(MethodTemplate::parse) + .collect(Collectors.toList()); + } catch (IOException ex) { + throw new Error("Error reading exclude method file", ex); } - } catch (IOException ex) { - throw new Error("Unexpected exception while parsing exclude methods file", ex); + } else { + methodsToExclude = new ArrayList<>(); } - return sigClasses; } } diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/DOMTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/DOMTest.java index b5eeed48290..cb0a106183a 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/DOMTest.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/DOMTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -22,8 +22,12 @@ */ package common.catalog; -/** - * @test @bug 8306055 +import java.net.ProxySelector; + +/* + * @test + * @bug 8306055 8359337 + * @summary verifies DOM's support of the JDK Catalog. * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @modules java.xml/jdk.xml.internal * @run driver common.catalog.DOMTest 0 // verifies default setting catalog.resolve=allow @@ -36,11 +40,18 @@ package common.catalog; * @run driver common.catalog.DOMTest 7 // verifies external DTD resolution with a custom Catalog while resolve=strict in API setting * @run driver common.catalog.DOMTest 8 // verifies external parameter are resolved with a custom Catalog though resolve=strict in API setting * @run driver common.catalog.DOMTest 9 // verifies XInclude are resolved with a custom Catalog though resolve=strict in API setting - * @summary verifies DOM's support of the JDK Catalog. */ public class DOMTest extends CatalogTestBase { - public static void main(String args[]) throws Exception { - new DOMTest().run(args[0]); + public static void main(String[] args) throws Exception { + final ProxySelector previous = ProxySelector.getDefault(); + // disable proxy + ProxySelector.setDefault(ProxySelector.of(null)); + try { + new DOMTest().run(args[0]); + } finally { + // reset to the previous proxy selector + ProxySelector.setDefault(previous); + } } public void run(String index) throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SAXTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SAXTest.java index 109568de287..8cbb1612c1a 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SAXTest.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/catalog/SAXTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -22,8 +22,12 @@ */ package common.catalog; -/** - * @test @bug 8306055 +import java.net.ProxySelector; + +/* + * @test + * @bug 8306055 8359337 + * @summary verifies DOM's support of the JDK Catalog. * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @modules java.xml/jdk.xml.internal * @run driver common.catalog.SAXTest 0 // verifies default setting catalog.resolve=allow @@ -36,12 +40,18 @@ package common.catalog; * @run driver common.catalog.SAXTest 7 // verifies external DTD resolution with a custom Catalog while resolve=strict in API setting * @run driver common.catalog.SAXTest 8 // verifies external parameter are resolved with a custom Catalog though resolve=strict in API setting * @run driver common.catalog.SAXTest 9 // verifies XInclude are resolved with a custom Catalog though resolve=strict in API setting - * @summary verifies DOM's support of the JDK Catalog. - */ public class SAXTest extends CatalogTestBase { - public static void main(String args[]) throws Exception { - new SAXTest().run(args[0]); + public static void main(String[] args) throws Exception { + final ProxySelector previous = ProxySelector.getDefault(); + // disable proxy + ProxySelector.setDefault(ProxySelector.of(null)); + try { + new SAXTest().run(args[0]); + } finally { + // reset to the previous proxy selector + ProxySelector.setDefault(previous); + } } public void run(String index) throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DOMTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DOMTest.java index 34d8893d43e..d6df7061a73 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DOMTest.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/DOMTest.java @@ -1,11 +1,33 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * Copyright (c) 2023, 2025, 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 common.dtd; -/** - * @test @bug 8306632 +import java.net.ProxySelector; + +/* + * @test + * @bug 8306632 8359337 + * @summary verifies DOM's support of the property jdk.xml.dtd.support. * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @modules java.xml/jdk.xml.internal * @run driver common.dtd.DOMTest 0 // verifies default setting dtd.support=allow @@ -15,11 +37,18 @@ package common.dtd; * @run driver common.dtd.DOMTest 4 // verifies DTD=ignore * @run driver common.dtd.DOMTest 5 // verifies disallow-doctype-decl=false * @run driver common.dtd.DOMTest 6 // verifies disallow-doctype-decl=true - * @summary verifies DOM's support of the property jdk.xml.dtd.support. */ public class DOMTest extends DTDTestBase { - public static void main(String args[]) throws Exception { - new DOMTest().run(args[0]); + public static void main(String[] args) throws Exception { + final ProxySelector previous = ProxySelector.getDefault(); + // disable proxy + ProxySelector.setDefault(ProxySelector.of(null)); + try { + new DOMTest().run(args[0]); + } finally { + // reset to the previous proxy selector + ProxySelector.setDefault(previous); + } } public void run(String index) throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/SAXTest.java b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/SAXTest.java index 219753dbb5e..569b070d242 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/common/dtd/SAXTest.java +++ b/test/jaxp/javax/xml/jaxp/unittest/common/dtd/SAXTest.java @@ -1,13 +1,35 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * Copyright (c) 2023, 2025, 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 common.dtd; +import java.net.ProxySelector; + import common.util.TestBase; -/** - * @test @bug 8306632 +/* + * @test + * @bug 8306632 8359337 + * @summary verifies SAX's support of the property jdk.xml.dtd.support. * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @modules java.xml/jdk.xml.internal * @run driver common.dtd.SAXTest 0 // verifies default setting dtd.support=allow @@ -17,11 +39,18 @@ import common.util.TestBase; * @run driver common.dtd.SAXTest 4 // verifies DTD=ignore * @run driver common.dtd.SAXTest 5 // verifies disallow-doctype-decl=false * @run driver common.dtd.SAXTest 6 // verifies disallow-doctype-decl=true - * @summary verifies SAX's support of the property jdk.xml.dtd.support. */ public class SAXTest extends DTDTestBase { - public static void main(String args[]) throws Exception { - new SAXTest().run(args[0]); + public static void main(String[] args) throws Exception { + final ProxySelector previous = ProxySelector.getDefault(); + // disable proxy + ProxySelector.setDefault(ProxySelector.of(null)); + try { + new SAXTest().run(args[0]); + } finally { + // reset to the previous proxy selector + ProxySelector.setDefault(previous); + } } public void run(String index) throws Exception { diff --git a/test/jaxp/javax/xml/jaxp/unittest/dom/DOMFeatureTest.java b/test/jaxp/javax/xml/jaxp/unittest/dom/DOMFeatureTest.java index febd9e70d5d..6f2be7ae9db 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/dom/DOMFeatureTest.java +++ b/test/jaxp/javax/xml/jaxp/unittest/dom/DOMFeatureTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -25,10 +25,13 @@ package dom; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.ProxySelector; + import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.testng.Assert; +import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import org.w3c.dom.Document; @@ -40,10 +43,11 @@ import org.xml.sax.SAXException; /* * @test - * @bug 8206132 - * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest - * @run testng dom.DOMFeatureTest + * @bug 8206132 8359337 * @summary Tests DOM features. + * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest + * @comment we use othervm because the test configures a system wide ProxySelector + * @run testng/othervm dom.DOMFeatureTest */ public class DOMFeatureTest { @@ -105,6 +109,13 @@ public class DOMFeatureTest { {true, XML3}, }; } + + @BeforeClass + static void beforeClass() { + // disable proxy + ProxySelector.setDefault(ProxySelector.of(null)); + } + /** * Verifies the EntityExpansion feature. * @param caseNo the case number diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 382ce443813..b84c38021e9 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -812,6 +812,7 @@ java/awt/font/GlyphVector/TestGlyphVectorLayout.java 8354987 generic-all java/awt/font/TextLayout/TestJustification.java 8250791 macosx-all java/awt/TrayIcon/DragEventSource/DragEventSource.java 8252242 macosx-all java/awt/FileDialog/DefaultFocusOwner/DefaultFocusOwner.java 7187728 macosx-all,linux-all +java/awt/FileDialog/DoubleActionESC.java 8356981 linux-all java/awt/print/PageFormat/Orient.java 8016055 macosx-all java/awt/TextArea/TextAreaCursorTest/HoveringAndDraggingTest.java 8024986 macosx-all,linux-all java/awt/TextComponent/CorrectTextComponentSelectionTest.java 8237220 macosx-all diff --git a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWithEliminatedLock.java b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWithEliminatedLock.java index a2dce62792b..61447d05bfc 100644 --- a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWithEliminatedLock.java +++ b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreadsWithEliminatedLock.java @@ -168,4 +168,4 @@ public class DumpThreadsWithEliminatedLock { Files.delete(file); return file; } -} \ No newline at end of file +} diff --git a/test/jdk/com/sun/net/httpserver/FileServerHandler.java b/test/jdk/com/sun/net/httpserver/FileServerHandler.java index 849b5fd06de..509adf7bf72 100644 --- a/test/jdk/com/sun/net/httpserver/FileServerHandler.java +++ b/test/jdk/com/sun/net/httpserver/FileServerHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, 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 @@ -43,7 +43,6 @@ public class FileServerHandler implements HttpHandler { this.docroot = docroot; } - int invocation = 1; public void handle (HttpExchange t) throws IOException { @@ -87,16 +86,16 @@ public class FileServerHandler implements HttpHandler { rmap.set ("Content-Type", "text/html"); t.sendResponseHeaders (200, 0); String[] list = f.list(); - OutputStream os = t.getResponseBody(); - PrintStream p = new PrintStream (os); - p.println ("

Directory listing for: " + path+ "

"); - p.println ("
    "); - for (int i=0; i"+list[i]+""); + try (final OutputStream os = t.getResponseBody(); + final PrintStream p = new PrintStream (os)) { + p.println("

    Directory listing for: " + path + "

    "); + p.println("
      "); + for (int i = 0; i < list.length; i++) { + p.println("
    • " + list[i] + "
    • "); + } + p.println("


    "); + p.flush(); } - p.println ("


"); - p.flush(); - p.close(); } else { int clen; if (fixedrequest != null) { @@ -105,10 +104,9 @@ public class FileServerHandler implements HttpHandler { clen = 0; } t.sendResponseHeaders (200, clen); - OutputStream os = t.getResponseBody(); - FileInputStream fis = new FileInputStream (f); int count = 0; - try { + try (final OutputStream os = t.getResponseBody(); + final FileInputStream fis = new FileInputStream (f)) { byte[] buf = new byte [16 * 1024]; int len; while ((len=fis.read (buf)) != -1) { @@ -118,8 +116,6 @@ public class FileServerHandler implements HttpHandler { } catch (IOException e) { e.printStackTrace(); } - fis.close(); - os.close(); } } diff --git a/test/jdk/com/sun/net/httpserver/Test12.java b/test/jdk/com/sun/net/httpserver/Test12.java index ab1d9d548e7..2a0ee1fc0a4 100644 --- a/test/jdk/com/sun/net/httpserver/Test12.java +++ b/test/jdk/com/sun/net/httpserver/Test12.java @@ -21,23 +21,12 @@ * questions. */ -/* - * @test - * @bug 6270015 - * @library /test/lib - * @build jdk.test.lib.Asserts - * jdk.test.lib.Utils - * jdk.test.lib.net.SimpleSSLContext - * jdk.test.lib.net.URIBuilder - * @run main/othervm Test12 - * @run main/othervm -Djava.net.preferIPv6Addresses=true Test12 - * @summary Light weight HTTP server - */ - import com.sun.net.httpserver.*; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.*; import java.io.*; import java.net.*; @@ -49,11 +38,19 @@ import static jdk.test.lib.Asserts.assertEquals; import static jdk.test.lib.Asserts.assertFileContentsEqual; import static jdk.test.lib.Utils.createTempFileOfSize; -/* basic http/s connectivity test - * Tests: - * - same as Test1, but in parallel +/* + * @test + * @bug 6270015 8359477 + * @summary Light weight HTTP server - basic http/s connectivity test, same as Test1, + * but in parallel + * @library /test/lib + * @build jdk.test.lib.Asserts + * jdk.test.lib.Utils + * jdk.test.lib.net.SimpleSSLContext + * jdk.test.lib.net.URIBuilder + * @run main/othervm Test12 + * @run main/othervm -Djava.net.preferIPv6Addresses=true Test12 */ - public class Test12 extends Test { private static final String TEMP_FILE_PREFIX = @@ -61,14 +58,12 @@ public class Test12 extends Test { static SSLContext ctx; - static boolean fail = false; - public static void main (String[] args) throws Exception { HttpServer s1 = null; HttpsServer s2 = null; - ExecutorService executor=null; Path smallFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23); Path largeFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 2730088); + final ExecutorService executor = Executors.newCachedThreadPool(); try { System.out.print ("Test12: "); InetAddress loopback = InetAddress.getLoopbackAddress(); @@ -80,7 +75,6 @@ public class Test12 extends Test { HttpHandler h = new FileServerHandler(smallFilePath.getParent().toString()); HttpContext c1 = s1.createContext ("/", h); HttpContext c2 = s2.createContext ("/", h); - executor = Executors.newCachedThreadPool(); s1.setExecutor (executor); s2.setExecutor (executor); ctx = new SimpleSSLContext().get(); @@ -90,7 +84,7 @@ public class Test12 extends Test { int port = s1.getAddress().getPort(); int httpsport = s2.getAddress().getPort(); - Runner r[] = new Runner[8]; + final Runner[] r = new Runner[8]; r[0] = new Runner (true, "http", port, smallFilePath); r[1] = new Runner (true, "http", port, largeFilePath); r[2] = new Runner (true, "https", httpsport, smallFilePath); @@ -99,95 +93,83 @@ public class Test12 extends Test { r[5] = new Runner (false, "http", port, largeFilePath); r[6] = new Runner (false, "https", httpsport, smallFilePath); r[7] = new Runner (false, "https", httpsport, largeFilePath); - start (r); - join (r); - System.out.println ("OK"); + // submit the tasks + final List> futures = new ArrayList<>(); + for (Runner runner : r) { + futures.add(executor.submit(runner)); + } + // wait for the tasks' completion + for (Future f : futures) { + f.get(); + } + System.out.println ("All " + futures.size() + " tasks completed successfully"); } finally { - if (s1 != null) + if (s1 != null) { s1.stop(0); - if (s2 != null) + } + if (s2 != null) { s2.stop(0); - if (executor != null) - executor.shutdown (); + } + executor.close(); + // it's OK to delete these files since the server side handlers + // serving these files have completed (guaranteed by the completion of Executor.close()) + System.out.println("deleting " + smallFilePath); Files.delete(smallFilePath); + System.out.println("deleting " + largeFilePath); Files.delete(largeFilePath); } } - static void start (Runner[] x) { - for (int i=0; i { boolean fixedLen; String protocol; int port; private final Path filePath; - Runner (boolean fixedLen, String protocol, int port, Path filePath) { + Runner(boolean fixedLen, String protocol, int port, Path filePath) { this.fixedLen=fixedLen; this.protocol=protocol; this.port=port; this.filePath = filePath; } - public void run () { - try { - URL url = URIBuilder.newBuilder() - .scheme(protocol) - .loopback() - .port(port) - .path("/" + filePath.getFileName()) - .toURL(); - HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); - if (urlc instanceof HttpsURLConnection) { - HttpsURLConnection urlcs = (HttpsURLConnection) urlc; - urlcs.setHostnameVerifier (new HostnameVerifier () { - public boolean verify (String s, SSLSession s1) { - return true; - } - }); - urlcs.setSSLSocketFactory (ctx.getSocketFactory()); - } - byte [] buf = new byte [4096]; - - if (fixedLen) { - urlc.setRequestProperty ("XFixed", "yes"); - } - InputStream is = urlc.getInputStream(); - File temp = File.createTempFile ("Test1", null); - temp.deleteOnExit(); - OutputStream fout = new BufferedOutputStream (new FileOutputStream(temp)); - int c, count = 0; - while ((c=is.read(buf)) != -1) { - count += c; - fout.write (buf, 0, c); - } - is.close(); - fout.close(); - - if (count != filePath.toFile().length()) { - throw new RuntimeException ("wrong amount of data returned"); - } - assertFileContentsEqual(filePath, temp.toPath()); - temp.delete(); - } catch (Exception e) { - e.printStackTrace(); - fail = true; + @Override + public Void call() throws Exception { + final URL url = URIBuilder.newBuilder() + .scheme(protocol) + .loopback() + .port(port) + .path("/" + filePath.getFileName()) + .toURL(); + final HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); + if (urlc instanceof HttpsURLConnection) { + HttpsURLConnection urlcs = (HttpsURLConnection) urlc; + urlcs.setHostnameVerifier (new HostnameVerifier () { + public boolean verify (String s, SSLSession s1) { + return true; + } + }); + urlcs.setSSLSocketFactory (ctx.getSocketFactory()); } + if (fixedLen) { + urlc.setRequestProperty ("XFixed", "yes"); + } + final Path temp = Files.createTempFile(Path.of("."), "Test12", null); + final long numReceived; + try (InputStream is = urlc.getInputStream(); + OutputStream fout = new BufferedOutputStream(new FileOutputStream(temp.toFile()))) { + numReceived = is.transferTo(fout); + } + System.out.println("received " + numReceived + " response bytes for " + url); + final long expected = filePath.toFile().length(); + if (numReceived != expected) { + throw new RuntimeException ("expected " + expected + " bytes, but received " + + numReceived); + } + assertFileContentsEqual(filePath, temp); + Files.delete(temp); + return null; } } - } diff --git a/test/jdk/java/awt/FileDialog/DoubleActionESC.java b/test/jdk/java/awt/FileDialog/DoubleActionESC.java index 748c3aeb5e4..e8bb4963aa0 100644 --- a/test/jdk/java/awt/FileDialog/DoubleActionESC.java +++ b/test/jdk/java/awt/FileDialog/DoubleActionESC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,12 +34,15 @@ import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.concurrent.CountDownLatch; +import static java.util.concurrent.TimeUnit.SECONDS; + /* * @test * @bug 5097243 * @summary Tests that FileDialog can be closed by ESC any time * @key headful * @run main DoubleActionESC + * @run main/othervm -Dsun.awt.disableGtkFileDialogs=true DoubleActionESC */ public class DoubleActionESC { @@ -49,47 +52,48 @@ public class DoubleActionESC { private static Robot robot; private static volatile Point p; private static volatile Dimension d; - private static volatile CountDownLatch latch; private static final int REPEAT_COUNT = 2; + private static final long LATCH_TIMEOUT = 10; + + private static final CountDownLatch latch = new CountDownLatch(REPEAT_COUNT); public static void main(String[] args) throws Exception { - latch = new CountDownLatch(1); - robot = new Robot(); - robot.setAutoDelay(100); + robot.setAutoDelay(50); try { EventQueue.invokeAndWait(() -> { createAndShowUI(); }); + robot.waitForIdle(); robot.delay(1000); + EventQueue.invokeAndWait(() -> { p = showBtn.getLocationOnScreen(); d = showBtn.getSize(); }); for (int i = 0; i < REPEAT_COUNT; ++i) { - Thread thread = new Thread(() -> { - robot.mouseMove(p.x + d.width / 2, p.y + d.height / 2); - robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); - robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); - }); - thread.start(); - robot.delay(3000); + robot.mouseMove(p.x + d.width / 2, p.y + d.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(1000); - Thread thread1 = new Thread(() -> { - robot.keyPress(KeyEvent.VK_ESCAPE); - robot.keyRelease(KeyEvent.VK_ESCAPE); - robot.waitForIdle(); - }); - thread1.start(); - robot.delay(3000); + robot.keyPress(KeyEvent.VK_ESCAPE); + robot.keyRelease(KeyEvent.VK_ESCAPE); + robot.waitForIdle(); + robot.delay(1000); } - latch.await(); - if (fd.isVisible()) { - throw new RuntimeException("File Dialog is not closed"); + if (!latch.await(LATCH_TIMEOUT, SECONDS)) { + throw new RuntimeException("Test failed: Latch timeout reached"); } + EventQueue.invokeAndWait(() -> { + if (fd.isVisible()) { + throw new RuntimeException("File Dialog is not closed"); + } + }); } finally { EventQueue.invokeAndWait(() -> { if (f != null) { diff --git a/test/jdk/java/awt/GraphicsDevice/FullScreenWindowRace.java b/test/jdk/java/awt/GraphicsDevice/FullScreenWindowRace.java new file mode 100644 index 00000000000..fcc95546764 --- /dev/null +++ b/test/jdk/java/awt/GraphicsDevice/FullScreenWindowRace.java @@ -0,0 +1,65 @@ +/* + * Copyright Amazon.com Inc. 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.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Window; + +/** + * @test + * @key headful + * @bug 8359266 + * @summary Tests for a race condition when setting a full-screen window + */ +public final class FullScreenWindowRace { + + public static void main(String[] args) throws InterruptedException { + Window window = new Window(null); + GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice(); + + Thread thread = new Thread(() -> { + while (gd.getFullScreenWindow() == null) { + // Busy wait - can be optimized away without volatile + } + }); + + thread.setDaemon(true); + thread.start(); + + // Give thread some time to start and begin the loop + Thread.sleep(2000); + + gd.setFullScreenWindow(window); + + thread.join(15000); + + boolean alive = thread.isAlive(); + + gd.setFullScreenWindow(null); + window.dispose(); + if (alive) { + throw new RuntimeException("Full screen window is NOT detected!"); + } + } +} diff --git a/test/jdk/java/lang/RuntimeTests/ExitLogging-ALL.properties b/test/jdk/java/lang/RuntimeTests/ExitLogging-ALL.properties new file mode 100644 index 00000000000..8e24f2cb34d --- /dev/null +++ b/test/jdk/java/lang/RuntimeTests/ExitLogging-ALL.properties @@ -0,0 +1,8 @@ +############################################################ +# java.lang.Runtime logging level to console to ALL +############################################################ + +handlers= java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.lang.Runtime.level = ALL diff --git a/test/jdk/java/lang/RuntimeTests/ExitLogging-FINE.properties b/test/jdk/java/lang/RuntimeTests/ExitLogging-FINE.properties index 6afd902c2d0..b9076403cea 100644 --- a/test/jdk/java/lang/RuntimeTests/ExitLogging-FINE.properties +++ b/test/jdk/java/lang/RuntimeTests/ExitLogging-FINE.properties @@ -1,5 +1,5 @@ ############################################################ -# Enable logging java.lang.Runtime to the console +# java.lang.Runtime logging level to console to FINE ############################################################ handlers= java.util.logging.ConsoleHandler diff --git a/test/jdk/java/lang/RuntimeTests/ExitLogging-FINER.properties b/test/jdk/java/lang/RuntimeTests/ExitLogging-FINER.properties new file mode 100644 index 00000000000..a31f4a35e93 --- /dev/null +++ b/test/jdk/java/lang/RuntimeTests/ExitLogging-FINER.properties @@ -0,0 +1,8 @@ +############################################################ +# java.lang.Runtime logging level to console to FINER +############################################################ + +handlers= java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.lang.Runtime.level = FINER diff --git a/test/jdk/java/lang/RuntimeTests/ExitLogging-INFO.properties b/test/jdk/java/lang/RuntimeTests/ExitLogging-INFO.properties index d44a836c760..c09c0adf1ed 100644 --- a/test/jdk/java/lang/RuntimeTests/ExitLogging-INFO.properties +++ b/test/jdk/java/lang/RuntimeTests/ExitLogging-INFO.properties @@ -1,5 +1,5 @@ ############################################################ -# Enable logging java.lang.Runtime to the console +# java.lang.Runtime logging level to console to INFO ############################################################ handlers= java.util.logging.ConsoleHandler diff --git a/test/jdk/java/lang/RuntimeTests/ExitLogging-OFF.properties b/test/jdk/java/lang/RuntimeTests/ExitLogging-OFF.properties new file mode 100644 index 00000000000..6e284846e92 --- /dev/null +++ b/test/jdk/java/lang/RuntimeTests/ExitLogging-OFF.properties @@ -0,0 +1,8 @@ +############################################################ +# java.lang.Runtime logging level to console to OFF +############################################################ + +handlers= java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.lang.Runtime.level = OFF diff --git a/test/jdk/java/lang/RuntimeTests/ExitLogging-SEVERE.properties b/test/jdk/java/lang/RuntimeTests/ExitLogging-SEVERE.properties new file mode 100644 index 00000000000..cb6d32581fc --- /dev/null +++ b/test/jdk/java/lang/RuntimeTests/ExitLogging-SEVERE.properties @@ -0,0 +1,8 @@ +############################################################ +# java.lang.Runtime logging level to console to SEVERE +############################################################ + +handlers= java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.lang.Runtime.level = SEVERE diff --git a/test/jdk/java/lang/RuntimeTests/ExitLogging-WARNING.properties b/test/jdk/java/lang/RuntimeTests/ExitLogging-WARNING.properties new file mode 100644 index 00000000000..5cd99c8d6c5 --- /dev/null +++ b/test/jdk/java/lang/RuntimeTests/ExitLogging-WARNING.properties @@ -0,0 +1,8 @@ +############################################################ +# java.lang.Runtime logging level to console to WARNING +############################################################ + +handlers= java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.lang.Runtime.level = WARNING diff --git a/test/jdk/java/lang/RuntimeTests/RuntimeExitLogTest.java b/test/jdk/java/lang/RuntimeTests/RuntimeExitLogTest.java index da40cdbd742..291c9895e19 100644 --- a/test/jdk/java/lang/RuntimeTests/RuntimeExitLogTest.java +++ b/test/jdk/java/lang/RuntimeTests/RuntimeExitLogTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -49,7 +49,7 @@ public class RuntimeExitLogTest { private static final String TEST_JDK = System.getProperty("test.jdk"); private static final String TEST_SRC = System.getProperty("test.src"); - + private static final String NEW_LINE = System.lineSeparator(); private static Object HOLD_LOGGER; /** @@ -64,31 +64,80 @@ public class RuntimeExitLogTest { System.exit(status); } + /** + * Generate a regular expression pattern that match the expected log output for a Runtime.exit() call. + * The pattern includes the method call stack trace and the exit status. + * @param status the exit status passed to the Runtime.exit() call + * @return the regex pattern as a string + */ + private static String generateStackTraceLogPattern(int status) { + return "(?s)^.+ java\\.lang\\.Shutdown logRuntimeExit\\n" + + ".*: Runtime\\.exit\\(\\) called with status: " + status + "\\n" + + "java\\.lang\\.Throwable: Runtime\\.exit\\(" + status + "\\)\\n" + + "\\s+at java\\.base/java\\.lang\\.Shutdown\\.logRuntimeExit\\(Shutdown\\.java:\\d+\\)\\n" + + "\\s+at(?: .+)"; + } + /** * Test various log level settings, and none. * @return a stream of arguments for parameterized test */ private static Stream logParamProvider() { return Stream.of( - // Logging enabled with level DEBUG + // Logging configuration using the java.util.logging.config.file property Arguments.of(List.of("-Djava.util.logging.config.file=" + - Path.of(TEST_SRC, "ExitLogging-FINE.properties").toString()), 1, - "Runtime.exit() called with status: 1"), - // Logging disabled due to level + Path.of(TEST_SRC, "ExitLogging-ALL.properties").toString()), 1, + generateStackTraceLogPattern(1)), Arguments.of(List.of("-Djava.util.logging.config.file=" + - Path.of(TEST_SRC, "ExitLogging-INFO.properties").toString()), 2, + Path.of(TEST_SRC, "ExitLogging-FINER.properties").toString()), 2, + generateStackTraceLogPattern(2)), + Arguments.of(List.of("-Djava.util.logging.config.file=" + + Path.of(TEST_SRC, "ExitLogging-FINE.properties").toString()), 3, + generateStackTraceLogPattern(3)), + Arguments.of(List.of("-Djava.util.logging.config.file=" + + Path.of(TEST_SRC, "ExitLogging-INFO.properties").toString()), 4, ""), - // Console logger + Arguments.of(List.of("-Djava.util.logging.config.file=" + + Path.of(TEST_SRC, "ExitLogging-WARNING.properties").toString()), 5, + ""), + Arguments.of(List.of("-Djava.util.logging.config.file=" + + Path.of(TEST_SRC, "ExitLogging-SEVERE.properties").toString()), 6, + ""), + Arguments.of(List.of("-Djava.util.logging.config.file=" + + Path.of(TEST_SRC, "ExitLogging-OFF.properties").toString()), 7, + ""), + + // Logging configuration using the jdk.system.logger.level property Arguments.of(List.of("--limit-modules", "java.base", - "-Djdk.system.logger.level=DEBUG"), 3, - "Runtime.exit() called with status: 3"), - // Console logger - Arguments.of(List.of(), 4, ""), + "-Djdk.system.logger.level=ALL"), 8, + generateStackTraceLogPattern(8)), + Arguments.of(List.of("--limit-modules", "java.base", + "-Djdk.system.logger.level=TRACE"), 9, + generateStackTraceLogPattern(9)), + Arguments.of(List.of("--limit-modules", "java.base", + "-Djdk.system.logger.level=DEBUG"), 10, + generateStackTraceLogPattern(10)), + Arguments.of(List.of("--limit-modules", "java.base", + "-Djdk.system.logger.level=INFO"), 11, + ""), + Arguments.of(List.of("--limit-modules", "java.base", + "-Djdk.system.logger.level=WARNING"), 12, + ""), + Arguments.of(List.of("--limit-modules", "java.base", + "-Djdk.system.logger.level=ERROR"), 13, + ""), + Arguments.of(List.of("--limit-modules", "java.base", + "-Djdk.system.logger.level=OFF"), 14, + ""), + // Throwing Handler Arguments.of(List.of("-DThrowingHandler", "-Djava.util.logging.config.file=" + - Path.of(TEST_SRC, "ExitLogging-FINE.properties").toString()), 5, - "Runtime.exit(5) logging failed: Exception in publish") + Path.of(TEST_SRC, "ExitLogging-FINE.properties").toString()), 15, + "Runtime\\.exit\\(15\\) logging failed: Exception in publish"), + + // Default console logging configuration with no additional parameters + Arguments.of(List.of(), 16, "") ); } @@ -115,13 +164,14 @@ public class RuntimeExitLogTest { try (BufferedReader reader = process.inputReader()) { List lines = reader.lines().toList(); boolean match = (expectMessage.isEmpty()) - ? lines.size() == 0 - : lines.stream().filter(s -> s.contains(expectMessage)).findFirst().isPresent(); + ? lines.isEmpty() + : String.join("\n", lines).matches(expectMessage); if (!match) { // Output lines for debug - System.err.println("Expected: \"" + expectMessage + "\""); + System.err.println("Expected pattern (line-break):"); + System.err.println(expectMessage.replaceAll("\\n", NEW_LINE)); System.err.println("---- Actual output begin"); - lines.forEach(l -> System.err.println(l)); + lines.forEach(System.err::println); System.err.println("---- Actual output end"); fail("Unexpected log contents"); } diff --git a/test/jdk/java/net/ServerSocket/LargeBacklogTest.java b/test/jdk/java/net/ServerSocket/LargeBacklogTest.java new file mode 100644 index 00000000000..bda976998a7 --- /dev/null +++ b/test/jdk/java/net/ServerSocket/LargeBacklogTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025, 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.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.ServerSocketChannel; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * @test + * @bug 8330940 + * @summary verify that java.net.ServerSocket and the server socket channels in java.nio.channels + * when configured with a backlog of >=200 on Windows, will allow for those many + * backlogged Socket connections + * @requires os.family == "windows" + * @run junit LargeBacklogTest + */ +class LargeBacklogTest { + + @Test + void testServerSocket() throws Exception { + final int backlog = 242; + // Create a ServerSocket configured with the given backlog. + // The ServerSocket never accept()s a connection so each connect() attempt + // will be backlogged. + try (var server = new ServerSocket(0, backlog, InetAddress.getLoopbackAddress())) { + final int serverPort = server.getLocalPort(); + testBackloggedConnects(backlog, serverPort); + } + } + + @Test + void testServerSocketChannel() throws Exception { + final int backlog = 213; + // Create a ServerSocketChannel configured with the given backlog. + // The channel never accept()s a connection so each connect() attempt + // will be backlogged. + try (var serverChannel = ServerSocketChannel.open()) { + serverChannel.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), backlog); + final int serverPort = ((InetSocketAddress) serverChannel.getLocalAddress()).getPort(); + testBackloggedConnects(backlog, serverPort); + } + } + + @Test + void testAsynchronousServerSocketChannel() throws Exception { + final int backlog = 209; + // Create a AsynchronousServerSocketChannel configured with the given backlog. + // The channel never accept()s a connection so each connect() attempt + // will be backlogged. + try (var serverChannel = AsynchronousServerSocketChannel.open()) { + serverChannel.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), backlog); + final int serverPort = ((InetSocketAddress) serverChannel.getLocalAddress()).getPort(); + testBackloggedConnects(backlog, serverPort); + } + } + + private static void testBackloggedConnects(final int backlog, final int serverPort) { + int numSuccessfulConnects = 0; + System.err.println("attempting " + backlog + " connections to port " + serverPort); + // attempt the Socket connections + for (int i = 1; i <= backlog; i++) { + try (final Socket sock = new Socket(InetAddress.getLoopbackAddress(), serverPort)) { + numSuccessfulConnects++; + System.err.println("connection " + i + " established " + sock); + } catch (IOException ioe) { + System.err.println("connection attempt " + i + " failed: " + ioe); + // do not attempt any more connections + break; + } + } + System.err.println(numSuccessfulConnects + " connections successfully established"); + // ideally we expect the number of successful connections to be equal to the backlog value. + // however in certain environments, it's possible that some other process attempts a + // connection to the server's port. so we allow for a small number of connection attempts + // to fail (due to exceeding the backlog) + final int minimumExpectedSuccessfulConns = backlog - 5; + if (numSuccessfulConnects < minimumExpectedSuccessfulConns) { + fail("expected at least " + minimumExpectedSuccessfulConns + + " successful connections for a backlog of " + backlog + ", but only " + + numSuccessfulConnects + " were successful"); + } + } +} diff --git a/test/jdk/java/net/httpclient/OriginTest.java b/test/jdk/java/net/httpclient/OriginTest.java new file mode 100644 index 00000000000..58310ecd9ad --- /dev/null +++ b/test/jdk/java/net/httpclient/OriginTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2025, 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.net.URI; +import java.util.Locale; + +import jdk.internal.net.http.Origin; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * @test + * @summary verify the behaviour of jdk.internal.net.http.Origin + * @modules java.net.http/jdk.internal.net.http + * @run junit OriginTest + */ +class OriginTest { + + @ParameterizedTest + @ValueSource(strings = {"foo", "Bar", "HttPS", "HTTP"}) + void testInvalidScheme(final String scheme) throws Exception { + final String validHost = "127.0.0.1"; + final int validPort = 80; + final IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> { + new Origin(scheme, validHost, validPort); + }); + assertTrue(iae.getMessage().contains("scheme"), + "unexpected exception message: " + iae.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"http", "https"}) + void testValidScheme(final String scheme) throws Exception { + final String validHost = "127.0.0.1"; + final int validPort = 80; + final Origin o1 = new Origin(scheme, validHost, validPort); + assertEquals(validHost, o1.host(), "unexpected host"); + assertEquals(validPort, o1.port(), "unexpected port"); + assertEquals(scheme, o1.scheme(), "unexpected scheme"); + + final URI uri = URI.create(scheme + "://" + validHost + ":" + validPort); + final Origin o2 = Origin.from(uri); + assertNotNull(o2, "null Origin for URI " + uri); + assertEquals(validHost, o2.host(), "unexpected host"); + assertEquals(validPort, o2.port(), "unexpected port"); + assertEquals(scheme, o2.scheme(), "unexpected scheme"); + } + + @ParameterizedTest + @ValueSource(strings = {"JDK.java.net", "[::1]", "[0:0:0:0:0:0:0:1]"}) + void testInvalidHost(final String host) throws Exception { + final String validScheme = "http"; + final int validPort = 8000; + final IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> { + new Origin(validScheme, host, validPort); + }); + assertTrue(iae.getMessage().contains("host"), + "unexpected exception message: " + iae.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"127.0.0.1", "localhost", "jdk.java.net", "::1", "0:0:0:0:0:0:0:1"}) + void testValidHost(final String host) throws Exception { + final String validScheme = "https"; + final int validPort = 42; + final Origin o1 = new Origin(validScheme, host, validPort); + assertEquals(host, o1.host(), "unexpected host"); + assertEquals(validPort, o1.port(), "unexpected port"); + assertEquals(validScheme, o1.scheme(), "unexpected scheme"); + + String uriHost = host; + if (host.contains(":")) { + uriHost = "[" + host + "]"; + } + final URI uri = URI.create(validScheme + "://" + uriHost + ":" + validPort); + final Origin o2 = Origin.from(uri); + assertNotNull(o2, "null Origin for URI " + uri); + assertEquals(host, o2.host(), "unexpected host"); + assertEquals(validPort, o2.port(), "unexpected port"); + assertEquals(validScheme, o2.scheme(), "unexpected scheme"); + } + + @ParameterizedTest + @ValueSource(ints = {-1, 0}) + void testInvalidPort(final int port) throws Exception { + final String validScheme = "http"; + final String validHost = "127.0.0.1"; + final IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, + () -> new Origin(validScheme, validHost, port)); + assertTrue(iae.getMessage().contains("port"), + "unexpected exception message: " + iae.getMessage()); + } + + @ParameterizedTest + @ValueSource(ints = {100, 1024, 80, 8080, 42}) + void testValidPort(final int port) throws Exception { + final String validScheme = "https"; + final String validHost = "localhost"; + final Origin o1 = new Origin(validScheme, validHost, port); + assertEquals(validHost, o1.host(), "unexpected host"); + assertEquals(port, o1.port(), "unexpected port"); + assertEquals(validScheme, o1.scheme(), "unexpected scheme"); + + final URI uri = URI.create(validScheme + "://" + validHost + ":" + port); + final Origin o2 = Origin.from(uri); + assertNotNull(o2, "null Origin for URI " + uri); + assertEquals(validHost, o2.host(), "unexpected host"); + assertEquals(port, o2.port(), "unexpected port"); + assertEquals(validScheme, o2.scheme(), "unexpected scheme"); + } + + @Test + void testInferredPort() throws Exception { + final URI httpURI = URI.create("http://localhost"); + final Origin httpOrigin = Origin.from(httpURI); + assertNotNull(httpOrigin, "null Origin for URI " + httpURI); + assertEquals("localhost", httpOrigin.host(), "unexpected host"); + assertEquals(80, httpOrigin.port(), "unexpected port"); + assertEquals("http", httpOrigin.scheme(), "unexpected scheme"); + + + final URI httpsURI = URI.create("https://[::1]"); + final Origin httpsOrigin = Origin.from(httpsURI); + assertNotNull(httpsOrigin, "null Origin for URI " + httpsURI); + assertEquals("::1", httpsOrigin.host(), "unexpected host"); + assertEquals(443, httpsOrigin.port(), "unexpected port"); + assertEquals("https", httpsOrigin.scheme(), "unexpected scheme"); + } + + @Test + void testFromURI() { + // non-lower case URI scheme is expected to be converted to lowercase in the Origin + // constructed through Origin.from(URI) + for (final String scheme : new String[]{"httPs", "HTTP"}) { + final String expectedScheme = scheme.toLowerCase(Locale.ROOT); + final URI uri = URI.create(scheme + "://localhost:1234"); + final Origin origin = Origin.from(uri); + assertNotNull(origin, "null Origin for URI " + uri); + assertEquals("localhost", origin.host(), "unexpected host"); + assertEquals(1234, origin.port(), "unexpected port"); + assertEquals(expectedScheme, origin.scheme(), "unexpected scheme"); + } + // URI without a port is expected to be defaulted to port 80 or 443 for http and https + // schemes respectively + for (final String scheme : new String[]{"http", "https"}) { + final int expectedPort = switch (scheme) { + case "http" -> 80; + case "https" -> 443; + default -> fail("unexpected scheme: " + scheme); + }; + final URI uri = URI.create(scheme + "://localhost"); + final Origin origin = Origin.from(uri); + assertNotNull(origin, "null Origin for URI " + uri); + assertEquals("localhost", origin.host(), "unexpected host"); + assertEquals(expectedPort, origin.port(), "unexpected port"); + assertEquals(scheme, origin.scheme(), "unexpected scheme"); + } + } +} diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java index 729fdb084e1..27d2b98a34d 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/ConnectionPoolTest.java @@ -33,6 +33,7 @@ import java.net.ProxySelector; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketOption; +import java.net.URI; import java.net.http.HttpHeaders; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; @@ -459,7 +460,9 @@ public class ConnectionPoolTest { InetSocketAddress address, InetSocketAddress proxy, boolean secured) { - super(address, impl, "testConn-" + IDS.incrementAndGet()); + final Origin originServer = Origin.from( + URI.create("http://"+ address.getHostString() + ":" + address.getPort())); + super(originServer, address, impl, "testConn-" + IDS.incrementAndGet()); this.key = ConnectionPool.cacheKey(secured, address, proxy); this.address = address; this.proxy = proxy; diff --git a/test/jdk/java/nio/file/FileStore/Basic.java b/test/jdk/java/nio/file/FileStore/Basic.java index 9bba08de71f..0a7b3d5e2f6 100644 --- a/test/jdk/java/nio/file/FileStore/Basic.java +++ b/test/jdk/java/nio/file/FileStore/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, 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 @@ -22,7 +22,7 @@ */ /* @test - * @bug 4313887 6873621 6979526 7006126 7020517 8264400 + * @bug 4313887 6873621 6979526 7006126 7020517 8264400 8360887 * @summary Unit test for java.nio.file.FileStore * @key intermittent * @library .. /test/lib @@ -67,6 +67,16 @@ public class Basic { } } + static void testFileAttributes(Path file, + Class viewClass, + String viewName) throws IOException { + FileStore store = Files.getFileStore(file); + boolean supported = store.supportsFileAttributeView(viewClass); + assertTrue(store.supportsFileAttributeView(viewName) == supported); + boolean haveView = Files.getFileAttributeView(file, viewClass) != null; + assertTrue(haveView == supported); + } + static void doTests(Path dir) throws IOException { /** * Test: Directory should be on FileStore that is writable @@ -97,15 +107,11 @@ public class Basic { * Test: File and FileStore attributes */ assertTrue(store1.supportsFileAttributeView("basic")); - assertTrue(store1.supportsFileAttributeView(BasicFileAttributeView.class)); - assertTrue(store1.supportsFileAttributeView("posix") == - store1.supportsFileAttributeView(PosixFileAttributeView.class)); - assertTrue(store1.supportsFileAttributeView("dos") == - store1.supportsFileAttributeView(DosFileAttributeView.class)); - assertTrue(store1.supportsFileAttributeView("acl") == - store1.supportsFileAttributeView(AclFileAttributeView.class)); - assertTrue(store1.supportsFileAttributeView("user") == - store1.supportsFileAttributeView(UserDefinedFileAttributeView.class)); + testFileAttributes(dir, BasicFileAttributeView.class, "basic"); + testFileAttributes(dir, PosixFileAttributeView.class, "posix"); + testFileAttributes(dir, DosFileAttributeView.class, "dos"); + testFileAttributes(dir, AclFileAttributeView.class, "acl"); + testFileAttributes(dir, UserDefinedFileAttributeView.class, "user"); /** * Test: Space atributes diff --git a/test/jdk/java/util/regex/RegExTest.java b/test/jdk/java/util/regex/RegExTest.java index 3338b4f5c2d..2bd2dc3e38d 100644 --- a/test/jdk/java/util/regex/RegExTest.java +++ b/test/jdk/java/util/regex/RegExTest.java @@ -2324,6 +2324,21 @@ public class RegExTest { check(p, "test\u00e4\u0300\u0323", true); Object[][] data = new Object[][] { + // JDK-8354490 + // emoji + emoji_component pair forms a single grapheme but remains + // as 2 separate characters in nfc. match & find should still work with the + // CANON_EQ flag, as long as the character class is appropriately specified. + {"^[^/]*\\.[^/]*$", "\u2764\ufe0ffile.txt", "m", true}, + { "\\p{IsEmoji}", "ab\u2764\ufe0fcd", "f", true }, + { "[\\p{IsEmoji}]", "ab\u2764\ufe0fcd", "f", true }, + { "\\p{IsEmoji}\\p{IsEmoji_Component}", "\u2764\ufe0f", "m", true }, + { "[\\p{IsEmoji}\\p{IsEmoji_Component}]{2}", "\u2764\ufe0f", "m", true }, + // greek with extra combining character + {"\\p{IsGreek}", "\u1f80\u0345", "f", true}, + {"[\\p{IsGreek}]", "\u1f80\u0345", "f", true}, + {"\\p{IsGreek}\\p{IsAlphabetic}", "\u1f80\u0345", "m", true}, + {"\\p{IsAlphabetic}*", "\u1f80\u0345", "m", true}, + {"[\\p{IsAlphabetic}]*", "\u1f80\u0345", "m", true}, // JDK-4867170 { "[\u1f80-\u1f82]", "ab\u1f80cd", "f", true }, diff --git a/test/jdk/javax/swing/JTree/TestTreeRowSelection.java b/test/jdk/javax/swing/JTree/TestTreeRowSelection.java new file mode 100644 index 00000000000..20dbc433e52 --- /dev/null +++ b/test/jdk/javax/swing/JTree/TestTreeRowSelection.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key headful + * @requires (os.family == "mac") + * @bug 8360462 + * @summary Verifies ctrl+shift+down selects next row + * and ctrl+shift+up selects previous row in Aqua L&F + * @run main TestTreeRowSelection + */ + +import java.awt.Robot; +import java.awt.event.KeyEvent; +import javax.swing.JFrame; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.tree.DefaultMutableTreeNode; + +public class TestTreeRowSelection { + static JTree tree; + static JFrame frame; + static volatile int selectedRowCount; + static volatile int curSelectedRowCount; + + public static void main(String[] args) throws Exception { + + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame(); + DefaultMutableTreeNode sports = new DefaultMutableTreeNode("sports"); + sports.add(new DefaultMutableTreeNode("basketball")); + sports.add(new DefaultMutableTreeNode("football")); + sports.add(new DefaultMutableTreeNode("cricket")); + sports.add(new DefaultMutableTreeNode("tennis")); + + tree = new JTree(sports); + tree.setSelectionRow(2); + + frame.getContentPane().add(tree); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(1000); + SwingUtilities.invokeAndWait(() -> { + selectedRowCount = tree.getSelectionCount(); + }); + System.out.println("rows selected " + selectedRowCount); + for (int i = 0; i < 2; i++) { + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_DOWN); + robot.keyRelease(KeyEvent.VK_DOWN); + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(500); + } + SwingUtilities.invokeAndWait(() -> { + curSelectedRowCount = tree.getSelectionCount(); + }); + System.out.println("rows selected " + curSelectedRowCount); + if (curSelectedRowCount != selectedRowCount + 2) { + throw new RuntimeException("ctrl+shift+down doesn't select next row"); + } + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_UP); + robot.keyRelease(KeyEvent.VK_UP); + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(500); + SwingUtilities.invokeAndWait(() -> { + curSelectedRowCount = tree.getSelectionCount(); + }); + System.out.println("rows selected " + curSelectedRowCount); + if (curSelectedRowCount != selectedRowCount + 1) { + throw new RuntimeException("ctrl+shift+up doesn't select previous row"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} diff --git a/test/jdk/javax/swing/border/LineBorder/ScaledLineBorderTest.java b/test/jdk/javax/swing/border/LineBorder/ScaledLineBorderTest.java index fc83b49597d..80faeed460e 100644 --- a/test/jdk/javax/swing/border/LineBorder/ScaledLineBorderTest.java +++ b/test/jdk/javax/swing/border/LineBorder/ScaledLineBorderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -42,15 +42,18 @@ import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; +import static sun.java2d.pipe.Region.clipRound; + /* * @test - * @bug 8282958 + * @bug 8282958 8349188 * @summary Verify LineBorder edges have the same width * @requires (os.family == "windows") + * @modules java.desktop/sun.java2d.pipe * @run main ScaledLineBorderTest */ public class ScaledLineBorderTest { - private static final Dimension SIZE = new Dimension(120, 25); + private static final Dimension SIZE = new Dimension(250, 50); private static final Color OUTER_COLOR = Color.BLACK; private static final Color BORDER_COLOR = Color.RED; @@ -59,12 +62,19 @@ public class ScaledLineBorderTest { private static final double[] scales = {1.00, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00}; + private static final int[] thickness = {1, 4, 10, 15}; - private static final List images = - new ArrayList<>(scales.length); + private record TestImage(BufferedImage image, + List panelLocations, + double scale, + int thickness) { + } + + private record TestUI(JComponent content, + List panelLocations, + int thickness) { + } - private static final List panelLocations = - new ArrayList<>(4); public static void main(String[] args) throws Exception { Collection params = Arrays.asList(args); @@ -74,29 +84,38 @@ public class ScaledLineBorderTest { } private static void testScaling(boolean showFrame, boolean saveImages) { - JComponent content = createUI(); - if (showFrame) { - showFrame(content); + for (int thickness : thickness) { + TestUI testUI = createUI(thickness); + if (showFrame) { + showFrame(testUI.content); + } + + List images = paintToImages(testUI, saveImages); + verifyBorderRendering(images, saveImages); + } + + if (errorCount > 0) { + throw new Error("Test failed: " + + errorCount + " error(s) detected - " + + errorMessage); } - paintToImages(content, saveImages); - verifyBorderRendering(saveImages); } - private static void verifyBorderRendering(final boolean saveImages) { - String errorMessage = null; - int errorCount = 0; - for (int i = 0; i < images.size(); i++) { - BufferedImage img = images.get(i); - double scaling = scales[i]; + private static String errorMessage = null; + private static int errorCount = 0; + + private static void verifyBorderRendering(final List images, + final boolean saveImages) { + for (TestImage test : images) { + final BufferedImage img = test.image; + final int effectiveThickness = clipRound(test.thickness * test.scale); try { - int thickness = (int) Math.floor(scaling); + checkVerticalBorders((int) (SIZE.width * test.scale / 2), effectiveThickness, img); - checkVerticalBorders(SIZE.width / 2, thickness, img); - - for (Point p : panelLocations) { - int y = (int) (p.y * scaling) + SIZE.height / 2; - checkHorizontalBorder(y, thickness, img); + for (Point p : test.panelLocations) { + int y = (int) ((p.y + (SIZE.height / 2)) * test.scale); + checkHorizontalBorder(y, effectiveThickness, img); } } catch (Error e) { if (errorMessage == null) { @@ -104,21 +123,13 @@ public class ScaledLineBorderTest { } errorCount++; - System.err.printf("Scaling: %.2f\n", scaling); + System.err.printf("Scale: %.2f; thickness: %d, effective: %d\n", + test.scale, test.thickness, effectiveThickness); e.printStackTrace(); - // Save the image if it wasn't already saved - if (!saveImages) { - saveImage(img, getImageFileName(scaling)); - } + saveImage(img, getImageFileName(test.scale, test.thickness)); } } - - if (errorCount > 0) { - throw new Error("Test failed: " - + errorCount + " error(s) detected - " - + errorMessage); - } } private static void checkVerticalBorders(final int x, @@ -220,17 +231,19 @@ public class ScaledLineBorderTest { x, y, color)); } - private static JComponent createUI() { + private static TestUI createUI(int thickness) { Box contentPanel = Box.createVerticalBox(); contentPanel.setBackground(OUTER_COLOR); + List panelLocations = new ArrayList<>(4); + Dimension childSize = null; for (int i = 0; i < 4; i++) { JComponent filler = new JPanel(null); filler.setBackground(INSIDE_COLOR); filler.setPreferredSize(SIZE); filler.setBounds(i, 0, SIZE.width, SIZE.height); - filler.setBorder(BorderFactory.createLineBorder(BORDER_COLOR)); + filler.setBorder(BorderFactory.createLineBorder(BORDER_COLOR, thickness)); JPanel childPanel = new JPanel(new BorderLayout()); childPanel.setBorder(BorderFactory.createEmptyBorder(0, i, 4, 4)); @@ -248,7 +261,7 @@ public class ScaledLineBorderTest { contentPanel.setSize(childSize.width, childSize.height * 4); - return contentPanel; + return new TestUI(contentPanel, panelLocations, thickness); } private static void showFrame(JComponent content) { @@ -260,28 +273,33 @@ public class ScaledLineBorderTest { frame.setVisible(true); } - private static void paintToImages(final JComponent content, - final boolean saveImages) { - for (double scaling : scales) { + private static List paintToImages(final TestUI testUI, + final boolean saveImages) { + final List images = new ArrayList<>(scales.length); + final JComponent content = testUI.content; + for (double scale : scales) { BufferedImage image = - new BufferedImage((int) Math.ceil(content.getWidth() * scaling), - (int) Math.ceil(content.getHeight() * scaling), + new BufferedImage((int) Math.ceil(content.getWidth() * scale), + (int) Math.ceil(content.getHeight() * scale), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = image.createGraphics(); - g2d.scale(scaling, scaling); + g2d.scale(scale, scale); content.paint(g2d); g2d.dispose(); if (saveImages) { - saveImage(image, getImageFileName(scaling)); + saveImage(image, getImageFileName(scale, testUI.thickness)); } - images.add(image); + images.add(new TestImage(image, testUI.panelLocations, + scale, testUI.thickness)); } + return images; } - private static String getImageFileName(final double scaling) { - return String.format("test%.2f.png", scaling); + private static String getImageFileName(final double scaling, + final int thickness) { + return String.format("test%02d@%.2f.png", thickness, scaling); } private static void saveImage(BufferedImage image, String filename) { diff --git a/test/jdk/javax/swing/border/LineBorder/ScaledTextFieldBorderTest.java b/test/jdk/javax/swing/border/LineBorder/ScaledTextFieldBorderTest.java index 4b076977313..2a732812e38 100644 --- a/test/jdk/javax/swing/border/LineBorder/ScaledTextFieldBorderTest.java +++ b/test/jdk/javax/swing/border/LineBorder/ScaledTextFieldBorderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -44,12 +44,15 @@ import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.border.LineBorder; +import static sun.java2d.pipe.Region.clipRound; + /* * @test - * @bug 8282958 + * @bug 8282958 8349188 * @summary Verify all the borders are rendered consistently for a JTextField * in Windows LaF which uses LineBorder * @requires (os.family == "windows") + * @modules java.desktop/sun.java2d.pipe * @run main ScaledTextFieldBorderTest */ public class ScaledTextFieldBorderTest { @@ -92,7 +95,7 @@ public class ScaledTextFieldBorderTest { BufferedImage img = images.get(i); double scaling = scales[i]; try { - int thickness = (int) Math.floor(scaling); + int thickness = clipRound(scaling); checkVerticalBorders(textFieldSize.width / 2, thickness, img); diff --git a/test/jdk/jdk/incubator/vector/BasicFloat16ArithTests.java b/test/jdk/jdk/incubator/vector/BasicFloat16ArithTests.java index 4ed95f698cf..bde705c5db4 100644 --- a/test/jdk/jdk/incubator/vector/BasicFloat16ArithTests.java +++ b/test/jdk/jdk/incubator/vector/BasicFloat16ArithTests.java @@ -186,7 +186,7 @@ public class BasicFloat16ArithTests { for(var testCase : testCases) { float arg = testCase[0]; float expected = testCase[1]; - Float16 result = negate(valueOf(arg)); + Float16 result = negate(valueOfExact(arg)); if (Float.compare(expected, result.floatValue()) != 0) { checkFloat16(result, expected, "negate(" + arg + ")"); @@ -213,7 +213,7 @@ public class BasicFloat16ArithTests { for(var testCase : testCases) { float arg = testCase[0]; float expected = testCase[1]; - Float16 result = abs(valueOf(arg)); + Float16 result = abs(valueOfExact(arg)); if (Float.compare(expected, result.floatValue()) != 0) { checkFloat16(result, expected, "abs(" + arg + ")"); @@ -238,7 +238,7 @@ public class BasicFloat16ArithTests { }; for(var testCase : testCases) { - boolean result = isNaN(valueOf(testCase)); + boolean result = isNaN(valueOfExact(testCase)); if (result) { throwRE("isNaN returned true for " + testCase); } @@ -254,8 +254,8 @@ public class BasicFloat16ArithTests { }; for(var infinity : infinities) { - boolean result1 = isFinite(valueOf(infinity)); - boolean result2 = isInfinite(valueOf(infinity)); + boolean result1 = isFinite(valueOfExact(infinity)); + boolean result2 = isInfinite(valueOfExact(infinity)); if (result1) { throwRE("Float16.isFinite returned true for " + infinity); @@ -282,8 +282,8 @@ public class BasicFloat16ArithTests { }; for(var finity : finities) { - boolean result1 = isFinite(valueOf(finity)); - boolean result2 = isInfinite(valueOf(finity)); + boolean result1 = isFinite(valueOfExact(finity)); + boolean result2 = isInfinite(valueOfExact(finity)); if (!result1) { throwRE("Float16.isFinite returned true for " + finity); @@ -301,12 +301,12 @@ public class BasicFloat16ArithTests { float small = 1.0f; float large = 2.0f; - if (min(valueOf(small), valueOf(large)).floatValue() != small) { + if (min(valueOfExact(small), valueOfExact(large)).floatValue() != small) { throwRE(String.format("min(%g, %g) not equal to %g)", small, large, small)); } - if (max(valueOf(small), valueOf(large)).floatValue() != large) { + if (max(valueOfExact(small), valueOfExact(large)).floatValue() != large) { throwRE(String.format("max(%g, %g) not equal to %g)", small, large, large)); } @@ -318,10 +318,10 @@ public class BasicFloat16ArithTests { */ private static void checkArith() { float a = 1.0f; - Float16 a16 = valueOf(a); + Float16 a16 = valueOfExact(a); float b = 2.0f; - Float16 b16 = valueOf(b); + Float16 b16 = valueOfExact(b); if (add(a16, b16).floatValue() != (a + b)) { throwRE("failure with " + a16 + " + " + b16); @@ -371,7 +371,7 @@ public class BasicFloat16ArithTests { for(var testCase : testCases) { float arg = testCase[0]; float expected = testCase[1]; - Float16 result = sqrt(valueOf(arg)); + Float16 result = sqrt(valueOfExact(arg)); if (Float.compare(expected, result.floatValue()) != 0) { checkFloat16(result, expected, "sqrt(" + arg + ")"); @@ -409,7 +409,7 @@ public class BasicFloat16ArithTests { float arg = testCase[0]; float expected = testCase[1]; // Exponents are in-range for Float16 - Float16 result = valueOf(getExponent(valueOf(arg))); + Float16 result = valueOfExact(getExponent(valueOfExact(arg))); if (Float.compare(expected, result.floatValue()) != 0) { checkFloat16(result, expected, "getExponent(" + arg + ")"); @@ -445,7 +445,7 @@ public class BasicFloat16ArithTests { float arg = testCase[0]; float expected = testCase[1]; // Exponents are in-range for Float16 - Float16 result = ulp(valueOf(arg)); + Float16 result = ulp(valueOfExact(arg)); if (Float.compare(expected, result.floatValue()) != 0) { checkFloat16(result, expected, "ulp(" + arg + ")"); @@ -602,7 +602,7 @@ public class BasicFloat16ArithTests { String input = testCase.input(); float expected = testCase.expected(); Float16 result = Float16.valueOf(input); - checkFloat16(result, expected, "Float16.valueOf(String) " + input); + checkFloat16(result, expected, "Float16.valueOfExact(String) " + input); } List negativeCases = List.of("0x1", @@ -747,7 +747,7 @@ public class BasicFloat16ArithTests { } private static void testSimple() { - final float ulpOneFp16 = ulp(valueOf(1.0f)).floatValue(); + final float ulpOneFp16 = ulp(valueOfExact(1.0f)).floatValue(); float [][] testCases = { {1.0f, 2.0f, 3.0f, @@ -781,7 +781,7 @@ public class BasicFloat16ArithTests { } private static void testRounding() { - final float ulpOneFp16 = ulp(valueOf(1.0f)).floatValue(); + final float ulpOneFp16 = ulp(valueOfExact(1.0f)).floatValue(); float [][] testCases = { // The product is equal to @@ -839,10 +839,10 @@ public class BasicFloat16ArithTests { } private static void testFusedMacCase(float input1, float input2, float input3, float expected) { - Float16 a = valueOf(input1); - Float16 b = valueOf(input2); - Float16 c = valueOf(input3); - Float16 d = valueOf(expected); + Float16 a = valueOfExact(input1); + Float16 b = valueOfExact(input2); + Float16 c = valueOfExact(input3); + Float16 d = valueOfExact(expected); test("Float16.fma(float)", a, b, c, Float16.fma(a, b, c), d); @@ -865,4 +865,20 @@ public class BasicFloat16ArithTests { throw new RuntimeException(); } } + + /** + * {@return a Float16 value converted from the {@code float} + * argument throwing an {@code ArithmeticException} if the + * conversion is inexact}. + * + * @param f the {@code float} value to convert exactly + * @throws ArithmeticException + */ + private static Float16 valueOfExact(float f) { + Float16 f16 = valueOf(f); + if (Float.compare(f16.floatValue(), f) != 0) { + throw new ArithmeticException("Inexact conversion to Float16 of float value " + f); + } + return f16; + } } diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 681c88654c0..5ac0bea3937 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -120,6 +120,7 @@ public class VMProps implements Callable> { map.put("vm.pageSize", this::vmPageSize); // vm.cds is true if the VM is compiled with cds support. map.put("vm.cds", this::vmCDS); + map.put("vm.cds.default.archive.available", this::vmCDSDefaultArchiveAvailable); map.put("vm.cds.custom.loaders", this::vmCDSForCustomLoaders); map.put("vm.cds.supports.aot.class.linking", this::vmCDSSupportsAOTClassLinking); map.put("vm.cds.supports.aot.code.caching", this::vmCDSSupportsAOTCodeCaching); @@ -424,6 +425,16 @@ public class VMProps implements Callable> { return "" + WB.isCDSIncluded(); } + /** + * Check for CDS default archive existence. + * + * @return true if CDS default archive classes.jsa exists in the JDK to be tested. + */ + protected String vmCDSDefaultArchiveAvailable() { + Path archive = Paths.get(System.getProperty("java.home"), "lib", "server", "classes.jsa"); + return "" + ("true".equals(vmCDS()) && Files.exists(archive)); + } + /** * Check for CDS support for custom loaders. * diff --git a/test/langtools/tools/javac/patterns/T8358801.java b/test/langtools/tools/javac/patterns/T8358801.java new file mode 100644 index 00000000000..11d51452aa1 --- /dev/null +++ b/test/langtools/tools/javac/patterns/T8358801.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8358801 + * @summary Verify variables introduced by let expressions are correctly undefined + * @library /tools/lib + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; + +import javax.tools.JavaFileObject; +import javax.tools.ToolProvider; + +import com.sun.source.util.JavacTask; +import java.lang.classfile.ClassFile; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import javax.tools.JavaCompiler; + +import toolbox.JavaTask; +import toolbox.Task; +import toolbox.TestRunner; +import toolbox.ToolBox; + +public class T8358801 extends TestRunner { + private ToolBox tb; + + public static void main(String... args) throws Exception { + new T8358801().runTests(); + } + + T8358801() { + super(System.err); + tb = new ToolBox(); + } + + public void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void testPatternsInJava(Path base) throws Exception { + Path classes = base.resolve("classes"); + + List files = new ArrayList<>(); + files.add(new ToolBox.JavaSource( + """ + public class Main { + private boolean test(String s, int i) { + if (s.subSequence(0, 1) instanceof Runnable r) { + return true; + } + + switch (i) { + case 0: + String clashing1 = null; + String clashing2 = null; + String clashing3 = null; + String clashing4 = null; + return true; + default: + System.out.println("correct"); + return true; + } + } + + public static void main(String[] args) { + new Main().test("hello", 1); + } + } + """ + )); + + if (Files.exists(classes)) { + tb.cleanDirectory(classes); + } else { + Files.createDirectories(classes); + } + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + Iterable options = Arrays.asList("-d", classes.toString()); + JavacTask task = (JavacTask) compiler.getTask(null, null, null, options, null, files); + + task.generate(); + + List errors = ClassFile.of().verify(classes.resolve("Main.class")); + + if (!errors.isEmpty()) { + throw new AssertionError("verify errors found: " + errors); + } + + List log = + new JavaTask(tb).classpath(classes.toString()) + .className("Main") + .run() + .writeAll() + .getOutputLines(Task.OutputKind.STDOUT); + List expected = List.of("correct"); + + if (!Objects.equals(log, expected)) { + throw new AssertionError("Incorrect result, expected: " + expected + + ", got: " + log); + } + } + +} diff --git a/test/lib-test/jdk/test/lib/jittester/MethodTemplateTest.java b/test/lib-test/jdk/test/lib/jittester/MethodTemplateTest.java new file mode 100644 index 00000000000..3cee24b4684 --- /dev/null +++ b/test/lib-test/jdk/test/lib/jittester/MethodTemplateTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2025, 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 jdk.test.lib.jittester; + +import java.lang.reflect.Executable; + +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/* + * @test + * @summary Unit tests for JITTester string method templates + * + * @library /test/lib + * /test/hotspot/jtreg/testlibrary/jittester/src + * + * @run testng jdk.test.lib.jittester.MethodTemplateTest + */ +public class MethodTemplateTest { + + @Test + public void testMatchingPatterns() throws NoSuchMethodException { + Tester.forMethod(System.class, "getenv", String.class) + .assertMatches("java/lang/System::getenv(Ljava/lang/String;)") + .assertMatches("*::getenv(Ljava/lang/String;)") + .assertMatches("java/lang/*::getenv(Ljava/lang/String;)") + .assertMatches("java/lang/System::*env*(Ljava/lang/String;)") + .assertMatches("java/lang/System::getenv") + .assertMatches("java/lang/System::getenv(*)"); + + Tester.forCtor(RuntimeException.class, Throwable.class) + .assertMatches("java/lang/RuntimeException::RuntimeException(Ljava/lang/Throwable;)"); + + Tester.forMethod(String.class, "regionMatches", int.class, String.class, int.class, int.class) + .assertMatches("java/lang/String::regionMatches(ILjava/lang/String;II)"); + } + + @Test + public void testNonMatchingPatterns() throws NoSuchMethodException { + Tester.forMethod(String.class, "regionMatches", int.class, String.class, int.class, int.class) + .assertDoesNotMatch("java/lang/String::regionMatches(IIILjava/lang/String;)"); + + Tester.forMethod(String.class, "endsWith", String.class) + .assertDoesNotMatch("java/lang/String::startsWith(Ljava/lang/String;)"); + } + + @Test + public void testWildcardStrings() { + assertTrue(new MethodTemplate.WildcardString("Torment") + .matches("Torment")); + + assertTrue(new MethodTemplate.WildcardString("Torm*") + .matches("Torment")); + + assertTrue(new MethodTemplate.WildcardString("*ent") + .matches("Torment")); + + assertTrue(new MethodTemplate.WildcardString("*") + .matches("Something")); + + assertTrue(new MethodTemplate.WildcardString("**") + .matches("Something")); + + assertTrue(new MethodTemplate.WildcardString("*Middle*") + .matches("OnlyMiddleMatches")); + + assertFalse(new MethodTemplate.WildcardString("Wrong") + .matches("Correct")); + assertFalse(new MethodTemplate.WildcardString("Joy") + .matches("Joyfull")); + assertFalse(new MethodTemplate.WildcardString("*Torm*") + .matches("Sorrow")); + } + + static final class Tester { + private final Executable executable; + + private Tester(Executable executable) { + this.executable = executable; + } + + public Tester assertMatches(String stringTemplate) { + MethodTemplate template = MethodTemplate.parse(stringTemplate); + assertTrue(template.matches(executable), + "Method '" + executable + "' does not match template '" + stringTemplate + "'"); + return this; + } + + public Tester assertDoesNotMatch(String stringTemplate) { + MethodTemplate template = MethodTemplate.parse(stringTemplate); + assertFalse(template.matches(executable), + "Method '" + executable + "' erroneously matches template '" + stringTemplate + "'"); + return this; + } + + public static Tester forMethod(Class klass, String name, Class... arguments) + throws NoSuchMethodException { + return new Tester(klass.getDeclaredMethod(name, arguments)); + } + + public static Tester forCtor(Class klass, Class... arguments) + throws NoSuchMethodException { + return new Tester(klass.getConstructor(arguments)); + } + } + +} diff --git a/test/lib/RedefineClassHelper.java b/test/lib/RedefineClassHelper.java index 88f31f8ba8f..ce27fb33f44 100644 --- a/test/lib/RedefineClassHelper.java +++ b/test/lib/RedefineClassHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -21,8 +21,14 @@ * questions. */ -import java.lang.instrument.Instrumentation; +import java.io.InputStream; +import java.lang.classfile.ClassElement; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.constant.ClassDesc; + import java.lang.instrument.ClassDefinition; +import java.lang.instrument.Instrumentation; import jdk.test.lib.compiler.InMemoryJavaCompiler; import jdk.test.lib.helpers.ClassFileInstaller; @@ -33,6 +39,7 @@ import jdk.test.lib.helpers.ClassFileInstaller; * * See sample test in test/testlibrary_tests/RedefineClassTest.java */ + public class RedefineClassHelper { public static Instrumentation instrumentation; @@ -61,6 +68,41 @@ public class RedefineClassHelper { instrumentation.redefineClasses(new ClassDefinition(clazz, bytecode)); } + private static byte[] getBytecodes(ClassLoader loader, String name) throws Exception { + try (InputStream is = loader.getResourceAsStream(name + ".class")) { + byte[] buf = is.readAllBytes(); + System.out.println("sizeof(" + name + ".class) == " + buf.length); + return buf; + } + } + + /* + * Copy the class defined by `bytes`, replacing the name of the class with `newClassName`, + * so that both old and new classes can be compiled by jtreg for the test. + * + * @param bytes read from the original class file. + * @param newClassName new class name for the returned class representation + * @return a copy of the class represented by `bytes` but with the name `newClassName` + */ + public static byte[] replaceClassName(byte[] bytes, String newClassName) throws Exception { + ClassModel classModel = ClassFile.of().parse(bytes); + return ClassFile.of().build(ClassDesc.of(newClassName), classModel::forEach); + } + + /* + * Replace class name in bytecodes to the class we're trying to redefine, so that both + * old and new classes can be compiled with jtreg for the test. + * + * @param loader ClassLoader to find the bytes for the old class. + * @param oldClassName old class name. + * @param newClassName new class name to replace with old class name. + * @return a copy of the class represented by `bytes` but with the name `newClassName` + */ + public static byte[] replaceClassName(ClassLoader loader, String oldClassName, String newClassName) throws Exception { + byte[] buf = getBytecodes(loader, oldClassName); + return replaceClassName(buf, newClassName); + } + /** * Main method to be invoked before test to create the redefineagent.jar */ diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index 7e98b57cb83..cfe17a0be14 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java @@ -262,7 +262,7 @@ abstract public class CDSAppTester { "class+load=debug", "aot=debug", "cds=debug", - "cds+class=debug")); + "aot+class=debug")); cmdLine = addCommonVMArgs(runMode, cmdLine); cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); return executeAndCheck(cmdLine, runMode, aotConfigurationFile, aotConfigurationFileLog); @@ -275,8 +275,9 @@ abstract public class CDSAppTester { "-XX:AOTCacheOutput=" + aotCacheFile, logToFile(aotCacheFileLog, "class+load=debug", - "cds=debug", - "cds+class=debug")); + "aot=debug", + "aot+class=debug", + "cds=debug")); cmdLine = addCommonVMArgs(runMode, cmdLine); cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); OutputAnalyzer out = executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog); @@ -310,7 +311,7 @@ abstract public class CDSAppTester { "cds=debug", "cds+class=debug", "aot+heap=warning", - "cds+resolve=debug")); + "aot+resolve=debug")); cmdLine = addCommonVMArgs(runMode, cmdLine); cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog); @@ -326,11 +327,11 @@ abstract public class CDSAppTester { "-XX:AOTConfiguration=" + aotConfigurationFile, "-XX:AOTCache=" + aotCacheFile, logToFile(aotCacheFileLog, - "aot=debug", "cds=debug", - "cds+class=debug", + "aot=debug", + "aot+class=debug", "aot+heap=warning", - "cds+resolve=debug")); + "aot+resolve=debug")); cmdLine = addCommonVMArgs(runMode, cmdLine); cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); return executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog); @@ -377,7 +378,7 @@ abstract public class CDSAppTester { "aot=debug", "cds=debug", "cds+class=debug", - "cds+resolve=debug", + "aot+resolve=debug", "class+load=debug")); cmdLine = addCommonVMArgs(runMode, cmdLine); }