mirror of
https://github.com/openjdk/jdk.git
synced 2026-08-03 14:47:03 +00:00
Merge branch 'JDK-8348611' into JDK-8344159
This commit is contained in:
commit
a0d489596d
@ -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);
|
||||
}
|
||||
|
||||
@ -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, <old q0>);
|
||||
// update v30, which was scalbn(1.0, <old q0>);
|
||||
addw(tmp2, rscratch1, 1023); // biased exponent
|
||||
lsl(tmp2, tmp2, 52); // put at correct position
|
||||
mov(i, jz);
|
||||
|
||||
@ -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(), "");
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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))));
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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 <typename T> void ArchiveHeapWriter::relocate_field_in_buffer(T* field_
|
||||
oop source_referent = load_source_oop_from_buffer<T>(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);
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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 */ \
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<address, address, 15889, AnyObj::C_HEAP, mtClassShared>;
|
||||
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<OopHandle, mtClassShared>* _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<Method*>* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <class T> static bool has_been_regenerated(T orig_obj) {
|
||||
return has_been_regenerated((address)orig_obj);
|
||||
}
|
||||
template <class T> 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 <class T> static bool is_regenerated_object(T regen_obj) {
|
||||
return is_regenerated_object((address)regen_obj);
|
||||
}
|
||||
template <class T> static T get_original_object(T regen_obj) {
|
||||
return (T)get_original_object((address)regen_obj);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_CDS_REGENERATEDCLASSES_HPP
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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<Method*>* _methods;
|
||||
GrowableArray<int>* _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);
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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<mtModule> {
|
||||
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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -136,6 +136,7 @@ private:
|
||||
public:
|
||||
AOTCodeAddressTable() :
|
||||
_extrs_addr(nullptr),
|
||||
_stubs_addr(nullptr),
|
||||
_shared_blobs_addr(nullptr),
|
||||
_C1_blobs_addr(nullptr),
|
||||
_extrs_length(0),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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<nmethod*>(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<nmethod*>(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
|
||||
|
||||
@ -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); }
|
||||
|
||||
@ -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]");
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<mtCompiler> {
|
||||
|
||||
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<mtCompiler> {
|
||||
|
||||
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; }
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -446,8 +446,6 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
|
||||
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;
|
||||
|
||||
@ -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 GCTraceConcTimeImpl<LogLevel::Info, LOG_TAGS(gc,
|
||||
};
|
||||
|
||||
void G1ConcurrentMarkThread::run_service() {
|
||||
_vtime_start = os::elapsedVTime();
|
||||
|
||||
while (wait_for_next_cycle()) {
|
||||
assert(in_progress(), "must be");
|
||||
|
||||
@ -133,9 +129,7 @@ void G1ConcurrentMarkThread::run_service() {
|
||||
|
||||
concurrent_cycle_end(_state == FullMark && !_cm->has_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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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) :
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@ -44,7 +44,7 @@ void G1RemSetSummary::update() {
|
||||
CollectData(G1RemSetSummary * summary) : _summary(summary), _counter(0) {}
|
||||
virtual void do_thread(Thread* t) {
|
||||
G1ConcurrentRefineThread* crt = static_cast<G1ConcurrentRefineThread*>(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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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") \
|
||||
\
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 <SafePointNode *> *safepoints);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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") \
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() },
|
||||
|
||||
|
||||
@ -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() >
|
||||
|
||||
@ -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(); ) {
|
||||
|
||||
@ -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") \
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user