merge latest changes from master branch
This commit is contained in:
commit
d4984d5e5a
4
make/autoconf/configure
vendored
4
make/autoconf/configure
vendored
@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012, 2023, 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.
|
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
#
|
#
|
||||||
# This code is free software; you can redistribute it and/or modify it
|
# This code is free software; you can redistribute it and/or modify it
|
||||||
@ -366,7 +366,7 @@ EOT
|
|||||||
|
|
||||||
# Print additional help, e.g. a list of toolchains and JVM features.
|
# Print additional help, e.g. a list of toolchains and JVM features.
|
||||||
# This must be done by the autoconf script.
|
# This must be done by the autoconf script.
|
||||||
( CONFIGURE_PRINT_ADDITIONAL_HELP=true . $generated_script PRINTF=printf )
|
( CONFIGURE_PRINT_ADDITIONAL_HELP=true . $generated_script PRINTF=printf ECHO=echo )
|
||||||
|
|
||||||
cat <<EOT
|
cat <<EOT
|
||||||
|
|
||||||
|
@ -6687,6 +6687,7 @@ instruct reinterpretL(iRegLdst dst) %{
|
|||||||
match(Set dst (VectorReinterpret dst));
|
match(Set dst (VectorReinterpret dst));
|
||||||
ins_cost(0);
|
ins_cost(0);
|
||||||
format %{ "reinterpret $dst" %}
|
format %{ "reinterpret $dst" %}
|
||||||
|
size(0);
|
||||||
ins_encode( /*empty*/ );
|
ins_encode( /*empty*/ );
|
||||||
ins_pipe(pipe_class_empty);
|
ins_pipe(pipe_class_empty);
|
||||||
%}
|
%}
|
||||||
@ -6695,6 +6696,7 @@ instruct reinterpretX(vecX dst) %{
|
|||||||
match(Set dst (VectorReinterpret dst));
|
match(Set dst (VectorReinterpret dst));
|
||||||
ins_cost(0);
|
ins_cost(0);
|
||||||
format %{ "reinterpret $dst" %}
|
format %{ "reinterpret $dst" %}
|
||||||
|
size(0);
|
||||||
ins_encode( /*empty*/ );
|
ins_encode( /*empty*/ );
|
||||||
ins_pipe(pipe_class_empty);
|
ins_pipe(pipe_class_empty);
|
||||||
%}
|
%}
|
||||||
@ -6814,7 +6816,6 @@ instruct cond_set_0_oop(iRegNdst dst, flagsRegSrc crx, iRegPsrc src1) %{
|
|||||||
format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// encode: preserve 0" %}
|
format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// encode: preserve 0" %}
|
||||||
size(4);
|
size(4);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
// This is a Power7 instruction for which no machine description exists.
|
|
||||||
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
|
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
|
||||||
%}
|
%}
|
||||||
ins_pipe(pipe_class_default);
|
ins_pipe(pipe_class_default);
|
||||||
@ -6946,7 +6947,6 @@ instruct cond_set_0_ptr(iRegPdst dst, flagsRegSrc crx, iRegPsrc src1) %{
|
|||||||
format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// decode: preserve 0" %}
|
format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// decode: preserve 0" %}
|
||||||
size(4);
|
size(4);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
// This is a Power7 instruction for which no machine description exists.
|
|
||||||
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
|
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
|
||||||
%}
|
%}
|
||||||
ins_pipe(pipe_class_default);
|
ins_pipe(pipe_class_default);
|
||||||
@ -7423,8 +7423,6 @@ instruct cmovI_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegIdst dst, iRegIsrc src)
|
|||||||
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
|
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
|
||||||
size(4);
|
size(4);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
// This is a Power7 instruction for which no machine description
|
|
||||||
// exists. Anyways, the scheduler should be off on Power7.
|
|
||||||
int cc = $cmp$$cmpcode;
|
int cc = $cmp$$cmpcode;
|
||||||
__ isel($dst$$Register, $crx$$CondRegister,
|
__ isel($dst$$Register, $crx$$CondRegister,
|
||||||
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
|
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
|
||||||
@ -7440,8 +7438,6 @@ instruct cmovL_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegLdst dst, iRegLsrc src)
|
|||||||
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
|
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
|
||||||
size(4);
|
size(4);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
// This is a Power7 instruction for which no machine description
|
|
||||||
// exists. Anyways, the scheduler should be off on Power7.
|
|
||||||
int cc = $cmp$$cmpcode;
|
int cc = $cmp$$cmpcode;
|
||||||
__ isel($dst$$Register, $crx$$CondRegister,
|
__ isel($dst$$Register, $crx$$CondRegister,
|
||||||
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
|
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
|
||||||
@ -7457,8 +7453,6 @@ instruct cmovN_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegNdst dst, iRegNsrc src)
|
|||||||
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
|
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
|
||||||
size(4);
|
size(4);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
// This is a Power7 instruction for which no machine description
|
|
||||||
// exists. Anyways, the scheduler should be off on Power7.
|
|
||||||
int cc = $cmp$$cmpcode;
|
int cc = $cmp$$cmpcode;
|
||||||
__ isel($dst$$Register, $crx$$CondRegister,
|
__ isel($dst$$Register, $crx$$CondRegister,
|
||||||
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
|
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
|
||||||
@ -7474,8 +7468,6 @@ instruct cmovP_reg_isel(cmpOp cmp, flagsRegSrc crx, iRegPdst dst, iRegPsrc src)
|
|||||||
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
|
format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %}
|
||||||
size(4);
|
size(4);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
// This is a Power7 instruction for which no machine description
|
|
||||||
// exists. Anyways, the scheduler should be off on Power7.
|
|
||||||
int cc = $cmp$$cmpcode;
|
int cc = $cmp$$cmpcode;
|
||||||
__ isel($dst$$Register, $crx$$CondRegister,
|
__ isel($dst$$Register, $crx$$CondRegister,
|
||||||
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
|
(Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register);
|
||||||
@ -9921,13 +9913,6 @@ instruct andcL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{
|
|||||||
// of java.lang.Float etc., e.g.
|
// of java.lang.Float etc., e.g.
|
||||||
// int floatToIntBits(float value)
|
// int floatToIntBits(float value)
|
||||||
// float intBitsToFloat(int bits)
|
// float intBitsToFloat(int bits)
|
||||||
//
|
|
||||||
// Notes on the implementation on ppc64:
|
|
||||||
// For Power7 and earlier, the rules are limited to those which move between a
|
|
||||||
// register and a stack-location, because we always have to go through memory
|
|
||||||
// when moving between a float register and an integer register.
|
|
||||||
// This restriction is removed in Power8 with the introduction of the mtfprd
|
|
||||||
// and mffprd instructions.
|
|
||||||
|
|
||||||
instruct moveL2D_reg(regD dst, iRegLsrc src) %{
|
instruct moveL2D_reg(regD dst, iRegLsrc src) %{
|
||||||
match(Set dst (MoveL2D src));
|
match(Set dst (MoveL2D src));
|
||||||
@ -12434,6 +12419,7 @@ instruct minI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
|
|||||||
effect(KILL cr0);
|
effect(KILL cr0);
|
||||||
ins_cost(DEFAULT_COST*2);
|
ins_cost(DEFAULT_COST*2);
|
||||||
|
|
||||||
|
size(8);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ cmpw(CR0, $src1$$Register, $src2$$Register);
|
__ cmpw(CR0, $src1$$Register, $src2$$Register);
|
||||||
__ isel($dst$$Register, CR0, Assembler::less, /*invert*/false, $src1$$Register, $src2$$Register);
|
__ isel($dst$$Register, CR0, Assembler::less, /*invert*/false, $src1$$Register, $src2$$Register);
|
||||||
@ -12447,6 +12433,7 @@ instruct maxI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
|
|||||||
effect(KILL cr0);
|
effect(KILL cr0);
|
||||||
ins_cost(DEFAULT_COST*2);
|
ins_cost(DEFAULT_COST*2);
|
||||||
|
|
||||||
|
size(8);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ cmpw(CR0, $src1$$Register, $src2$$Register);
|
__ cmpw(CR0, $src1$$Register, $src2$$Register);
|
||||||
__ isel($dst$$Register, CR0, Assembler::greater, /*invert*/false, $src1$$Register, $src2$$Register);
|
__ isel($dst$$Register, CR0, Assembler::greater, /*invert*/false, $src1$$Register, $src2$$Register);
|
||||||
@ -12456,7 +12443,6 @@ instruct maxI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
|
|||||||
|
|
||||||
//---------- Population Count Instructions ------------------------------------
|
//---------- Population Count Instructions ------------------------------------
|
||||||
|
|
||||||
// Popcnt for Power7.
|
|
||||||
instruct popCountI(iRegIdst dst, iRegIsrc src) %{
|
instruct popCountI(iRegIdst dst, iRegIsrc src) %{
|
||||||
match(Set dst (PopCountI src));
|
match(Set dst (PopCountI src));
|
||||||
predicate(UsePopCountInstruction);
|
predicate(UsePopCountInstruction);
|
||||||
@ -12470,7 +12456,6 @@ instruct popCountI(iRegIdst dst, iRegIsrc src) %{
|
|||||||
ins_pipe(pipe_class_default);
|
ins_pipe(pipe_class_default);
|
||||||
%}
|
%}
|
||||||
|
|
||||||
// Popcnt for Power7.
|
|
||||||
instruct popCountL(iRegIdst dst, iRegLsrc src) %{
|
instruct popCountL(iRegIdst dst, iRegLsrc src) %{
|
||||||
predicate(UsePopCountInstruction);
|
predicate(UsePopCountInstruction);
|
||||||
match(Set dst (PopCountL src));
|
match(Set dst (PopCountL src));
|
||||||
@ -13299,6 +13284,7 @@ instruct repl2F_immF0(iRegLdst dst, immF_0 zero) %{
|
|||||||
Matcher::vector_element_basic_type(n) == T_FLOAT);
|
Matcher::vector_element_basic_type(n) == T_FLOAT);
|
||||||
|
|
||||||
format %{ "LI $dst, #0 \t// replicate2F" %}
|
format %{ "LI $dst, #0 \t// replicate2F" %}
|
||||||
|
size(4);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ li($dst$$Register, 0x0);
|
__ li($dst$$Register, 0x0);
|
||||||
%}
|
%}
|
||||||
@ -13927,6 +13913,7 @@ instruct overflowAddL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
|
|||||||
match(Set cr0 (OverflowAddL op1 op2));
|
match(Set cr0 (OverflowAddL op1 op2));
|
||||||
|
|
||||||
format %{ "add_ $op1, $op2\t# overflow check long" %}
|
format %{ "add_ $op1, $op2\t# overflow check long" %}
|
||||||
|
size(12);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ li(R0, 0);
|
__ li(R0, 0);
|
||||||
__ mtxer(R0); // clear XER.SO
|
__ mtxer(R0); // clear XER.SO
|
||||||
@ -13939,6 +13926,7 @@ instruct overflowSubL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
|
|||||||
match(Set cr0 (OverflowSubL op1 op2));
|
match(Set cr0 (OverflowSubL op1 op2));
|
||||||
|
|
||||||
format %{ "subfo_ R0, $op2, $op1\t# overflow check long" %}
|
format %{ "subfo_ R0, $op2, $op1\t# overflow check long" %}
|
||||||
|
size(12);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ li(R0, 0);
|
__ li(R0, 0);
|
||||||
__ mtxer(R0); // clear XER.SO
|
__ mtxer(R0); // clear XER.SO
|
||||||
@ -13951,6 +13939,7 @@ instruct overflowNegL_reg(flagsRegCR0 cr0, immL_0 zero, iRegLsrc op2) %{
|
|||||||
match(Set cr0 (OverflowSubL zero op2));
|
match(Set cr0 (OverflowSubL zero op2));
|
||||||
|
|
||||||
format %{ "nego_ R0, $op2\t# overflow check long" %}
|
format %{ "nego_ R0, $op2\t# overflow check long" %}
|
||||||
|
size(12);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ li(R0, 0);
|
__ li(R0, 0);
|
||||||
__ mtxer(R0); // clear XER.SO
|
__ mtxer(R0); // clear XER.SO
|
||||||
@ -13963,6 +13952,7 @@ instruct overflowMulL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
|
|||||||
match(Set cr0 (OverflowMulL op1 op2));
|
match(Set cr0 (OverflowMulL op1 op2));
|
||||||
|
|
||||||
format %{ "mulldo_ R0, $op1, $op2\t# overflow check long" %}
|
format %{ "mulldo_ R0, $op1, $op2\t# overflow check long" %}
|
||||||
|
size(12);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ li(R0, 0);
|
__ li(R0, 0);
|
||||||
__ mtxer(R0); // clear XER.SO
|
__ mtxer(R0); // clear XER.SO
|
||||||
@ -14001,6 +13991,7 @@ instruct repl4F_immF0(vecX dst, immF_0 zero) %{
|
|||||||
Matcher::vector_element_basic_type(n) == T_FLOAT);
|
Matcher::vector_element_basic_type(n) == T_FLOAT);
|
||||||
|
|
||||||
format %{ "XXLXOR $dst, $zero \t// replicate4F" %}
|
format %{ "XXLXOR $dst, $zero \t// replicate4F" %}
|
||||||
|
size(4);
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
__ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
|
__ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
|
||||||
%}
|
%}
|
||||||
|
@ -203,15 +203,15 @@ void VM_Version::common_initialize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Misc Intrinsics could depend on RVV
|
// Misc Intrinsics that could depend on RVV.
|
||||||
|
|
||||||
if (UseZba || UseRVV) {
|
if (!AvoidUnalignedAccesses && (UseZba || UseRVV)) {
|
||||||
if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
|
if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
|
||||||
FLAG_SET_DEFAULT(UseCRC32Intrinsics, true);
|
FLAG_SET_DEFAULT(UseCRC32Intrinsics, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
|
if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
|
||||||
warning("CRC32 intrinsic requires Zba or RVV instructions (not available on this CPU)");
|
warning("CRC32 intrinsic are not available on this CPU.");
|
||||||
}
|
}
|
||||||
FLAG_SET_DEFAULT(UseCRC32Intrinsics, false);
|
FLAG_SET_DEFAULT(UseCRC32Intrinsics, false);
|
||||||
}
|
}
|
||||||
|
@ -465,13 +465,19 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M
|
|||||||
__ call_VM_leaf0(CAST_FROM_FN_PTR(address, SharedRuntime::dtan));
|
__ call_VM_leaf0(CAST_FROM_FN_PTR(address, SharedRuntime::dtan));
|
||||||
}
|
}
|
||||||
} else if (kind == Interpreter::java_lang_math_tanh) {
|
} else if (kind == Interpreter::java_lang_math_tanh) {
|
||||||
assert(StubRoutines::dtanh() != nullptr, "not initialized");
|
if (StubRoutines::dtanh() != nullptr) {
|
||||||
__ movdbl(xmm0, Address(rsp, wordSize));
|
__ movdbl(xmm0, Address(rsp, wordSize));
|
||||||
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dtanh())));
|
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dtanh())));
|
||||||
|
} else {
|
||||||
|
return nullptr; // Fallback to default implementation
|
||||||
|
}
|
||||||
} else if (kind == Interpreter::java_lang_math_cbrt) {
|
} else if (kind == Interpreter::java_lang_math_cbrt) {
|
||||||
assert(StubRoutines::dcbrt() != nullptr, "not initialized");
|
if (StubRoutines::dcbrt() != nullptr) {
|
||||||
__ movdbl(xmm0, Address(rsp, wordSize));
|
__ movdbl(xmm0, Address(rsp, wordSize));
|
||||||
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
|
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
|
||||||
|
} else {
|
||||||
|
return nullptr; // Fallback to default implementation
|
||||||
|
}
|
||||||
} else if (kind == Interpreter::java_lang_math_abs) {
|
} else if (kind == Interpreter::java_lang_math_abs) {
|
||||||
assert(StubRoutines::x86::double_sign_mask() != nullptr, "not initialized");
|
assert(StubRoutines::x86::double_sign_mask() != nullptr, "not initialized");
|
||||||
__ movdbl(xmm0, Address(rsp, wordSize));
|
__ movdbl(xmm0, Address(rsp, wordSize));
|
||||||
|
@ -10531,7 +10531,8 @@ instruct xorI_rReg_im1_ndd(rRegI dst, rRegI src, immI_M1 imm)
|
|||||||
// Xor Register with Immediate
|
// Xor Register with Immediate
|
||||||
instruct xorI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
|
instruct xorI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
|
||||||
%{
|
%{
|
||||||
predicate(!UseAPX);
|
// Strict predicate check to make selection of xorI_rReg_im1 cost agnostic if immI src is -1.
|
||||||
|
predicate(!UseAPX && n->in(2)->bottom_type()->is_int()->get_con() != -1);
|
||||||
match(Set dst (XorI dst src));
|
match(Set dst (XorI dst src));
|
||||||
effect(KILL cr);
|
effect(KILL cr);
|
||||||
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
||||||
@ -10545,7 +10546,8 @@ instruct xorI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
|
|||||||
|
|
||||||
instruct xorI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr)
|
instruct xorI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr)
|
||||||
%{
|
%{
|
||||||
predicate(UseAPX);
|
// Strict predicate check to make selection of xorI_rReg_im1_ndd cost agnostic if immI src2 is -1.
|
||||||
|
predicate(UseAPX && n->in(2)->bottom_type()->is_int()->get_con() != -1);
|
||||||
match(Set dst (XorI src1 src2));
|
match(Set dst (XorI src1 src2));
|
||||||
effect(KILL cr);
|
effect(KILL cr);
|
||||||
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
||||||
@ -10563,6 +10565,7 @@ instruct xorI_rReg_mem_imm_ndd(rRegI dst, memory src1, immI src2, rFlagsReg cr)
|
|||||||
predicate(UseAPX);
|
predicate(UseAPX);
|
||||||
match(Set dst (XorI (LoadI src1) src2));
|
match(Set dst (XorI (LoadI src1) src2));
|
||||||
effect(KILL cr);
|
effect(KILL cr);
|
||||||
|
ins_cost(150);
|
||||||
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
||||||
|
|
||||||
format %{ "exorl $dst, $src1, $src2\t# int ndd" %}
|
format %{ "exorl $dst, $src1, $src2\t# int ndd" %}
|
||||||
@ -11205,7 +11208,8 @@ instruct xorL_rReg_im1_ndd(rRegL dst,rRegL src, immL_M1 imm)
|
|||||||
// Xor Register with Immediate
|
// Xor Register with Immediate
|
||||||
instruct xorL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
|
instruct xorL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
|
||||||
%{
|
%{
|
||||||
predicate(!UseAPX);
|
// Strict predicate check to make selection of xorL_rReg_im1 cost agnostic if immL32 src is -1.
|
||||||
|
predicate(!UseAPX && n->in(2)->bottom_type()->is_long()->get_con() != -1L);
|
||||||
match(Set dst (XorL dst src));
|
match(Set dst (XorL dst src));
|
||||||
effect(KILL cr);
|
effect(KILL cr);
|
||||||
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
||||||
@ -11219,7 +11223,8 @@ instruct xorL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
|
|||||||
|
|
||||||
instruct xorL_rReg_rReg_imm(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr)
|
instruct xorL_rReg_rReg_imm(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr)
|
||||||
%{
|
%{
|
||||||
predicate(UseAPX);
|
// Strict predicate check to make selection of xorL_rReg_im1_ndd cost agnostic if immL32 src2 is -1.
|
||||||
|
predicate(UseAPX && n->in(2)->bottom_type()->is_long()->get_con() != -1L);
|
||||||
match(Set dst (XorL src1 src2));
|
match(Set dst (XorL src1 src2));
|
||||||
effect(KILL cr);
|
effect(KILL cr);
|
||||||
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
||||||
@ -11238,6 +11243,7 @@ instruct xorL_rReg_mem_imm(rRegL dst, memory src1, immL32 src2, rFlagsReg cr)
|
|||||||
match(Set dst (XorL (LoadL src1) src2));
|
match(Set dst (XorL (LoadL src1) src2));
|
||||||
effect(KILL cr);
|
effect(KILL cr);
|
||||||
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
||||||
|
ins_cost(150);
|
||||||
|
|
||||||
format %{ "exorq $dst, $src1, $src2\t# long ndd" %}
|
format %{ "exorq $dst, $src1, $src2\t# long ndd" %}
|
||||||
ins_encode %{
|
ins_encode %{
|
||||||
|
@ -154,7 +154,8 @@ julong os::Bsd::available_memory() {
|
|||||||
assert(kerr == KERN_SUCCESS,
|
assert(kerr == KERN_SUCCESS,
|
||||||
"host_statistics64 failed - check mach_host_self() and count");
|
"host_statistics64 failed - check mach_host_self() and count");
|
||||||
if (kerr == KERN_SUCCESS) {
|
if (kerr == KERN_SUCCESS) {
|
||||||
available = vmstat.free_count * os::vm_page_size();
|
// free_count is just a lowerbound, other page categories can be freed too and make memory available
|
||||||
|
available = (vmstat.free_count + vmstat.inactive_count + vmstat.purgeable_count) * os::vm_page_size();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return available;
|
return available;
|
||||||
|
@ -3740,6 +3740,7 @@ void ClassFileParser::apply_parsed_class_metadata(
|
|||||||
_cp->set_pool_holder(this_klass);
|
_cp->set_pool_holder(this_klass);
|
||||||
this_klass->set_constants(_cp);
|
this_klass->set_constants(_cp);
|
||||||
this_klass->set_fieldinfo_stream(_fieldinfo_stream);
|
this_klass->set_fieldinfo_stream(_fieldinfo_stream);
|
||||||
|
this_klass->set_fieldinfo_search_table(_fieldinfo_search_table);
|
||||||
this_klass->set_fields_status(_fields_status);
|
this_klass->set_fields_status(_fields_status);
|
||||||
this_klass->set_methods(_methods);
|
this_klass->set_methods(_methods);
|
||||||
this_klass->set_inner_classes(_inner_classes);
|
this_klass->set_inner_classes(_inner_classes);
|
||||||
@ -3749,6 +3750,8 @@ void ClassFileParser::apply_parsed_class_metadata(
|
|||||||
this_klass->set_permitted_subclasses(_permitted_subclasses);
|
this_klass->set_permitted_subclasses(_permitted_subclasses);
|
||||||
this_klass->set_record_components(_record_components);
|
this_klass->set_record_components(_record_components);
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(_cp, _fieldinfo_stream, _fieldinfo_search_table));
|
||||||
|
|
||||||
// Delay the setting of _local_interfaces and _transitive_interfaces until after
|
// Delay the setting of _local_interfaces and _transitive_interfaces until after
|
||||||
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
|
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
|
||||||
// be shared with _transitive_interfaces and _transitive_interfaces may be shared with
|
// be shared with _transitive_interfaces and _transitive_interfaces may be shared with
|
||||||
@ -5056,6 +5059,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
|
|||||||
// note that is not safe to use the fields in the parser from this point on
|
// note that is not safe to use the fields in the parser from this point on
|
||||||
assert(nullptr == _cp, "invariant");
|
assert(nullptr == _cp, "invariant");
|
||||||
assert(nullptr == _fieldinfo_stream, "invariant");
|
assert(nullptr == _fieldinfo_stream, "invariant");
|
||||||
|
assert(nullptr == _fieldinfo_search_table, "invariant");
|
||||||
assert(nullptr == _fields_status, "invariant");
|
assert(nullptr == _fields_status, "invariant");
|
||||||
assert(nullptr == _methods, "invariant");
|
assert(nullptr == _methods, "invariant");
|
||||||
assert(nullptr == _inner_classes, "invariant");
|
assert(nullptr == _inner_classes, "invariant");
|
||||||
@ -5276,6 +5280,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
|
|||||||
_super_klass(),
|
_super_klass(),
|
||||||
_cp(nullptr),
|
_cp(nullptr),
|
||||||
_fieldinfo_stream(nullptr),
|
_fieldinfo_stream(nullptr),
|
||||||
|
_fieldinfo_search_table(nullptr),
|
||||||
_fields_status(nullptr),
|
_fields_status(nullptr),
|
||||||
_methods(nullptr),
|
_methods(nullptr),
|
||||||
_inner_classes(nullptr),
|
_inner_classes(nullptr),
|
||||||
@ -5352,6 +5357,7 @@ void ClassFileParser::clear_class_metadata() {
|
|||||||
// deallocated if classfile parsing returns an error.
|
// deallocated if classfile parsing returns an error.
|
||||||
_cp = nullptr;
|
_cp = nullptr;
|
||||||
_fieldinfo_stream = nullptr;
|
_fieldinfo_stream = nullptr;
|
||||||
|
_fieldinfo_search_table = nullptr;
|
||||||
_fields_status = nullptr;
|
_fields_status = nullptr;
|
||||||
_methods = nullptr;
|
_methods = nullptr;
|
||||||
_inner_classes = nullptr;
|
_inner_classes = nullptr;
|
||||||
@ -5374,6 +5380,7 @@ ClassFileParser::~ClassFileParser() {
|
|||||||
if (_fieldinfo_stream != nullptr) {
|
if (_fieldinfo_stream != nullptr) {
|
||||||
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_stream);
|
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_stream);
|
||||||
}
|
}
|
||||||
|
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_search_table);
|
||||||
|
|
||||||
if (_fields_status != nullptr) {
|
if (_fields_status != nullptr) {
|
||||||
MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status);
|
MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status);
|
||||||
@ -5774,6 +5781,7 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st
|
|||||||
_fieldinfo_stream =
|
_fieldinfo_stream =
|
||||||
FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
|
FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
|
||||||
injected_fields_count, loader_data(), CHECK);
|
injected_fields_count, loader_data(), CHECK);
|
||||||
|
_fieldinfo_search_table = FieldInfoStream::create_search_table(_cp, _fieldinfo_stream, _loader_data, CHECK);
|
||||||
_fields_status =
|
_fields_status =
|
||||||
MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(),
|
MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(),
|
||||||
FieldStatus(0), CHECK);
|
FieldStatus(0), CHECK);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -123,6 +123,7 @@ class ClassFileParser {
|
|||||||
const InstanceKlass* _super_klass;
|
const InstanceKlass* _super_klass;
|
||||||
ConstantPool* _cp;
|
ConstantPool* _cp;
|
||||||
Array<u1>* _fieldinfo_stream;
|
Array<u1>* _fieldinfo_stream;
|
||||||
|
Array<u1>* _fieldinfo_search_table;
|
||||||
Array<FieldStatus>* _fields_status;
|
Array<FieldStatus>* _fields_status;
|
||||||
Array<Method*>* _methods;
|
Array<Method*>* _methods;
|
||||||
Array<u2>* _inner_classes;
|
Array<u2>* _inner_classes;
|
||||||
|
@ -301,7 +301,7 @@ void FieldLayout::reconstruct_layout(const InstanceKlass* ik, bool& has_instance
|
|||||||
BasicType last_type;
|
BasicType last_type;
|
||||||
int last_offset = -1;
|
int last_offset = -1;
|
||||||
while (ik != nullptr) {
|
while (ik != nullptr) {
|
||||||
for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
|
for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
|
||||||
BasicType type = Signature::basic_type(fs.signature());
|
BasicType type = Signature::basic_type(fs.signature());
|
||||||
// distinction between static and non-static fields is missing
|
// distinction between static and non-static fields is missing
|
||||||
if (fs.access_flags().is_static()) continue;
|
if (fs.access_flags().is_static()) continue;
|
||||||
@ -461,7 +461,7 @@ void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlas
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
const InstanceKlass* ik = super;
|
const InstanceKlass* ik = super;
|
||||||
while (!found && ik != nullptr) {
|
while (!found && ik != nullptr) {
|
||||||
for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
|
for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
|
||||||
if (fs.offset() == b->offset()) {
|
if (fs.offset() == b->offset()) {
|
||||||
output->print_cr(" @%d \"%s\" %s %d/%d %s",
|
output->print_cr(" @%d \"%s\" %s %d/%d %s",
|
||||||
b->offset(),
|
b->offset(),
|
||||||
|
@ -967,6 +967,13 @@ void java_lang_Class::fixup_mirror(Klass* k, TRAPS) {
|
|||||||
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, k->class_loader_data(), CHECK);
|
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, k->class_loader_data(), CHECK);
|
||||||
ik->set_fieldinfo_stream(new_fis);
|
ik->set_fieldinfo_stream(new_fis);
|
||||||
MetadataFactory::free_array<u1>(k->class_loader_data(), old_stream);
|
MetadataFactory::free_array<u1>(k->class_loader_data(), old_stream);
|
||||||
|
|
||||||
|
Array<u1>* old_table = ik->fieldinfo_search_table();
|
||||||
|
Array<u1>* search_table = FieldInfoStream::create_search_table(ik->constants(), new_fis, k->class_loader_data(), CHECK);
|
||||||
|
ik->set_fieldinfo_search_table(search_table);
|
||||||
|
MetadataFactory::free_array<u1>(k->class_loader_data(), old_table);
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(ik->constants(), new_fis, search_table));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,8 +289,6 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
|
|||||||
case vmIntrinsics::_dsin:
|
case vmIntrinsics::_dsin:
|
||||||
case vmIntrinsics::_dcos:
|
case vmIntrinsics::_dcos:
|
||||||
case vmIntrinsics::_dtan:
|
case vmIntrinsics::_dtan:
|
||||||
case vmIntrinsics::_dtanh:
|
|
||||||
case vmIntrinsics::_dcbrt:
|
|
||||||
case vmIntrinsics::_dlog:
|
case vmIntrinsics::_dlog:
|
||||||
case vmIntrinsics::_dexp:
|
case vmIntrinsics::_dexp:
|
||||||
case vmIntrinsics::_dpow:
|
case vmIntrinsics::_dpow:
|
||||||
@ -316,6 +314,13 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
|
|||||||
case vmIntrinsics::_fmaF:
|
case vmIntrinsics::_fmaF:
|
||||||
if (!InlineMathNatives || !UseFMA) return true;
|
if (!InlineMathNatives || !UseFMA) return true;
|
||||||
break;
|
break;
|
||||||
|
case vmIntrinsics::_dtanh:
|
||||||
|
case vmIntrinsics::_dcbrt:
|
||||||
|
if (!InlineMathNatives || !InlineIntrinsics) return true;
|
||||||
|
#if defined(AMD64) && (defined(COMPILER1) || defined(COMPILER2))
|
||||||
|
if (!UseLibmIntrinsic) return true;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
case vmIntrinsics::_floatToFloat16:
|
case vmIntrinsics::_floatToFloat16:
|
||||||
case vmIntrinsics::_float16ToFloat:
|
case vmIntrinsics::_float16ToFloat:
|
||||||
if (!InlineIntrinsics) return true;
|
if (!InlineIntrinsics) return true;
|
||||||
|
@ -625,6 +625,34 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool ShenandoahBarrierC2Support::is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store,
|
||||||
|
Node* control) {
|
||||||
|
return maybe_load->is_Load() && phase->C->can_alias(store->adr_type(), phase->C->get_alias_index(maybe_load->adr_type())) &&
|
||||||
|
phase->ctrl_or_self(maybe_load) == control;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShenandoahBarrierC2Support::maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq) {
|
||||||
|
if (!maybe_store->is_Store() && !maybe_store->is_LoadStore()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Node* mem = maybe_store->in(MemNode::Memory);
|
||||||
|
for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
|
||||||
|
Node* u = mem->fast_out(i);
|
||||||
|
if (is_anti_dependent_load_at_control(phase, u, maybe_store, control)) {
|
||||||
|
wq.push(u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShenandoahBarrierC2Support::push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl, Unique_Node_List &wq) {
|
||||||
|
for (uint i = 0; i < n->req(); i++) {
|
||||||
|
Node* in = n->in(i);
|
||||||
|
if (in != nullptr && phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl) {
|
||||||
|
wq.push(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node* n, PhaseIdealLoop* phase) {
|
bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node* n, PhaseIdealLoop* phase) {
|
||||||
// That both nodes have the same control is not sufficient to prove
|
// That both nodes have the same control is not sufficient to prove
|
||||||
// domination, verify that there's no path from d to n
|
// domination, verify that there's no path from d to n
|
||||||
@ -639,22 +667,9 @@ bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node*
|
|||||||
if (m->is_Phi() && m->in(0)->is_Loop()) {
|
if (m->is_Phi() && m->in(0)->is_Loop()) {
|
||||||
assert(phase->ctrl_or_self(m->in(LoopNode::EntryControl)) != c, "following loop entry should lead to new control");
|
assert(phase->ctrl_or_self(m->in(LoopNode::EntryControl)) != c, "following loop entry should lead to new control");
|
||||||
} else {
|
} else {
|
||||||
if (m->is_Store() || m->is_LoadStore()) {
|
// Take anti-dependencies into account
|
||||||
// Take anti-dependencies into account
|
maybe_push_anti_dependent_loads(phase, m, c, wq);
|
||||||
Node* mem = m->in(MemNode::Memory);
|
push_data_inputs_at_control(phase, m, c, wq);
|
||||||
for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
|
|
||||||
Node* u = mem->fast_out(i);
|
|
||||||
if (u->is_Load() && phase->C->can_alias(m->adr_type(), phase->C->get_alias_index(u->adr_type())) &&
|
|
||||||
phase->ctrl_or_self(u) == c) {
|
|
||||||
wq.push(u);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (uint i = 0; i < m->req(); i++) {
|
|
||||||
if (m->in(i) != nullptr && phase->ctrl_or_self(m->in(i)) == c) {
|
|
||||||
wq.push(m->in(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1006,7 +1021,20 @@ void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* lo
|
|||||||
phase->register_new_node(val, ctrl);
|
phase->register_new_node(val, ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase) {
|
void ShenandoahBarrierC2Support::collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl, Node* init_raw_mem) {
|
||||||
|
nodes_above_barrier.clear();
|
||||||
|
if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) {
|
||||||
|
nodes_above_barrier.push(init_raw_mem);
|
||||||
|
}
|
||||||
|
for (uint next = 0; next < nodes_above_barrier.size(); next++) {
|
||||||
|
Node* n = nodes_above_barrier.at(next);
|
||||||
|
// Take anti-dependencies into account
|
||||||
|
maybe_push_anti_dependent_loads(phase, n, ctrl, nodes_above_barrier);
|
||||||
|
push_data_inputs_at_control(phase, n, ctrl, nodes_above_barrier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase) {
|
||||||
Node* ctrl = phase->get_ctrl(barrier);
|
Node* ctrl = phase->get_ctrl(barrier);
|
||||||
Node* init_raw_mem = fixer.find_mem(ctrl, barrier);
|
Node* init_raw_mem = fixer.find_mem(ctrl, barrier);
|
||||||
|
|
||||||
@ -1017,30 +1045,17 @@ void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const Mem
|
|||||||
// control will be after the expanded barrier. The raw memory (if
|
// control will be after the expanded barrier. The raw memory (if
|
||||||
// its memory is control dependent on the barrier's input control)
|
// its memory is control dependent on the barrier's input control)
|
||||||
// must stay above the barrier.
|
// must stay above the barrier.
|
||||||
uses_to_ignore.clear();
|
collect_nodes_above_barrier(nodes_above_barrier, phase, ctrl, init_raw_mem);
|
||||||
if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) {
|
|
||||||
uses_to_ignore.push(init_raw_mem);
|
|
||||||
}
|
|
||||||
for (uint next = 0; next < uses_to_ignore.size(); next++) {
|
|
||||||
Node *n = uses_to_ignore.at(next);
|
|
||||||
for (uint i = 0; i < n->req(); i++) {
|
|
||||||
Node* in = n->in(i);
|
|
||||||
if (in != nullptr && phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl) {
|
|
||||||
uses_to_ignore.push(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (DUIterator_Fast imax, i = ctrl->fast_outs(imax); i < imax; i++) {
|
for (DUIterator_Fast imax, i = ctrl->fast_outs(imax); i < imax; i++) {
|
||||||
Node* u = ctrl->fast_out(i);
|
Node* u = ctrl->fast_out(i);
|
||||||
if (u->_idx < last &&
|
if (u->_idx < last &&
|
||||||
u != barrier &&
|
u != barrier &&
|
||||||
!u->depends_only_on_test() && // preserve dependency on test
|
!u->depends_only_on_test() && // preserve dependency on test
|
||||||
!uses_to_ignore.member(u) &&
|
!nodes_above_barrier.member(u) &&
|
||||||
(u->in(0) != ctrl || (!u->is_Region() && !u->is_Phi())) &&
|
(u->in(0) != ctrl || (!u->is_Region() && !u->is_Phi())) &&
|
||||||
(ctrl->Opcode() != Op_CatchProj || u->Opcode() != Op_CreateEx)) {
|
(ctrl->Opcode() != Op_CatchProj || u->Opcode() != Op_CreateEx)) {
|
||||||
Node* old_c = phase->ctrl_or_self(u);
|
Node* old_c = phase->ctrl_or_self(u);
|
||||||
Node* c = old_c;
|
if (old_c != ctrl ||
|
||||||
if (c != ctrl ||
|
|
||||||
is_dominator_same_ctrl(old_c, barrier, u, phase) ||
|
is_dominator_same_ctrl(old_c, barrier, u, phase) ||
|
||||||
ShenandoahBarrierSetC2::is_shenandoah_state_load(u)) {
|
ShenandoahBarrierSetC2::is_shenandoah_state_load(u)) {
|
||||||
phase->igvn().rehash_node_delayed(u);
|
phase->igvn().rehash_node_delayed(u);
|
||||||
@ -1315,7 +1330,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
|
|||||||
|
|
||||||
// Expand load-reference-barriers
|
// Expand load-reference-barriers
|
||||||
MemoryGraphFixer fixer(Compile::AliasIdxRaw, true, phase);
|
MemoryGraphFixer fixer(Compile::AliasIdxRaw, true, phase);
|
||||||
Unique_Node_List uses_to_ignore;
|
Unique_Node_List nodes_above_barriers;
|
||||||
for (int i = state->load_reference_barriers_count() - 1; i >= 0; i--) {
|
for (int i = state->load_reference_barriers_count() - 1; i >= 0; i--) {
|
||||||
ShenandoahLoadReferenceBarrierNode* lrb = state->load_reference_barrier(i);
|
ShenandoahLoadReferenceBarrierNode* lrb = state->load_reference_barrier(i);
|
||||||
uint last = phase->C->unique();
|
uint last = phase->C->unique();
|
||||||
@ -1410,7 +1425,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
|
|||||||
Node* out_val = val_phi;
|
Node* out_val = val_phi;
|
||||||
phase->register_new_node(val_phi, region);
|
phase->register_new_node(val_phi, region);
|
||||||
|
|
||||||
fix_ctrl(lrb, region, fixer, uses, uses_to_ignore, last, phase);
|
fix_ctrl(lrb, region, fixer, uses, nodes_above_barriers, last, phase);
|
||||||
|
|
||||||
ctrl = orig_ctrl;
|
ctrl = orig_ctrl;
|
||||||
|
|
||||||
|
@ -62,8 +62,12 @@ private:
|
|||||||
PhaseIdealLoop* phase, int flags);
|
PhaseIdealLoop* phase, int flags);
|
||||||
static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr,
|
static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr,
|
||||||
DecoratorSet decorators, PhaseIdealLoop* phase);
|
DecoratorSet decorators, PhaseIdealLoop* phase);
|
||||||
|
|
||||||
|
static void collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl,
|
||||||
|
Node* init_raw_mem);
|
||||||
|
|
||||||
static void test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, Node* val, Node* raw_mem, PhaseIdealLoop* phase);
|
static void test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, Node* val, Node* raw_mem, PhaseIdealLoop* phase);
|
||||||
static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase);
|
static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase);
|
||||||
|
|
||||||
static Node* get_load_addr(PhaseIdealLoop* phase, VectorSet& visited, Node* lrb);
|
static Node* get_load_addr(PhaseIdealLoop* phase, VectorSet& visited, Node* lrb);
|
||||||
public:
|
public:
|
||||||
@ -76,6 +80,11 @@ public:
|
|||||||
static bool expand(Compile* C, PhaseIterGVN& igvn);
|
static bool expand(Compile* C, PhaseIterGVN& igvn);
|
||||||
static void pin_and_expand(PhaseIdealLoop* phase);
|
static void pin_and_expand(PhaseIdealLoop* phase);
|
||||||
|
|
||||||
|
static void push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl,
|
||||||
|
Unique_Node_List &wq);
|
||||||
|
static bool is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store, Node* control);
|
||||||
|
|
||||||
|
static void maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq);
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
static void verify(RootNode* root);
|
static void verify(RootNode* root);
|
||||||
#endif
|
#endif
|
||||||
|
@ -216,6 +216,7 @@ class outputStream;
|
|||||||
LOG_TAG(valuebasedclasses) \
|
LOG_TAG(valuebasedclasses) \
|
||||||
LOG_TAG(verification) \
|
LOG_TAG(verification) \
|
||||||
LOG_TAG(verify) \
|
LOG_TAG(verify) \
|
||||||
|
LOG_TAG(vmatree) \
|
||||||
LOG_TAG(vmmutex) \
|
LOG_TAG(vmmutex) \
|
||||||
LOG_TAG(vmoperation) \
|
LOG_TAG(vmoperation) \
|
||||||
LOG_TAG(vmthread) \
|
LOG_TAG(vmthread) \
|
||||||
|
@ -91,7 +91,7 @@ void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* st
|
|||||||
NMTUtil::tag_to_name(prev->val().out.mem_tag()));
|
NMTUtil::tag_to_name(prev->val().out.mem_tag()));
|
||||||
{
|
{
|
||||||
StreamIndentor si(stream, 4);
|
StreamIndentor si(stream, 4);
|
||||||
_stack_storage.get(prev->val().out.stack()).print_on(stream);
|
_stack_storage.get(prev->val().out.reserved_stack()).print_on(stream);
|
||||||
}
|
}
|
||||||
stream->cr();
|
stream->cr();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -43,11 +43,8 @@
|
|||||||
class NativeCallStackStorage : public CHeapObjBase {
|
class NativeCallStackStorage : public CHeapObjBase {
|
||||||
public:
|
public:
|
||||||
using StackIndex = int;
|
using StackIndex = int;
|
||||||
|
|
||||||
private:
|
|
||||||
constexpr static const StackIndex invalid = std::numeric_limits<StackIndex>::max() - 1;
|
constexpr static const StackIndex invalid = std::numeric_limits<StackIndex>::max() - 1;
|
||||||
|
|
||||||
public:
|
|
||||||
static bool equals(const StackIndex a, const StackIndex b) {
|
static bool equals(const StackIndex a, const StackIndex b) {
|
||||||
return a == b;
|
return a == b;
|
||||||
}
|
}
|
||||||
|
@ -28,21 +28,228 @@
|
|||||||
#include "utilities/globalDefinitions.hpp"
|
#include "utilities/globalDefinitions.hpp"
|
||||||
#include "utilities/growableArray.hpp"
|
#include "utilities/growableArray.hpp"
|
||||||
|
|
||||||
const VMATree::RegionData VMATree::empty_regiondata{NativeCallStackStorage::StackIndex{}, mtNone};
|
|
||||||
|
// Semantics
|
||||||
|
// This tree is used to store and track the state of virtual memory regions.
|
||||||
|
// The nodes in the tree are key-value pairs where the key is the memory address and the value is the State of the memory regions.
|
||||||
|
// The State of a region describes whether the region is released, reserved or committed, which MemTag it has and where in
|
||||||
|
// Hotspot (using call-stacks) it is reserved or committed.
|
||||||
|
// Each node holds the State of the regions to its left and right. Each memory region is described by two
|
||||||
|
// memory addresses for its start and end.
|
||||||
|
// For example, to describe the region that starts at memory address 0xA000 with size 0x1000, there will be two nodes
|
||||||
|
// with the keys 0xA000 (node A) and 0xB000 (node B) in the tree. The value of the key-value pairs of node A and
|
||||||
|
// node B describe the region's State, using right of A and left of B (<--left--A--right-->.....<--left--B--right-->...).
|
||||||
|
//
|
||||||
|
// Virtual memory can be reserved, committed, uncommitted and released. For each operation a request
|
||||||
|
// (<from-address, to-address, operation, tag, call-stack, which-tag-to-use >) is sent to the tree to handle.
|
||||||
|
//
|
||||||
|
// The expected changes are described here for each operation:
|
||||||
|
//
|
||||||
|
// ### Reserve a region
|
||||||
|
// When a region is reserved, all the overlapping regions in the tree should:
|
||||||
|
// - be marked as Reserved
|
||||||
|
// - take MemTag of the operation
|
||||||
|
// - store call-stack of the request to the reserve call-stack
|
||||||
|
// - clear commit call-stack
|
||||||
|
//
|
||||||
|
// ### Commit a region
|
||||||
|
// When a region is committed, all the overlapping regions in the tree should:
|
||||||
|
// - be marked as Committed
|
||||||
|
// - take MemTag of the operation or MemTag of the existing region, depends on which-tag-to-use in the request
|
||||||
|
// - if the region is in Released state
|
||||||
|
// - mark the region as both Reserved and Committed
|
||||||
|
// - store the call-stack of the request to the reserve call-stack
|
||||||
|
// - store the call-stack of the request to the commit call-stack
|
||||||
|
//
|
||||||
|
// ### Uncommit a region
|
||||||
|
// When a region is uncommitted, all the overlapping regions in the tree should:
|
||||||
|
// - be ignored if the region is in Released state
|
||||||
|
// - be marked as Reserved
|
||||||
|
// - not change the MemTag
|
||||||
|
// - not change the reserve call-stack
|
||||||
|
// - clear commit call-stack
|
||||||
|
//
|
||||||
|
// ### Release a region
|
||||||
|
// When a region is released, all the overlapping regions in the tree should:
|
||||||
|
// - be marked as Released
|
||||||
|
// - set the MemTag to mtNone
|
||||||
|
// - clear both reserve and commit call-stack
|
||||||
|
//
|
||||||
|
// --- Accounting
|
||||||
|
// After each operation, the tree should be able to report how much memory is reserved or committed per MemTag.
|
||||||
|
// So for each region that changes to a new State, the report should contain (separately for each tag) the amount
|
||||||
|
// of reserve and commit that are changed (increased or decreased) due to the operation.
|
||||||
|
|
||||||
|
const VMATree::RegionData VMATree::empty_regiondata{NativeCallStackStorage::invalid, mtNone};
|
||||||
|
|
||||||
const char* VMATree::statetype_strings[3] = {
|
const char* VMATree::statetype_strings[3] = {
|
||||||
"reserved", "committed", "released",
|
"released", "reserved", "committed"
|
||||||
};
|
};
|
||||||
|
|
||||||
VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType state,
|
VMATree::SIndex VMATree::get_new_reserve_callstack(const SIndex es, const StateType ex, const RequestInfo& req) const {
|
||||||
|
const SIndex ES = NativeCallStackStorage::invalid; // Empty Stack
|
||||||
|
const SIndex rq = req.callstack;
|
||||||
|
const int op = req.op_to_index();
|
||||||
|
const Operation oper = req.op();
|
||||||
|
assert(op >= 0 && op < 4, "should be");
|
||||||
|
assert(op >= 0 && op < 4, "should be");
|
||||||
|
// existing state
|
||||||
|
SIndex result[4][3] = {// Rl Rs C
|
||||||
|
{ES, ES, ES}, // op == Release
|
||||||
|
{rq, rq, rq}, // op == Reserve
|
||||||
|
{es, es, es}, // op == Commit
|
||||||
|
{es, es, es} // op == Uncommit
|
||||||
|
};
|
||||||
|
// When committing a Released region, the reserve-call-stack of the region should also be as what is in the request
|
||||||
|
if (oper == Operation::Commit && ex == StateType::Released) {
|
||||||
|
return rq;
|
||||||
|
} else {
|
||||||
|
return result[op][state_to_index(ex)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VMATree::SIndex VMATree::get_new_commit_callstack(const SIndex es, const StateType ex, const RequestInfo& req) const {
|
||||||
|
const SIndex ES = NativeCallStackStorage::invalid; // Empty Stack
|
||||||
|
const SIndex rq = req.callstack;
|
||||||
|
const int op_index = req.op_to_index();
|
||||||
|
const Operation op = req.op();
|
||||||
|
assert(op_index >= 0 && op_index < 4, "should be");
|
||||||
|
// existing state
|
||||||
|
SIndex result[4][3] = {// Rl Rs C
|
||||||
|
{ES, ES, ES}, // op == Release
|
||||||
|
{ES, ES, ES}, // op == Reserve
|
||||||
|
{rq, rq, rq}, // op == Commit
|
||||||
|
{ES, ES, ES} // op == Uncommit
|
||||||
|
};
|
||||||
|
return result[op_index][state_to_index(ex)];
|
||||||
|
}
|
||||||
|
|
||||||
|
VMATree::StateType VMATree::get_new_state(const StateType ex, const RequestInfo& req) const {
|
||||||
|
const StateType Rl = StateType::Released;
|
||||||
|
const StateType Rs = StateType::Reserved;
|
||||||
|
const StateType C = StateType::Committed;
|
||||||
|
const int op = req.op_to_index();
|
||||||
|
assert(op >= 0 && op < 4, "should be");
|
||||||
|
// existing state
|
||||||
|
StateType result[4][3] = {// Rl Rs C
|
||||||
|
{Rl, Rl, Rl}, // op == Release
|
||||||
|
{Rs, Rs, Rs}, // op == Reserve
|
||||||
|
{ C, C, C}, // op == Commit
|
||||||
|
{Rl, Rs, Rs} // op == Uncommit
|
||||||
|
};
|
||||||
|
return result[op][state_to_index(ex)];
|
||||||
|
}
|
||||||
|
|
||||||
|
MemTag VMATree::get_new_tag(const MemTag ex, const RequestInfo& req) const {
|
||||||
|
switch(req.op()) {
|
||||||
|
case Operation::Release:
|
||||||
|
return mtNone;
|
||||||
|
case Operation::Reserve:
|
||||||
|
return req.tag;
|
||||||
|
case Operation::Commit:
|
||||||
|
return req.use_tag_inplace ? ex : req.tag;
|
||||||
|
case Operation::Uncommit:
|
||||||
|
return ex;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return mtNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMATree::compute_summary_diff(const SingleDiff::delta region_size,
|
||||||
|
const MemTag current_tag,
|
||||||
|
const StateType& ex,
|
||||||
|
const RequestInfo& req,
|
||||||
|
const MemTag operation_tag,
|
||||||
|
SummaryDiff& diff) const {
|
||||||
|
const StateType Rl = StateType::Released;
|
||||||
|
const StateType Rs = StateType::Reserved;
|
||||||
|
const StateType C = StateType::Committed;
|
||||||
|
const int op = req.op_to_index();
|
||||||
|
const Operation oper = req.op();
|
||||||
|
assert(op >= 0 && op < 4, "should be");
|
||||||
|
|
||||||
|
SingleDiff::delta a = region_size;
|
||||||
|
// A region with size `a` has a state as <column> and an operation is requested as in <row>
|
||||||
|
// The region has tag `current_tag` and the operation has tag `operation_tag`.
|
||||||
|
// For each state, we decide how much to be added/subtracted from current_tag to operation_tag. Two tables for reserve and commit.
|
||||||
|
// Each pair of <x,y> in the table means add `x` to current_tag and add `y` to operation_tag. There are 3 pairs in each row for 3 states.
|
||||||
|
// For example, `reserve[1][4,5]` says `-a,a` means:
|
||||||
|
// - we are reserving with operation_tag a region which is already commited with current_tag
|
||||||
|
// - since we are reserving, then `a` will be added to operation_tag. (`y` is `a`)
|
||||||
|
// - since we uncommitting (by reserving) then `a` is to be subtracted from current_tag. (`x` is `-a`).
|
||||||
|
// - amount of uncommitted size is in table `commit[1][4,5]` which is `-a,0` that means subtract `a` from current_tag.
|
||||||
|
// existing state
|
||||||
|
SingleDiff::delta reserve[4][3*2] = {// Rl Rs C
|
||||||
|
{0,0, -a,0, -a,0 }, // op == Release
|
||||||
|
{0,a, -a,a, -a,a }, // op == Reserve
|
||||||
|
{0,a, -a,a, -a,a }, // op == Commit
|
||||||
|
{0,0, 0,0, 0,0 } // op == Uncommit
|
||||||
|
};
|
||||||
|
SingleDiff::delta commit[4][3*2] = {// Rl Rs C
|
||||||
|
{0,0, 0,0, -a,0 }, // op == Release
|
||||||
|
{0,0, 0,0, -a,0 }, // op == Reserve
|
||||||
|
{0,a, 0,a, -a,a }, // op == Commit
|
||||||
|
{0,0, 0,0, -a,0 } // op == Uncommit
|
||||||
|
};
|
||||||
|
SingleDiff& from_rescom = diff.tag[NMTUtil::tag_to_index(current_tag)];
|
||||||
|
SingleDiff& to_rescom = diff.tag[NMTUtil::tag_to_index(operation_tag)];
|
||||||
|
int st = state_to_index(ex);
|
||||||
|
from_rescom.reserve += reserve[op][st * 2 ];
|
||||||
|
to_rescom.reserve += reserve[op][st * 2 + 1];
|
||||||
|
from_rescom.commit += commit[op][st * 2 ];
|
||||||
|
to_rescom.commit += commit[op][st * 2 + 1];
|
||||||
|
|
||||||
|
}
|
||||||
|
// update the region state between n1 and n2. Since n1 and n2 are pointers, any update of them will be visible from tree.
|
||||||
|
// If n1 is noop, it can be removed because its left region (n1->val().in) is already decided and its right state (n1->val().out) is decided here.
|
||||||
|
// The state of right of n2 (n2->val().out) cannot be decided here yet.
|
||||||
|
void VMATree::update_region(TreapNode* n1, TreapNode* n2, const RequestInfo& req, SummaryDiff& diff) {
|
||||||
|
assert(n1 != nullptr,"sanity");
|
||||||
|
assert(n2 != nullptr,"sanity");
|
||||||
|
//.........n1......n2......
|
||||||
|
// ^------^
|
||||||
|
// |
|
||||||
|
IntervalState exSt = n1->val().out; // existing state info
|
||||||
|
|
||||||
|
|
||||||
|
StateType existing_state = exSt.type();
|
||||||
|
MemTag existing_tag = exSt.mem_tag();
|
||||||
|
SIndex existing_reserve_callstack = exSt.reserved_stack();
|
||||||
|
SIndex existing_commit_callstack = exSt.committed_stack();
|
||||||
|
|
||||||
|
StateType new_state = get_new_state(existing_state, req);
|
||||||
|
MemTag new_tag = get_new_tag(n1->val().out.mem_tag(), req);
|
||||||
|
SIndex new_reserve_callstack = get_new_reserve_callstack(existing_reserve_callstack, existing_state, req);
|
||||||
|
SIndex new_commit_callstack = get_new_commit_callstack(existing_commit_callstack, existing_state, req);
|
||||||
|
|
||||||
|
// n1........n2
|
||||||
|
// out-->
|
||||||
|
n1->val().out.set_tag(new_tag);
|
||||||
|
n1->val().out.set_type(new_state);
|
||||||
|
n1->val().out.set_reserve_stack(new_reserve_callstack);
|
||||||
|
n1->val().out.set_commit_stack(new_commit_callstack);
|
||||||
|
|
||||||
|
// n1........n2
|
||||||
|
// <--in
|
||||||
|
n2->val().in.set_tag(new_tag);
|
||||||
|
n2->val().in.set_type(new_state);
|
||||||
|
n2->val().in.set_reserve_stack(new_reserve_callstack);
|
||||||
|
n2->val().in.set_commit_stack(new_commit_callstack);
|
||||||
|
|
||||||
|
SingleDiff::delta region_size = n2->key() - n1->key();
|
||||||
|
compute_summary_diff(region_size, existing_tag, existing_state, req, new_tag, diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
VMATree::SummaryDiff VMATree::register_mapping(position _A, position _B, StateType state,
|
||||||
const RegionData& metadata, bool use_tag_inplace) {
|
const RegionData& metadata, bool use_tag_inplace) {
|
||||||
assert(!use_tag_inplace || metadata.mem_tag == mtNone,
|
|
||||||
"If using use_tag_inplace, then the supplied tag should be mtNone, was instead: %s", NMTUtil::tag_to_name(metadata.mem_tag));
|
if (_A == _B) {
|
||||||
if (A == B) {
|
|
||||||
// A 0-sized mapping isn't worth recording.
|
|
||||||
return SummaryDiff();
|
return SummaryDiff();
|
||||||
}
|
}
|
||||||
|
assert(_A < _B, "should be");
|
||||||
|
SummaryDiff diff;
|
||||||
|
RequestInfo req{_A, _B, state, metadata.mem_tag, metadata.stack_idx, use_tag_inplace};
|
||||||
IntervalChange stA{
|
IntervalChange stA{
|
||||||
IntervalState{StateType::Released, empty_regiondata},
|
IntervalState{StateType::Released, empty_regiondata},
|
||||||
IntervalState{ state, metadata}
|
IntervalState{ state, metadata}
|
||||||
@ -51,176 +258,400 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType
|
|||||||
IntervalState{ state, metadata},
|
IntervalState{ state, metadata},
|
||||||
IntervalState{StateType::Released, empty_regiondata}
|
IntervalState{StateType::Released, empty_regiondata}
|
||||||
};
|
};
|
||||||
|
stA.out.set_commit_stack(NativeCallStackStorage::invalid);
|
||||||
|
stB.in.set_commit_stack(NativeCallStackStorage::invalid);
|
||||||
|
VMATreap::Range rA = _tree.find_enclosing_range(_A);
|
||||||
|
VMATreap::Range rB = _tree.find_enclosing_range(_B);
|
||||||
|
|
||||||
// First handle A.
|
// nodes: .....X.......Y...Z......W........U
|
||||||
// Find closest node that is LEQ A
|
// request: A------------------B
|
||||||
bool LEQ_A_found = false;
|
// X,Y = enclosing_nodes(A)
|
||||||
AddressState LEQ_A;
|
// W,U = enclosing_nodes(B)
|
||||||
TreapNode* leqA_n = _tree.closest_leq(A);
|
// The cases are whether or not X and Y exists and X == A. (A == Y doesn't happen since it is searched by 'lt' predicate)
|
||||||
if (leqA_n == nullptr) {
|
// The cases are whether or not W and U exists and W == B. (B == U doesn't happen since it is searched by 'lt' predicate)
|
||||||
assert(!use_tag_inplace, "Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B);
|
|
||||||
if (use_tag_inplace) {
|
// We update regions in 3 sections: 1) X..A..Y, 2) Y....W, 3) W..B..U
|
||||||
log_debug(nmt)("Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B);
|
// Y: is the closest node greater than A, but less than B
|
||||||
|
// W: is the closest node less than B, but greater than A
|
||||||
|
// The regions in [Y,W) are updated in a loop. We update X..A..Y before the loop and W..B..U after the loop.
|
||||||
|
// The table below summarizes the overlap cases. The overlapping case depends on whether X, Y, W and U exist or not,
|
||||||
|
// and if they exist whether they are the same or not.
|
||||||
|
// In the notations here, when there is not dot ('.') between two nodes it meaans that they are the same. For example,
|
||||||
|
// ...XA....Y.... means X == A.
|
||||||
|
|
||||||
|
|
||||||
|
// row 0: .........A..................B.....
|
||||||
|
// row 1: .........A...YW.............B..... // it is impossible, since it means only one node exists in the tree.
|
||||||
|
// row 2: .........A...Y..........W...B.....
|
||||||
|
// row 3: .........A...Y.............WB.....
|
||||||
|
|
||||||
|
// row 4: .....X...A..................B.....
|
||||||
|
// row 5: .....X...A...YW.............B.....
|
||||||
|
// row 6: .....X...A...Y..........W...B.....
|
||||||
|
// row 7: .....X...A...Y.............WB.....
|
||||||
|
|
||||||
|
// row 8: ........XA..................B.....
|
||||||
|
// row 9: ........XA...YW.............B.....
|
||||||
|
// row 10: ........XA...Y..........W...B.....
|
||||||
|
// row 11: ........XA...Y.............WB.....
|
||||||
|
|
||||||
|
// row 12: .........A..................B....U
|
||||||
|
// row 13: .........A...YW.............B....U
|
||||||
|
// row 14: .........A...Y..........W...B....U
|
||||||
|
// row 15: .........A...Y.............WB....U
|
||||||
|
|
||||||
|
// row 16: .....X...A..................B....U
|
||||||
|
// row 17: .....X...A...YW.............B....U
|
||||||
|
// row 18: .....X...A...Y..........W...B....U
|
||||||
|
// row 19: .....X...A...Y.............WB....U
|
||||||
|
|
||||||
|
// row 20: ........XA..................B....U
|
||||||
|
// row 21: ........XA...YW.............B....U
|
||||||
|
// row 22: ........XA...Y..........W...B....U
|
||||||
|
// row 23: ........XA...Y.............WB....U
|
||||||
|
|
||||||
|
|
||||||
|
// We intentionally did not summarize/compress the cases to keep them as separate.
|
||||||
|
// This expanded way of describing the cases helps us to understand/analyze/verify/debug/maintain
|
||||||
|
// the corresponding code more easily.
|
||||||
|
// Mapping of table to row, row to switch-case should be consistent. If one changes, the others have
|
||||||
|
// to be updated accordingly. The sequence of dependecies is: table -> row no -> switch(row)-case -> code.
|
||||||
|
// Meaning that whenever any of one item in this sequence is changed, the rest of the consequent items to
|
||||||
|
// be checked/changed.
|
||||||
|
|
||||||
|
TreapNode* X = rA.start;
|
||||||
|
TreapNode* Y = rA.end;
|
||||||
|
TreapNode* W = rB.start;
|
||||||
|
TreapNode* U = rB.end;
|
||||||
|
TreapNode nA{_A, stA, 0}; // the node that represents A
|
||||||
|
TreapNode nB{_B, stB, 0}; // the node that represents B
|
||||||
|
TreapNode* A = &nA;
|
||||||
|
TreapNode* B = &nB;
|
||||||
|
auto upsert_if= [&](TreapNode* node) {
|
||||||
|
if (!node->val().is_noop()) {
|
||||||
|
_tree.upsert(node->key(), node->val());
|
||||||
}
|
}
|
||||||
// No match. We add the A node directly, unless it would have no effect.
|
};
|
||||||
if (!stA.is_noop()) {
|
// update region between n1 and n2
|
||||||
_tree.upsert(A, stA);
|
auto update = [&](TreapNode* n1, TreapNode* n2) {
|
||||||
|
update_region(n1, n2, req, diff);
|
||||||
|
};
|
||||||
|
auto remove_if = [&](TreapNode* node) -> bool{
|
||||||
|
if (node->val().is_noop()) {
|
||||||
|
_tree.remove(node->key());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
return false;
|
||||||
LEQ_A_found = true;
|
};
|
||||||
LEQ_A = AddressState{leqA_n->key(), leqA_n->val()};
|
GrowableArrayCHeap<position, mtNMT> to_be_removed;
|
||||||
StateType leqA_state = leqA_n->val().out.type();
|
// update regions in [Y,W)
|
||||||
StateType new_state = stA.out.type();
|
auto update_loop = [&]() {
|
||||||
// If we specify use_tag_inplace then the new region takes over the current tag instead of the tag in metadata.
|
TreapNode* prev = nullptr;
|
||||||
// This is important because the VirtualMemoryTracker API doesn't require supplying the tag for some operations.
|
_tree.visit_range_in_order(_A + 1, _B + 1, [&](TreapNode* curr) {
|
||||||
if (use_tag_inplace) {
|
if (prev != nullptr) {
|
||||||
assert(leqA_n->val().out.type() != StateType::Released, "Should not use inplace the tag of a released region");
|
update_region(prev, curr, req, diff);
|
||||||
MemTag tag = leqA_n->val().out.mem_tag();
|
// during visit, structure of the tree should not be changed
|
||||||
stA.out.set_tag(tag);
|
// keep the keys to be removed, and remove them later
|
||||||
stB.in.set_tag(tag);
|
if (prev->val().is_noop()) {
|
||||||
}
|
to_be_removed.push(prev->key());
|
||||||
|
}
|
||||||
// Unless we know better, let B's outgoing state be the outgoing state of the node at or preceding A.
|
|
||||||
// Consider the case where the found node is the start of a region enclosing [A,B)
|
|
||||||
stB.out = out_state(leqA_n);
|
|
||||||
|
|
||||||
// Direct address match.
|
|
||||||
if (leqA_n->key() == A) {
|
|
||||||
// Take over in state from old address.
|
|
||||||
stA.in = in_state(leqA_n);
|
|
||||||
|
|
||||||
// We may now be able to merge two regions:
|
|
||||||
// If the node's old state matches the new, it becomes a noop. That happens, for example,
|
|
||||||
// when expanding a committed area: commit [x1, A); ... commit [A, x3)
|
|
||||||
// and the result should be a larger area, [x1, x3). In that case, the middle node (A and le_n)
|
|
||||||
// is not needed anymore. So we just remove the old node.
|
|
||||||
stB.in = stA.out;
|
|
||||||
if (stA.is_noop()) {
|
|
||||||
// invalidates leqA_n
|
|
||||||
_tree.remove(leqA_n->key());
|
|
||||||
} else {
|
|
||||||
// If the state is not matching then we have different operations, such as:
|
|
||||||
// reserve [x1, A); ... commit [A, x2); or
|
|
||||||
// reserve [x1, A), mem_tag1; ... reserve [A, x2), mem_tag2; or
|
|
||||||
// reserve [A, x1), mem_tag1; ... reserve [A, x2), mem_tag2;
|
|
||||||
// then we re-use the existing out node, overwriting its old metadata.
|
|
||||||
leqA_n->val() = stA;
|
|
||||||
}
|
}
|
||||||
} else {
|
prev = curr;
|
||||||
// The address must be smaller.
|
});
|
||||||
assert(A > leqA_n->key(), "must be");
|
};
|
||||||
|
// update region of [A,T)
|
||||||
|
auto update_A = [&](TreapNode* T) {
|
||||||
|
A->val().out = A->val().in;
|
||||||
|
update(A, T);
|
||||||
|
};
|
||||||
|
bool X_exists = X != nullptr;
|
||||||
|
bool Y_exists = Y != nullptr && Y->key() <= _B;
|
||||||
|
bool W_exists = W != nullptr && W->key() > _A;
|
||||||
|
bool U_exists = U != nullptr;
|
||||||
|
bool X_eq_A = X_exists && X->key() == _A;
|
||||||
|
bool W_eq_B = W_exists && W->key() == _B;
|
||||||
|
bool Y_eq_W = Y_exists && W_exists && W->key() == Y->key();
|
||||||
|
int row = -1;
|
||||||
|
#ifdef ASSERT
|
||||||
|
auto print_case = [&]() {
|
||||||
|
log_trace(vmatree)(" req: %4d---%4d", (int)_A, (int)_B);
|
||||||
|
log_trace(vmatree)(" row: %2d", row);
|
||||||
|
log_trace(vmatree)(" X: %4ld", X_exists ? (long)X->key() : -1);
|
||||||
|
log_trace(vmatree)(" Y: %4ld", Y_exists ? (long)Y->key() : -1);
|
||||||
|
log_trace(vmatree)(" W: %4ld", W_exists ? (long)W->key() : -1);
|
||||||
|
log_trace(vmatree)(" U: %4ld", U_exists ? (long)U->key() : -1);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
// Order of the nodes if they exist are as: X <= A < Y <= W <= B < U
|
||||||
|
// A---------------------------B
|
||||||
|
// X Y YW WB U
|
||||||
|
// XA Y YW WB U
|
||||||
|
if (!X_exists && !Y_exists && !U_exists) { row = 0; }
|
||||||
|
if (!X_exists && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 1; }
|
||||||
|
if (!X_exists && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 2; }
|
||||||
|
if (!X_exists && Y_exists && W_eq_B && !U_exists) { row = 3; }
|
||||||
|
|
||||||
// We add a new node, but only if there would be a state change. If there would not be a
|
if ( X_exists && !Y_exists && !U_exists) { row = 4; }
|
||||||
// state change, we just omit the node.
|
if ( X_exists && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 5; }
|
||||||
// That happens, for example, when reserving within an already reserved region with identical metadata.
|
if ( X_exists && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 6; }
|
||||||
stA.in = out_state(leqA_n); // .. and the region's prior state is the incoming state
|
if ( X_exists && Y_exists && W_eq_B && !U_exists) { row = 7; }
|
||||||
if (stA.is_noop()) {
|
|
||||||
// Nothing to do.
|
if ( X_eq_A && !Y_exists && !U_exists) { row = 8; }
|
||||||
} else {
|
if ( X_eq_A && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 9; }
|
||||||
// Add new node.
|
if ( X_eq_A && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 10; }
|
||||||
_tree.upsert(A, stA);
|
if ( X_eq_A && Y_exists && W_eq_B && !U_exists) { row = 11; }
|
||||||
}
|
|
||||||
|
if (!X_exists && !Y_exists && U_exists) { row = 12; }
|
||||||
|
if (!X_exists && Y_exists && Y_eq_W && !W_eq_B && U_exists) { row = 13; }
|
||||||
|
if (!X_exists && Y_exists && !Y_eq_W && !W_eq_B && U_exists) { row = 14; }
|
||||||
|
if (!X_exists && Y_exists && W_eq_B && U_exists) { row = 15; }
|
||||||
|
|
||||||
|
if ( X_exists && !Y_exists && U_exists) { row = 16; }
|
||||||
|
if ( X_exists && Y_exists && Y_eq_W && !W_eq_B && U_exists) { row = 17; }
|
||||||
|
if ( X_exists && Y_exists && !Y_eq_W && !W_eq_B && U_exists) { row = 18; }
|
||||||
|
if ( X_exists && Y_exists && W_eq_B && U_exists) { row = 19; }
|
||||||
|
|
||||||
|
if ( X_eq_A && !Y_exists && U_exists) { row = 20; }
|
||||||
|
if ( X_eq_A && Y_exists && Y_eq_W && !W_eq_B && U_exists) { row = 21; }
|
||||||
|
if ( X_eq_A && Y_exists && !Y_eq_W && !W_eq_B && U_exists) { row = 22; }
|
||||||
|
if ( X_eq_A && Y_exists && W_eq_B && U_exists) { row = 23; }
|
||||||
|
|
||||||
|
DEBUG_ONLY(print_case();)
|
||||||
|
switch(row) {
|
||||||
|
// row 0: .........A..................B.....
|
||||||
|
case 0: {
|
||||||
|
update_A(B);
|
||||||
|
upsert_if(A);
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
// row 1: .........A...YW.............B.....
|
||||||
|
case 1: {
|
||||||
// Now we handle B.
|
ShouldNotReachHere();
|
||||||
// We first search all nodes that are (A, B]. All of these nodes
|
break;
|
||||||
// need to be deleted and summary accounted for. The last node before B determines B's outgoing state.
|
|
||||||
// If there is no node between A and B, its A's incoming state.
|
|
||||||
GrowableArrayCHeap<AddressState, mtNMT> to_be_deleted_inbetween_a_b;
|
|
||||||
bool B_needs_insert = true;
|
|
||||||
|
|
||||||
// Find all nodes between (A, B] and record their addresses and values. Also update B's
|
|
||||||
// outgoing state.
|
|
||||||
_tree.visit_range_in_order(A + 1, B + 1, [&](TreapNode* head) {
|
|
||||||
int cmp_B = PositionComparator::cmp(head->key(), B);
|
|
||||||
stB.out = out_state(head);
|
|
||||||
if (cmp_B < 0) {
|
|
||||||
// Record all nodes preceding B.
|
|
||||||
to_be_deleted_inbetween_a_b.push({head->key(), head->val()});
|
|
||||||
} else if (cmp_B == 0) {
|
|
||||||
// Re-purpose B node, unless it would result in a noop node, in
|
|
||||||
// which case record old node at B for deletion and summary accounting.
|
|
||||||
if (stB.is_noop()) {
|
|
||||||
to_be_deleted_inbetween_a_b.push(AddressState{B, head->val()});
|
|
||||||
} else {
|
|
||||||
head->val() = stB;
|
|
||||||
}
|
|
||||||
B_needs_insert = false;
|
|
||||||
}
|
}
|
||||||
});
|
// row 2: .........A...Y..........W...B.....
|
||||||
|
case 2: {
|
||||||
// Insert B node if needed
|
update_A(Y);
|
||||||
if (B_needs_insert && // Was not already inserted
|
upsert_if(A);
|
||||||
!stB.is_noop()) // The operation is differing
|
update_loop();
|
||||||
{
|
remove_if(Y);
|
||||||
_tree.upsert(B, stB);
|
update(W, B);
|
||||||
}
|
remove_if(W);
|
||||||
|
upsert_if(B);
|
||||||
// We now need to:
|
break;
|
||||||
// a) Delete all nodes between (A, B]. Including B in the case of a noop.
|
|
||||||
// b) Perform summary accounting
|
|
||||||
SummaryDiff diff;
|
|
||||||
|
|
||||||
if (to_be_deleted_inbetween_a_b.length() == 0 && LEQ_A_found) {
|
|
||||||
// We must have smashed a hole in an existing region (or replaced it entirely).
|
|
||||||
// LEQ_A < A < B <= C
|
|
||||||
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(LEQ_A.out().mem_tag())];
|
|
||||||
if (LEQ_A.out().type() == StateType::Reserved) {
|
|
||||||
rescom.reserve -= B - A;
|
|
||||||
} else if (LEQ_A.out().type() == StateType::Committed) {
|
|
||||||
rescom.commit -= B - A;
|
|
||||||
rescom.reserve -= B - A;
|
|
||||||
}
|
}
|
||||||
}
|
// row 3: .........A...Y.............WB.....
|
||||||
|
case 3: {
|
||||||
// Track the previous node.
|
update_A(Y);
|
||||||
AddressState prev{A, stA};
|
upsert_if(A);
|
||||||
for (int i = 0; i < to_be_deleted_inbetween_a_b.length(); i++) {
|
update_loop();
|
||||||
const AddressState delete_me = to_be_deleted_inbetween_a_b.at(i);
|
remove_if(W);
|
||||||
_tree.remove(delete_me.address);
|
break;
|
||||||
|
|
||||||
// Perform summary accounting
|
|
||||||
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(delete_me.in().mem_tag())];
|
|
||||||
if (delete_me.in().type() == StateType::Reserved) {
|
|
||||||
rescom.reserve -= delete_me.address - prev.address;
|
|
||||||
} else if (delete_me.in().type() == StateType::Committed) {
|
|
||||||
rescom.commit -= delete_me.address - prev.address;
|
|
||||||
rescom.reserve -= delete_me.address - prev.address;
|
|
||||||
}
|
}
|
||||||
prev = delete_me;
|
// row 4: .....X...A..................B.....
|
||||||
}
|
case 4: {
|
||||||
|
A->val().in = X->val().out;
|
||||||
if (prev.address != A && prev.out().type() != StateType::Released) {
|
update_A(B);
|
||||||
// The last node wasn't released, so it must be connected to a node outside of (A, B)
|
upsert_if(A);
|
||||||
// A - prev - B - (some node >= B)
|
upsert_if(B);
|
||||||
// It might be that prev.address == B == (some node >= B), this is fine.
|
break;
|
||||||
if (prev.out().type() == StateType::Reserved) {
|
|
||||||
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(prev.out().mem_tag())];
|
|
||||||
rescom.reserve -= B - prev.address;
|
|
||||||
} else if (prev.out().type() == StateType::Committed) {
|
|
||||||
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(prev.out().mem_tag())];
|
|
||||||
rescom.commit -= B - prev.address;
|
|
||||||
rescom.reserve -= B - prev.address;
|
|
||||||
}
|
}
|
||||||
|
// row 5: .....X...A...YW.............B.....
|
||||||
|
case 5: {
|
||||||
|
A->val().in = X->val().out;
|
||||||
|
update_A(Y);
|
||||||
|
upsert_if(A);
|
||||||
|
update(Y, B);
|
||||||
|
remove_if(Y);
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 6: .....X...A...Y..........W...B.....
|
||||||
|
case 6: {
|
||||||
|
A->val().in = X->val().out;
|
||||||
|
update_A(Y);
|
||||||
|
upsert_if(A);
|
||||||
|
update_loop();
|
||||||
|
update(W, B);
|
||||||
|
remove_if(W);
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 7: .....X...A...Y.............WB.....
|
||||||
|
case 7: {
|
||||||
|
A->val().in = X->val().out;
|
||||||
|
update_A(Y);
|
||||||
|
upsert_if(A);
|
||||||
|
update_loop();
|
||||||
|
remove_if(W);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 8: ........XA..................B.....
|
||||||
|
case 8: {
|
||||||
|
update(X, B);
|
||||||
|
remove_if(X);
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 9: ........XA...YW.............B.....
|
||||||
|
case 9: {
|
||||||
|
update(X, Y);
|
||||||
|
remove_if(X);
|
||||||
|
update(W, B);
|
||||||
|
remove_if(W);
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 10: ........XA...Y..........W...B.....
|
||||||
|
case 10: {
|
||||||
|
update(X, Y);
|
||||||
|
remove_if(X);
|
||||||
|
update_loop();
|
||||||
|
update(W, B);
|
||||||
|
remove_if(W);
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 11: ........XA...Y.............WB.....
|
||||||
|
case 11: {
|
||||||
|
update(X, Y);
|
||||||
|
remove_if(X);
|
||||||
|
update_loop();
|
||||||
|
remove_if(W);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 12: .........A..................B....U
|
||||||
|
case 12: {
|
||||||
|
update_A(B);
|
||||||
|
upsert_if(A);
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 13: .........A...YW.............B....U
|
||||||
|
case 13: {
|
||||||
|
update_A(Y);
|
||||||
|
upsert_if(A);
|
||||||
|
update(W, B);
|
||||||
|
remove_if(W);
|
||||||
|
B->val().out = U->val().in;
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 14: .........A...Y..........W...B....U
|
||||||
|
case 14: {
|
||||||
|
update_A(Y);
|
||||||
|
upsert_if(A);
|
||||||
|
update_loop();
|
||||||
|
update(W, B);
|
||||||
|
remove_if(W);
|
||||||
|
B->val().out = U->val().in;
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 15: .........A...Y.............WB....U
|
||||||
|
case 15: {
|
||||||
|
update_A(Y);
|
||||||
|
upsert_if(A);
|
||||||
|
update_loop();
|
||||||
|
remove_if(W);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 16: .....X...A..................B....U
|
||||||
|
case 16: {
|
||||||
|
A->val().in = X->val().out;
|
||||||
|
update_A(B);
|
||||||
|
upsert_if(A);
|
||||||
|
B->val().out = U->val().in;
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 17: .....X...A...YW.............B....U
|
||||||
|
case 17: {
|
||||||
|
A->val().in = X->val().out;
|
||||||
|
update_A(Y);
|
||||||
|
upsert_if(A);
|
||||||
|
update(W, B);
|
||||||
|
remove_if(W);
|
||||||
|
B->val().out = U->val().in;
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 18: .....X...A...Y..........W...B....U
|
||||||
|
case 18: {
|
||||||
|
A->val().in = X->val().out;
|
||||||
|
update_A(Y);
|
||||||
|
upsert_if(A);
|
||||||
|
update_loop();
|
||||||
|
update(W, B);
|
||||||
|
remove_if(W);
|
||||||
|
B->val().out = U->val().in;
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 19: .....X...A...Y.............WB....U
|
||||||
|
case 19: {
|
||||||
|
A->val().in = X->val().out;
|
||||||
|
update_A(Y);
|
||||||
|
upsert_if(A);
|
||||||
|
update_loop();
|
||||||
|
remove_if(W);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 20: ........XA..................B....U
|
||||||
|
case 20: {
|
||||||
|
update(X, B);
|
||||||
|
remove_if(X);
|
||||||
|
B->val().out = U->val().in;
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 21: ........XA...YW.............B....U
|
||||||
|
case 21: {
|
||||||
|
update(X, Y);
|
||||||
|
remove_if(X);
|
||||||
|
update(W, B);
|
||||||
|
remove_if(W);
|
||||||
|
B->val().out = U->val().in;
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 22: ........XA...Y..........W...B....U
|
||||||
|
case 22: {
|
||||||
|
update(X, Y);
|
||||||
|
remove_if(X);
|
||||||
|
update_loop();
|
||||||
|
update(W, B);
|
||||||
|
remove_if(W);
|
||||||
|
B->val().out = U->val().in;
|
||||||
|
upsert_if(B);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// row 23: ........XA...Y.............WB....U
|
||||||
|
case 23: {
|
||||||
|
update(X, Y);
|
||||||
|
remove_if(X);
|
||||||
|
update_loop();
|
||||||
|
remove_if(W);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ShouldNotReachHere();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, we can register the new region [A, B)'s summary data.
|
// Remove the 'noop' nodes that found inside the loop
|
||||||
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(stA.out.mem_tag())];
|
while(to_be_removed.length() != 0) {
|
||||||
if (state == StateType::Reserved) {
|
_tree.remove(to_be_removed.pop());
|
||||||
rescom.reserve += B - A;
|
|
||||||
} else if (state == StateType::Committed) {
|
|
||||||
rescom.commit += B - A;
|
|
||||||
rescom.reserve += B - A;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
void VMATree::print_on(outputStream* out) {
|
void VMATree::print_on(outputStream* out) {
|
||||||
visit_in_order([&](TreapNode* current) {
|
visit_in_order([&](TreapNode* current) {
|
||||||
out->print("%zu (%s) - %s - ", current->key(), NMTUtil::tag_to_name(out_state(current).mem_tag()),
|
out->print("%zu (%s) - %s [%d, %d]-> ", current->key(), NMTUtil::tag_to_name(out_state(current).mem_tag()),
|
||||||
statetype_to_string(out_state(current).type()));
|
statetype_to_string(out_state(current).type()), current->val().out.reserved_stack(), current->val().out.committed_stack());
|
||||||
});
|
});
|
||||||
out->cr();
|
out->cr();
|
||||||
}
|
}
|
||||||
@ -268,7 +699,7 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con
|
|||||||
SummaryDiff diff;
|
SummaryDiff diff;
|
||||||
// Ignore any released ranges, these must be mtNone and have no stack
|
// Ignore any released ranges, these must be mtNone and have no stack
|
||||||
if (type != StateType::Released) {
|
if (type != StateType::Released) {
|
||||||
RegionData new_data = RegionData(out.stack(), tag);
|
RegionData new_data = RegionData(out.reserved_stack(), tag);
|
||||||
SummaryDiff result = register_mapping(from, end, type, new_data);
|
SummaryDiff result = register_mapping(from, end, type, new_data);
|
||||||
diff.add(result);
|
diff.add(result);
|
||||||
}
|
}
|
||||||
@ -289,7 +720,7 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con
|
|||||||
StateType type = out.type();
|
StateType type = out.type();
|
||||||
|
|
||||||
if (type != StateType::Released) {
|
if (type != StateType::Released) {
|
||||||
RegionData new_data = RegionData(out.stack(), tag);
|
RegionData new_data = RegionData(out.reserved_stack(), tag);
|
||||||
SummaryDiff result = register_mapping(from, end, type, new_data);
|
SummaryDiff result = register_mapping(from, end, type, new_data);
|
||||||
diff.add(result);
|
diff.add(result);
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ class VMATree {
|
|||||||
public:
|
public:
|
||||||
using position = size_t;
|
using position = size_t;
|
||||||
using size = size_t;
|
using size = size_t;
|
||||||
|
using SIndex = NativeCallStackStorage::StackIndex;
|
||||||
|
|
||||||
class PositionComparator {
|
class PositionComparator {
|
||||||
public:
|
public:
|
||||||
@ -55,7 +56,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class StateType : uint8_t { Reserved, Committed, Released, LAST };
|
enum class StateType : uint8_t { Released, Reserved, Committed, LAST };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const char* statetype_strings[static_cast<uint8_t>(StateType::LAST)];
|
static const char* statetype_strings[static_cast<uint8_t>(StateType::LAST)];
|
||||||
@ -70,12 +71,12 @@ public:
|
|||||||
|
|
||||||
// Each point has some stack and a tag associated with it.
|
// Each point has some stack and a tag associated with it.
|
||||||
struct RegionData {
|
struct RegionData {
|
||||||
const NativeCallStackStorage::StackIndex stack_idx;
|
const SIndex stack_idx;
|
||||||
const MemTag mem_tag;
|
const MemTag mem_tag;
|
||||||
|
|
||||||
RegionData() : stack_idx(), mem_tag(mtNone) {}
|
RegionData() : stack_idx(), mem_tag(mtNone) {}
|
||||||
|
|
||||||
RegionData(NativeCallStackStorage::StackIndex stack_idx, MemTag mem_tag)
|
RegionData(SIndex stack_idx, MemTag mem_tag)
|
||||||
: stack_idx(stack_idx), mem_tag(mem_tag) {}
|
: stack_idx(stack_idx), mem_tag(mem_tag) {}
|
||||||
|
|
||||||
static bool equals(const RegionData& a, const RegionData& b) {
|
static bool equals(const RegionData& a, const RegionData& b) {
|
||||||
@ -91,15 +92,27 @@ private:
|
|||||||
private:
|
private:
|
||||||
// Store the type and mem_tag as two bytes
|
// Store the type and mem_tag as two bytes
|
||||||
uint8_t type_tag[2];
|
uint8_t type_tag[2];
|
||||||
NativeCallStackStorage::StackIndex sidx;
|
NativeCallStackStorage::StackIndex _reserved_stack;
|
||||||
|
NativeCallStackStorage::StackIndex _committed_stack;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IntervalState() : type_tag{0,0}, sidx() {}
|
IntervalState() : type_tag{0,0}, _reserved_stack(NativeCallStackStorage::invalid), _committed_stack(NativeCallStackStorage::invalid) {}
|
||||||
|
IntervalState(const StateType type,
|
||||||
|
const MemTag mt,
|
||||||
|
const NativeCallStackStorage::StackIndex res_stack,
|
||||||
|
const NativeCallStackStorage::StackIndex com_stack) {
|
||||||
|
assert(!(type == StateType::Released) || mt == mtNone, "Released state-type must have memory tag mtNone");
|
||||||
|
type_tag[0] = static_cast<uint8_t>(type);
|
||||||
|
type_tag[1] = static_cast<uint8_t>(mt);
|
||||||
|
_reserved_stack = res_stack;
|
||||||
|
_committed_stack = com_stack;
|
||||||
|
}
|
||||||
IntervalState(const StateType type, const RegionData data) {
|
IntervalState(const StateType type, const RegionData data) {
|
||||||
assert(!(type == StateType::Released) || data.mem_tag == mtNone, "Released state-type must have memory tag mtNone");
|
assert(!(type == StateType::Released) || data.mem_tag == mtNone, "Released state-type must have memory tag mtNone");
|
||||||
type_tag[0] = static_cast<uint8_t>(type);
|
type_tag[0] = static_cast<uint8_t>(type);
|
||||||
type_tag[1] = static_cast<uint8_t>(data.mem_tag);
|
type_tag[1] = static_cast<uint8_t>(data.mem_tag);
|
||||||
sidx = data.stack_idx;
|
_reserved_stack = data.stack_idx;
|
||||||
|
_committed_stack = NativeCallStackStorage::invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
StateType type() const {
|
StateType type() const {
|
||||||
@ -110,16 +123,50 @@ private:
|
|||||||
return static_cast<MemTag>(type_tag[1]);
|
return static_cast<MemTag>(type_tag[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegionData regiondata() const {
|
RegionData reserved_regiondata() const {
|
||||||
return RegionData{sidx, mem_tag()};
|
return RegionData{_reserved_stack, mem_tag()};
|
||||||
|
}
|
||||||
|
RegionData committed_regiondata() const {
|
||||||
|
return RegionData{_committed_stack, mem_tag()};
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_tag(MemTag tag) {
|
void set_tag(MemTag tag) {
|
||||||
type_tag[1] = static_cast<uint8_t>(tag);
|
type_tag[1] = static_cast<uint8_t>(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeCallStackStorage::StackIndex stack() const {
|
NativeCallStackStorage::StackIndex reserved_stack() const {
|
||||||
return sidx;
|
return _reserved_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeCallStackStorage::StackIndex committed_stack() const {
|
||||||
|
return _committed_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_reserve_stack(NativeCallStackStorage::StackIndex idx) {
|
||||||
|
_reserved_stack = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_commit_stack(NativeCallStackStorage::StackIndex idx) {
|
||||||
|
_committed_stack = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_reserved_stack() {
|
||||||
|
return _reserved_stack != NativeCallStackStorage::invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_committed_stack() {
|
||||||
|
return _committed_stack != NativeCallStackStorage::invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_type(StateType t) {
|
||||||
|
type_tag[0] = static_cast<uint8_t>(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool equals(const IntervalState& other) const {
|
||||||
|
return mem_tag() == other.mem_tag() &&
|
||||||
|
type() == other.type() &&
|
||||||
|
reserved_stack() == other.reserved_stack() &&
|
||||||
|
committed_stack() == other.committed_stack();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -130,8 +177,14 @@ private:
|
|||||||
IntervalState out;
|
IntervalState out;
|
||||||
|
|
||||||
bool is_noop() {
|
bool is_noop() {
|
||||||
|
if (in.type() == StateType::Released &&
|
||||||
|
in.type() == out.type() &&
|
||||||
|
in.mem_tag() == out.mem_tag()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return in.type() == out.type() &&
|
return in.type() == out.type() &&
|
||||||
RegionData::equals(in.regiondata(), out.regiondata());
|
RegionData::equals(in.reserved_regiondata(), out.reserved_regiondata()) &&
|
||||||
|
RegionData::equals(in.committed_regiondata(), out.committed_regiondata());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -193,8 +246,44 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Operation {Release, Reserve, Commit, Uncommit};
|
||||||
|
struct RequestInfo {
|
||||||
|
position A, B;
|
||||||
|
StateType _op;
|
||||||
|
MemTag tag;
|
||||||
|
SIndex callstack;
|
||||||
|
bool use_tag_inplace;
|
||||||
|
Operation op() const {
|
||||||
|
return
|
||||||
|
_op == StateType::Reserved && !use_tag_inplace ? Operation::Reserve :
|
||||||
|
_op == StateType::Committed ? Operation::Commit :
|
||||||
|
_op == StateType::Reserved && use_tag_inplace ? Operation::Uncommit :
|
||||||
|
Operation::Release;
|
||||||
|
}
|
||||||
|
|
||||||
|
int op_to_index() const {
|
||||||
|
return
|
||||||
|
_op == StateType::Reserved && !use_tag_inplace ? 1 :
|
||||||
|
_op == StateType::Committed ? 2 :
|
||||||
|
_op == StateType::Reserved && use_tag_inplace ? 3 :
|
||||||
|
0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SummaryDiff register_mapping(position A, position B, StateType state, const RegionData& metadata, bool use_tag_inplace = false);
|
SummaryDiff register_mapping(position A, position B, StateType state, const RegionData& metadata, bool use_tag_inplace = false);
|
||||||
|
StateType get_new_state(const StateType existinting_state, const RequestInfo& req) const;
|
||||||
|
MemTag get_new_tag(const MemTag existinting_tag, const RequestInfo& req) const;
|
||||||
|
SIndex get_new_reserve_callstack(const SIndex existinting_stack, const StateType ex, const RequestInfo& req) const;
|
||||||
|
SIndex get_new_commit_callstack(const SIndex existinting_stack, const StateType ex, const RequestInfo& req) const;
|
||||||
|
void compute_summary_diff(const SingleDiff::delta region_size, const MemTag t1, const StateType& ex, const RequestInfo& req, const MemTag new_tag, SummaryDiff& diff) const;
|
||||||
|
void update_region(TreapNode* n1, TreapNode* n2, const RequestInfo& req, SummaryDiff& diff);
|
||||||
|
int state_to_index(const StateType st) const {
|
||||||
|
return
|
||||||
|
st == StateType::Released ? 0 :
|
||||||
|
st == StateType::Reserved ? 1 :
|
||||||
|
st == StateType::Committed ? 2 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SummaryDiff reserve_mapping(position from, size size, const RegionData& metadata) {
|
SummaryDiff reserve_mapping(position from, size size, const RegionData& metadata) {
|
||||||
|
@ -22,8 +22,11 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "cds/cdsConfig.hpp"
|
||||||
#include "oops/fieldInfo.inline.hpp"
|
#include "oops/fieldInfo.inline.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
|
#include "utilities/packedTable.hpp"
|
||||||
|
|
||||||
void FieldInfo::print(outputStream* os, ConstantPool* cp) {
|
void FieldInfo::print(outputStream* os, ConstantPool* cp) {
|
||||||
os->print_cr("index=%d name_index=%d name=%s signature_index=%d signature=%s offset=%d "
|
os->print_cr("index=%d name_index=%d name=%s signature_index=%d signature=%s offset=%d "
|
||||||
@ -37,8 +40,10 @@ void FieldInfo::print(outputStream* os, ConstantPool* cp) {
|
|||||||
field_flags().as_uint(),
|
field_flags().as_uint(),
|
||||||
initializer_index(),
|
initializer_index(),
|
||||||
generic_signature_index(),
|
generic_signature_index(),
|
||||||
_field_flags.is_injected() ? lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8(),
|
_field_flags.is_generic() ? (_field_flags.is_injected() ?
|
||||||
contended_group());
|
lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8()
|
||||||
|
) : "",
|
||||||
|
is_contended() ? contended_group() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldInfo::print_from_growable_array(outputStream* os, GrowableArray<FieldInfo>* array, ConstantPool* cp) {
|
void FieldInfo::print_from_growable_array(outputStream* os, GrowableArray<FieldInfo>* array, ConstantPool* cp) {
|
||||||
@ -62,13 +67,17 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
|
|||||||
StreamSizer s;
|
StreamSizer s;
|
||||||
StreamFieldSizer sizer(&s);
|
StreamFieldSizer sizer(&s);
|
||||||
|
|
||||||
|
assert(fields->length() == java_fields + injected_fields, "must be");
|
||||||
|
|
||||||
sizer.consumer()->accept_uint(java_fields);
|
sizer.consumer()->accept_uint(java_fields);
|
||||||
sizer.consumer()->accept_uint(injected_fields);
|
sizer.consumer()->accept_uint(injected_fields);
|
||||||
for (int i = 0; i < fields->length(); i++) {
|
for (int i = 0; i < fields->length(); i++) {
|
||||||
FieldInfo* fi = fields->adr_at(i);
|
FieldInfo* fi = fields->adr_at(i);
|
||||||
sizer.map_field_info(*fi);
|
sizer.map_field_info(*fi);
|
||||||
}
|
}
|
||||||
int storage_size = sizer.consumer()->position() + 1;
|
// Originally there was an extra byte with 0 terminating the reading;
|
||||||
|
// now we check limits instead.
|
||||||
|
int storage_size = sizer.consumer()->position();
|
||||||
Array<u1>* const fis = MetadataFactory::new_array<u1>(loader_data, storage_size, CHECK_NULL);
|
Array<u1>* const fis = MetadataFactory::new_array<u1>(loader_data, storage_size, CHECK_NULL);
|
||||||
|
|
||||||
using StreamWriter = UNSIGNED5::Writer<Array<u1>*, int, ArrayHelper<Array<u1>*, int>>;
|
using StreamWriter = UNSIGNED5::Writer<Array<u1>*, int, ArrayHelper<Array<u1>*, int>>;
|
||||||
@ -79,15 +88,14 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
|
|||||||
writer.consumer()->accept_uint(java_fields);
|
writer.consumer()->accept_uint(java_fields);
|
||||||
writer.consumer()->accept_uint(injected_fields);
|
writer.consumer()->accept_uint(injected_fields);
|
||||||
for (int i = 0; i < fields->length(); i++) {
|
for (int i = 0; i < fields->length(); i++) {
|
||||||
FieldInfo* fi = fields->adr_at(i);
|
writer.map_field_info(fields->at(i));
|
||||||
writer.map_field_info(*fi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
FieldInfoReader r(fis);
|
FieldInfoReader r(fis);
|
||||||
int jfc = r.next_uint();
|
int jfc, ifc;
|
||||||
|
r.read_field_counts(&jfc, &ifc);
|
||||||
assert(jfc == java_fields, "Must be");
|
assert(jfc == java_fields, "Must be");
|
||||||
int ifc = r.next_uint();
|
|
||||||
assert(ifc == injected_fields, "Must be");
|
assert(ifc == injected_fields, "Must be");
|
||||||
for (int i = 0; i < jfc + ifc; i++) {
|
for (int i = 0; i < jfc + ifc; i++) {
|
||||||
FieldInfo fi;
|
FieldInfo fi;
|
||||||
@ -113,30 +121,221 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
|
|||||||
return fis;
|
return fis;
|
||||||
}
|
}
|
||||||
|
|
||||||
GrowableArray<FieldInfo>* FieldInfoStream::create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count) {
|
int FieldInfoStream::compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2) {
|
||||||
int length = FieldInfoStream::num_total_fields(fis);
|
int cmp = n1->fast_compare(n2);
|
||||||
GrowableArray<FieldInfo>* array = new GrowableArray<FieldInfo>(length);
|
return cmp != 0 ? cmp : s1->fast_compare(s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// We use both name and signature during the comparison; while JLS require unique
|
||||||
|
// names for fields, JVMS requires only unique name + signature combination.
|
||||||
|
struct field_pos {
|
||||||
|
Symbol* _name;
|
||||||
|
Symbol* _signature;
|
||||||
|
int _index;
|
||||||
|
int _position;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FieldInfoSupplier: public PackedTableBuilder::Supplier {
|
||||||
|
const field_pos* _positions;
|
||||||
|
size_t _elements;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FieldInfoSupplier(const field_pos* positions, size_t elements): _positions(positions), _elements(elements) {}
|
||||||
|
|
||||||
|
bool next(uint32_t* key, uint32_t* value) override {
|
||||||
|
if (_elements == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*key = _positions->_position;
|
||||||
|
*value = _positions->_index;
|
||||||
|
++_positions;
|
||||||
|
--_elements;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Array<u1>* FieldInfoStream::create_search_table(ConstantPool* cp, const Array<u1>* fis, ClassLoaderData* loader_data, TRAPS) {
|
||||||
|
if (CDSConfig::is_dumping_dynamic_archive()) {
|
||||||
|
// We cannot use search table; in case of dynamic archives it should be sorted by "requested" addresses,
|
||||||
|
// but Symbol* addresses are coming from _constants, which has "buffered" addresses.
|
||||||
|
// For background, see new comments inside allocate_node_impl in symbolTable.cpp
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
FieldInfoReader r(fis);
|
FieldInfoReader r(fis);
|
||||||
*java_fields_count = r.next_uint();
|
int java_fields;
|
||||||
*injected_fields_count = r.next_uint();
|
int injected_fields;
|
||||||
|
r.read_field_counts(&java_fields, &injected_fields);
|
||||||
|
assert(java_fields >= 0, "must be");
|
||||||
|
if (java_fields == 0 || fis->length() == 0 || static_cast<uint>(java_fields) < BinarySearchThreshold) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceMark rm;
|
||||||
|
field_pos* positions = NEW_RESOURCE_ARRAY(field_pos, java_fields);
|
||||||
|
for (int i = 0; i < java_fields; ++i) {
|
||||||
|
assert(r.has_next(), "number of fields must match");
|
||||||
|
|
||||||
|
positions[i]._position = r.position();
|
||||||
|
FieldInfo fi;
|
||||||
|
r.read_field_info(fi);
|
||||||
|
|
||||||
|
positions[i]._name = fi.name(cp);
|
||||||
|
positions[i]._signature = fi.signature(cp);
|
||||||
|
positions[i]._index = i;
|
||||||
|
}
|
||||||
|
auto compare_pair = [](const void* v1, const void* v2) {
|
||||||
|
const field_pos* p1 = reinterpret_cast<const field_pos*>(v1);
|
||||||
|
const field_pos* p2 = reinterpret_cast<const field_pos*>(v2);
|
||||||
|
return compare_name_and_sig(p1->_name, p1->_signature, p2->_name, p2->_signature);
|
||||||
|
};
|
||||||
|
qsort(positions, java_fields, sizeof(field_pos), compare_pair);
|
||||||
|
|
||||||
|
PackedTableBuilder builder(fis->length() - 1, java_fields - 1);
|
||||||
|
Array<u1>* table = MetadataFactory::new_array<u1>(loader_data, java_fields * builder.element_bytes(), CHECK_NULL);
|
||||||
|
FieldInfoSupplier supplier(positions, java_fields);
|
||||||
|
builder.fill(table->data(), static_cast<size_t>(table->length()), supplier);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
GrowableArray<FieldInfo>* FieldInfoStream::create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count) {
|
||||||
|
FieldInfoReader r(fis);
|
||||||
|
r.read_field_counts(java_fields_count, injected_fields_count);
|
||||||
|
int length = *java_fields_count + *injected_fields_count;
|
||||||
|
|
||||||
|
GrowableArray<FieldInfo>* array = new GrowableArray<FieldInfo>(length);
|
||||||
while (r.has_next()) {
|
while (r.has_next()) {
|
||||||
FieldInfo fi;
|
FieldInfo fi;
|
||||||
r.read_field_info(fi);
|
r.read_field_info(fi);
|
||||||
array->append(fi);
|
array->append(fi);
|
||||||
}
|
}
|
||||||
assert(array->length() == length, "Must be");
|
assert(array->length() == length, "Must be");
|
||||||
assert(array->length() == *java_fields_count + *injected_fields_count, "Must be");
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldInfoStream::print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp) {
|
void FieldInfoStream::print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp) {
|
||||||
int length = FieldInfoStream::num_total_fields(fis);
|
|
||||||
FieldInfoReader r(fis);
|
FieldInfoReader r(fis);
|
||||||
int java_field_count = r.next_uint();
|
int java_fields_count;
|
||||||
int injected_fields_count = r.next_uint();
|
int injected_fields_count;
|
||||||
|
r.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
while (r.has_next()) {
|
while (r.has_next()) {
|
||||||
FieldInfo fi;
|
FieldInfo fi;
|
||||||
r.read_field_info(fi);
|
r.read_field_info(fi);
|
||||||
fi.print(os, cp);
|
fi.print(os, cp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FieldInfoComparator: public PackedTableLookup::Comparator {
|
||||||
|
const FieldInfoReader* _reader;
|
||||||
|
ConstantPool* _cp;
|
||||||
|
const Symbol* _name;
|
||||||
|
const Symbol* _signature;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FieldInfoComparator(const FieldInfoReader* reader, ConstantPool* cp, const Symbol* name, const Symbol* signature):
|
||||||
|
_reader(reader), _cp(cp), _name(name), _signature(signature) {}
|
||||||
|
|
||||||
|
int compare_to(uint32_t position) override {
|
||||||
|
FieldInfoReader r2(*_reader);
|
||||||
|
r2.set_position_and_next_index(position, -1);
|
||||||
|
u2 name_index, sig_index;
|
||||||
|
r2.read_name_and_signature(&name_index, &sig_index);
|
||||||
|
Symbol* mid_name = _cp->symbol_at(name_index);
|
||||||
|
Symbol* mid_sig = _cp->symbol_at(sig_index);
|
||||||
|
|
||||||
|
return FieldInfoStream::compare_name_and_sig(_name, _signature, mid_name, mid_sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
void reset(uint32_t position) override {
|
||||||
|
FieldInfoReader r2(*_reader);
|
||||||
|
r2.set_position_and_next_index(position, -1);
|
||||||
|
u2 name_index, signature_index;
|
||||||
|
r2.read_name_and_signature(&name_index, &signature_index);
|
||||||
|
_name = _cp->symbol_at(name_index);
|
||||||
|
_signature = _cp->symbol_at(signature_index);
|
||||||
|
}
|
||||||
|
#endif // ASSERT
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
void FieldInfoStream::validate_search_table(ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table) {
|
||||||
|
if (search_table == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FieldInfoReader reader(fis);
|
||||||
|
int java_fields, injected_fields;
|
||||||
|
reader.read_field_counts(&java_fields, &injected_fields);
|
||||||
|
assert(java_fields > 0, "must be");
|
||||||
|
|
||||||
|
PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
|
||||||
|
assert(lookup.element_bytes() * java_fields == static_cast<unsigned int>(search_table->length()), "size does not match");
|
||||||
|
|
||||||
|
FieldInfoComparator comparator(&reader, cp, nullptr, nullptr);
|
||||||
|
// Check 1: assert that elements have the correct order based on the comparison function
|
||||||
|
lookup.validate_order(comparator);
|
||||||
|
|
||||||
|
// Check 2: Iterate through the original stream (not just search_table) and try if lookup works as expected
|
||||||
|
reader.set_position_and_next_index(0, 0);
|
||||||
|
reader.read_field_counts(&java_fields, &injected_fields);
|
||||||
|
while (reader.has_next()) {
|
||||||
|
int field_start = reader.position();
|
||||||
|
FieldInfo fi;
|
||||||
|
reader.read_field_info(fi);
|
||||||
|
if (fi.field_flags().is_injected()) {
|
||||||
|
// checking only java fields that precede injected ones
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldInfoReader r2(fis);
|
||||||
|
int index = r2.search_table_lookup(search_table, fi.name(cp), fi.signature(cp), cp, java_fields);
|
||||||
|
assert(index == static_cast<int>(fi.index()), "wrong index: %d != %u", index, fi.index());
|
||||||
|
assert(index == r2.next_index(), "index should match");
|
||||||
|
assert(field_start == r2.position(), "must find the same position");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ASSERT
|
||||||
|
|
||||||
|
void FieldInfoStream::print_search_table(outputStream* st, ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table) {
|
||||||
|
if (search_table == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FieldInfoReader reader(fis);
|
||||||
|
int java_fields, injected_fields;
|
||||||
|
reader.read_field_counts(&java_fields, &injected_fields);
|
||||||
|
assert(java_fields > 0, "must be");
|
||||||
|
PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
|
||||||
|
auto printer = [&] (size_t offset, uint32_t position, uint32_t index) {
|
||||||
|
reader.set_position_and_next_index(position, -1);
|
||||||
|
u2 name_index, sig_index;
|
||||||
|
reader.read_name_and_signature(&name_index, &sig_index);
|
||||||
|
Symbol* name = cp->symbol_at(name_index);
|
||||||
|
Symbol* sig = cp->symbol_at(sig_index);
|
||||||
|
st->print(" [%zu] #%d,#%d = ", offset, name_index, sig_index);
|
||||||
|
name->print_symbol_on(st);
|
||||||
|
st->print(":");
|
||||||
|
sig->print_symbol_on(st);
|
||||||
|
st->print(" @ %p,%p", name, sig);
|
||||||
|
st->cr();
|
||||||
|
};
|
||||||
|
|
||||||
|
lookup.iterate(printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FieldInfoReader::search_table_lookup(const Array<u1>* search_table, const Symbol* name, const Symbol* signature, ConstantPool* cp, int java_fields) {
|
||||||
|
assert(java_fields >= 0, "must be");
|
||||||
|
if (java_fields == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
FieldInfoComparator comp(this, cp, name, signature);
|
||||||
|
PackedTableLookup lookup(_r.limit() - 1, java_fields - 1, search_table);
|
||||||
|
uint32_t position;
|
||||||
|
static_assert(sizeof(uint32_t) == sizeof(_next_index), "field size assert");
|
||||||
|
if (lookup.search(comp, &position, reinterpret_cast<uint32_t*>(&_next_index))) {
|
||||||
|
_r.set_position(static_cast<int>(position));
|
||||||
|
return _next_index;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -222,29 +222,28 @@ public:
|
|||||||
void map_field_info(const FieldInfo& fi);
|
void map_field_info(const FieldInfo& fi);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Gadget for decoding and reading the stream of field records.
|
// Gadget for decoding and reading the stream of field records.
|
||||||
class FieldInfoReader {
|
class FieldInfoReader {
|
||||||
friend class FieldInfoStream;
|
|
||||||
friend class ClassFileParser;
|
|
||||||
friend class FieldStreamBase;
|
|
||||||
friend class FieldInfo;
|
|
||||||
|
|
||||||
UNSIGNED5::Reader<const u1*, int> _r;
|
UNSIGNED5::Reader<const u1*, int> _r;
|
||||||
int _next_index;
|
int _next_index;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FieldInfoReader(const Array<u1>* fi);
|
FieldInfoReader(const Array<u1>* fi);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t next_uint() { return _r.next_uint(); }
|
inline uint32_t next_uint() { return _r.next_uint(); }
|
||||||
void skip(int n) { int s = _r.try_skip(n); assert(s == n,""); }
|
void skip(int n) { int s = _r.try_skip(n); assert(s == n,""); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int has_next() { return _r.has_next(); }
|
void read_field_counts(int* java_fields, int* injected_fields);
|
||||||
int position() { return _r.position(); }
|
int has_next() const { return _r.position() < _r.limit(); }
|
||||||
int next_index() { return _next_index; }
|
int position() const { return _r.position(); }
|
||||||
|
int next_index() const { return _next_index; }
|
||||||
|
void read_name_and_signature(u2* name_index, u2* signature_index);
|
||||||
void read_field_info(FieldInfo& fi);
|
void read_field_info(FieldInfo& fi);
|
||||||
|
|
||||||
|
int search_table_lookup(const Array<u1>* search_table, const Symbol* name, const Symbol* signature, ConstantPool* cp, int java_fields);
|
||||||
|
|
||||||
// skip a whole field record, both required and optional bits
|
// skip a whole field record, both required and optional bits
|
||||||
FieldInfoReader& skip_field_info();
|
FieldInfoReader& skip_field_info();
|
||||||
|
|
||||||
@ -271,6 +270,11 @@ class FieldInfoStream : AllStatic {
|
|||||||
friend class JavaFieldStream;
|
friend class JavaFieldStream;
|
||||||
friend class FieldStreamBase;
|
friend class FieldStreamBase;
|
||||||
friend class ClassFileParser;
|
friend class ClassFileParser;
|
||||||
|
friend class FieldInfoReader;
|
||||||
|
friend class FieldInfoComparator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static int num_java_fields(const Array<u1>* fis);
|
static int num_java_fields(const Array<u1>* fis);
|
||||||
@ -278,9 +282,14 @@ class FieldInfoStream : AllStatic {
|
|||||||
static int num_total_fields(const Array<u1>* fis);
|
static int num_total_fields(const Array<u1>* fis);
|
||||||
|
|
||||||
static Array<u1>* create_FieldInfoStream(GrowableArray<FieldInfo>* fields, int java_fields, int injected_fields,
|
static Array<u1>* create_FieldInfoStream(GrowableArray<FieldInfo>* fields, int java_fields, int injected_fields,
|
||||||
ClassLoaderData* loader_data, TRAPS);
|
ClassLoaderData* loader_data, TRAPS);
|
||||||
|
static Array<u1>* create_search_table(ConstantPool* cp, const Array<u1>* fis, ClassLoaderData* loader_data, TRAPS);
|
||||||
static GrowableArray<FieldInfo>* create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count);
|
static GrowableArray<FieldInfo>* create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count);
|
||||||
static void print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp);
|
static void print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp);
|
||||||
|
|
||||||
|
DEBUG_ONLY(static void validate_search_table(ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table);)
|
||||||
|
|
||||||
|
static void print_search_table(outputStream* st, ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FieldStatus {
|
class FieldStatus {
|
||||||
|
@ -56,16 +56,27 @@ inline Symbol* FieldInfo::lookup_symbol(int symbol_index) const {
|
|||||||
|
|
||||||
inline int FieldInfoStream::num_injected_java_fields(const Array<u1>* fis) {
|
inline int FieldInfoStream::num_injected_java_fields(const Array<u1>* fis) {
|
||||||
FieldInfoReader fir(fis);
|
FieldInfoReader fir(fis);
|
||||||
fir.skip(1);
|
int java_fields_count;
|
||||||
return fir.next_uint();
|
int injected_fields_count;
|
||||||
|
fir.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
|
return injected_fields_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int FieldInfoStream::num_total_fields(const Array<u1>* fis) {
|
inline int FieldInfoStream::num_total_fields(const Array<u1>* fis) {
|
||||||
FieldInfoReader fir(fis);
|
FieldInfoReader fir(fis);
|
||||||
return fir.next_uint() + fir.next_uint();
|
int java_fields_count;
|
||||||
|
int injected_fields_count;
|
||||||
|
fir.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
|
return java_fields_count + injected_fields_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int FieldInfoStream::num_java_fields(const Array<u1>* fis) { return FieldInfoReader(fis).next_uint(); }
|
inline int FieldInfoStream::num_java_fields(const Array<u1>* fis) {
|
||||||
|
FieldInfoReader fir(fis);
|
||||||
|
int java_fields_count;
|
||||||
|
int injected_fields_count;
|
||||||
|
fir.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
|
return java_fields_count;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename CON>
|
template<typename CON>
|
||||||
inline void Mapper<CON>::map_field_info(const FieldInfo& fi) {
|
inline void Mapper<CON>::map_field_info(const FieldInfo& fi) {
|
||||||
@ -94,13 +105,22 @@ inline void Mapper<CON>::map_field_info(const FieldInfo& fi) {
|
|||||||
|
|
||||||
|
|
||||||
inline FieldInfoReader::FieldInfoReader(const Array<u1>* fi)
|
inline FieldInfoReader::FieldInfoReader(const Array<u1>* fi)
|
||||||
: _r(fi->data(), 0),
|
: _r(fi->data(), fi->length()),
|
||||||
_next_index(0) { }
|
_next_index(0) { }
|
||||||
|
|
||||||
|
inline void FieldInfoReader::read_field_counts(int* java_fields, int* injected_fields) {
|
||||||
|
*java_fields = next_uint();
|
||||||
|
*injected_fields = next_uint();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void FieldInfoReader::read_name_and_signature(u2* name_index, u2* signature_index) {
|
||||||
|
*name_index = checked_cast<u2>(next_uint());
|
||||||
|
*signature_index = checked_cast<u2>(next_uint());
|
||||||
|
}
|
||||||
|
|
||||||
inline void FieldInfoReader::read_field_info(FieldInfo& fi) {
|
inline void FieldInfoReader::read_field_info(FieldInfo& fi) {
|
||||||
fi._index = _next_index++;
|
fi._index = _next_index++;
|
||||||
fi._name_index = checked_cast<u2>(next_uint());
|
read_name_and_signature(&fi._name_index, &fi._signature_index);
|
||||||
fi._signature_index = checked_cast<u2>(next_uint());
|
|
||||||
fi._offset = next_uint();
|
fi._offset = next_uint();
|
||||||
fi._access_flags = AccessFlags(checked_cast<u2>(next_uint()));
|
fi._access_flags = AccessFlags(checked_cast<u2>(next_uint()));
|
||||||
fi._field_flags = FieldInfo::FieldFlags(next_uint());
|
fi._field_flags = FieldInfo::FieldFlags(next_uint());
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -56,17 +56,23 @@ class FieldStreamBase : public StackObj {
|
|||||||
|
|
||||||
inline FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit);
|
inline FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit);
|
||||||
|
|
||||||
inline FieldStreamBase(Array<u1>* fieldinfo_stream, ConstantPool* constants);
|
inline FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initialize() {
|
void initialize() {
|
||||||
int java_fields_count = _reader.next_uint();
|
int java_fields_count;
|
||||||
int injected_fields_count = _reader.next_uint();
|
int injected_fields_count;
|
||||||
assert( _limit <= java_fields_count + injected_fields_count, "Safety check");
|
_reader.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||||
|
if (_limit < _index) {
|
||||||
|
_limit = java_fields_count + injected_fields_count;
|
||||||
|
} else {
|
||||||
|
assert( _limit <= java_fields_count + injected_fields_count, "Safety check");
|
||||||
|
}
|
||||||
if (_limit != 0) {
|
if (_limit != 0) {
|
||||||
_reader.read_field_info(_fi_buf);
|
_reader.read_field_info(_fi_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline FieldStreamBase(InstanceKlass* klass);
|
inline FieldStreamBase(InstanceKlass* klass);
|
||||||
|
|
||||||
@ -138,8 +144,11 @@ class FieldStreamBase : public StackObj {
|
|||||||
|
|
||||||
// Iterate over only the Java fields
|
// Iterate over only the Java fields
|
||||||
class JavaFieldStream : public FieldStreamBase {
|
class JavaFieldStream : public FieldStreamBase {
|
||||||
|
Array<u1>* _search_table;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JavaFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants(), 0, k->java_fields_count()) {}
|
JavaFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants(), 0, k->java_fields_count()),
|
||||||
|
_search_table(k->fieldinfo_search_table()) {}
|
||||||
|
|
||||||
u2 name_index() const {
|
u2 name_index() const {
|
||||||
assert(!field()->field_flags().is_injected(), "regular only");
|
assert(!field()->field_flags().is_injected(), "regular only");
|
||||||
@ -149,7 +158,6 @@ class JavaFieldStream : public FieldStreamBase {
|
|||||||
u2 signature_index() const {
|
u2 signature_index() const {
|
||||||
assert(!field()->field_flags().is_injected(), "regular only");
|
assert(!field()->field_flags().is_injected(), "regular only");
|
||||||
return field()->signature_index();
|
return field()->signature_index();
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u2 generic_signature_index() const {
|
u2 generic_signature_index() const {
|
||||||
@ -164,6 +172,10 @@ class JavaFieldStream : public FieldStreamBase {
|
|||||||
assert(!field()->field_flags().is_injected(), "regular only");
|
assert(!field()->field_flags().is_injected(), "regular only");
|
||||||
return field()->initializer_index();
|
return field()->initializer_index();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Performs either a linear search or binary search through the stream
|
||||||
|
// looking for a matching name/signature combo
|
||||||
|
bool lookup(const Symbol* name, const Symbol* signature);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -176,7 +188,6 @@ class InternalFieldStream : public FieldStreamBase {
|
|||||||
|
|
||||||
class AllFieldStream : public FieldStreamBase {
|
class AllFieldStream : public FieldStreamBase {
|
||||||
public:
|
public:
|
||||||
AllFieldStream(Array<u1>* fieldinfo, ConstantPool* constants): FieldStreamBase(fieldinfo, constants) {}
|
|
||||||
AllFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants()) {}
|
AllFieldStream(const InstanceKlass* k): FieldStreamBase(k->fieldinfo_stream(), k->constants()) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -33,22 +33,18 @@
|
|||||||
FieldStreamBase::FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit) :
|
FieldStreamBase::FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants, int start, int limit) :
|
||||||
_fieldinfo_stream(fieldinfo_stream),
|
_fieldinfo_stream(fieldinfo_stream),
|
||||||
_reader(FieldInfoReader(_fieldinfo_stream)),
|
_reader(FieldInfoReader(_fieldinfo_stream)),
|
||||||
_constants(constantPoolHandle(Thread::current(), constants)), _index(start) {
|
_constants(constantPoolHandle(Thread::current(), constants)),
|
||||||
_index = start;
|
_index(start),
|
||||||
if (limit < start) {
|
_limit(limit) {
|
||||||
_limit = FieldInfoStream::num_total_fields(_fieldinfo_stream);
|
|
||||||
} else {
|
|
||||||
_limit = limit;
|
|
||||||
}
|
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldStreamBase::FieldStreamBase(Array<u1>* fieldinfo_stream, ConstantPool* constants) :
|
FieldStreamBase::FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants) :
|
||||||
_fieldinfo_stream(fieldinfo_stream),
|
_fieldinfo_stream(fieldinfo_stream),
|
||||||
_reader(FieldInfoReader(_fieldinfo_stream)),
|
_reader(FieldInfoReader(_fieldinfo_stream)),
|
||||||
_constants(constantPoolHandle(Thread::current(), constants)),
|
_constants(constantPoolHandle(Thread::current(), constants)),
|
||||||
_index(0),
|
_index(0),
|
||||||
_limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
|
_limit(-1) {
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,9 +53,28 @@ FieldStreamBase::FieldStreamBase(InstanceKlass* klass) :
|
|||||||
_reader(FieldInfoReader(_fieldinfo_stream)),
|
_reader(FieldInfoReader(_fieldinfo_stream)),
|
||||||
_constants(constantPoolHandle(Thread::current(), klass->constants())),
|
_constants(constantPoolHandle(Thread::current(), klass->constants())),
|
||||||
_index(0),
|
_index(0),
|
||||||
_limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
|
_limit(-1) {
|
||||||
assert(klass == field_holder(), "");
|
assert(klass == field_holder(), "");
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool JavaFieldStream::lookup(const Symbol* name, const Symbol* signature) {
|
||||||
|
if (_search_table != nullptr) {
|
||||||
|
int index = _reader.search_table_lookup(_search_table, name, signature, _constants(), _limit);
|
||||||
|
if (index >= 0) {
|
||||||
|
assert(index < _limit, "must be");
|
||||||
|
_index = index;
|
||||||
|
_reader.read_field_info(_fi_buf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (; !done(); next()) {
|
||||||
|
if (this->name() == name && this->signature() == signature) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // SHARE_OOPS_FIELDSTREAMS_INLINE_HPP
|
#endif // SHARE_OOPS_FIELDSTREAMS_INLINE_HPP
|
||||||
|
@ -686,6 +686,11 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
|
|||||||
}
|
}
|
||||||
set_fieldinfo_stream(nullptr);
|
set_fieldinfo_stream(nullptr);
|
||||||
|
|
||||||
|
if (fieldinfo_search_table() != nullptr && !fieldinfo_search_table()->is_shared()) {
|
||||||
|
MetadataFactory::free_array<u1>(loader_data, fieldinfo_search_table());
|
||||||
|
}
|
||||||
|
set_fieldinfo_search_table(nullptr);
|
||||||
|
|
||||||
if (fields_status() != nullptr && !fields_status()->is_shared()) {
|
if (fields_status() != nullptr && !fields_status()->is_shared()) {
|
||||||
MetadataFactory::free_array<FieldStatus>(loader_data, fields_status());
|
MetadataFactory::free_array<FieldStatus>(loader_data, fields_status());
|
||||||
}
|
}
|
||||||
@ -1786,13 +1791,12 @@ FieldInfo InstanceKlass::field(int index) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
|
bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
|
||||||
for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
|
JavaFieldStream fs(this);
|
||||||
Symbol* f_name = fs.name();
|
if (fs.lookup(name, sig)) {
|
||||||
Symbol* f_sig = fs.signature();
|
assert(fs.name() == name, "name must match");
|
||||||
if (f_name == name && f_sig == sig) {
|
assert(fs.signature() == sig, "signature must match");
|
||||||
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.to_FieldInfo());
|
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.to_FieldInfo());
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2610,6 +2614,7 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it->push(&_fieldinfo_stream);
|
it->push(&_fieldinfo_stream);
|
||||||
|
it->push(&_fieldinfo_search_table);
|
||||||
// _fields_status might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update()
|
// _fields_status might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update()
|
||||||
it->push(&_fields_status, MetaspaceClosure::_writable);
|
it->push(&_fields_status, MetaspaceClosure::_writable);
|
||||||
|
|
||||||
@ -2710,6 +2715,8 @@ void InstanceKlass::remove_unshareable_info() {
|
|||||||
DEBUG_ONLY(_shared_class_load_count = 0);
|
DEBUG_ONLY(_shared_class_load_count = 0);
|
||||||
|
|
||||||
remove_unshareable_flags();
|
remove_unshareable_flags();
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceKlass::remove_unshareable_flags() {
|
void InstanceKlass::remove_unshareable_flags() {
|
||||||
@ -2816,6 +2823,8 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
|
|||||||
if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation() && !is_value_based()) {
|
if (DiagnoseSyncOnValueBasedClasses && has_value_based_class_annotation() && !is_value_based()) {
|
||||||
set_is_value_based();
|
set_is_value_based();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a class or any of its supertypes has a version older than 50.
|
// Check if a class or any of its supertypes has a version older than 50.
|
||||||
@ -3760,6 +3769,11 @@ void InstanceKlass::print_on(outputStream* st) const {
|
|||||||
map++;
|
map++;
|
||||||
}
|
}
|
||||||
st->cr();
|
st->cr();
|
||||||
|
|
||||||
|
if (fieldinfo_search_table() != nullptr) {
|
||||||
|
st->print_cr(BULLET"---- field info search table:");
|
||||||
|
FieldInfoStream::print_search_table(st, _constants, _fieldinfo_stream, _fieldinfo_search_table);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceKlass::print_value_on(outputStream* st) const {
|
void InstanceKlass::print_value_on(outputStream* st) const {
|
||||||
|
@ -276,6 +276,7 @@ class InstanceKlass: public Klass {
|
|||||||
|
|
||||||
// Fields information is stored in an UNSIGNED5 encoded stream (see fieldInfo.hpp)
|
// Fields information is stored in an UNSIGNED5 encoded stream (see fieldInfo.hpp)
|
||||||
Array<u1>* _fieldinfo_stream;
|
Array<u1>* _fieldinfo_stream;
|
||||||
|
Array<u1>* _fieldinfo_search_table;
|
||||||
Array<FieldStatus>* _fields_status;
|
Array<FieldStatus>* _fields_status;
|
||||||
|
|
||||||
// embedded Java vtable follows here
|
// embedded Java vtable follows here
|
||||||
@ -398,6 +399,9 @@ class InstanceKlass: public Klass {
|
|||||||
Array<u1>* fieldinfo_stream() const { return _fieldinfo_stream; }
|
Array<u1>* fieldinfo_stream() const { return _fieldinfo_stream; }
|
||||||
void set_fieldinfo_stream(Array<u1>* fis) { _fieldinfo_stream = fis; }
|
void set_fieldinfo_stream(Array<u1>* fis) { _fieldinfo_stream = fis; }
|
||||||
|
|
||||||
|
Array<u1>* fieldinfo_search_table() const { return _fieldinfo_search_table; }
|
||||||
|
void set_fieldinfo_search_table(Array<u1>* table) { _fieldinfo_search_table = table; }
|
||||||
|
|
||||||
Array<FieldStatus>* fields_status() const {return _fields_status; }
|
Array<FieldStatus>* fields_status() const {return _fields_status; }
|
||||||
void set_fields_status(Array<FieldStatus>* array) { _fields_status = array; }
|
void set_fields_status(Array<FieldStatus>* array) { _fields_status = array; }
|
||||||
|
|
||||||
|
@ -678,10 +678,12 @@
|
|||||||
"Print progress during Iterative Global Value Numbering") \
|
"Print progress during Iterative Global Value Numbering") \
|
||||||
\
|
\
|
||||||
develop(uint, VerifyIterativeGVN, 0, \
|
develop(uint, VerifyIterativeGVN, 0, \
|
||||||
"Verify Iterative Global Value Numbering" \
|
"Verify Iterative Global Value Numbering =DCBA, with:" \
|
||||||
"=XY, with Y: verify Def-Use modifications during IGVN" \
|
" D: verify Node::Identity did not miss opportunities" \
|
||||||
" X: verify that type(n) == n->Value() after IGVN" \
|
" C: verify Node::Ideal did not miss opportunities" \
|
||||||
"X and Y in 0=off; 1=on") \
|
" B: verify that type(n) == n->Value() after IGVN" \
|
||||||
|
" A: verify Def-Use modifications during IGVN" \
|
||||||
|
"Each can be 0=off or 1=on") \
|
||||||
constraint(VerifyIterativeGVNConstraintFunc, AtParse) \
|
constraint(VerifyIterativeGVNConstraintFunc, AtParse) \
|
||||||
\
|
\
|
||||||
develop(bool, TraceCISCSpill, false, \
|
develop(bool, TraceCISCSpill, false, \
|
||||||
|
@ -6580,6 +6580,10 @@ bool LibraryCallKit::inline_vectorizedMismatch() {
|
|||||||
memory_phi = _gvn.transform(memory_phi);
|
memory_phi = _gvn.transform(memory_phi);
|
||||||
result_phi = _gvn.transform(result_phi);
|
result_phi = _gvn.transform(result_phi);
|
||||||
|
|
||||||
|
record_for_igvn(exit_block);
|
||||||
|
record_for_igvn(memory_phi);
|
||||||
|
record_for_igvn(result_phi);
|
||||||
|
|
||||||
set_control(exit_block);
|
set_control(exit_block);
|
||||||
set_all_memory(memory_phi);
|
set_all_memory(memory_phi);
|
||||||
set_result(result_phi);
|
set_result(result_phi);
|
||||||
|
@ -1072,7 +1072,11 @@ void PhaseIterGVN::optimize() {
|
|||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
void PhaseIterGVN::verify_optimize() {
|
void PhaseIterGVN::verify_optimize() {
|
||||||
if (is_verify_Value()) {
|
assert(_worklist.size() == 0, "igvn worklist must be empty before verify");
|
||||||
|
|
||||||
|
if (is_verify_Value() ||
|
||||||
|
is_verify_Ideal() ||
|
||||||
|
is_verify_Identity()) {
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
Unique_Node_List worklist;
|
Unique_Node_List worklist;
|
||||||
bool failure = false;
|
bool failure = false;
|
||||||
@ -1080,7 +1084,10 @@ void PhaseIterGVN::verify_optimize() {
|
|||||||
worklist.push(C->root());
|
worklist.push(C->root());
|
||||||
for (uint j = 0; j < worklist.size(); ++j) {
|
for (uint j = 0; j < worklist.size(); ++j) {
|
||||||
Node* n = worklist.at(j);
|
Node* n = worklist.at(j);
|
||||||
failure |= verify_node_value(n);
|
if (is_verify_Value()) { failure |= verify_Value_for(n); }
|
||||||
|
if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, false); }
|
||||||
|
if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, true); }
|
||||||
|
if (is_verify_Identity()) { failure |= verify_Identity_for(n); }
|
||||||
// traverse all inputs and outputs
|
// traverse all inputs and outputs
|
||||||
for (uint i = 0; i < n->req(); i++) {
|
for (uint i = 0; i < n->req(); i++) {
|
||||||
if (n->in(i) != nullptr) {
|
if (n->in(i) != nullptr) {
|
||||||
@ -1097,6 +1104,27 @@ void PhaseIterGVN::verify_optimize() {
|
|||||||
// in the verification code above if that is not possible for some reason (like Load nodes).
|
// in the verification code above if that is not possible for some reason (like Load nodes).
|
||||||
assert(!failure, "Missed optimization opportunity in PhaseIterGVN");
|
assert(!failure, "Missed optimization opportunity in PhaseIterGVN");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verify_empty_worklist(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhaseIterGVN::verify_empty_worklist(Node* node) {
|
||||||
|
// Verify that the igvn worklist is empty. If no optimization happened, then
|
||||||
|
// nothing needs to be on the worklist.
|
||||||
|
if (_worklist.size() == 0) { return; }
|
||||||
|
|
||||||
|
stringStream ss; // Print as a block without tty lock.
|
||||||
|
for (uint j = 0; j < _worklist.size(); j++) {
|
||||||
|
Node* n = _worklist.at(j);
|
||||||
|
ss.print("igvn.worklist[%d] ", j);
|
||||||
|
n->dump("\n", false, &ss);
|
||||||
|
}
|
||||||
|
if (_worklist.size() != 0 && node != nullptr) {
|
||||||
|
ss.print_cr("Previously optimized:");
|
||||||
|
node->dump("\n", false, &ss);
|
||||||
|
}
|
||||||
|
tty->print_cr("%s", ss.as_string());
|
||||||
|
assert(false, "igvn worklist must still be empty after verify");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that type(n) == n->Value(), return true if we have a failure.
|
// Check that type(n) == n->Value(), return true if we have a failure.
|
||||||
@ -1104,7 +1132,7 @@ void PhaseIterGVN::verify_optimize() {
|
|||||||
// (1) Integer "widen" changes, but the range is the same.
|
// (1) Integer "widen" changes, but the range is the same.
|
||||||
// (2) LoadNode performs deep traversals. Load is not notified for changes far away.
|
// (2) LoadNode performs deep traversals. Load is not notified for changes far away.
|
||||||
// (3) CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away.
|
// (3) CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away.
|
||||||
bool PhaseIterGVN::verify_node_value(Node* n) {
|
bool PhaseIterGVN::verify_Value_for(Node* n) {
|
||||||
// If we assert inside type(n), because the type is still a null, then maybe
|
// If we assert inside type(n), because the type is still a null, then maybe
|
||||||
// the node never went through gvn.transform, which would be a bug.
|
// the node never went through gvn.transform, which would be a bug.
|
||||||
const Type* told = type(n);
|
const Type* told = type(n);
|
||||||
@ -1149,15 +1177,873 @@ bool PhaseIterGVN::verify_node_value(Node* n) {
|
|||||||
// after loop-opts, so that should take care of many of these cases.
|
// after loop-opts, so that should take care of many of these cases.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
tty->cr();
|
|
||||||
tty->print_cr("Missed Value optimization:");
|
stringStream ss; // Print as a block without tty lock.
|
||||||
n->dump_bfs(1, nullptr, "");
|
ss.cr();
|
||||||
tty->print_cr("Current type:");
|
ss.print_cr("Missed Value optimization:");
|
||||||
told->dump_on(tty);
|
n->dump_bfs(1, nullptr, "", &ss);
|
||||||
tty->cr();
|
ss.print_cr("Current type:");
|
||||||
tty->print_cr("Optimized type:");
|
told->dump_on(&ss);
|
||||||
tnew->dump_on(tty);
|
ss.cr();
|
||||||
tty->cr();
|
ss.print_cr("Optimized type:");
|
||||||
|
tnew->dump_on(&ss);
|
||||||
|
ss.cr();
|
||||||
|
tty->print_cr("%s", ss.as_string());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all Ideal optimizations that could be done were done.
|
||||||
|
// Returns true if it found missed optimization opportunities and
|
||||||
|
// false otherwise (no missed optimization, or skipped verification).
|
||||||
|
bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) {
|
||||||
|
// First, we check a list of exceptions, where we skip verification,
|
||||||
|
// because there are known cases where Ideal can optimize after IGVN.
|
||||||
|
// Some may be expected and cannot be fixed, and others should be fixed.
|
||||||
|
switch (n->Opcode()) {
|
||||||
|
// RangeCheckNode::Ideal looks up the chain for about 999 nodes
|
||||||
|
// (see "Range-Check scan limit"). So, it is possible that something
|
||||||
|
// is optimized in that input subgraph, and the RangeCheck was not
|
||||||
|
// added to the worklist because it would be too expensive to walk
|
||||||
|
// down the graph for 1000 nodes and put all on the worklist.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xbatch --version
|
||||||
|
case Op_RangeCheck:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// IfNode::Ideal does:
|
||||||
|
// Node* prev_dom = search_identical(dist, igvn);
|
||||||
|
// which means we seach up the CFG, traversing at most up to a distance.
|
||||||
|
// If anything happens rather far away from the If, we may not put the If
|
||||||
|
// back on the worklist.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
|
||||||
|
case Op_If:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// IfNode::simple_subsuming
|
||||||
|
// Looks for dominating test that subsumes the current test.
|
||||||
|
// Notification could be difficult because of larger distance.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// runtime/exceptionMsgs/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsExceptionTest.java#id1
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_CountedLoopEnd:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// LongCountedLoopEndNode::Ideal
|
||||||
|
// Probably same issue as above.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/predicates/assertion/TestAssertionPredicates.java#NoLoopPredicationXbatch
|
||||||
|
// -XX:StressLongCountedLoop=2000000 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_LongCountedLoopEnd:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// RegionNode::Ideal does "Skip around the useless IF diamond".
|
||||||
|
// 245 IfTrue === 244
|
||||||
|
// 258 If === 245 257
|
||||||
|
// 259 IfTrue === 258 [[ 263 ]]
|
||||||
|
// 260 IfFalse === 258 [[ 263 ]]
|
||||||
|
// 263 Region === 263 260 259 [[ 263 268 ]]
|
||||||
|
// to
|
||||||
|
// 245 IfTrue === 244
|
||||||
|
// 263 Region === 263 245 _ [[ 263 268 ]]
|
||||||
|
//
|
||||||
|
// "Useless" means that there is no code in either branch of the If.
|
||||||
|
// I found a case where this was not done yet during IGVN.
|
||||||
|
// Why does the Region not get added to IGVN worklist when the If diamond becomes useless?
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
|
||||||
|
case Op_Region:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// In AddNode::Ideal, we call "commute", which swaps the inputs so
|
||||||
|
// that smaller idx are first. Tracking it back, it led me to
|
||||||
|
// PhaseIdealLoop::remix_address_expressions which swapped the edges.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// Before PhaseIdealLoop::remix_address_expressions
|
||||||
|
// 154 AddI === _ 12 144
|
||||||
|
// After PhaseIdealLoop::remix_address_expressions
|
||||||
|
// 154 AddI === _ 144 12
|
||||||
|
// After AddNode::Ideal
|
||||||
|
// 154 AddI === _ 12 144
|
||||||
|
//
|
||||||
|
// I suspect that the node should be added to the IGVN worklist after
|
||||||
|
// PhaseIdealLoop::remix_address_expressions
|
||||||
|
//
|
||||||
|
// This is the only case I looked at, there may be others. Found like this:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xbatch --version
|
||||||
|
//
|
||||||
|
// The following hit the same logic in PhaseIdealLoop::remix_address_expressions.
|
||||||
|
//
|
||||||
|
// Note: currently all of these fail also for other reasons, for example
|
||||||
|
// because of "commute" doing the reordering with the phi below. Once
|
||||||
|
// that is resolved, we can come back to this issue here.
|
||||||
|
//
|
||||||
|
// case Op_AddD:
|
||||||
|
// case Op_AddI:
|
||||||
|
// case Op_AddL:
|
||||||
|
// case Op_AddF:
|
||||||
|
// case Op_MulI:
|
||||||
|
// case Op_MulL:
|
||||||
|
// case Op_MulF:
|
||||||
|
// case Op_MulD:
|
||||||
|
// if (n->in(1)->_idx > n->in(2)->_idx) {
|
||||||
|
// // Expect "commute" to revert this case.
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// break; // keep verifying
|
||||||
|
|
||||||
|
// AddFNode::Ideal calls "commute", which can reorder the inputs for this:
|
||||||
|
// Check for tight loop increments: Loop-phi of Add of loop-phi
|
||||||
|
// It wants to take the phi into in(1):
|
||||||
|
// 471 Phi === 435 38 390
|
||||||
|
// 390 AddF === _ 471 391
|
||||||
|
//
|
||||||
|
// Other Associative operators are also affected equally.
|
||||||
|
//
|
||||||
|
// Investigate why this does not happen earlier during IGVN.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_AddD:
|
||||||
|
//case Op_AddI: // Also affected for other reasons, see case further down.
|
||||||
|
//case Op_AddL: // Also affected for other reasons, see case further down.
|
||||||
|
case Op_AddF:
|
||||||
|
case Op_MulI:
|
||||||
|
case Op_MulL:
|
||||||
|
case Op_MulF:
|
||||||
|
case Op_MulD:
|
||||||
|
case Op_MinF:
|
||||||
|
case Op_MinD:
|
||||||
|
case Op_MaxF:
|
||||||
|
case Op_MaxD:
|
||||||
|
// XorINode::Ideal
|
||||||
|
// Found with:
|
||||||
|
// compiler/intrinsics/chacha/TestChaCha20.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_XorI:
|
||||||
|
case Op_XorL:
|
||||||
|
// It seems we may have similar issues with the HF cases.
|
||||||
|
// Found with aarch64:
|
||||||
|
// compiler/vectorization/TestFloat16VectorOperations.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_AddHF:
|
||||||
|
case Op_MulHF:
|
||||||
|
case Op_MaxHF:
|
||||||
|
case Op_MinHF:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// In MulNode::Ideal the edges can be swapped to help value numbering:
|
||||||
|
//
|
||||||
|
// // We are OK if right is a constant, or right is a load and
|
||||||
|
// // left is a non-constant.
|
||||||
|
// if( !(t2->singleton() ||
|
||||||
|
// (in(2)->is_Load() && !(t1->singleton() || in(1)->is_Load())) ) ) {
|
||||||
|
// if( t1->singleton() || // Left input is a constant?
|
||||||
|
// // Otherwise, sort inputs (commutativity) to help value numbering.
|
||||||
|
// (in(1)->_idx > in(2)->_idx) ) {
|
||||||
|
// swap_edges(1, 2);
|
||||||
|
//
|
||||||
|
// Why was this not done earlier during IGVN?
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_AndI:
|
||||||
|
// Same for AndL.
|
||||||
|
// Found with:
|
||||||
|
// compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_AndL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// SubLNode::Ideal does transform like:
|
||||||
|
// Convert "c1 - (y+c0)" into "(c1-c0) - y"
|
||||||
|
//
|
||||||
|
// In IGVN before verification:
|
||||||
|
// 8423 ConvI2L === _ 3519 [[ 8424 ]] #long:-2
|
||||||
|
// 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www
|
||||||
|
// 8424 AddL === _ 8422 8423 [[ 8383 ]] !orig=[8382]
|
||||||
|
// 8016 ConL === 0 [[ 8383 ]] #long:0
|
||||||
|
// 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154]
|
||||||
|
//
|
||||||
|
// And then in verification:
|
||||||
|
// 8338 ConL === 0 [[ 8339 8424 ]] #long:-2 <----- Was constant folded.
|
||||||
|
// 8422 ConvI2L === _ 8399 [[ 8424 ]] #long:3..256:www
|
||||||
|
// 8424 AddL === _ 8422 8338 [[ 8383 ]] !orig=[8382]
|
||||||
|
// 8016 ConL === 0 [[ 8383 ]] #long:0
|
||||||
|
// 8383 SubL === _ 8016 8424 [[ 8156 ]] !orig=[8154]
|
||||||
|
//
|
||||||
|
// So the form changed from:
|
||||||
|
// c1 - (y + [8423 ConvI2L])
|
||||||
|
// to
|
||||||
|
// c1 - (y + -2)
|
||||||
|
// but the SubL was not added to the IGVN worklist. Investigate why.
|
||||||
|
// There could be other issues too.
|
||||||
|
//
|
||||||
|
// There seems to be a related AddL IGVN optimization that triggers
|
||||||
|
// the same SubL optimization, so investigate that too.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
|
||||||
|
case Op_SubL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// SubINode::Ideal does
|
||||||
|
// Convert "x - (y+c0)" into "(x-y) - c0" AND
|
||||||
|
// Convert "c1 - (y+c0)" into "(c1-c0) - y"
|
||||||
|
//
|
||||||
|
// Investigate why this does not yet happen during IGVN.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// test/hotspot/jtreg/compiler/c2/IVTest.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_SubI:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// AddNode::IdealIL does transform like:
|
||||||
|
// Convert x + (con - y) into "(x - y) + con"
|
||||||
|
//
|
||||||
|
// In IGVN before verification:
|
||||||
|
// 8382 ConvI2L
|
||||||
|
// 8381 ConvI2L === _ 791 [[ 8383 ]] #long:0
|
||||||
|
// 8383 SubL === _ 8381 8382
|
||||||
|
// 8168 ConvI2L
|
||||||
|
// 8156 AddL === _ 8168 8383 [[ 8158 ]]
|
||||||
|
//
|
||||||
|
// And then in verification:
|
||||||
|
// 8424 AddL
|
||||||
|
// 8016 ConL === 0 [[ 8383 ]] #long:0 <--- Was constant folded.
|
||||||
|
// 8383 SubL === _ 8016 8424
|
||||||
|
// 8168 ConvI2L
|
||||||
|
// 8156 AddL === _ 8168 8383 [[ 8158 ]]
|
||||||
|
//
|
||||||
|
// So the form changed from:
|
||||||
|
// x + (ConvI2L(0) - [8382 ConvI2L])
|
||||||
|
// to
|
||||||
|
// x + (0 - [8424 AddL])
|
||||||
|
// but the AddL was not added to the IGVN worklist. Investigate why.
|
||||||
|
// There could be other issues, too. For example with "commute", see above.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
|
||||||
|
case Op_AddL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// SubTypeCheckNode::Ideal calls SubTypeCheckNode::verify_helper, which does
|
||||||
|
// Node* cmp = phase->transform(new CmpPNode(subklass, in(SuperKlass)));
|
||||||
|
// record_for_cleanup(cmp, phase);
|
||||||
|
// This verification code in the Ideal code creates new nodes, and checks
|
||||||
|
// if they fold in unexpected ways. This means some nodes are created and
|
||||||
|
// added to the worklist, even if the SubTypeCheck is not optimized. This
|
||||||
|
// goes agains the assumption of the verification here, which assumes that
|
||||||
|
// if the node is not optimized, then no new nodes should be created, and
|
||||||
|
// also no nodes should be added to the worklist.
|
||||||
|
// I see two options:
|
||||||
|
// 1) forbid what verify_helper does, because for each Ideal call it
|
||||||
|
// uses memory and that is suboptimal. But it is not clear how that
|
||||||
|
// verification can be done otherwise.
|
||||||
|
// 2) Special case the verification here. Probably the new nodes that
|
||||||
|
// were just created are dead, i.e. they are not connected down to
|
||||||
|
// root. We could verify that, and remove those nodes from the graph
|
||||||
|
// by setting all their inputs to nullptr. And of course we would
|
||||||
|
// have to remove those nodes from the worklist.
|
||||||
|
// Maybe there are other options too, I did not dig much deeper yet.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xbatch --version
|
||||||
|
case Op_SubTypeCheck:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// LoopLimitNode::Ideal when stride is constant power-of-2, we can do a lowering
|
||||||
|
// to other nodes: Conv, Add, Sub, Mul, And ...
|
||||||
|
//
|
||||||
|
// 107 ConI === 0 [[ ... ]] #int:2
|
||||||
|
// 84 LoadRange === _ 7 83
|
||||||
|
// 50 ConI === 0 [[ ... ]] #int:0
|
||||||
|
// 549 LoopLimit === _ 50 84 107
|
||||||
|
//
|
||||||
|
// I stepped backward, to see how the node was generated, and I found that it was
|
||||||
|
// created in PhaseIdealLoop::exact_limit and not changed since. It is added to the
|
||||||
|
// IGVN worklist. I quickly checked when it goes into LoopLimitNode::Ideal after
|
||||||
|
// that, and it seems we want to skip lowering it until after loop-opts, but never
|
||||||
|
// add call record_for_post_loop_opts_igvn. This would be an easy fix, but there
|
||||||
|
// could be other issues too.
|
||||||
|
//
|
||||||
|
// Fond with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
|
||||||
|
case Op_LoopLimit:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// PhiNode::Ideal calls split_flow_path, which tries to do this:
|
||||||
|
// "This optimization tries to find two or more inputs of phi with the same constant
|
||||||
|
// value. It then splits them into a separate Phi, and according Region."
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// 130 DecodeN === _ 129
|
||||||
|
// 50 ConP === 0 [[ 18 91 99 18 ]] #null
|
||||||
|
// 18 Phi === 14 50 130 50 [[ 133 ]] #java/lang/Object * Oop:java/lang/Object *
|
||||||
|
//
|
||||||
|
// turns into:
|
||||||
|
//
|
||||||
|
// 50 ConP === 0 [[ 99 91 18 ]] #null
|
||||||
|
// 130 DecodeN === _ 129 [[ 18 ]]
|
||||||
|
// 18 Phi === 14 130 50 [[ 133 ]] #java/lang/Object * Oop:java/lang/Object *
|
||||||
|
//
|
||||||
|
// We would have to investigate why this optimization does not happen during IGVN.
|
||||||
|
// There could also be other issues - I did not investigate further yet.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
|
||||||
|
case Op_Phi:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MemBarNode::Ideal does "Eliminate volatile MemBars for scalar replaced objects".
|
||||||
|
// For examle "The allocated object does not escape".
|
||||||
|
//
|
||||||
|
// It seems the difference to earlier calls to MemBarNode::Ideal, is that there
|
||||||
|
// alloc->as_Allocate()->does_not_escape_thread() returned false, but in verification
|
||||||
|
// it returned true. Why does the MemBarStoreStore not get added to the IGVN
|
||||||
|
// worklist when this change happens?
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
|
||||||
|
case Op_MemBarStoreStore:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// ConvI2LNode::Ideal converts
|
||||||
|
// 648 AddI === _ 583 645 [[ 661 ]]
|
||||||
|
// 661 ConvI2L === _ 648 [[ 664 ]] #long:0..maxint-1:www
|
||||||
|
// into
|
||||||
|
// 772 ConvI2L === _ 645 [[ 773 ]] #long:-120..maxint-61:www
|
||||||
|
// 771 ConvI2L === _ 583 [[ 773 ]] #long:60..120:www
|
||||||
|
// 773 AddL === _ 771 772 [[ ]]
|
||||||
|
//
|
||||||
|
// We have to investigate why this does not happen during IGVN in this case.
|
||||||
|
// There could also be other issues - I did not investigate further yet.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
|
||||||
|
case Op_ConvI2L:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// AddNode::IdealIL can do this transform (and similar other ones):
|
||||||
|
// Convert "a*b+a*c into a*(b+c)
|
||||||
|
// The example had AddI(MulI(a, b), MulI(a, c)). Why did this not happen
|
||||||
|
// during IGVN? There was a mutation for one of the MulI, and only
|
||||||
|
// after that the pattern was as needed for the optimization. The MulI
|
||||||
|
// was added to the IGVN worklist, but not the AddI. This probably
|
||||||
|
// can be fixed by adding the correct pattern in add_users_of_use_to_worklist.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_AddI:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// ArrayCopyNode::Ideal
|
||||||
|
// calls ArrayCopyNode::prepare_array_copy
|
||||||
|
// calls Compile::conv_I2X_index -> is called with sizetype = intcon(0), I think that
|
||||||
|
// is not expected, and we create a range int:0..-1
|
||||||
|
// calls Compile::constrained_convI2L -> creates ConvI2L(intcon(1), int:0..-1)
|
||||||
|
// note: the type is already empty!
|
||||||
|
// calls PhaseIterGVN::transform
|
||||||
|
// calls PhaseIterGVN::transform_old
|
||||||
|
// calls PhaseIterGVN::subsume_node -> subsume ConvI2L with TOP
|
||||||
|
// calls Unique_Node_List::push -> pushes TOP to worklist
|
||||||
|
//
|
||||||
|
// Once we get back to ArrayCopyNode::prepare_array_copy, we get back TOP, and
|
||||||
|
// return false. This means we eventually return nullptr from ArrayCopyNode::Ideal.
|
||||||
|
//
|
||||||
|
// Question: is it ok to push anything to the worklist during ::Ideal, if we will
|
||||||
|
// return nullptr, indicating nothing happened?
|
||||||
|
// Is it smart to do transform in Compile::constrained_convI2L, and then
|
||||||
|
// check for TOP in calls ArrayCopyNode::prepare_array_copy?
|
||||||
|
// Should we just allow TOP to land on the worklist, as an exception?
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/arraycopy/TestArrayCopyAsLoadsStores.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_ArrayCopy:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// CastLLNode::Ideal
|
||||||
|
// calls ConstraintCastNode::optimize_integer_cast -> pushes CastLL through SubL
|
||||||
|
//
|
||||||
|
// Could be a notification issue, where updates inputs of CastLL do not notify
|
||||||
|
// down through SubL to CastLL.
|
||||||
|
//
|
||||||
|
// Found With:
|
||||||
|
// compiler/c2/TestMergeStoresMemorySegment.java#byte-array
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_CastLL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Similar case happens to CastII
|
||||||
|
//
|
||||||
|
// Found With:
|
||||||
|
// compiler/c2/TestScalarReplacementMaxLiveNodes.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_CastII:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MaxLNode::Ideal
|
||||||
|
// calls AddNode::Ideal
|
||||||
|
// calls commute -> decides to swap edges
|
||||||
|
//
|
||||||
|
// Another notification issue, because we check inputs of inputs?
|
||||||
|
// MaxL -> Phi -> Loop
|
||||||
|
// MaxL -> Phi -> MaxL
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/c2/irTests/TestIfMinMax.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_MaxL:
|
||||||
|
case Op_MinL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// OrINode::Ideal
|
||||||
|
// calls AddNode::Ideal
|
||||||
|
// calls commute -> left is Load, right not -> commute.
|
||||||
|
//
|
||||||
|
// Not sure why notification does not work here, seems like
|
||||||
|
// the depth is only 1, so it should work. Needs investigation.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/codegen/TestCharVect2.java#id0
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_OrI:
|
||||||
|
case Op_OrL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Bool -> constant folded to 1.
|
||||||
|
// Issue with notification?
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/c2/irTests/TestVectorizationMismatchedAccess.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_Bool:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// LShiftLNode::Ideal
|
||||||
|
// Looks at pattern: "(x + x) << c0", converts it to "x << (c0 + 1)"
|
||||||
|
// Probably a notification issue.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/conversions/TestMoveConvI2LOrCastIIThruAddIs.java
|
||||||
|
// -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_LShiftL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// LShiftINode::Ideal
|
||||||
|
// pattern: ((x + con1) << con2) -> x << con2 + con1 << con2
|
||||||
|
// Could be issue with notification of inputs of inputs
|
||||||
|
//
|
||||||
|
// Side-note: should cases like these not be shared between
|
||||||
|
// LShiftI and LShiftL?
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/escapeAnalysis/Test6689060.java
|
||||||
|
// -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_LShiftI:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// AddPNode::Ideal seems to do set_req without removing lock first.
|
||||||
|
// Found with various vector tests tier1-tier3.
|
||||||
|
case Op_AddP:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// StrIndexOfNode::Ideal
|
||||||
|
// Found in tier1-3.
|
||||||
|
case Op_StrIndexOf:
|
||||||
|
case Op_StrIndexOfChar:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// StrEqualsNode::Identity
|
||||||
|
//
|
||||||
|
// Found (linux x64 only?) with:
|
||||||
|
// serviceability/sa/ClhsdbThreadContext.java
|
||||||
|
// -XX:+UnlockExperimentalVMOptions -XX:LockingMode=1 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_StrEquals:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// AryEqNode::Ideal
|
||||||
|
// Not investigated. Reshapes itself and adds lots of nodes to the worklist.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// vmTestbase/vm/mlvm/meth/stress/compiler/i2c_c2i/Test.java
|
||||||
|
// -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:+StressUnstableIfTraps -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_AryEq:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MergeMemNode::Ideal
|
||||||
|
// Found in tier1-3. Did not investigate further yet.
|
||||||
|
case Op_MergeMem:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// URShiftINode::Ideal
|
||||||
|
// Found in tier1-3. Did not investigate further yet.
|
||||||
|
case Op_URShiftI:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// CMoveINode::Ideal
|
||||||
|
// Found in tier1-3. Did not investigate further yet.
|
||||||
|
case Op_CMoveI:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// CmpPNode::Ideal calls isa_const_java_mirror
|
||||||
|
// and generates new constant nodes, even if no progress is made.
|
||||||
|
// We can probably rewrite this so that only types are generated.
|
||||||
|
// It seems that object types are not hashed, we could investigate
|
||||||
|
// if that is an option as well.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=1110 -Xcomp --version
|
||||||
|
case Op_CmpP:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MinINode::Ideal
|
||||||
|
// Did not investigate, but there are some patterns that might
|
||||||
|
// need more notification.
|
||||||
|
case Op_MinI:
|
||||||
|
case Op_MaxI: // preemptively removed it as well.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->is_Load()) {
|
||||||
|
// LoadNode::Ideal uses tries to find an earlier memory state, and
|
||||||
|
// checks can_see_stored_value for it.
|
||||||
|
//
|
||||||
|
// Investigate why this was not already done during IGVN.
|
||||||
|
// A similar issue happens with Identity.
|
||||||
|
//
|
||||||
|
// There seem to be other cases where loads go up some steps, like
|
||||||
|
// LoadNode::Ideal going up 10x steps to find dominating load.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// test/hotspot/jtreg/compiler/arraycopy/TestCloneAccess.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->is_Store()) {
|
||||||
|
// StoreNode::Ideal can do this:
|
||||||
|
// // Capture an unaliased, unconditional, simple store into an initializer.
|
||||||
|
// // Or, if it is independent of the allocation, hoist it above the allocation.
|
||||||
|
// That replaces the Store with a MergeMem.
|
||||||
|
//
|
||||||
|
// We have to investigate why this does not happen during IGVN in this case.
|
||||||
|
// There could also be other issues - I did not investigate further yet.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=0100 -Xcomp --version
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->is_Vector()) {
|
||||||
|
// VectorNode::Ideal swaps edges, but only for ops
|
||||||
|
// that are deemed commutable. But swap_edges
|
||||||
|
// requires the hash to be invariant when the edges
|
||||||
|
// are swapped, which is not implemented for these
|
||||||
|
// vector nodes. This seems not to create any trouble
|
||||||
|
// usually, but we can also get graphs where in the
|
||||||
|
// end the nodes are not all commuted, so there is
|
||||||
|
// definitively an issue here.
|
||||||
|
//
|
||||||
|
// Probably we have two options: kill the hash, or
|
||||||
|
// properly make the hash commutation friendly.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/vectorapi/TestMaskedMacroLogicVector.java
|
||||||
|
// -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -XX:+UseParallelGC -XX:+UseNUMA
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->is_Region()) {
|
||||||
|
// LoopNode::Ideal calls RegionNode::Ideal.
|
||||||
|
// CountedLoopNode::Ideal calls RegionNode::Ideal too.
|
||||||
|
// But I got an issue because RegionNode::optimize_trichotomy
|
||||||
|
// then modifies another node, and pushes nodes to the worklist
|
||||||
|
// Not sure if this is ok, modifying another node like that.
|
||||||
|
// Maybe it is, then we need to look into what to do with
|
||||||
|
// the nodes that are now on the worklist, maybe just clear
|
||||||
|
// them out again. But maybe modifying other nodes like that
|
||||||
|
// is also bad design. In the end, we return nullptr for
|
||||||
|
// the current CountedLoop. But the extra nodes on the worklist
|
||||||
|
// trip the asserts later on.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/eliminateAutobox/TestShortBoxing.java
|
||||||
|
// -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->is_CallJava()) {
|
||||||
|
// CallStaticJavaNode::Ideal
|
||||||
|
// Led to a crash:
|
||||||
|
// assert((is_CallStaticJava() && cg->is_mh_late_inline()) || (is_CallDynamicJava() && cg->is_virtual_late_inline())) failed: mismatch
|
||||||
|
//
|
||||||
|
// Did not investigate yet, could be a bug.
|
||||||
|
// Or maybe it does not expect to be called during verification.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// test/jdk/jdk/incubator/vector/VectorRuns.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
|
||||||
|
// CallDynamicJavaNode::Ideal, and I think also for CallStaticJavaNode::Ideal
|
||||||
|
// and possibly their subclasses.
|
||||||
|
// During late inlining it can call CallJavaNode::register_for_late_inline
|
||||||
|
// That means we do more rounds of late inlining, but might fail.
|
||||||
|
// Then we do IGVN again, and register the node again for late inlining.
|
||||||
|
// This creates an endless cycle. Everytime we try late inlining, we
|
||||||
|
// are also creating more nodes, especially SafePoint and MergeMem.
|
||||||
|
// These nodes are immediately rejected when the inlining fails in the
|
||||||
|
// do_late_inline_check, but they still grow the memory, until we hit
|
||||||
|
// the MemLimit and crash.
|
||||||
|
// The assumption here seems that CallDynamicJavaNode::Ideal does not get
|
||||||
|
// called repeatedly, and eventually we terminate. I fear this is not
|
||||||
|
// a great assumption to make. We should investigate more.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-U
|
||||||
|
// -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of nodes shoud not increase.
|
||||||
|
uint old_unique = C->unique();
|
||||||
|
|
||||||
|
Node* i = n->Ideal(this, can_reshape);
|
||||||
|
// If there was no new Idealization, we are probably happy.
|
||||||
|
if (i == nullptr) {
|
||||||
|
if (old_unique < C->unique()) {
|
||||||
|
stringStream ss; // Print as a block without tty lock.
|
||||||
|
ss.cr();
|
||||||
|
ss.print_cr("Ideal optimization did not make progress but created new unused nodes.");
|
||||||
|
ss.print_cr(" old_unique = %d, unique = %d", old_unique, C->unique());
|
||||||
|
n->dump_bfs(1, nullptr, "", &ss);
|
||||||
|
tty->print_cr("%s", ss.as_string());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_empty_worklist(n);
|
||||||
|
|
||||||
|
// Everything is good.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We just saw a new Idealization which was not done during IGVN.
|
||||||
|
stringStream ss; // Print as a block without tty lock.
|
||||||
|
ss.cr();
|
||||||
|
ss.print_cr("Missed Ideal optimization (can_reshape=%s):", can_reshape ? "true": "false");
|
||||||
|
if (i == n) {
|
||||||
|
ss.print_cr("The node was reshaped by Ideal.");
|
||||||
|
} else {
|
||||||
|
ss.print_cr("The node was replaced by Ideal.");
|
||||||
|
ss.print_cr("Old node:");
|
||||||
|
n->dump_bfs(1, nullptr, "", &ss);
|
||||||
|
}
|
||||||
|
ss.print_cr("The result after Ideal:");
|
||||||
|
i->dump_bfs(1, nullptr, "", &ss);
|
||||||
|
tty->print_cr("%s", ss.as_string());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all Identity optimizations that could be done were done.
|
||||||
|
// Returns true if it found missed optimization opportunities and
|
||||||
|
// false otherwise (no missed optimization, or skipped verification).
|
||||||
|
bool PhaseIterGVN::verify_Identity_for(Node* n) {
|
||||||
|
// First, we check a list of exceptions, where we skip verification,
|
||||||
|
// because there are known cases where Ideal can optimize after IGVN.
|
||||||
|
// Some may be expected and cannot be fixed, and others should be fixed.
|
||||||
|
switch (n->Opcode()) {
|
||||||
|
// SafePointNode::Identity can remove SafePoints, but wants to wait until
|
||||||
|
// after loopopts:
|
||||||
|
// // Transforming long counted loops requires a safepoint node. Do not
|
||||||
|
// // eliminate a safepoint until loop opts are over.
|
||||||
|
// if (in(0)->is_Proj() && !phase->C->major_progress()) {
|
||||||
|
//
|
||||||
|
// I think the check for major_progress does delay it until after loopopts
|
||||||
|
// but it does not ensure that the node is on the IGVN worklist after
|
||||||
|
// loopopts. I think we should try to instead check for
|
||||||
|
// phase->C->post_loop_opts_phase() and call record_for_post_loop_opts_igvn.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
|
||||||
|
case Op_SafePoint:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MergeMemNode::Identity replaces the MergeMem with its base_memory if it
|
||||||
|
// does not record any other memory splits.
|
||||||
|
//
|
||||||
|
// I did not deeply investigate, but it looks like MergeMemNode::Identity
|
||||||
|
// never got called during IGVN for this node, investigate why.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
|
||||||
|
case Op_MergeMem:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// ConstraintCastNode::Identity finds casts that are the same, except that
|
||||||
|
// the control is "higher up", i.e. dominates. The call goes via
|
||||||
|
// ConstraintCastNode::dominating_cast to PhaseGVN::is_dominator_helper,
|
||||||
|
// which traverses up to 100 idom steps. If anything gets optimized somewhere
|
||||||
|
// away from the cast, but within 100 idom steps, the cast may not be
|
||||||
|
// put on the IGVN worklist any more.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
|
||||||
|
case Op_CastPP:
|
||||||
|
case Op_CastII:
|
||||||
|
case Op_CastLL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Same issue for CheckCastPP, uses ConstraintCastNode::Identity and
|
||||||
|
// checks dominator, which may be changed, but too far up for notification
|
||||||
|
// to work.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/c2/irTests/TestSkeletonPredicates.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_CheckCastPP:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// In SubNode::Identity, we do:
|
||||||
|
// Convert "(X+Y) - Y" into X and "(X+Y) - X" into Y
|
||||||
|
// In the example, the AddI had an input replaced, the AddI is
|
||||||
|
// added to the IGVN worklist, but the SubI is one link further
|
||||||
|
// down and is not added. I checked add_users_of_use_to_worklist
|
||||||
|
// where I would expect the SubI would be added, and I cannot
|
||||||
|
// find the pattern, only this one:
|
||||||
|
// If changed AddI/SubI inputs, check CmpU for range check optimization.
|
||||||
|
//
|
||||||
|
// Fix this "notification" issue and check if there are any other
|
||||||
|
// issues.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
|
||||||
|
case Op_SubI:
|
||||||
|
case Op_SubL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// PhiNode::Identity checks for patterns like:
|
||||||
|
// r = (x != con) ? x : con;
|
||||||
|
// that can be constant folded to "x".
|
||||||
|
//
|
||||||
|
// Call goes through PhiNode::is_cmove_id and CMoveNode::is_cmove_id.
|
||||||
|
// I suspect there was some earlier change to one of the inputs, but
|
||||||
|
// not all relevant outputs were put on the IGVN worklist.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_Phi:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// ConvI2LNode::Identity does
|
||||||
|
// convert I2L(L2I(x)) => x
|
||||||
|
//
|
||||||
|
// Investigate why this did not already happen during IGVN.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-A
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_ConvI2L:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MaxNode::find_identity_operation
|
||||||
|
// Finds patterns like Max(A, Max(A, B)) -> Max(A, B)
|
||||||
|
// This can be a 2-hop search, so maybe notification is not
|
||||||
|
// good enough.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/codegen/TestBooleanVect.java
|
||||||
|
// -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_MaxL:
|
||||||
|
case Op_MinL:
|
||||||
|
case Op_MaxI:
|
||||||
|
case Op_MinI:
|
||||||
|
case Op_MaxF:
|
||||||
|
case Op_MinF:
|
||||||
|
case Op_MaxHF:
|
||||||
|
case Op_MinHF:
|
||||||
|
case Op_MaxD:
|
||||||
|
case Op_MinD:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
// AddINode::Identity
|
||||||
|
// Converts (x-y)+y to x
|
||||||
|
// Could be issue with notification
|
||||||
|
//
|
||||||
|
// Turns out AddL does the same.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// compiler/c2/Test6792161.java
|
||||||
|
// -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
|
||||||
|
case Op_AddI:
|
||||||
|
case Op_AddL:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// AbsINode::Identity
|
||||||
|
// Not investigated yet.
|
||||||
|
case Op_AbsI:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->is_Load()) {
|
||||||
|
// LoadNode::Identity tries to look for an earlier store value via
|
||||||
|
// can_see_stored_value. I found an example where this led to
|
||||||
|
// an Allocation, where we could assume the value was still zero.
|
||||||
|
// So the LoadN can be replaced with a zerocon.
|
||||||
|
//
|
||||||
|
// Investigate why this was not already done during IGVN.
|
||||||
|
// A similar issue happens with Ideal.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// java -XX:VerifyIterativeGVN=1000 -Xcomp --version
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->is_Store()) {
|
||||||
|
// StoreNode::Identity
|
||||||
|
// Not investigated, but found missing optimization for StoreI.
|
||||||
|
// Looks like a StoreI is replaced with an InitializeNode.
|
||||||
|
//
|
||||||
|
// Found with:
|
||||||
|
// applications/ctw/modules/java_base_2.java
|
||||||
|
// -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -Djava.awt.headless=true -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->is_Vector()) {
|
||||||
|
// Found with tier1-3. Not investigated yet.
|
||||||
|
// The observed issue was with AndVNode::Identity
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* i = n->Identity(this);
|
||||||
|
// If we cannot find any other Identity, we are happy.
|
||||||
|
if (i == n) {
|
||||||
|
verify_empty_worklist(n);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The verification just found a new Identity that was not found during IGVN.
|
||||||
|
stringStream ss; // Print as a block without tty lock.
|
||||||
|
ss.cr();
|
||||||
|
ss.print_cr("Missed Identity optimization:");
|
||||||
|
ss.print_cr("Old node:");
|
||||||
|
n->dump_bfs(1, nullptr, "", &ss);
|
||||||
|
ss.print_cr("New node:");
|
||||||
|
i->dump_bfs(1, nullptr, "", &ss);
|
||||||
|
tty->print_cr("%s", ss.as_string());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1890,12 +2776,12 @@ void PhaseCCP::analyze() {
|
|||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
// For every node n on verify list, check if type(n) == n->Value()
|
// For every node n on verify list, check if type(n) == n->Value()
|
||||||
// We have a list of exceptions, see comments in verify_node_value.
|
// We have a list of exceptions, see comments in verify_Value_for.
|
||||||
void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) {
|
void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) {
|
||||||
bool failure = false;
|
bool failure = false;
|
||||||
while (worklist_verify.size()) {
|
while (worklist_verify.size()) {
|
||||||
Node* n = worklist_verify.pop();
|
Node* n = worklist_verify.pop();
|
||||||
failure |= verify_node_value(n);
|
failure |= verify_Value_for(n);
|
||||||
}
|
}
|
||||||
// If we get this assert, check why the reported nodes were not processed again in CCP.
|
// If we get this assert, check why the reported nodes were not processed again in CCP.
|
||||||
// We should either make sure that these nodes are properly added back to the CCP worklist
|
// We should either make sure that these nodes are properly added back to the CCP worklist
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -494,7 +494,10 @@ public:
|
|||||||
void optimize();
|
void optimize();
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
void verify_optimize();
|
void verify_optimize();
|
||||||
bool verify_node_value(Node* n);
|
bool verify_Value_for(Node* n);
|
||||||
|
bool verify_Ideal_for(Node* n, bool can_reshape);
|
||||||
|
bool verify_Identity_for(Node* n);
|
||||||
|
void verify_empty_worklist(Node* n);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
@ -593,6 +596,14 @@ public:
|
|||||||
// '-XX:VerifyIterativeGVN=10'
|
// '-XX:VerifyIterativeGVN=10'
|
||||||
return ((VerifyIterativeGVN % 100) / 10) == 1;
|
return ((VerifyIterativeGVN % 100) / 10) == 1;
|
||||||
}
|
}
|
||||||
|
static bool is_verify_Ideal() {
|
||||||
|
// '-XX:VerifyIterativeGVN=100'
|
||||||
|
return ((VerifyIterativeGVN % 1000) / 100) == 1;
|
||||||
|
}
|
||||||
|
static bool is_verify_Identity() {
|
||||||
|
// '-XX:VerifyIterativeGVN=1000'
|
||||||
|
return ((VerifyIterativeGVN % 10000) / 1000) == 1;
|
||||||
|
}
|
||||||
protected:
|
protected:
|
||||||
// Sub-quadratic implementation of '-XX:VerifyIterativeGVN=1' (Use-Def verification).
|
// Sub-quadratic implementation of '-XX:VerifyIterativeGVN=1' (Use-Def verification).
|
||||||
julong _verify_counter;
|
julong _verify_counter;
|
||||||
|
@ -12862,16 +12862,17 @@ myInit() {
|
|||||||
parameters uniquely identify the current location
|
parameters uniquely identify the current location
|
||||||
(where the exception was detected) and allow
|
(where the exception was detected) and allow
|
||||||
the mapping to source file and line number when that information is
|
the mapping to source file and line number when that information is
|
||||||
available. The <code>exception</code> field identifies the thrown
|
available. The <code>exception</code> parameter identifies the thrown
|
||||||
exception object. The <code>catch_method</code>
|
exception object. The <code>catch_method</code>
|
||||||
and <code>catch_location</code> identify the location of the catch clause,
|
and <code>catch_location</code> identify the location of the catch clause,
|
||||||
if any, that handles the thrown exception. If there is no such catch clause,
|
if any, that handles the thrown exception. If there is no such catch clause,
|
||||||
each field is set to 0. There is no guarantee that the thread will ever
|
the <code>catch_method</code> is set to null and the <code>catch_location</code>is set to 0.
|
||||||
|
There is no guarantee that the thread will ever
|
||||||
reach this catch clause. If there are native methods on the call stack
|
reach this catch clause. If there are native methods on the call stack
|
||||||
between the throw location and the catch clause, the exception may
|
between the throw location and the catch clause, the exception may
|
||||||
be reset by one of those native methods.
|
be reset by one of those native methods.
|
||||||
Similarly, exceptions that are reported as uncaught (<code>catch_klass</code>
|
Similarly, exceptions that are reported as uncaught (<code>catch_method</code>
|
||||||
et al. set to 0) may in fact be caught by native code.
|
set to null) may in fact be caught by native code.
|
||||||
Agents can check for these occurrences by monitoring
|
Agents can check for these occurrences by monitoring
|
||||||
<eventlink id="ExceptionCatch"></eventlink> events.
|
<eventlink id="ExceptionCatch"></eventlink> events.
|
||||||
Note that finally clauses are implemented as catch and re-throw. Therefore they
|
Note that finally clauses are implemented as catch and re-throw. Therefore they
|
||||||
@ -12960,7 +12961,7 @@ myInit() {
|
|||||||
available. For exceptions caught in a Java programming language method, the
|
available. For exceptions caught in a Java programming language method, the
|
||||||
<code>exception</code> object identifies the exception object. Exceptions
|
<code>exception</code> object identifies the exception object. Exceptions
|
||||||
caught in native methods are not necessarily available by the time the
|
caught in native methods are not necessarily available by the time the
|
||||||
exception catch is reported, so the <code>exception</code> field is set
|
exception catch is reported, so the <code>exception</code> parameter is set
|
||||||
to null.
|
to null.
|
||||||
</description>
|
</description>
|
||||||
<origin>jvmdi</origin>
|
<origin>jvmdi</origin>
|
||||||
|
@ -3550,6 +3550,13 @@ void VM_RedefineClasses::set_new_constant_pool(
|
|||||||
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, scratch_class->class_loader_data(), CHECK);
|
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, scratch_class->class_loader_data(), CHECK);
|
||||||
scratch_class->set_fieldinfo_stream(new_fis);
|
scratch_class->set_fieldinfo_stream(new_fis);
|
||||||
MetadataFactory::free_array<u1>(scratch_class->class_loader_data(), old_stream);
|
MetadataFactory::free_array<u1>(scratch_class->class_loader_data(), old_stream);
|
||||||
|
|
||||||
|
Array<u1>* old_table = scratch_class->fieldinfo_search_table();
|
||||||
|
Array<u1>* search_table = FieldInfoStream::create_search_table(scratch_class->constants(), new_fis, scratch_class->class_loader_data(), CHECK);
|
||||||
|
scratch_class->set_fieldinfo_search_table(search_table);
|
||||||
|
MetadataFactory::free_array<u1>(scratch_class->class_loader_data(), old_table);
|
||||||
|
|
||||||
|
DEBUG_ONLY(FieldInfoStream::validate_search_table(scratch_class->constants(), new_fis, search_table));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update constant pool indices in the inner classes info to use
|
// Update constant pool indices in the inner classes info to use
|
||||||
|
@ -1075,6 +1075,22 @@ bool WhiteBox::validate_cgroup(bool cgroups_v2_enabled,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool WhiteBox::is_asan_enabled() {
|
||||||
|
#ifdef ADDRESS_SANITIZER
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WhiteBox::is_ubsan_enabled() {
|
||||||
|
#ifdef UNDEFINED_BEHAVIOR_SANITIZER
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool WhiteBox::compile_method(Method* method, int comp_level, int bci, JavaThread* THREAD) {
|
bool WhiteBox::compile_method(Method* method, int comp_level, int bci, JavaThread* THREAD) {
|
||||||
// Screen for unavailable/bad comp level or null method
|
// Screen for unavailable/bad comp level or null method
|
||||||
AbstractCompiler* comp = CompileBroker::compiler(comp_level);
|
AbstractCompiler* comp = CompileBroker::compiler(comp_level);
|
||||||
@ -1886,6 +1902,14 @@ WB_ENTRY(jboolean, WB_IsMonitorInflated(JNIEnv* env, jobject wb, jobject obj))
|
|||||||
return (jboolean) obj_oop->mark().has_monitor();
|
return (jboolean) obj_oop->mark().has_monitor();
|
||||||
WB_END
|
WB_END
|
||||||
|
|
||||||
|
WB_ENTRY(jboolean, WB_IsAsanEnabled(JNIEnv* env))
|
||||||
|
return (jboolean) WhiteBox::is_asan_enabled();
|
||||||
|
WB_END
|
||||||
|
|
||||||
|
WB_ENTRY(jboolean, WB_IsUbsanEnabled(JNIEnv* env))
|
||||||
|
return (jboolean) WhiteBox::is_ubsan_enabled();
|
||||||
|
WB_END
|
||||||
|
|
||||||
WB_ENTRY(jlong, WB_getInUseMonitorCount(JNIEnv* env, jobject wb))
|
WB_ENTRY(jlong, WB_getInUseMonitorCount(JNIEnv* env, jobject wb))
|
||||||
return (jlong) WhiteBox::get_in_use_monitor_count();
|
return (jlong) WhiteBox::get_in_use_monitor_count();
|
||||||
WB_END
|
WB_END
|
||||||
@ -2882,6 +2906,8 @@ static JNINativeMethod methods[] = {
|
|||||||
(void*)&WB_AddModuleExportsToAll },
|
(void*)&WB_AddModuleExportsToAll },
|
||||||
{CC"deflateIdleMonitors", CC"()Z", (void*)&WB_DeflateIdleMonitors },
|
{CC"deflateIdleMonitors", CC"()Z", (void*)&WB_DeflateIdleMonitors },
|
||||||
{CC"isMonitorInflated0", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsMonitorInflated },
|
{CC"isMonitorInflated0", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsMonitorInflated },
|
||||||
|
{CC"isAsanEnabled", CC"()Z", (void*)&WB_IsAsanEnabled },
|
||||||
|
{CC"isUbsanEnabled", CC"()Z", (void*)&WB_IsUbsanEnabled },
|
||||||
{CC"getInUseMonitorCount", CC"()J", (void*)&WB_getInUseMonitorCount },
|
{CC"getInUseMonitorCount", CC"()J", (void*)&WB_getInUseMonitorCount },
|
||||||
{CC"getLockStackCapacity", CC"()I", (void*)&WB_getLockStackCapacity },
|
{CC"getLockStackCapacity", CC"()I", (void*)&WB_getLockStackCapacity },
|
||||||
{CC"supportsRecursiveLightweightLocking", CC"()Z", (void*)&WB_supportsRecursiveLightweightLocking },
|
{CC"supportsRecursiveLightweightLocking", CC"()Z", (void*)&WB_supportsRecursiveLightweightLocking },
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -72,6 +72,9 @@ class WhiteBox : public AllStatic {
|
|||||||
#ifdef LINUX
|
#ifdef LINUX
|
||||||
static bool validate_cgroup(bool cgroups_v2_enabled, const char* controllers_file, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags);
|
static bool validate_cgroup(bool cgroups_v2_enabled, const char* controllers_file, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags);
|
||||||
#endif
|
#endif
|
||||||
|
// provide info about enabling of Address Sanitizer / Undefined Behavior Sanitizer
|
||||||
|
static bool is_asan_enabled();
|
||||||
|
static bool is_ubsan_enabled();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_PRIMS_WHITEBOX_HPP
|
#endif // SHARE_PRIMS_WHITEBOX_HPP
|
||||||
|
@ -299,8 +299,9 @@ JVMFlag::Error TypeProfileLevelConstraintFunc(uint value, bool verbose) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JVMFlag::Error VerifyIterativeGVNConstraintFunc(uint value, bool verbose) {
|
JVMFlag::Error VerifyIterativeGVNConstraintFunc(uint value, bool verbose) {
|
||||||
|
const int max_modes = 4;
|
||||||
uint original_value = value;
|
uint original_value = value;
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < max_modes; i++) {
|
||||||
if (value % 10 > 1) {
|
if (value % 10 > 1) {
|
||||||
JVMFlag::printError(verbose,
|
JVMFlag::printError(verbose,
|
||||||
"Invalid value (" UINT32_FORMAT ") "
|
"Invalid value (" UINT32_FORMAT ") "
|
||||||
@ -312,7 +313,7 @@ JVMFlag::Error VerifyIterativeGVNConstraintFunc(uint value, bool verbose) {
|
|||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
JVMFlag::printError(verbose,
|
JVMFlag::printError(verbose,
|
||||||
"Invalid value (" UINT32_FORMAT ") "
|
"Invalid value (" UINT32_FORMAT ") "
|
||||||
"for VerifyIterativeGVN: maximal 2 digits\n", original_value);
|
"for VerifyIterativeGVN: maximal %d digits\n", original_value, max_modes);
|
||||||
return JVMFlag::VIOLATES_CONSTRAINT;
|
return JVMFlag::VIOLATES_CONSTRAINT;
|
||||||
}
|
}
|
||||||
return JVMFlag::SUCCESS;
|
return JVMFlag::SUCCESS;
|
||||||
|
@ -2005,6 +2005,10 @@ const int ObjectAlignmentInBytes = 8;
|
|||||||
product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC, \
|
product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC, \
|
||||||
"Use an extra lock during Thread start and exit to alleviate" \
|
"Use an extra lock during Thread start and exit to alleviate" \
|
||||||
"contention on Threads_lock.") \
|
"contention on Threads_lock.") \
|
||||||
|
\
|
||||||
|
develop(uint, BinarySearchThreshold, 16, \
|
||||||
|
"Minimal number of elements in a sorted collection to prefer" \
|
||||||
|
"binary search over simple linear search." ) \
|
||||||
|
|
||||||
// end of RUNTIME_FLAGS
|
// end of RUNTIME_FLAGS
|
||||||
|
|
||||||
|
@ -465,9 +465,7 @@ HandshakeState::HandshakeState(JavaThread* target) :
|
|||||||
_queue(),
|
_queue(),
|
||||||
_lock(Monitor::nosafepoint, "HandshakeState_lock"),
|
_lock(Monitor::nosafepoint, "HandshakeState_lock"),
|
||||||
_active_handshaker(),
|
_active_handshaker(),
|
||||||
_async_exceptions_blocked(false),
|
_async_exceptions_blocked(false) {
|
||||||
_suspended(false),
|
|
||||||
_async_suspend_handshake(false) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HandshakeState::~HandshakeState() {
|
HandshakeState::~HandshakeState() {
|
||||||
@ -699,128 +697,8 @@ HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* ma
|
|||||||
return op == match_op ? HandshakeState::_succeeded : HandshakeState::_processed;
|
return op == match_op ? HandshakeState::_succeeded : HandshakeState::_processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandshakeState::do_self_suspend() {
|
|
||||||
assert(Thread::current() == _handshakee, "should call from _handshakee");
|
|
||||||
assert(_lock.owned_by_self(), "Lock must be held");
|
|
||||||
assert(!_handshakee->has_last_Java_frame() || _handshakee->frame_anchor()->walkable(), "should have walkable stack");
|
|
||||||
assert(_handshakee->thread_state() == _thread_blocked, "Caller should have transitioned to _thread_blocked");
|
|
||||||
|
|
||||||
while (is_suspended()) {
|
|
||||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended", p2i(_handshakee));
|
|
||||||
_lock.wait_without_safepoint_check();
|
|
||||||
}
|
|
||||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " resumed", p2i(_handshakee));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the closure that prevents a suspended JavaThread from
|
|
||||||
// escaping the suspend request.
|
|
||||||
class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure {
|
|
||||||
public:
|
|
||||||
ThreadSelfSuspensionHandshake() : AsyncHandshakeClosure("ThreadSelfSuspensionHandshake") {}
|
|
||||||
void do_thread(Thread* thr) {
|
|
||||||
JavaThread* current = JavaThread::cast(thr);
|
|
||||||
assert(current == Thread::current(), "Must be self executed.");
|
|
||||||
JavaThreadState jts = current->thread_state();
|
|
||||||
|
|
||||||
current->set_thread_state(_thread_blocked);
|
|
||||||
current->handshake_state()->do_self_suspend();
|
|
||||||
current->set_thread_state(jts);
|
|
||||||
current->handshake_state()->set_async_suspend_handshake(false);
|
|
||||||
}
|
|
||||||
virtual bool is_suspend() { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
bool HandshakeState::suspend_with_handshake(bool register_vthread_SR) {
|
|
||||||
assert(_handshakee->threadObj() != nullptr, "cannot suspend with a null threadObj");
|
|
||||||
if (_handshakee->is_exiting()) {
|
|
||||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " exiting", p2i(_handshakee));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (has_async_suspend_handshake()) {
|
|
||||||
if (is_suspended()) {
|
|
||||||
// Target is already suspended.
|
|
||||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " already suspended", p2i(_handshakee));
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// Target is going to wake up and leave suspension.
|
|
||||||
// Let's just stop the thread from doing that.
|
|
||||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " re-suspended", p2i(_handshakee));
|
|
||||||
set_suspended(true, register_vthread_SR);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no suspend request
|
|
||||||
assert(!is_suspended(), "cannot be suspended without a suspend request");
|
|
||||||
// Thread is safe, so it must execute the request, thus we can count it as suspended
|
|
||||||
// from this point.
|
|
||||||
set_suspended(true, register_vthread_SR);
|
|
||||||
set_async_suspend_handshake(true);
|
|
||||||
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended, arming ThreadSuspension", p2i(_handshakee));
|
|
||||||
ThreadSelfSuspensionHandshake* ts = new ThreadSelfSuspensionHandshake();
|
|
||||||
Handshake::execute(ts, _handshakee);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the closure that synchronously honors the suspend request.
|
|
||||||
class SuspendThreadHandshake : public HandshakeClosure {
|
|
||||||
bool _register_vthread_SR;
|
|
||||||
bool _did_suspend;
|
|
||||||
public:
|
|
||||||
SuspendThreadHandshake(bool register_vthread_SR) : HandshakeClosure("SuspendThread"),
|
|
||||||
_register_vthread_SR(register_vthread_SR), _did_suspend(false) {}
|
|
||||||
void do_thread(Thread* thr) {
|
|
||||||
JavaThread* target = JavaThread::cast(thr);
|
|
||||||
_did_suspend = target->handshake_state()->suspend_with_handshake(_register_vthread_SR);
|
|
||||||
}
|
|
||||||
bool did_suspend() { return _did_suspend; }
|
|
||||||
};
|
|
||||||
|
|
||||||
bool HandshakeState::suspend(bool register_vthread_SR) {
|
|
||||||
JVMTI_ONLY(assert(!_handshakee->is_in_VTMS_transition(), "no suspend allowed in VTMS transition");)
|
|
||||||
JavaThread* self = JavaThread::current();
|
|
||||||
if (_handshakee == self) {
|
|
||||||
// If target is the current thread we can bypass the handshake machinery
|
|
||||||
// and just suspend directly
|
|
||||||
ThreadBlockInVM tbivm(self);
|
|
||||||
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
set_suspended(true, register_vthread_SR);
|
|
||||||
do_self_suspend();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
SuspendThreadHandshake st(register_vthread_SR);
|
|
||||||
Handshake::execute(&st, _handshakee);
|
|
||||||
return st.did_suspend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandshakeState::resume(bool register_vthread_SR) {
|
|
||||||
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
if (!is_suspended()) {
|
|
||||||
assert(!_handshakee->is_suspended(), "cannot be suspended without a suspend request");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Resume the thread.
|
|
||||||
set_suspended(false, register_vthread_SR);
|
|
||||||
_lock.notify();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandshakeState::set_suspended(bool is_suspend, bool register_vthread_SR) {
|
|
||||||
#if INCLUDE_JVMTI
|
|
||||||
if (register_vthread_SR) {
|
|
||||||
assert(_handshakee->is_vthread_mounted(), "sanity check");
|
|
||||||
if (is_suspend) {
|
|
||||||
JvmtiVTSuspender::register_vthread_suspend(_handshakee->vthread());
|
|
||||||
} else {
|
|
||||||
JvmtiVTSuspender::register_vthread_resume(_handshakee->vthread());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Atomic::store(&_suspended, is_suspend);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandshakeState::handle_unsafe_access_error() {
|
void HandshakeState::handle_unsafe_access_error() {
|
||||||
if (is_suspended()) {
|
if (_handshakee->is_suspended()) {
|
||||||
// A suspend handshake was added to the queue after the
|
// A suspend handshake was added to the queue after the
|
||||||
// unsafe access error. Since the suspender has already
|
// unsafe access error. Since the suspender has already
|
||||||
// considered this JT as suspended and assumes it won't go
|
// considered this JT as suspended and assumes it won't go
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -35,8 +35,6 @@
|
|||||||
class HandshakeOperation;
|
class HandshakeOperation;
|
||||||
class AsyncHandshakeOperation;
|
class AsyncHandshakeOperation;
|
||||||
class JavaThread;
|
class JavaThread;
|
||||||
class SuspendThreadHandshake;
|
|
||||||
class ThreadSelfSuspensionHandshake;
|
|
||||||
class UnsafeAccessErrorHandshake;
|
class UnsafeAccessErrorHandshake;
|
||||||
class ThreadsListHandle;
|
class ThreadsListHandle;
|
||||||
|
|
||||||
@ -88,8 +86,6 @@ class JvmtiRawMonitor;
|
|||||||
// operation is only done by either VMThread/Handshaker on behalf of the
|
// operation is only done by either VMThread/Handshaker on behalf of the
|
||||||
// JavaThread or by the target JavaThread itself.
|
// JavaThread or by the target JavaThread itself.
|
||||||
class HandshakeState {
|
class HandshakeState {
|
||||||
friend ThreadSelfSuspensionHandshake;
|
|
||||||
friend SuspendThreadHandshake;
|
|
||||||
friend UnsafeAccessErrorHandshake;
|
friend UnsafeAccessErrorHandshake;
|
||||||
friend JavaThread;
|
friend JavaThread;
|
||||||
// This a back reference to the JavaThread,
|
// This a back reference to the JavaThread,
|
||||||
@ -98,7 +94,7 @@ class HandshakeState {
|
|||||||
// The queue containing handshake operations to be performed on _handshakee.
|
// The queue containing handshake operations to be performed on _handshakee.
|
||||||
FilterQueue<HandshakeOperation*> _queue;
|
FilterQueue<HandshakeOperation*> _queue;
|
||||||
// Provides mutual exclusion to this state and queue. Also used for
|
// Provides mutual exclusion to this state and queue. Also used for
|
||||||
// JavaThread suspend/resume operations.
|
// JavaThread suspend/resume operations performed by SuspendResumeManager.
|
||||||
Monitor _lock;
|
Monitor _lock;
|
||||||
// Set to the thread executing the handshake operation.
|
// Set to the thread executing the handshake operation.
|
||||||
Thread* volatile _active_handshaker;
|
Thread* volatile _active_handshaker;
|
||||||
@ -160,31 +156,5 @@ class HandshakeState {
|
|||||||
bool async_exceptions_blocked() { return _async_exceptions_blocked; }
|
bool async_exceptions_blocked() { return _async_exceptions_blocked; }
|
||||||
void set_async_exceptions_blocked(bool b) { _async_exceptions_blocked = b; }
|
void set_async_exceptions_blocked(bool b) { _async_exceptions_blocked = b; }
|
||||||
void handle_unsafe_access_error();
|
void handle_unsafe_access_error();
|
||||||
|
|
||||||
// Suspend/resume support
|
|
||||||
private:
|
|
||||||
// This flag is true when the thread owning this
|
|
||||||
// HandshakeState (the _handshakee) is suspended.
|
|
||||||
volatile bool _suspended;
|
|
||||||
// This flag is true while there is async handshake (trap)
|
|
||||||
// on queue. Since we do only need one, we can reuse it if
|
|
||||||
// thread gets suspended again (after a resume)
|
|
||||||
// and we have not yet processed it.
|
|
||||||
bool _async_suspend_handshake;
|
|
||||||
|
|
||||||
// Called from the suspend handshake.
|
|
||||||
bool suspend_with_handshake(bool register_vthread_SR);
|
|
||||||
// Called from the async handshake (the trap)
|
|
||||||
// to stop a thread from continuing execution when suspended.
|
|
||||||
void do_self_suspend();
|
|
||||||
|
|
||||||
bool is_suspended() { return Atomic::load(&_suspended); }
|
|
||||||
void set_suspended(bool to, bool register_vthread_SR);
|
|
||||||
bool has_async_suspend_handshake() { return _async_suspend_handshake; }
|
|
||||||
void set_async_suspend_handshake(bool to) { _async_suspend_handshake = to; }
|
|
||||||
|
|
||||||
bool suspend(bool register_vthread_SR);
|
|
||||||
bool resume(bool register_vthread_SR);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_RUNTIME_HANDSHAKE_HPP
|
#endif // SHARE_RUNTIME_HANDSHAKE_HPP
|
||||||
|
@ -498,6 +498,7 @@ JavaThread::JavaThread(MemTag mem_tag) :
|
|||||||
_pending_interrupted_exception(false),
|
_pending_interrupted_exception(false),
|
||||||
|
|
||||||
_handshake(this),
|
_handshake(this),
|
||||||
|
_suspend_resume_manager(this, &_handshake._lock),
|
||||||
|
|
||||||
_popframe_preserved_args(nullptr),
|
_popframe_preserved_args(nullptr),
|
||||||
_popframe_preserved_args_size(0),
|
_popframe_preserved_args_size(0),
|
||||||
@ -1200,13 +1201,13 @@ bool JavaThread::java_suspend(bool register_vthread_SR) {
|
|||||||
|
|
||||||
guarantee(Thread::is_JavaThread_protected(/* target */ this),
|
guarantee(Thread::is_JavaThread_protected(/* target */ this),
|
||||||
"target JavaThread is not protected in calling context.");
|
"target JavaThread is not protected in calling context.");
|
||||||
return this->handshake_state()->suspend(register_vthread_SR);
|
return this->suspend_resume_manager()->suspend(register_vthread_SR);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JavaThread::java_resume(bool register_vthread_SR) {
|
bool JavaThread::java_resume(bool register_vthread_SR) {
|
||||||
guarantee(Thread::is_JavaThread_protected_by_TLH(/* target */ this),
|
guarantee(Thread::is_JavaThread_protected_by_TLH(/* target */ this),
|
||||||
"missing ThreadsListHandle in calling context.");
|
"missing ThreadsListHandle in calling context.");
|
||||||
return this->handshake_state()->resume(register_vthread_SR);
|
return this->suspend_resume_manager()->resume(register_vthread_SR);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for another thread to perform object reallocation and relocking on behalf of
|
// Wait for another thread to perform object reallocation and relocking on behalf of
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "runtime/safepointMechanism.hpp"
|
#include "runtime/safepointMechanism.hpp"
|
||||||
#include "runtime/stackWatermarkSet.hpp"
|
#include "runtime/stackWatermarkSet.hpp"
|
||||||
#include "runtime/stackOverflow.hpp"
|
#include "runtime/stackOverflow.hpp"
|
||||||
|
#include "runtime/suspendResumeManager.hpp"
|
||||||
#include "runtime/thread.hpp"
|
#include "runtime/thread.hpp"
|
||||||
#include "runtime/threadHeapSampler.hpp"
|
#include "runtime/threadHeapSampler.hpp"
|
||||||
#include "runtime/threadIdentifier.hpp"
|
#include "runtime/threadIdentifier.hpp"
|
||||||
@ -694,9 +695,13 @@ private:
|
|||||||
|
|
||||||
// Suspend/resume support for JavaThread
|
// Suspend/resume support for JavaThread
|
||||||
// higher-level suspension/resume logic called by the public APIs
|
// higher-level suspension/resume logic called by the public APIs
|
||||||
|
private:
|
||||||
|
SuspendResumeManager _suspend_resume_manager;
|
||||||
|
public:
|
||||||
bool java_suspend(bool register_vthread_SR);
|
bool java_suspend(bool register_vthread_SR);
|
||||||
bool java_resume(bool register_vthread_SR);
|
bool java_resume(bool register_vthread_SR);
|
||||||
bool is_suspended() { return _handshake.is_suspended(); }
|
bool is_suspended() { return _suspend_resume_manager.is_suspended(); }
|
||||||
|
SuspendResumeManager* suspend_resume_manager() { return &_suspend_resume_manager; }
|
||||||
|
|
||||||
// Check for async exception in addition to safepoint.
|
// Check for async exception in addition to safepoint.
|
||||||
static void check_special_condition_for_native_trans(JavaThread *thread);
|
static void check_special_condition_for_native_trans(JavaThread *thread);
|
||||||
|
158
src/hotspot/share/runtime/suspendResumeManager.cpp
Normal file
158
src/hotspot/share/runtime/suspendResumeManager.cpp
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "logging/log.hpp"
|
||||||
|
#include "logging/logStream.hpp"
|
||||||
|
#include "memory/resourceArea.hpp"
|
||||||
|
#include "prims/jvmtiThreadState.hpp"
|
||||||
|
#include "runtime/atomic.hpp"
|
||||||
|
#include "runtime/globals.hpp"
|
||||||
|
#include "runtime/handshake.hpp"
|
||||||
|
#include "runtime/interfaceSupport.inline.hpp"
|
||||||
|
#include "runtime/javaThread.inline.hpp"
|
||||||
|
#include "runtime/suspendResumeManager.hpp"
|
||||||
|
|
||||||
|
// This is the closure that prevents a suspended JavaThread from
|
||||||
|
// escaping the suspend request.
|
||||||
|
class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure {
|
||||||
|
public:
|
||||||
|
ThreadSelfSuspensionHandshake() : AsyncHandshakeClosure("ThreadSelfSuspensionHandshake") {}
|
||||||
|
void do_thread(Thread* thr) {
|
||||||
|
JavaThread* current = JavaThread::cast(thr);
|
||||||
|
assert(current == Thread::current(), "Must be self executed.");
|
||||||
|
JavaThreadState jts = current->thread_state();
|
||||||
|
|
||||||
|
current->set_thread_state(_thread_blocked);
|
||||||
|
current->suspend_resume_manager()->do_owner_suspend();
|
||||||
|
current->set_thread_state(jts);
|
||||||
|
current->suspend_resume_manager()->set_async_suspend_handshake(false);
|
||||||
|
}
|
||||||
|
virtual bool is_suspend() { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the closure that synchronously honors the suspend request.
|
||||||
|
class SuspendThreadHandshake : public HandshakeClosure {
|
||||||
|
bool _register_vthread_SR;
|
||||||
|
bool _did_suspend;
|
||||||
|
public:
|
||||||
|
SuspendThreadHandshake(bool register_vthread_SR) : HandshakeClosure("SuspendThread"),
|
||||||
|
_register_vthread_SR(register_vthread_SR), _did_suspend(false) {
|
||||||
|
}
|
||||||
|
void do_thread(Thread* thr) {
|
||||||
|
JavaThread* target = JavaThread::cast(thr);
|
||||||
|
_did_suspend = target->suspend_resume_manager()->suspend_with_handshake(_register_vthread_SR);
|
||||||
|
}
|
||||||
|
bool did_suspend() { return _did_suspend; }
|
||||||
|
};
|
||||||
|
|
||||||
|
void SuspendResumeManager::set_suspended(bool is_suspend, bool register_vthread_SR) {
|
||||||
|
#if INCLUDE_JVMTI
|
||||||
|
if (register_vthread_SR) {
|
||||||
|
assert(_target->is_vthread_mounted(), "sanity check");
|
||||||
|
if (is_suspend) {
|
||||||
|
JvmtiVTSuspender::register_vthread_suspend(_target->vthread());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JvmtiVTSuspender::register_vthread_resume(_target->vthread());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Atomic::store(&_suspended, is_suspend);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SuspendResumeManager::suspend(bool register_vthread_SR) {
|
||||||
|
JVMTI_ONLY(assert(!_target->is_in_VTMS_transition(), "no suspend allowed in VTMS transition");)
|
||||||
|
JavaThread* self = JavaThread::current();
|
||||||
|
if (_target == self) {
|
||||||
|
// If target is the current thread we can bypass the handshake machinery
|
||||||
|
// and just suspend directly
|
||||||
|
ThreadBlockInVM tbivm(self);
|
||||||
|
MutexLocker ml(_state_lock, Mutex::_no_safepoint_check_flag);
|
||||||
|
set_suspended(true, register_vthread_SR);
|
||||||
|
do_owner_suspend();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
SuspendThreadHandshake st(register_vthread_SR);
|
||||||
|
Handshake::execute(&st, _target);
|
||||||
|
return st.did_suspend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SuspendResumeManager::resume(bool register_vthread_SR) {
|
||||||
|
MutexLocker ml(_state_lock, Mutex::_no_safepoint_check_flag);
|
||||||
|
if (!is_suspended()) {
|
||||||
|
assert(!_target->is_suspended(), "cannot be suspended without a suspend request");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Resume the thread.
|
||||||
|
set_suspended(false, register_vthread_SR);
|
||||||
|
_state_lock->notify();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuspendResumeManager::do_owner_suspend() {
|
||||||
|
assert(Thread::current() == _target, "should call from _target");
|
||||||
|
assert(_state_lock->owned_by_self(), "Lock must be held");
|
||||||
|
assert(!_target->has_last_Java_frame() || _target->frame_anchor()->walkable(), "should have walkable stack");
|
||||||
|
assert(_target->thread_state() == _thread_blocked, "Caller should have transitioned to _thread_blocked");
|
||||||
|
|
||||||
|
while (is_suspended()) {
|
||||||
|
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended", p2i(_target));
|
||||||
|
_state_lock->wait_without_safepoint_check();
|
||||||
|
}
|
||||||
|
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " resumed", p2i(_target));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SuspendResumeManager::suspend_with_handshake(bool register_vthread_SR) {
|
||||||
|
assert(_target->threadObj() != nullptr, "cannot suspend with a null threadObj");
|
||||||
|
if (_target->is_exiting()) {
|
||||||
|
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " exiting", p2i(_target));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (has_async_suspend_handshake()) {
|
||||||
|
if (is_suspended()) {
|
||||||
|
// Target is already suspended.
|
||||||
|
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " already suspended", p2i(_target));
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// Target is going to wake up and leave suspension.
|
||||||
|
// Let's just stop the thread from doing that.
|
||||||
|
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " re-suspended", p2i(_target));
|
||||||
|
set_suspended(true, register_vthread_SR);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no suspend request
|
||||||
|
assert(!is_suspended(), "cannot be suspended without a suspend request");
|
||||||
|
// Thread is safe, so it must execute the request, thus we can count it as suspended
|
||||||
|
// from this point.
|
||||||
|
set_suspended(true, register_vthread_SR);
|
||||||
|
set_async_suspend_handshake(true);
|
||||||
|
log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended, arming ThreadSuspension", p2i(_target));
|
||||||
|
ThreadSelfSuspensionHandshake* ts = new ThreadSelfSuspensionHandshake();
|
||||||
|
Handshake::execute(ts, _target);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SuspendResumeManager::SuspendResumeManager(JavaThread* thread, Monitor* state_lock) : _target(thread), _state_lock(state_lock), _suspended(false), _async_suspend_handshake(false) {}
|
70
src/hotspot/share/runtime/suspendResumeManager.hpp
Normal file
70
src/hotspot/share/runtime/suspendResumeManager.hpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHARE_RUNTIME_SUSPENDRESUMEMANAGER_HPP
|
||||||
|
#define SHARE_RUNTIME_SUSPENDRESUMEMANAGER_HPP
|
||||||
|
|
||||||
|
class SuspendThreadHandshake;
|
||||||
|
class ThreadSelfSuspensionHandshake;
|
||||||
|
|
||||||
|
class SuspendResumeManager {
|
||||||
|
friend SuspendThreadHandshake;
|
||||||
|
friend ThreadSelfSuspensionHandshake;
|
||||||
|
friend JavaThread;
|
||||||
|
|
||||||
|
JavaThread* _target;
|
||||||
|
Monitor* _state_lock;
|
||||||
|
|
||||||
|
SuspendResumeManager(JavaThread* thread, Monitor* state_lock);
|
||||||
|
|
||||||
|
// This flag is true when the thread owning this
|
||||||
|
// SuspendResumeManager (the _target) is suspended.
|
||||||
|
volatile bool _suspended;
|
||||||
|
// This flag is true while there is async handshake (trap)
|
||||||
|
// on queue. Since we do only need one, we can reuse it if
|
||||||
|
// thread gets suspended again (after a resume)
|
||||||
|
// and we have not yet processed it.
|
||||||
|
bool _async_suspend_handshake;
|
||||||
|
|
||||||
|
bool suspend(bool register_vthread_SR);
|
||||||
|
bool resume(bool register_vthread_SR);
|
||||||
|
|
||||||
|
// Called from the async handshake (the trap)
|
||||||
|
// to stop a thread from continuing execution when suspended.
|
||||||
|
void do_owner_suspend();
|
||||||
|
|
||||||
|
// Called from the suspend handshake.
|
||||||
|
bool suspend_with_handshake(bool register_vthread_SR);
|
||||||
|
|
||||||
|
void set_suspended(bool to, bool register_vthread_SR);
|
||||||
|
|
||||||
|
bool is_suspended() {
|
||||||
|
return Atomic::load(&_suspended);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_async_suspend_handshake() { return _async_suspend_handshake; }
|
||||||
|
void set_async_suspend_handshake(bool to) { _async_suspend_handshake = to; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHARE_RUNTIME_SUSPENDRESUMEMANAGER_HPP
|
113
src/hotspot/share/utilities/packedTable.cpp
Normal file
113
src/hotspot/share/utilities/packedTable.cpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "utilities/align.hpp"
|
||||||
|
#include "utilities/count_leading_zeros.hpp"
|
||||||
|
#include "utilities/packedTable.hpp"
|
||||||
|
|
||||||
|
// The thresholds are inclusive, and in practice the limits are rounded
|
||||||
|
// to the nearest power-of-two - 1.
|
||||||
|
// Based on the max_key and max_value we figure out the number of bits required to store
|
||||||
|
// key and value; imagine that only as bits (not aligned to byte boundary... yet).
|
||||||
|
// Then we concatenate the bits for key and value, and 'add' 1-7 padding zeroes
|
||||||
|
// (high-order bits) to align on bytes.
|
||||||
|
// In the end we have each element in the table consuming 1-8 bytes (case with 0 bits for key
|
||||||
|
// is ruled out).
|
||||||
|
PackedTableBase::PackedTableBase(uint32_t max_key, uint32_t max_value) {
|
||||||
|
unsigned int key_bits = max_key == 0 ? 0 : 32 - count_leading_zeros(max_key);
|
||||||
|
unsigned int value_bits = max_value == 0 ? 0 : 32 - count_leading_zeros(max_value);
|
||||||
|
_element_bytes = align_up(key_bits + value_bits, 8) / 8;
|
||||||
|
// shifting left by 32 is undefined behaviour, and in practice returns 1
|
||||||
|
_key_mask = key_bits >= 32 ? -1 : (1U << key_bits) - 1;
|
||||||
|
_value_shift = key_bits;
|
||||||
|
_value_mask = value_bits >= 32 ? -1 : (1U << value_bits) - 1;
|
||||||
|
guarantee(_element_bytes > 0, "wouldn't work");
|
||||||
|
assert(_element_bytes <= sizeof(uint64_t), "shouldn't happen");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: we require the supplier to provide the elements in the final order as we can't easily sort
|
||||||
|
// within this method - qsort() accepts only pure function as comparator.
|
||||||
|
void PackedTableBuilder::fill(u1* table, size_t table_length, Supplier &supplier) const {
|
||||||
|
uint32_t key, value;
|
||||||
|
size_t offset = 0;
|
||||||
|
for (; offset <= table_length && supplier.next(&key, &value); offset += _element_bytes) {
|
||||||
|
assert((key & ~_key_mask) == 0, "key out of bounds");
|
||||||
|
assert((value & ~_value_mask) == 0, "value out of bounds: %x vs. %x (%x)", value, _value_mask, ~_value_mask);
|
||||||
|
uint64_t element = static_cast<uint64_t>(key) | (static_cast<uint64_t>(value) << _value_shift);
|
||||||
|
for (unsigned int i = 0; i < _element_bytes; ++i) {
|
||||||
|
table[offset + i] = static_cast<u1>(0xFF & element);
|
||||||
|
element >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(offset == table_length, "Did not fill whole array");
|
||||||
|
assert(!supplier.next(&key, &value), "Supplier has more elements");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t PackedTableLookup::read_element(size_t offset) const {
|
||||||
|
uint64_t element = 0;
|
||||||
|
for (unsigned int i = 0; i < _element_bytes; ++i) {
|
||||||
|
element |= static_cast<uint64_t>(_table[offset + i]) << (8 * i);
|
||||||
|
}
|
||||||
|
assert((element & ~((uint64_t) _key_mask | ((uint64_t) _value_mask << _value_shift))) == 0, "read too much");
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PackedTableLookup::search(Comparator& comparator, uint32_t* found_key, uint32_t* found_value) const {
|
||||||
|
unsigned int low = 0, high = checked_cast<unsigned int>(_table_length / _element_bytes);
|
||||||
|
assert(low < high, "must be");
|
||||||
|
while (low < high) {
|
||||||
|
unsigned int mid = low + (high - low) / 2;
|
||||||
|
assert(mid >= low && mid < high, "integer overflow?");
|
||||||
|
uint64_t element = read_element(_element_bytes * mid);
|
||||||
|
// Ignoring high 32 bits in element on purpose
|
||||||
|
uint32_t key = static_cast<uint32_t>(element) & _key_mask;
|
||||||
|
int cmp = comparator.compare_to(key);
|
||||||
|
if (cmp == 0) {
|
||||||
|
*found_key = key;
|
||||||
|
// Since __builtin_memcpy in read_element does not copy bits outside the element
|
||||||
|
// anything above _value_mask << _value_shift should be zero.
|
||||||
|
*found_value = checked_cast<uint32_t>(element >> _value_shift) & _value_mask;
|
||||||
|
return true;
|
||||||
|
} else if (cmp < 0) {
|
||||||
|
high = mid;
|
||||||
|
} else {
|
||||||
|
low = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
void PackedTableLookup::validate_order(Comparator &comparator) const {
|
||||||
|
auto validator = [&] (size_t offset, uint32_t key, uint32_t value) {
|
||||||
|
if (offset != 0) {
|
||||||
|
assert(comparator.compare_to(key) < 0, "not sorted");
|
||||||
|
}
|
||||||
|
comparator.reset(key);
|
||||||
|
};
|
||||||
|
iterate(validator);
|
||||||
|
}
|
||||||
|
#endif
|
123
src/hotspot/share/utilities/packedTable.hpp
Normal file
123
src/hotspot/share/utilities/packedTable.hpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "oops/array.hpp"
|
||||||
|
#include "utilities/globalDefinitions.hpp"
|
||||||
|
|
||||||
|
// Base for space-optimized structure supporting binary search. Each element
|
||||||
|
// consists of up to 32-bit key, and up to 32-bit value; these are packed
|
||||||
|
// into a bit-record with 1-byte alignment.
|
||||||
|
// The keys are ordered according to a custom comparator.
|
||||||
|
class PackedTableBase {
|
||||||
|
protected:
|
||||||
|
unsigned int _element_bytes;
|
||||||
|
uint32_t _key_mask;
|
||||||
|
unsigned int _value_shift;
|
||||||
|
uint32_t _value_mask;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PackedTableBase(uint32_t max_key, uint32_t max_value);
|
||||||
|
|
||||||
|
// Returns number of bytes each element will occupy.
|
||||||
|
inline unsigned int element_bytes(void) const { return _element_bytes; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper class for constructing a packed table in the provided array.
|
||||||
|
class PackedTableBuilder: public PackedTableBase {
|
||||||
|
public:
|
||||||
|
class Supplier {
|
||||||
|
public:
|
||||||
|
// Returns elements with already ordered keys.
|
||||||
|
// This function should return true when the key and value was set,
|
||||||
|
// and false when there's no more elements.
|
||||||
|
// Packed table does NOT support duplicate keys.
|
||||||
|
virtual bool next(uint32_t* key, uint32_t* value) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The thresholds are inclusive, and in practice the limits are rounded
|
||||||
|
// to the nearest power-of-two - 1.
|
||||||
|
// See PackedTableBase constructor for details.
|
||||||
|
PackedTableBuilder(uint32_t max_key, uint32_t max_value): PackedTableBase(max_key, max_value) {}
|
||||||
|
|
||||||
|
// Constructs a packed table in the provided array, filling it with elements
|
||||||
|
// from the supplier. Note that no comparator is requied by this method -
|
||||||
|
// the supplier must return elements with already ordered keys.
|
||||||
|
// The table_length (in bytes) should match number of elements provided
|
||||||
|
// by the supplier (when Supplier::next() returns false the whole array should
|
||||||
|
// be filled).
|
||||||
|
void fill(u1* table, size_t table_length, Supplier &supplier) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper class for lookup in a packed table.
|
||||||
|
class PackedTableLookup: public PackedTableBase {
|
||||||
|
const u1* const _table;
|
||||||
|
const size_t _table_length;
|
||||||
|
|
||||||
|
uint64_t read_element(size_t offset) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// The comparator implementation does not have to store a key (uint32_t);
|
||||||
|
// the idea is that key can point into a different structure that hosts data
|
||||||
|
// suitable for the actual comparison. That's why PackedTableLookup::search(...)
|
||||||
|
// returns the key it found as well as the value.
|
||||||
|
class Comparator {
|
||||||
|
public:
|
||||||
|
// Returns negative/0/positive if the target referred to by this comparator
|
||||||
|
// is lower/equal/higher than the target referred to by the key.
|
||||||
|
virtual int compare_to(uint32_t key) = 0;
|
||||||
|
// Changes the target this comparator refers to.
|
||||||
|
DEBUG_ONLY(virtual void reset(uint32_t key) = 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The thresholds are inclusive, and in practice the limits are rounded
|
||||||
|
// to the nearest power-of-two - 1.
|
||||||
|
// See PackedTableBase constructor for details.
|
||||||
|
PackedTableLookup(uint32_t max_key, uint32_t max_value, const u1 *table, size_t table_length):
|
||||||
|
PackedTableBase(max_key, max_value), _table(table), _table_length(table_length) {}
|
||||||
|
|
||||||
|
PackedTableLookup(uint32_t max_key, uint32_t max_value, const Array<u1> *table):
|
||||||
|
PackedTableLookup(max_key, max_value, table->data(), static_cast<size_t>(table->length())) {}
|
||||||
|
|
||||||
|
// Performs a binary search in the packed table, looking for an element with key
|
||||||
|
// referring to a target equal according to the comparator.
|
||||||
|
// When the element is found, found_key and found_value are updated from the element
|
||||||
|
// and the function returns true.
|
||||||
|
// When the element is not found, found_key and found_value are not changed and
|
||||||
|
// the function returns false.
|
||||||
|
bool search(Comparator& comparator, uint32_t* found_key, uint32_t* found_value) const;
|
||||||
|
|
||||||
|
// Asserts that elements in the packed table follow the order defined by the comparator.
|
||||||
|
DEBUG_ONLY(void validate_order(Comparator &comparator) const);
|
||||||
|
|
||||||
|
template<typename Function>
|
||||||
|
void iterate(Function func) const {
|
||||||
|
for (size_t offset = 0; offset < _table_length; offset += _element_bytes) {
|
||||||
|
uint64_t element = read_element(offset);
|
||||||
|
uint32_t key = static_cast<uint32_t>(element) & _key_mask;
|
||||||
|
uint32_t value = checked_cast<uint32_t>(element >> _value_shift) & _value_mask;
|
||||||
|
func(offset, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -261,7 +261,7 @@ class UNSIGNED5 : AllStatic {
|
|||||||
ARR _array;
|
ARR _array;
|
||||||
OFF _limit;
|
OFF _limit;
|
||||||
OFF _position;
|
OFF _position;
|
||||||
int next_length() {
|
int next_length() const {
|
||||||
return UNSIGNED5::check_length(_array, _position, _limit, GET());
|
return UNSIGNED5::check_length(_array, _position, _limit, GET());
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
@ -270,7 +270,7 @@ class UNSIGNED5 : AllStatic {
|
|||||||
uint32_t next_uint() {
|
uint32_t next_uint() {
|
||||||
return UNSIGNED5::read_uint(_array, _position, _limit, GET());
|
return UNSIGNED5::read_uint(_array, _position, _limit, GET());
|
||||||
}
|
}
|
||||||
bool has_next() {
|
bool has_next() const {
|
||||||
return next_length() != 0;
|
return next_length() != 0;
|
||||||
}
|
}
|
||||||
// tries to skip count logical entries; returns actual number skipped
|
// tries to skip count logical entries; returns actual number skipped
|
||||||
@ -284,8 +284,9 @@ class UNSIGNED5 : AllStatic {
|
|||||||
return actual;
|
return actual;
|
||||||
}
|
}
|
||||||
ARR array() { return _array; }
|
ARR array() { return _array; }
|
||||||
OFF limit() { return _limit; }
|
OFF limit() const { return _limit; }
|
||||||
OFF position() { return _position; }
|
OFF position() const { return _position; }
|
||||||
|
void set_limit(OFF limit) { _limit = limit; }
|
||||||
void set_position(OFF position) { _position = position; }
|
void set_position(OFF position) { _position = position; }
|
||||||
|
|
||||||
// For debugging, even in product builds (see debug.cpp).
|
// For debugging, even in product builds (see debug.cpp).
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -234,4 +234,28 @@ class Bits { // package-private
|
|||||||
// of an element by element copy. These numbers may change over time.
|
// of an element by element copy. These numbers may change over time.
|
||||||
static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6;
|
static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6;
|
||||||
static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6;
|
static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6;
|
||||||
|
|
||||||
|
// Maximum number of bytes to set in one call to {@code Unsafe.setMemory}.
|
||||||
|
// This threshold allows safepoint polling during large memory operations.
|
||||||
|
static final long UNSAFE_SET_THRESHOLD = 1024 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a block of memory starting from a given address to a specified byte value.
|
||||||
|
*
|
||||||
|
* @param srcAddr
|
||||||
|
* the starting memory address
|
||||||
|
* @param count
|
||||||
|
* the number of bytes to set
|
||||||
|
* @param value
|
||||||
|
* the byte value to set
|
||||||
|
*/
|
||||||
|
static void setMemory(long srcAddr, long count, byte value) {
|
||||||
|
long offset = 0;
|
||||||
|
while (offset < count) {
|
||||||
|
long len = Math.min(UNSAFE_SET_THRESHOLD, count - offset);
|
||||||
|
UNSAFE.setMemory(srcAddr + offset, len, value);
|
||||||
|
offset += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -114,7 +114,7 @@ class Direct$Type$Buffer$RW$$BO$
|
|||||||
Bits.unreserveMemory(size, cap);
|
Bits.unreserveMemory(size, cap);
|
||||||
throw x;
|
throw x;
|
||||||
}
|
}
|
||||||
UNSAFE.setMemory(base, size, (byte) 0);
|
Bits.setMemory(base, size, (byte) 0);
|
||||||
if (pa && (base % ps != 0)) {
|
if (pa && (base % ps != 0)) {
|
||||||
// Round up to page boundary
|
// Round up to page boundary
|
||||||
address = base + ps - (base & (ps - 1));
|
address = base + ps - (base & (ps - 1));
|
||||||
|
@ -150,7 +150,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
transient Node<E> last;
|
transient Node<E> last;
|
||||||
|
|
||||||
/** Number of items in the deque */
|
/** Number of items in the deque */
|
||||||
private transient int count;
|
private transient volatile int count;
|
||||||
|
|
||||||
/** @serial Maximum number of items in the deque */
|
/** @serial Maximum number of items in the deque */
|
||||||
private final int capacity;
|
private final int capacity;
|
||||||
@ -206,10 +206,13 @@ public class LinkedBlockingDeque<E>
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Links node as first element, or returns false if full.
|
* Links node as first element, or returns false if full.
|
||||||
|
*
|
||||||
|
* @return true if the node was added; false otherwise
|
||||||
*/
|
*/
|
||||||
private boolean linkFirst(Node<E> node) {
|
private boolean linkFirst(Node<E> node) {
|
||||||
// assert lock.isHeldByCurrentThread();
|
// assert lock.isHeldByCurrentThread();
|
||||||
if (count >= capacity)
|
int c;
|
||||||
|
if ((c = count) >= capacity)
|
||||||
return false;
|
return false;
|
||||||
Node<E> f = first;
|
Node<E> f = first;
|
||||||
node.next = f;
|
node.next = f;
|
||||||
@ -218,17 +221,20 @@ public class LinkedBlockingDeque<E>
|
|||||||
last = node;
|
last = node;
|
||||||
else
|
else
|
||||||
f.prev = node;
|
f.prev = node;
|
||||||
++count;
|
count = c + 1;
|
||||||
notEmpty.signal();
|
notEmpty.signal();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Links node as last element, or returns false if full.
|
* Links node as last element, or returns false if full.
|
||||||
|
*
|
||||||
|
* @return true if the node was added; false otherwise
|
||||||
*/
|
*/
|
||||||
private boolean linkLast(Node<E> node) {
|
private boolean linkLast(Node<E> node) {
|
||||||
// assert lock.isHeldByCurrentThread();
|
// assert lock.isHeldByCurrentThread();
|
||||||
if (count >= capacity)
|
int c;
|
||||||
|
if ((c = count) >= capacity)
|
||||||
return false;
|
return false;
|
||||||
Node<E> l = last;
|
Node<E> l = last;
|
||||||
node.prev = l;
|
node.prev = l;
|
||||||
@ -237,7 +243,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
first = node;
|
first = node;
|
||||||
else
|
else
|
||||||
l.next = node;
|
l.next = node;
|
||||||
++count;
|
count = c + 1;
|
||||||
notEmpty.signal();
|
notEmpty.signal();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -334,6 +340,8 @@ public class LinkedBlockingDeque<E>
|
|||||||
*/
|
*/
|
||||||
public boolean offerFirst(E e) {
|
public boolean offerFirst(E e) {
|
||||||
if (e == null) throw new NullPointerException();
|
if (e == null) throw new NullPointerException();
|
||||||
|
if (count >= capacity)
|
||||||
|
return false;
|
||||||
Node<E> node = new Node<E>(e);
|
Node<E> node = new Node<E>(e);
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
@ -349,6 +357,8 @@ public class LinkedBlockingDeque<E>
|
|||||||
*/
|
*/
|
||||||
public boolean offerLast(E e) {
|
public boolean offerLast(E e) {
|
||||||
if (e == null) throw new NullPointerException();
|
if (e == null) throw new NullPointerException();
|
||||||
|
if (count >= capacity)
|
||||||
|
return false;
|
||||||
Node<E> node = new Node<E>(e);
|
Node<E> node = new Node<E>(e);
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
@ -367,7 +377,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
if (e == null) throw new NullPointerException();
|
if (e == null) throw new NullPointerException();
|
||||||
Node<E> node = new Node<E>(e);
|
Node<E> node = new Node<E>(e);
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lockInterruptibly();
|
||||||
try {
|
try {
|
||||||
while (!linkFirst(node))
|
while (!linkFirst(node))
|
||||||
notFull.await();
|
notFull.await();
|
||||||
@ -384,7 +394,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
if (e == null) throw new NullPointerException();
|
if (e == null) throw new NullPointerException();
|
||||||
Node<E> node = new Node<E>(e);
|
Node<E> node = new Node<E>(e);
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lockInterruptibly();
|
||||||
try {
|
try {
|
||||||
while (!linkLast(node))
|
while (!linkLast(node))
|
||||||
notFull.await();
|
notFull.await();
|
||||||
@ -458,6 +468,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public E pollFirst() {
|
public E pollFirst() {
|
||||||
|
if (count == 0) return null;
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
@ -468,6 +479,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public E pollLast() {
|
public E pollLast() {
|
||||||
|
if (count == 0) return null;
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
@ -479,7 +491,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
|
|
||||||
public E takeFirst() throws InterruptedException {
|
public E takeFirst() throws InterruptedException {
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lockInterruptibly();
|
||||||
try {
|
try {
|
||||||
E x;
|
E x;
|
||||||
while ( (x = unlinkFirst()) == null)
|
while ( (x = unlinkFirst()) == null)
|
||||||
@ -492,7 +504,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
|
|
||||||
public E takeLast() throws InterruptedException {
|
public E takeLast() throws InterruptedException {
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lockInterruptibly();
|
||||||
try {
|
try {
|
||||||
E x;
|
E x;
|
||||||
while ( (x = unlinkLast()) == null)
|
while ( (x = unlinkLast()) == null)
|
||||||
@ -558,6 +570,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public E peekFirst() {
|
public E peekFirst() {
|
||||||
|
if (count == 0) return null;
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
@ -568,6 +581,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public E peekLast() {
|
public E peekLast() {
|
||||||
|
if (count == 0) return null;
|
||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
@ -718,13 +732,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
* insert or remove an element.
|
* insert or remove an element.
|
||||||
*/
|
*/
|
||||||
public int remainingCapacity() {
|
public int remainingCapacity() {
|
||||||
final ReentrantLock lock = this.lock;
|
return capacity - count;
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
return capacity - count;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -806,13 +814,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
* @return the number of elements in this deque
|
* @return the number of elements in this deque
|
||||||
*/
|
*/
|
||||||
public int size() {
|
public int size() {
|
||||||
final ReentrantLock lock = this.lock;
|
return count;
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
return count;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -858,7 +860,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
|
|
||||||
// Copy c into a private chain of Nodes
|
// Copy c into a private chain of Nodes
|
||||||
Node<E> beg = null, end = null;
|
Node<E> beg = null, end = null;
|
||||||
int n = 0;
|
long n = 0;
|
||||||
for (E e : c) {
|
for (E e : c) {
|
||||||
Objects.requireNonNull(e);
|
Objects.requireNonNull(e);
|
||||||
n++;
|
n++;
|
||||||
@ -878,14 +880,15 @@ public class LinkedBlockingDeque<E>
|
|||||||
final ReentrantLock lock = this.lock;
|
final ReentrantLock lock = this.lock;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
if (count + n <= capacity) {
|
long cnt;
|
||||||
|
if ((cnt = count + n) <= capacity) {
|
||||||
beg.prev = last;
|
beg.prev = last;
|
||||||
if (first == null)
|
if (first == null)
|
||||||
first = beg;
|
first = beg;
|
||||||
else
|
else
|
||||||
last.next = beg;
|
last.next = beg;
|
||||||
last = end;
|
last = end;
|
||||||
count += n;
|
count = (int)cnt;
|
||||||
notEmpty.signalAll();
|
notEmpty.signalAll();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -894,6 +897,7 @@ public class LinkedBlockingDeque<E>
|
|||||||
}
|
}
|
||||||
// Fall back to historic non-atomic implementation, failing
|
// Fall back to historic non-atomic implementation, failing
|
||||||
// with IllegalStateException when the capacity is exceeded.
|
// with IllegalStateException when the capacity is exceeded.
|
||||||
|
beg = end = null; // help GC
|
||||||
return super.addAll(c);
|
return super.addAll(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -994,8 +998,8 @@ public class LinkedBlockingDeque<E>
|
|||||||
for (Node<E> f = first; f != null; ) {
|
for (Node<E> f = first; f != null; ) {
|
||||||
f.item = null;
|
f.item = null;
|
||||||
Node<E> n = f.next;
|
Node<E> n = f.next;
|
||||||
f.prev = null;
|
f.prev = f;
|
||||||
f.next = null;
|
f.next = f;
|
||||||
f = n;
|
f = n;
|
||||||
}
|
}
|
||||||
first = last = null;
|
first = last = null;
|
||||||
|
@ -405,8 +405,7 @@ module java.base {
|
|||||||
uses sun.text.spi.JavaTimeDateTimePatternProvider;
|
uses sun.text.spi.JavaTimeDateTimePatternProvider;
|
||||||
uses sun.util.spi.CalendarProvider;
|
uses sun.util.spi.CalendarProvider;
|
||||||
uses sun.util.locale.provider.LocaleDataMetaInfo;
|
uses sun.util.locale.provider.LocaleDataMetaInfo;
|
||||||
uses sun.util.resources.LocaleData.CommonResourceBundleProvider;
|
uses sun.util.resources.LocaleData.LocaleDataResourceBundleProvider;
|
||||||
uses sun.util.resources.LocaleData.SupplementaryResourceBundleProvider;
|
|
||||||
|
|
||||||
// Built-in service providers that are located via ServiceLoader
|
// Built-in service providers that are located via ServiceLoader
|
||||||
|
|
||||||
|
@ -94,6 +94,15 @@ public class Net {
|
|||||||
return EXCLUSIVE_BIND;
|
return EXCLUSIVE_BIND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final StableValue<Boolean> SHUTDOWN_WRITE_BEFORE_CLOSE = StableValue.of();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether a TCP connection should be shutdown for writing before closing.
|
||||||
|
*/
|
||||||
|
static boolean shouldShutdownWriteBeforeClose() {
|
||||||
|
return SHUTDOWN_WRITE_BEFORE_CLOSE.orElseSet(Net::shouldShutdownWriteBeforeClose0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells whether both IPV6_XXX and IP_XXX socket options should be set on
|
* Tells whether both IPV6_XXX and IP_XXX socket options should be set on
|
||||||
* IPv6 sockets. On some kernels, both IPV6_XXX and IP_XXX socket options
|
* IPv6 sockets. On some kernels, both IPV6_XXX and IP_XXX socket options
|
||||||
@ -462,6 +471,8 @@ public class Net {
|
|||||||
*/
|
*/
|
||||||
private static native int isExclusiveBindAvailable();
|
private static native int isExclusiveBindAvailable();
|
||||||
|
|
||||||
|
private static native boolean shouldShutdownWriteBeforeClose0();
|
||||||
|
|
||||||
private static native boolean shouldSetBothIPv4AndIPv6Options0();
|
private static native boolean shouldSetBothIPv4AndIPv6Options0();
|
||||||
|
|
||||||
private static native boolean canIPv6SocketJoinIPv4Group0();
|
private static native boolean canIPv6SocketJoinIPv4Group0();
|
||||||
|
@ -846,7 +846,7 @@ class SocketChannelImpl
|
|||||||
/**
|
/**
|
||||||
* Marks the beginning of a connect operation that might block.
|
* Marks the beginning of a connect operation that might block.
|
||||||
* @param blocking true if configured blocking
|
* @param blocking true if configured blocking
|
||||||
* @param isa the remote address
|
* @param sa the remote socket address
|
||||||
* @throws ClosedChannelException if the channel is closed
|
* @throws ClosedChannelException if the channel is closed
|
||||||
* @throws AlreadyConnectedException if already connected
|
* @throws AlreadyConnectedException if already connected
|
||||||
* @throws ConnectionPendingException is a connection is pending
|
* @throws ConnectionPendingException is a connection is pending
|
||||||
@ -1070,8 +1070,8 @@ class SocketChannelImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the socket if there are no I/O operations in progress and the
|
* Closes the socket if there are no I/O operations in progress (or no I/O
|
||||||
* channel is not registered with a Selector.
|
* operations tracked), and the channel is not registered with a Selector.
|
||||||
*/
|
*/
|
||||||
private boolean tryClose() throws IOException {
|
private boolean tryClose() throws IOException {
|
||||||
assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
|
assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
|
||||||
@ -1096,11 +1096,21 @@ class SocketChannelImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes this channel when configured in blocking mode.
|
* Closes this channel when configured in blocking mode. If there are no I/O
|
||||||
|
* operations in progress (or tracked), then the channel's socket is closed. If
|
||||||
|
* there are I/O operations in progress then the behavior is platform specific.
|
||||||
*
|
*
|
||||||
* If there is an I/O operation in progress then the socket is pre-closed
|
* On Unix systems, the channel's socket is pre-closed. This unparks any virtual
|
||||||
* and the I/O threads signalled, in which case the final close is deferred
|
* threads that are blocked in I/O operations on this channel. If there are
|
||||||
* until all I/O operations complete.
|
* platform threads blocked on the channel's socket then the socket is dup'ed
|
||||||
|
* and the platform threads signalled. The final close is deferred until all I/O
|
||||||
|
* operations complete.
|
||||||
|
*
|
||||||
|
* On Windows, the channel's socket is pre-closed. This unparks any virtual
|
||||||
|
* threads that are blocked in I/O operations on this channel. If there are no
|
||||||
|
* virtual threads blocked in I/O operations on this channel then the channel's
|
||||||
|
* socket is closed. If there are virtual threads in I/O then the final close is
|
||||||
|
* deferred until all I/O operations on virtual threads complete.
|
||||||
*
|
*
|
||||||
* Note that a channel configured blocking may be registered with a Selector
|
* Note that a channel configured blocking may be registered with a Selector
|
||||||
* This arises when a key is canceled and the channel configured to blocking
|
* This arises when a key is canceled and the channel configured to blocking
|
||||||
@ -1112,17 +1122,17 @@ class SocketChannelImpl
|
|||||||
boolean connected = (state == ST_CONNECTED);
|
boolean connected = (state == ST_CONNECTED);
|
||||||
state = ST_CLOSING;
|
state = ST_CLOSING;
|
||||||
|
|
||||||
if (!tryClose()) {
|
if (connected && Net.shouldShutdownWriteBeforeClose()) {
|
||||||
// shutdown output when linger interval not set to 0
|
// shutdown output when linger interval not set to 0
|
||||||
if (connected) {
|
try {
|
||||||
try {
|
var SO_LINGER = StandardSocketOptions.SO_LINGER;
|
||||||
var SO_LINGER = StandardSocketOptions.SO_LINGER;
|
if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
|
||||||
if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
|
Net.shutdown(fd, Net.SHUT_WR);
|
||||||
Net.shutdown(fd, Net.SHUT_WR);
|
}
|
||||||
}
|
} catch (IOException ignore) { }
|
||||||
} catch (IOException ignore) { }
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!tryClose()) {
|
||||||
// prepare file descriptor for closing
|
// prepare file descriptor for closing
|
||||||
nd.preClose(fd, readerThread, writerThread);
|
nd.preClose(fd, readerThread, writerThread);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,9 +23,6 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
|
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
|
||||||
* (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
|
* (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
|
||||||
@ -79,11 +76,11 @@
|
|||||||
|
|
||||||
package sun.text.resources;
|
package sun.text.resources;
|
||||||
|
|
||||||
import sun.util.resources.ParallelListResourceBundle;
|
import sun.util.resources.OpenListResourceBundle;
|
||||||
|
|
||||||
public class FormatData extends ParallelListResourceBundle {
|
public class FormatData extends OpenListResourceBundle {
|
||||||
/**
|
/**
|
||||||
* Overrides ListResourceBundle
|
* Overrides OpenListResourceBundle
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected final Object[][] getContents() {
|
protected final Object[][] getContents() {
|
||||||
@ -119,6 +116,109 @@ public class FormatData extends ParallelListResourceBundle {
|
|||||||
"Reiwa",
|
"Reiwa",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Moved from JavaTimeSupplementary
|
||||||
|
final String[] sharedQuarterNames = {
|
||||||
|
"Q1",
|
||||||
|
"Q2",
|
||||||
|
"Q3",
|
||||||
|
"Q4",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedQuarterNarrows = {
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedDatePatterns = {
|
||||||
|
"GGGG y MMMM d, EEEE",
|
||||||
|
"GGGG y MMMM d",
|
||||||
|
"GGGG y MMM d",
|
||||||
|
"G y-MM-dd",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedDayAbbrs = {
|
||||||
|
"Sun",
|
||||||
|
"Mon",
|
||||||
|
"Tue",
|
||||||
|
"Wed",
|
||||||
|
"Thu",
|
||||||
|
"Fri",
|
||||||
|
"Sat",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedDayNarrows = {
|
||||||
|
"S",
|
||||||
|
"M",
|
||||||
|
"T",
|
||||||
|
"W",
|
||||||
|
"T",
|
||||||
|
"F",
|
||||||
|
"S",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedEras = {
|
||||||
|
"",
|
||||||
|
"AH",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedMonthNarrows = {
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
"5",
|
||||||
|
"6",
|
||||||
|
"7",
|
||||||
|
"8",
|
||||||
|
"9",
|
||||||
|
"10",
|
||||||
|
"11",
|
||||||
|
"12",
|
||||||
|
"",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedTimePatterns = {
|
||||||
|
"HH:mm:ss zzzz",
|
||||||
|
"HH:mm:ss z",
|
||||||
|
"HH:mm:ss",
|
||||||
|
"HH:mm",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedAmPmMarkers = {
|
||||||
|
"AM",
|
||||||
|
"PM",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedJavaTimeDatePatterns = {
|
||||||
|
"G y MMMM d, EEEE",
|
||||||
|
"G y MMMM d",
|
||||||
|
"G y MMM d",
|
||||||
|
"GGGGG y-MM-dd",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedShortEras = {
|
||||||
|
"Before R.O.C.",
|
||||||
|
"R.O.C.",
|
||||||
|
};
|
||||||
|
|
||||||
|
final String[] sharedMonthAbbrs = {
|
||||||
|
"Jan",
|
||||||
|
"Feb",
|
||||||
|
"Mar",
|
||||||
|
"Apr",
|
||||||
|
"May",
|
||||||
|
"Jun",
|
||||||
|
"Jul",
|
||||||
|
"Aug",
|
||||||
|
"Sep",
|
||||||
|
"Oct",
|
||||||
|
"Nov",
|
||||||
|
"Dec",
|
||||||
|
"",
|
||||||
|
};
|
||||||
|
|
||||||
return new Object[][] {
|
return new Object[][] {
|
||||||
{ "MonthNames",
|
{ "MonthNames",
|
||||||
new String[] {
|
new String[] {
|
||||||
@ -138,39 +238,9 @@ public class FormatData extends ParallelListResourceBundle {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ "MonthAbbreviations",
|
{ "MonthAbbreviations",
|
||||||
new String[] {
|
sharedMonthAbbrs },
|
||||||
"Jan", // abb january
|
|
||||||
"Feb", // abb february
|
|
||||||
"Mar", // abb march
|
|
||||||
"Apr", // abb april
|
|
||||||
"May", // abb may
|
|
||||||
"Jun", // abb june
|
|
||||||
"Jul", // abb july
|
|
||||||
"Aug", // abb august
|
|
||||||
"Sep", // abb september
|
|
||||||
"Oct", // abb october
|
|
||||||
"Nov", // abb november
|
|
||||||
"Dec", // abb december
|
|
||||||
"" // abb month 13 if applicable
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "MonthNarrows",
|
{ "MonthNarrows",
|
||||||
new String[] {
|
sharedMonthNarrows },
|
||||||
"1",
|
|
||||||
"2",
|
|
||||||
"3",
|
|
||||||
"4",
|
|
||||||
"5",
|
|
||||||
"6",
|
|
||||||
"7",
|
|
||||||
"8",
|
|
||||||
"9",
|
|
||||||
"10",
|
|
||||||
"11",
|
|
||||||
"12",
|
|
||||||
"",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "DayNames",
|
{ "DayNames",
|
||||||
new String[] {
|
new String[] {
|
||||||
"Sunday", // Sunday
|
"Sunday", // Sunday
|
||||||
@ -183,33 +253,11 @@ public class FormatData extends ParallelListResourceBundle {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ "DayAbbreviations",
|
{ "DayAbbreviations",
|
||||||
new String[] {
|
sharedDayAbbrs },
|
||||||
"Sun", // abb Sunday
|
|
||||||
"Mon", // abb Monday
|
|
||||||
"Tue", // abb Tuesday
|
|
||||||
"Wed", // abb Wednesday
|
|
||||||
"Thu", // abb Thursday
|
|
||||||
"Fri", // abb Friday
|
|
||||||
"Sat" // abb Saturday
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "DayNarrows",
|
{ "DayNarrows",
|
||||||
new String[] {
|
sharedDayNarrows },
|
||||||
"S",
|
|
||||||
"M",
|
|
||||||
"T",
|
|
||||||
"W",
|
|
||||||
"T",
|
|
||||||
"F",
|
|
||||||
"S",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "AmPmMarkers",
|
{ "AmPmMarkers",
|
||||||
new String[] {
|
sharedAmPmMarkers },
|
||||||
"AM", // am marker
|
|
||||||
"PM" // pm marker
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "narrow.AmPmMarkers",
|
{ "narrow.AmPmMarkers",
|
||||||
new String[] {
|
new String[] {
|
||||||
"a", // am marker
|
"a", // am marker
|
||||||
@ -227,22 +275,17 @@ public class FormatData extends ParallelListResourceBundle {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ "buddhist.Eras",
|
{ "buddhist.Eras",
|
||||||
buddhistEras
|
buddhistEras },
|
||||||
},
|
|
||||||
{ "buddhist.short.Eras",
|
{ "buddhist.short.Eras",
|
||||||
buddhistEras
|
buddhistEras },
|
||||||
},
|
|
||||||
{ "buddhist.narrow.Eras",
|
{ "buddhist.narrow.Eras",
|
||||||
buddhistEras
|
buddhistEras },
|
||||||
},
|
|
||||||
{ "japanese.Eras",
|
{ "japanese.Eras",
|
||||||
japaneseEras },
|
japaneseEras },
|
||||||
{ "japanese.short.Eras",
|
{ "japanese.short.Eras",
|
||||||
japaneseEraAbbrs
|
japaneseEraAbbrs },
|
||||||
},
|
|
||||||
{ "japanese.narrow.Eras",
|
{ "japanese.narrow.Eras",
|
||||||
japaneseEraAbbrs
|
japaneseEraAbbrs },
|
||||||
},
|
|
||||||
{ "japanese.FirstYear",
|
{ "japanese.FirstYear",
|
||||||
new String[] { // Japanese imperial calendar year name
|
new String[] { // Japanese imperial calendar year name
|
||||||
// empty in English
|
// empty in English
|
||||||
@ -898,6 +941,164 @@ public class FormatData extends ParallelListResourceBundle {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ "DateTimePatternChars", "GyMdkHmsSEDFwWahKzZ" },
|
{ "DateTimePatternChars", "GyMdkHmsSEDFwWahKzZ" },
|
||||||
|
|
||||||
|
// Moved from JavaTimeSupplementary
|
||||||
|
{ "QuarterAbbreviations",
|
||||||
|
sharedQuarterNames },
|
||||||
|
{ "QuarterNames",
|
||||||
|
sharedQuarterNames },
|
||||||
|
{ "QuarterNarrows",
|
||||||
|
sharedQuarterNarrows },
|
||||||
|
{ "field.dayperiod",
|
||||||
|
"Dayperiod" },
|
||||||
|
{ "field.era",
|
||||||
|
"Era" },
|
||||||
|
{ "field.hour",
|
||||||
|
"Hour" },
|
||||||
|
{ "field.minute",
|
||||||
|
"Minute" },
|
||||||
|
{ "field.month",
|
||||||
|
"Month" },
|
||||||
|
{ "field.second",
|
||||||
|
"Second" },
|
||||||
|
{ "field.week",
|
||||||
|
"Week" },
|
||||||
|
{ "field.weekday",
|
||||||
|
"Day of the Week" },
|
||||||
|
{ "field.year",
|
||||||
|
"Year" },
|
||||||
|
{ "field.zone",
|
||||||
|
"Zone" },
|
||||||
|
{ "islamic.DatePatterns",
|
||||||
|
sharedDatePatterns },
|
||||||
|
{ "islamic.DayAbbreviations",
|
||||||
|
sharedDayAbbrs },
|
||||||
|
{ "islamic.DayNames",
|
||||||
|
sharedDayAbbrs },
|
||||||
|
{ "islamic.DayNarrows",
|
||||||
|
sharedDayNarrows },
|
||||||
|
{ "islamic.Eras",
|
||||||
|
sharedEras },
|
||||||
|
{ "islamic.MonthAbbreviations",
|
||||||
|
new String[] {
|
||||||
|
"Muh.",
|
||||||
|
"Saf.",
|
||||||
|
"Rab. I",
|
||||||
|
"Rab. II",
|
||||||
|
"Jum. I",
|
||||||
|
"Jum. II",
|
||||||
|
"Raj.",
|
||||||
|
"Sha.",
|
||||||
|
"Ram.",
|
||||||
|
"Shaw.",
|
||||||
|
"Dhuʻl-Q.",
|
||||||
|
"Dhuʻl-H.",
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "islamic.MonthNames",
|
||||||
|
new String[] {
|
||||||
|
"Muharram",
|
||||||
|
"Safar",
|
||||||
|
"Rabiʻ I",
|
||||||
|
"Rabiʻ II",
|
||||||
|
"Jumada I",
|
||||||
|
"Jumada II",
|
||||||
|
"Rajab",
|
||||||
|
"Shaʻban",
|
||||||
|
"Ramadan",
|
||||||
|
"Shawwal",
|
||||||
|
"Dhuʻl-Qiʻdah",
|
||||||
|
"Dhuʻl-Hijjah",
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "islamic.MonthNarrows",
|
||||||
|
sharedMonthNarrows },
|
||||||
|
{ "islamic.QuarterNames",
|
||||||
|
sharedQuarterNames },
|
||||||
|
{ "islamic.QuarterNarrows",
|
||||||
|
sharedQuarterNarrows },
|
||||||
|
{ "islamic.TimePatterns",
|
||||||
|
sharedTimePatterns },
|
||||||
|
{ "islamic.abbreviated.AmPmMarkers",
|
||||||
|
sharedAmPmMarkers },
|
||||||
|
{ "islamic.long.Eras",
|
||||||
|
sharedEras },
|
||||||
|
{ "islamic.narrow.Eras",
|
||||||
|
sharedEras },
|
||||||
|
{ "islamic.short.Eras",
|
||||||
|
sharedEras },
|
||||||
|
{ "java.time.buddhist.DatePatterns",
|
||||||
|
sharedJavaTimeDatePatterns },
|
||||||
|
{ "java.time.buddhist.long.Eras",
|
||||||
|
new String[] {
|
||||||
|
"BC",
|
||||||
|
"BE",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "java.time.buddhist.short.Eras",
|
||||||
|
buddhistEras },
|
||||||
|
{ "java.time.islamic.DatePatterns",
|
||||||
|
sharedJavaTimeDatePatterns },
|
||||||
|
{ "java.time.japanese.DatePatterns",
|
||||||
|
new String[] {
|
||||||
|
"G y MMMM d (EEEE)",
|
||||||
|
"G y MMMM d",
|
||||||
|
"G y MMM d",
|
||||||
|
"GGGGGy.MM.dd",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "java.time.japanese.long.Eras",
|
||||||
|
japaneseEras },
|
||||||
|
{ "java.time.japanese.short.Eras",
|
||||||
|
japaneseEras },
|
||||||
|
{ "java.time.long.Eras",
|
||||||
|
new String[] {
|
||||||
|
"BCE",
|
||||||
|
"CE",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "java.time.roc.DatePatterns",
|
||||||
|
sharedJavaTimeDatePatterns },
|
||||||
|
{ "java.time.short.Eras",
|
||||||
|
julianEras },
|
||||||
|
{ "roc.AmPmMarkers",
|
||||||
|
sharedAmPmMarkers },
|
||||||
|
{ "roc.DatePatterns",
|
||||||
|
sharedDatePatterns },
|
||||||
|
{ "roc.DayNames",
|
||||||
|
sharedDayAbbrs },
|
||||||
|
{ "roc.DayNarrows",
|
||||||
|
sharedDayNarrows },
|
||||||
|
{ "roc.Eras",
|
||||||
|
sharedShortEras },
|
||||||
|
{ "roc.MonthAbbreviations",
|
||||||
|
sharedMonthAbbrs },
|
||||||
|
{ "roc.MonthNames",
|
||||||
|
sharedMonthAbbrs },
|
||||||
|
{ "roc.MonthNarrows",
|
||||||
|
sharedMonthNarrows },
|
||||||
|
{ "roc.QuarterNames",
|
||||||
|
sharedQuarterNames },
|
||||||
|
{ "roc.QuarterNarrows",
|
||||||
|
sharedQuarterNarrows },
|
||||||
|
{ "roc.TimePatterns",
|
||||||
|
sharedTimePatterns },
|
||||||
|
{ "roc.abbreviated.AmPmMarkers",
|
||||||
|
sharedAmPmMarkers },
|
||||||
|
{ "roc.long.Eras",
|
||||||
|
sharedShortEras },
|
||||||
|
{ "roc.narrow.AmPmMarkers",
|
||||||
|
sharedAmPmMarkers },
|
||||||
|
{ "roc.narrow.Eras",
|
||||||
|
sharedShortEras },
|
||||||
|
{ "roc.short.Eras",
|
||||||
|
sharedShortEras },
|
||||||
|
{ "timezone.gmtFormat",
|
||||||
|
"GMT{0}" },
|
||||||
|
{ "timezone.hourFormat",
|
||||||
|
"+HH:mm;-HH:mm" },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,353 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation. Oracle designates this
|
|
||||||
* particular file as subject to the "Classpath" exception as provided
|
|
||||||
* by Oracle in the LICENSE file that accompanied this code.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* COPYRIGHT AND PERMISSION NOTICE
|
|
||||||
*
|
|
||||||
* Copyright (C) 1991-2016 Unicode, Inc. All rights reserved.
|
|
||||||
* Distributed under the Terms of Use in
|
|
||||||
* http://www.unicode.org/copyright.html.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of the Unicode data files and any associated documentation
|
|
||||||
* (the "Data Files") or Unicode software and any associated documentation
|
|
||||||
* (the "Software") to deal in the Data Files or Software
|
|
||||||
* without restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, and/or sell copies of
|
|
||||||
* the Data Files or Software, and to permit persons to whom the Data Files
|
|
||||||
* or Software are furnished to do so, provided that
|
|
||||||
* (a) this copyright and permission notice appear with all copies
|
|
||||||
* of the Data Files or Software,
|
|
||||||
* (b) this copyright and permission notice appear in associated
|
|
||||||
* documentation, and
|
|
||||||
* (c) there is clear notice in each modified Data File or in the Software
|
|
||||||
* as well as in the documentation associated with the Data File(s) or
|
|
||||||
* Software that the data or software has been modified.
|
|
||||||
*
|
|
||||||
* THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
||||||
* ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
||||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
||||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
|
|
||||||
* NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
|
|
||||||
* DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
||||||
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
||||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
||||||
* PERFORMANCE OF THE DATA FILES OR SOFTWARE.
|
|
||||||
*
|
|
||||||
* Except as contained in this notice, the name of a copyright holder
|
|
||||||
* shall not be used in advertising or otherwise to promote the sale,
|
|
||||||
* use or other dealings in these Data Files or Software without prior
|
|
||||||
* written authorization of the copyright holder.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Note: this file has been generated by a tool.
|
|
||||||
|
|
||||||
package sun.text.resources;
|
|
||||||
|
|
||||||
import sun.util.resources.OpenListResourceBundle;
|
|
||||||
|
|
||||||
public class JavaTimeSupplementary extends OpenListResourceBundle {
|
|
||||||
@Override
|
|
||||||
protected final Object[][] getContents() {
|
|
||||||
final String[] sharedQuarterNames = {
|
|
||||||
"Q1",
|
|
||||||
"Q2",
|
|
||||||
"Q3",
|
|
||||||
"Q4",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedQuarterNarrows = {
|
|
||||||
"1",
|
|
||||||
"2",
|
|
||||||
"3",
|
|
||||||
"4",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedDatePatterns = {
|
|
||||||
"GGGG y MMMM d, EEEE",
|
|
||||||
"GGGG y MMMM d",
|
|
||||||
"GGGG y MMM d",
|
|
||||||
"G y-MM-dd",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedDayNames = {
|
|
||||||
"Sun",
|
|
||||||
"Mon",
|
|
||||||
"Tue",
|
|
||||||
"Wed",
|
|
||||||
"Thu",
|
|
||||||
"Fri",
|
|
||||||
"Sat",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedDayNarrows = {
|
|
||||||
"S",
|
|
||||||
"M",
|
|
||||||
"T",
|
|
||||||
"W",
|
|
||||||
"T",
|
|
||||||
"F",
|
|
||||||
"S",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedEras = {
|
|
||||||
"",
|
|
||||||
"AH",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedMonthNarrows = {
|
|
||||||
"1",
|
|
||||||
"2",
|
|
||||||
"3",
|
|
||||||
"4",
|
|
||||||
"5",
|
|
||||||
"6",
|
|
||||||
"7",
|
|
||||||
"8",
|
|
||||||
"9",
|
|
||||||
"10",
|
|
||||||
"11",
|
|
||||||
"12",
|
|
||||||
"",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedTimePatterns = {
|
|
||||||
"HH:mm:ss zzzz",
|
|
||||||
"HH:mm:ss z",
|
|
||||||
"HH:mm:ss",
|
|
||||||
"HH:mm",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedAmPmMarkers = {
|
|
||||||
"AM",
|
|
||||||
"PM",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedJavaTimeDatePatterns = {
|
|
||||||
"G y MMMM d, EEEE",
|
|
||||||
"G y MMMM d",
|
|
||||||
"G y MMM d",
|
|
||||||
"GGGGG y-MM-dd",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedJavaTimeLongEras = {
|
|
||||||
"",
|
|
||||||
"Meiji",
|
|
||||||
"Taisho",
|
|
||||||
"Showa",
|
|
||||||
"Heisei",
|
|
||||||
"Reiwa",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedShortEras = {
|
|
||||||
"Before R.O.C.",
|
|
||||||
"R.O.C.",
|
|
||||||
};
|
|
||||||
|
|
||||||
final String[] sharedMonthNames = {
|
|
||||||
"Jan",
|
|
||||||
"Feb",
|
|
||||||
"Mar",
|
|
||||||
"Apr",
|
|
||||||
"May",
|
|
||||||
"Jun",
|
|
||||||
"Jul",
|
|
||||||
"Aug",
|
|
||||||
"Sep",
|
|
||||||
"Oct",
|
|
||||||
"Nov",
|
|
||||||
"Dec",
|
|
||||||
"",
|
|
||||||
};
|
|
||||||
|
|
||||||
return new Object[][] {
|
|
||||||
{ "QuarterAbbreviations",
|
|
||||||
sharedQuarterNames },
|
|
||||||
{ "QuarterNames",
|
|
||||||
sharedQuarterNames },
|
|
||||||
{ "QuarterNarrows",
|
|
||||||
sharedQuarterNarrows },
|
|
||||||
{ "field.dayperiod",
|
|
||||||
"Dayperiod" },
|
|
||||||
{ "field.era",
|
|
||||||
"Era" },
|
|
||||||
{ "field.hour",
|
|
||||||
"Hour" },
|
|
||||||
{ "field.minute",
|
|
||||||
"Minute" },
|
|
||||||
{ "field.month",
|
|
||||||
"Month" },
|
|
||||||
{ "field.second",
|
|
||||||
"Second" },
|
|
||||||
{ "field.week",
|
|
||||||
"Week" },
|
|
||||||
{ "field.weekday",
|
|
||||||
"Day of the Week" },
|
|
||||||
{ "field.year",
|
|
||||||
"Year" },
|
|
||||||
{ "field.zone",
|
|
||||||
"Zone" },
|
|
||||||
{ "islamic.DatePatterns",
|
|
||||||
sharedDatePatterns },
|
|
||||||
{ "islamic.DayAbbreviations",
|
|
||||||
sharedDayNames },
|
|
||||||
{ "islamic.DayNames",
|
|
||||||
sharedDayNames },
|
|
||||||
{ "islamic.DayNarrows",
|
|
||||||
sharedDayNarrows },
|
|
||||||
{ "islamic.Eras",
|
|
||||||
sharedEras },
|
|
||||||
{ "islamic.MonthAbbreviations",
|
|
||||||
new String[] {
|
|
||||||
"Muh.",
|
|
||||||
"Saf.",
|
|
||||||
"Rab. I",
|
|
||||||
"Rab. II",
|
|
||||||
"Jum. I",
|
|
||||||
"Jum. II",
|
|
||||||
"Raj.",
|
|
||||||
"Sha.",
|
|
||||||
"Ram.",
|
|
||||||
"Shaw.",
|
|
||||||
"Dhuʻl-Q.",
|
|
||||||
"Dhuʻl-H.",
|
|
||||||
"",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "islamic.MonthNames",
|
|
||||||
new String[] {
|
|
||||||
"Muharram",
|
|
||||||
"Safar",
|
|
||||||
"Rabiʻ I",
|
|
||||||
"Rabiʻ II",
|
|
||||||
"Jumada I",
|
|
||||||
"Jumada II",
|
|
||||||
"Rajab",
|
|
||||||
"Shaʻban",
|
|
||||||
"Ramadan",
|
|
||||||
"Shawwal",
|
|
||||||
"Dhuʻl-Qiʻdah",
|
|
||||||
"Dhuʻl-Hijjah",
|
|
||||||
"",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "islamic.MonthNarrows",
|
|
||||||
sharedMonthNarrows },
|
|
||||||
{ "islamic.QuarterNames",
|
|
||||||
sharedQuarterNames },
|
|
||||||
{ "islamic.QuarterNarrows",
|
|
||||||
sharedQuarterNarrows },
|
|
||||||
{ "islamic.TimePatterns",
|
|
||||||
sharedTimePatterns },
|
|
||||||
{ "islamic.abbreviated.AmPmMarkers",
|
|
||||||
sharedAmPmMarkers },
|
|
||||||
{ "islamic.long.Eras",
|
|
||||||
sharedEras },
|
|
||||||
{ "islamic.narrow.Eras",
|
|
||||||
sharedEras },
|
|
||||||
{ "islamic.short.Eras",
|
|
||||||
sharedEras },
|
|
||||||
{ "java.time.buddhist.DatePatterns",
|
|
||||||
sharedJavaTimeDatePatterns },
|
|
||||||
{ "java.time.buddhist.long.Eras",
|
|
||||||
new String[] {
|
|
||||||
"BC",
|
|
||||||
"BE",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "java.time.buddhist.short.Eras",
|
|
||||||
new String[] {
|
|
||||||
"BC",
|
|
||||||
"B.E.",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "java.time.islamic.DatePatterns",
|
|
||||||
sharedJavaTimeDatePatterns },
|
|
||||||
{ "java.time.japanese.DatePatterns",
|
|
||||||
new String[] {
|
|
||||||
"G y MMMM d (EEEE)",
|
|
||||||
"G y MMMM d",
|
|
||||||
"G y MMM d",
|
|
||||||
"GGGGGy.MM.dd",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "java.time.japanese.long.Eras",
|
|
||||||
sharedJavaTimeLongEras },
|
|
||||||
{ "java.time.japanese.short.Eras",
|
|
||||||
sharedJavaTimeLongEras },
|
|
||||||
{ "java.time.long.Eras",
|
|
||||||
new String[] {
|
|
||||||
"BCE",
|
|
||||||
"CE",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "java.time.roc.DatePatterns",
|
|
||||||
sharedJavaTimeDatePatterns },
|
|
||||||
{ "java.time.short.Eras",
|
|
||||||
new String[] {
|
|
||||||
"BC",
|
|
||||||
"AD",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "roc.AmPmMarkers",
|
|
||||||
sharedAmPmMarkers },
|
|
||||||
{ "roc.DatePatterns",
|
|
||||||
sharedDatePatterns },
|
|
||||||
{ "roc.DayNames",
|
|
||||||
sharedDayNames },
|
|
||||||
{ "roc.DayNarrows",
|
|
||||||
sharedDayNarrows },
|
|
||||||
{ "roc.Eras",
|
|
||||||
sharedShortEras },
|
|
||||||
{ "roc.MonthAbbreviations",
|
|
||||||
sharedMonthNames },
|
|
||||||
{ "roc.MonthNames",
|
|
||||||
sharedMonthNames },
|
|
||||||
{ "roc.MonthNarrows",
|
|
||||||
sharedMonthNarrows },
|
|
||||||
{ "roc.QuarterNames",
|
|
||||||
sharedQuarterNames },
|
|
||||||
{ "roc.QuarterNarrows",
|
|
||||||
sharedQuarterNarrows },
|
|
||||||
{ "roc.TimePatterns",
|
|
||||||
sharedTimePatterns },
|
|
||||||
{ "roc.abbreviated.AmPmMarkers",
|
|
||||||
sharedAmPmMarkers },
|
|
||||||
{ "roc.long.Eras",
|
|
||||||
sharedShortEras },
|
|
||||||
{ "roc.narrow.AmPmMarkers",
|
|
||||||
sharedAmPmMarkers },
|
|
||||||
{ "roc.narrow.Eras",
|
|
||||||
sharedShortEras },
|
|
||||||
{ "roc.short.Eras",
|
|
||||||
sharedShortEras },
|
|
||||||
{ "timezone.gmtFormat",
|
|
||||||
"GMT{0}" },
|
|
||||||
{ "timezone.hourFormat",
|
|
||||||
"+HH:mm;-HH:mm" },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation. Oracle designates this
|
|
||||||
* particular file as subject to the "Classpath" exception as provided
|
|
||||||
* by Oracle in the LICENSE file that accompanied this code.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sun.text.resources;
|
|
||||||
|
|
||||||
import java.util.spi.ResourceBundleProvider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for the internal locale data provider for which {@code ResourceBundle}
|
|
||||||
* searches.
|
|
||||||
*/
|
|
||||||
public interface JavaTimeSupplementaryProvider extends ResourceBundleProvider {
|
|
||||||
}
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -65,7 +65,6 @@ import java.util.stream.Stream;
|
|||||||
import jdk.internal.util.StaticProperty;
|
import jdk.internal.util.StaticProperty;
|
||||||
import sun.util.resources.LocaleData;
|
import sun.util.resources.LocaleData;
|
||||||
import sun.util.resources.OpenListResourceBundle;
|
import sun.util.resources.OpenListResourceBundle;
|
||||||
import sun.util.resources.ParallelListResourceBundle;
|
|
||||||
import sun.util.resources.TimeZoneNamesBundle;
|
import sun.util.resources.TimeZoneNamesBundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -579,11 +578,7 @@ public class LocaleResources {
|
|||||||
* resources required by JSR 310.
|
* resources required by JSR 310.
|
||||||
*/
|
*/
|
||||||
public ResourceBundle getJavaTimeFormatData() {
|
public ResourceBundle getJavaTimeFormatData() {
|
||||||
ResourceBundle rb = localeData.getDateFormatData(locale);
|
return localeData.getDateFormatData(locale);
|
||||||
if (rb instanceof ParallelListResourceBundle) {
|
|
||||||
localeData.setSupplementary((ParallelListResourceBundle) rb);
|
|
||||||
}
|
|
||||||
return rb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -44,7 +44,6 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.MissingResourceException;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -143,30 +142,6 @@ public class LocaleData {
|
|||||||
return getBundle(type.getTextResourcesPackage() + ".FormatData", locale);
|
return getBundle(type.getTextResourcesPackage() + ".FormatData", locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSupplementary(ParallelListResourceBundle formatData) {
|
|
||||||
if (!formatData.areParallelContentsComplete()) {
|
|
||||||
String suppName = type.getTextResourcesPackage() + ".JavaTimeSupplementary";
|
|
||||||
setSupplementary(suppName, formatData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean setSupplementary(String suppName, ParallelListResourceBundle formatData) {
|
|
||||||
ParallelListResourceBundle parent = (ParallelListResourceBundle) formatData.getParent();
|
|
||||||
boolean resetKeySet = false;
|
|
||||||
if (parent != null) {
|
|
||||||
resetKeySet = setSupplementary(suppName, parent);
|
|
||||||
}
|
|
||||||
OpenListResourceBundle supp = getSupplementary(suppName, formatData.getLocale());
|
|
||||||
formatData.setParallelContents(supp);
|
|
||||||
resetKeySet |= supp != null;
|
|
||||||
// If any parents or this bundle has parallel data, reset keyset to create
|
|
||||||
// a new keyset with the data.
|
|
||||||
if (resetKeySet) {
|
|
||||||
formatData.resetKeySet();
|
|
||||||
}
|
|
||||||
return resetKeySet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a number format data resource bundle, using privileges
|
* Gets a number format data resource bundle, using privileges
|
||||||
* to allow accessing a sun.* package.
|
* to allow accessing a sun.* package.
|
||||||
@ -179,18 +154,7 @@ public class LocaleData {
|
|||||||
return Bundles.of(baseName, locale, LocaleDataStrategy.INSTANCE);
|
return Bundles.of(baseName, locale, LocaleDataStrategy.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OpenListResourceBundle getSupplementary(final String baseName, final Locale locale) {
|
public abstract static class LocaleDataResourceBundleProvider
|
||||||
OpenListResourceBundle rb = null;
|
|
||||||
try {
|
|
||||||
rb = (OpenListResourceBundle) Bundles.of(baseName, locale,
|
|
||||||
SupplementaryStrategy.INSTANCE);
|
|
||||||
} catch (MissingResourceException e) {
|
|
||||||
// return null if no supplementary is available
|
|
||||||
}
|
|
||||||
return rb;
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract static class LocaleDataResourceBundleProvider
|
|
||||||
implements ResourceBundleProvider {
|
implements ResourceBundleProvider {
|
||||||
/**
|
/**
|
||||||
* Changes baseName to its module dependent package name and
|
* Changes baseName to its module dependent package name and
|
||||||
@ -212,20 +176,6 @@ public class LocaleData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A ResourceBundleProvider implementation for loading locale data
|
|
||||||
* resource bundles except for the java.time supplementary data.
|
|
||||||
*/
|
|
||||||
public abstract static class CommonResourceBundleProvider extends LocaleDataResourceBundleProvider {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ResourceBundleProvider implementation for loading supplementary
|
|
||||||
* resource bundles for java.time.
|
|
||||||
*/
|
|
||||||
public abstract static class SupplementaryResourceBundleProvider extends LocaleDataResourceBundleProvider {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bundles.Strategy implementations
|
// Bundles.Strategy implementations
|
||||||
|
|
||||||
private static class LocaleDataStrategy implements Bundles.Strategy {
|
private static class LocaleDataStrategy implements Bundles.Strategy {
|
||||||
@ -254,18 +204,20 @@ public class LocaleData {
|
|||||||
if (candidates == null) {
|
if (candidates == null) {
|
||||||
LocaleProviderAdapter.Type type = baseName.contains(DOTCLDR) ? CLDR : JRE;
|
LocaleProviderAdapter.Type type = baseName.contains(DOTCLDR) ? CLDR : JRE;
|
||||||
LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
|
LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
|
||||||
candidates = adapter instanceof ResourceBundleBasedAdapter ?
|
candidates = adapter instanceof ResourceBundleBasedAdapter rbba ?
|
||||||
((ResourceBundleBasedAdapter)adapter).getCandidateLocales(baseName, locale) :
|
rbba.getCandidateLocales(baseName, locale) :
|
||||||
defaultControl.getCandidateLocales(baseName, locale);
|
defaultControl.getCandidateLocales(baseName, locale);
|
||||||
|
|
||||||
// Weed out Locales which are known to have no resource bundles
|
// Weed out Locales which are known to have no resource bundles
|
||||||
int lastDot = baseName.lastIndexOf('.');
|
int lastDot = baseName.lastIndexOf('.');
|
||||||
String category = (lastDot >= 0) ? baseName.substring(lastDot + 1) : baseName;
|
String category = (lastDot >= 0) ? baseName.substring(lastDot + 1) : baseName;
|
||||||
Set<String> langtags = ((JRELocaleProviderAdapter)adapter).getLanguageTagSet(category);
|
if (adapter instanceof JRELocaleProviderAdapter jlpa) {
|
||||||
if (!langtags.isEmpty()) {
|
var langtags = jlpa.getLanguageTagSet(category);
|
||||||
for (Iterator<Locale> itr = candidates.iterator(); itr.hasNext();) {
|
if (!langtags.isEmpty()) {
|
||||||
if (!adapter.isSupportedProviderLocale(itr.next(), langtags)) {
|
for (Iterator<Locale> itr = candidates.iterator(); itr.hasNext();) {
|
||||||
itr.remove();
|
if (!jlpa.isSupportedProviderLocale(itr.next(), langtags)) {
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,36 +254,7 @@ public class LocaleData {
|
|||||||
public Class<? extends ResourceBundleProvider> getResourceBundleProviderType(String baseName,
|
public Class<? extends ResourceBundleProvider> getResourceBundleProviderType(String baseName,
|
||||||
Locale locale) {
|
Locale locale) {
|
||||||
return inJavaBaseModule(baseName, locale) ?
|
return inJavaBaseModule(baseName, locale) ?
|
||||||
null : CommonResourceBundleProvider.class;
|
null : LocaleDataResourceBundleProvider.class;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SupplementaryStrategy extends LocaleDataStrategy {
|
|
||||||
private static final SupplementaryStrategy INSTANCE
|
|
||||||
= new SupplementaryStrategy();
|
|
||||||
// TODO: avoid hard-coded Locales
|
|
||||||
private static final Set<Locale> JAVA_BASE_LOCALES
|
|
||||||
= Set.of(Locale.ROOT, Locale.ENGLISH, Locale.US);
|
|
||||||
|
|
||||||
private SupplementaryStrategy() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Locale> getCandidateLocales(String baseName, Locale locale) {
|
|
||||||
// Specify only the given locale
|
|
||||||
return List.of(locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<? extends ResourceBundleProvider> getResourceBundleProviderType(String baseName,
|
|
||||||
Locale locale) {
|
|
||||||
return inJavaBaseModule(baseName, locale) ?
|
|
||||||
null : SupplementaryResourceBundleProvider.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean inJavaBaseModule(String baseName, Locale locale) {
|
|
||||||
return JAVA_BASE_LOCALES.contains(locale);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,259 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation. Oracle designates this
|
|
||||||
* particular file as subject to the "Classpath" exception as provided
|
|
||||||
* by Oracle in the LICENSE file that accompanied this code.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sun.util.resources;
|
|
||||||
|
|
||||||
import java.util.AbstractSet;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.atomic.AtomicMarkableReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ParallelListResourceBundle is another variant of ListResourceBundle
|
|
||||||
* supporting "parallel" contents provided by another resource bundle
|
|
||||||
* (OpenListResourceBundle). Parallel contents, if any, are added into this
|
|
||||||
* bundle on demand.
|
|
||||||
*
|
|
||||||
* @author Masayoshi Okutsu
|
|
||||||
*/
|
|
||||||
public abstract class ParallelListResourceBundle extends ResourceBundle {
|
|
||||||
private volatile ConcurrentMap<String, Object> lookup;
|
|
||||||
private volatile Set<String> keyset;
|
|
||||||
private final AtomicMarkableReference<Object[][]> parallelContents
|
|
||||||
= new AtomicMarkableReference<>(null, false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sole constructor. (For invocation by subclass constructors, typically
|
|
||||||
* implicit.)
|
|
||||||
*/
|
|
||||||
protected ParallelListResourceBundle() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array in which each item is a pair of objects in an
|
|
||||||
* Object array. The first element of each pair is the key, which
|
|
||||||
* must be a String, and the second element is the value
|
|
||||||
* associated with that key. See the class description for
|
|
||||||
* details.
|
|
||||||
*
|
|
||||||
* @return an array of an Object array representing a key-value pair.
|
|
||||||
*/
|
|
||||||
protected abstract Object[][] getContents();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the parent of this resource bundle or null if there's no parent.
|
|
||||||
*
|
|
||||||
* @return the parent or null if no parent
|
|
||||||
*/
|
|
||||||
ResourceBundle getParent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the parallel contents to the data given by rb. If rb is null, this
|
|
||||||
* bundle will be marked as `complete'.
|
|
||||||
*
|
|
||||||
* @param rb an OpenResourceBundle for parallel contents, or null indicating
|
|
||||||
* there are no parallel contents for this bundle
|
|
||||||
*/
|
|
||||||
public void setParallelContents(OpenListResourceBundle rb) {
|
|
||||||
if (rb == null) {
|
|
||||||
parallelContents.compareAndSet(null, null, false, true);
|
|
||||||
} else {
|
|
||||||
parallelContents.compareAndSet(null, rb.getContents(), false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if any parallel contents have been set or if this bundle is
|
|
||||||
* marked as complete.
|
|
||||||
*
|
|
||||||
* @return true if any parallel contents have been processed
|
|
||||||
*/
|
|
||||||
boolean areParallelContentsComplete() {
|
|
||||||
// Quick check for `complete'
|
|
||||||
if (parallelContents.isMarked()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
boolean[] done = new boolean[1];
|
|
||||||
Object[][] data = parallelContents.get(done);
|
|
||||||
return data != null || done[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object handleGetObject(String key) {
|
|
||||||
if (key == null) {
|
|
||||||
throw new NullPointerException();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadLookupTablesIfNecessary();
|
|
||||||
return lookup.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Enumeration<String> getKeys() {
|
|
||||||
return Collections.enumeration(keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsKey(String key) {
|
|
||||||
return keySet().contains(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Set<String> handleKeySet() {
|
|
||||||
loadLookupTablesIfNecessary();
|
|
||||||
return lookup.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("UnusedAssignment")
|
|
||||||
public Set<String> keySet() {
|
|
||||||
Set<String> ks;
|
|
||||||
while ((ks = keyset) == null) {
|
|
||||||
ks = new KeySet(handleKeySet(), parent);
|
|
||||||
synchronized (this) {
|
|
||||||
if (keyset == null) {
|
|
||||||
keyset = ks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discards any cached keyset value. This method is called from
|
|
||||||
* LocaleData for re-creating a KeySet.
|
|
||||||
*/
|
|
||||||
synchronized void resetKeySet() {
|
|
||||||
keyset = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the lookup table if they haven't been loaded already.
|
|
||||||
*/
|
|
||||||
void loadLookupTablesIfNecessary() {
|
|
||||||
ConcurrentMap<String, Object> map = lookup;
|
|
||||||
if (map == null) {
|
|
||||||
map = new ConcurrentHashMap<>();
|
|
||||||
for (Object[] item : getContents()) {
|
|
||||||
map.put((String) item[0], item[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's any parallel contents data, merge the data into map.
|
|
||||||
Object[][] data = parallelContents.getReference();
|
|
||||||
if (data != null) {
|
|
||||||
for (Object[] item : data) {
|
|
||||||
map.putIfAbsent((String) item[0], item[1]);
|
|
||||||
}
|
|
||||||
parallelContents.set(null, true);
|
|
||||||
}
|
|
||||||
if (lookup == null) {
|
|
||||||
synchronized (this) {
|
|
||||||
if (lookup == null) {
|
|
||||||
lookup = map;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class implements the Set interface for
|
|
||||||
* ParallelListResourceBundle methods.
|
|
||||||
*/
|
|
||||||
private static class KeySet extends AbstractSet<String> {
|
|
||||||
private final Set<String> set;
|
|
||||||
private final ResourceBundle parent;
|
|
||||||
|
|
||||||
private KeySet(Set<String> set, ResourceBundle parent) {
|
|
||||||
this.set = set;
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(Object o) {
|
|
||||||
if (set.contains(o)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return (parent != null) ? parent.containsKey((String) o) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<String> iterator() {
|
|
||||||
if (parent == null) {
|
|
||||||
return set.iterator();
|
|
||||||
}
|
|
||||||
return new Iterator<>() {
|
|
||||||
private Iterator<String> itr = set.iterator();
|
|
||||||
private boolean usingParent;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
if (itr.hasNext()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!usingParent) {
|
|
||||||
Set<String> nextset = new HashSet<>(parent.keySet());
|
|
||||||
nextset.removeAll(set);
|
|
||||||
itr = nextset.iterator();
|
|
||||||
usingParent = true;
|
|
||||||
}
|
|
||||||
return itr.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String next() {
|
|
||||||
if (hasNext()) {
|
|
||||||
return itr.next();
|
|
||||||
}
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
if (parent == null) {
|
|
||||||
return set.size();
|
|
||||||
}
|
|
||||||
Set<String> allset = new HashSet<>(set);
|
|
||||||
allset.addAll(parent.keySet());
|
|
||||||
return allset.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -205,6 +205,11 @@ Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_sun_nio_ch_Net_shouldShutdownWriteBeforeClose0(JNIEnv *env, jclass clazz) {
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_sun_nio_ch_Net_shouldSetBothIPv4AndIPv6Options0(JNIEnv* env, jclass cl)
|
Java_sun_nio_ch_Net_shouldSetBothIPv4AndIPv6Options0(JNIEnv* env, jclass cl)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2001, 2023, 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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -117,6 +117,11 @@ Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_sun_nio_ch_Net_shouldShutdownWriteBeforeClose0(JNIEnv *env, jclass clazz) {
|
||||||
|
return JNI_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_sun_nio_ch_Net_shouldSetBothIPv4AndIPv6Options0(JNIEnv* env, jclass cl)
|
Java_sun_nio_ch_Net_shouldSetBothIPv4AndIPv6Options0(JNIEnv* env, jclass cl)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -91,23 +91,4 @@ public class BasicPasswordFieldUI extends BasicTextFieldUI {
|
|||||||
public View create(Element elem) {
|
public View create(Element elem) {
|
||||||
return new PasswordView(elem);
|
return new PasswordView(elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the action map for Password Field. This map provides
|
|
||||||
* same actions for double mouse click and
|
|
||||||
* and for triple mouse click (see bug 4231444).
|
|
||||||
*/
|
|
||||||
|
|
||||||
ActionMap createActionMap() {
|
|
||||||
ActionMap map = super.createActionMap();
|
|
||||||
if (map.get(DefaultEditorKit.selectWordAction) != null) {
|
|
||||||
Action a = map.get(DefaultEditorKit.selectLineAction);
|
|
||||||
if (a != null) {
|
|
||||||
map.remove(DefaultEditorKit.selectWordAction);
|
|
||||||
map.put(DefaultEditorKit.selectWordAction, a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -647,6 +647,22 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
|
|||||||
TransferHandler.getCopyAction());
|
TransferHandler.getCopyAction());
|
||||||
map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
|
map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
|
||||||
TransferHandler.getPasteAction());
|
TransferHandler.getPasteAction());
|
||||||
|
|
||||||
|
if (getComponent() instanceof JPasswordField) {
|
||||||
|
// Edit the action map for Password Field. This map provides
|
||||||
|
// same actions for double mouse click and
|
||||||
|
// and for triple mouse click (see bugs 4231444, 8354646).
|
||||||
|
|
||||||
|
if (map.get(DefaultEditorKit.selectWordAction) != null) {
|
||||||
|
map.remove(DefaultEditorKit.selectWordAction);
|
||||||
|
|
||||||
|
Action a = map.get(DefaultEditorKit.selectLineAction);
|
||||||
|
if (a != null) {
|
||||||
|
map.put(DefaultEditorKit.selectWordAction, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -108,19 +108,4 @@ public class SynthPasswordFieldUI extends SynthTextFieldUI {
|
|||||||
int y, int w, int h) {
|
int y, int w, int h) {
|
||||||
context.getPainter().paintPasswordFieldBorder(context, g, x, y, w, h);
|
context.getPainter().paintPasswordFieldBorder(context, g, x, y, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void installKeyboardActions() {
|
|
||||||
super.installKeyboardActions();
|
|
||||||
ActionMap map = SwingUtilities.getUIActionMap(getComponent());
|
|
||||||
if (map != null && map.get(DefaultEditorKit.selectWordAction) != null) {
|
|
||||||
Action a = map.get(DefaultEditorKit.selectLineAction);
|
|
||||||
if (a != null) {
|
|
||||||
map.put(DefaultEditorKit.selectWordAction, a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -33,6 +33,6 @@ module jdk.localedata {
|
|||||||
provides sun.util.locale.provider.LocaleDataMetaInfo with
|
provides sun.util.locale.provider.LocaleDataMetaInfo with
|
||||||
sun.util.resources.cldr.provider.CLDRLocaleDataMetaInfo,
|
sun.util.resources.cldr.provider.CLDRLocaleDataMetaInfo,
|
||||||
sun.util.resources.provider.NonBaseLocaleDataMetaInfo;
|
sun.util.resources.provider.NonBaseLocaleDataMetaInfo;
|
||||||
provides sun.util.resources.LocaleData.CommonResourceBundleProvider with
|
provides sun.util.resources.LocaleData.LocaleDataResourceBundleProvider with
|
||||||
sun.util.resources.provider.LocaleDataProvider;
|
sun.util.resources.provider.LocaleDataProvider;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,9 +25,9 @@
|
|||||||
|
|
||||||
package sun.text.resources.ext;
|
package sun.text.resources.ext;
|
||||||
|
|
||||||
import sun.util.resources.ParallelListResourceBundle;
|
import sun.util.resources.OpenListResourceBundle;
|
||||||
|
|
||||||
public class FormatData extends ParallelListResourceBundle {
|
public class FormatData extends OpenListResourceBundle {
|
||||||
/**
|
/**
|
||||||
* Exists to keep sun.text.resources.ext package alive
|
* Exists to keep sun.text.resources.ext package alive
|
||||||
* with IncludeLocales jlink plugin
|
* with IncludeLocales jlink plugin
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -76,11 +76,11 @@
|
|||||||
|
|
||||||
package sun.text.resources.ext;
|
package sun.text.resources.ext;
|
||||||
|
|
||||||
import sun.util.resources.ParallelListResourceBundle;
|
import sun.util.resources.OpenListResourceBundle;
|
||||||
|
|
||||||
public class FormatData_ja extends ParallelListResourceBundle {
|
public class FormatData_ja extends OpenListResourceBundle {
|
||||||
/**
|
/**
|
||||||
* Overrides ParallelListResourceBundle
|
* Overrides OpenListResourceBundle
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected final Object[][] getContents() {
|
protected final Object[][] getContents() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -32,7 +32,7 @@ import sun.util.resources.LocaleData;
|
|||||||
/**
|
/**
|
||||||
* Service Provider for loading locale data resource bundles in jdk.localedata
|
* Service Provider for loading locale data resource bundles in jdk.localedata
|
||||||
*/
|
*/
|
||||||
public class LocaleDataProvider extends LocaleData.CommonResourceBundleProvider {
|
public class LocaleDataProvider extends LocaleData.LocaleDataResourceBundleProvider {
|
||||||
@Override
|
@Override
|
||||||
public ResourceBundle getBundle(String baseName, Locale locale) {
|
public ResourceBundle getBundle(String baseName, Locale locale) {
|
||||||
var bundleName = toBundleName(baseName, locale);
|
var bundleName = toBundleName(baseName, locale);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -140,6 +140,9 @@ public class DocCheck extends TestRunner {
|
|||||||
var baseDir = DOCS_DIR.resolve(DIR);
|
var baseDir = DOCS_DIR.resolve(DIR);
|
||||||
fileTester.processFiles(baseDir);
|
fileTester.processFiles(baseDir);
|
||||||
files = fileTester.getFiles();
|
files = fileTester.getFiles();
|
||||||
|
if (html) {
|
||||||
|
new TidyChecker();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileChecker> getCheckers() {
|
public List<FileChecker> getCheckers() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -26,6 +26,6 @@
|
|||||||
* @bug 8337109
|
* @bug 8337109
|
||||||
* @summary Check the html in the generated documentation
|
* @summary Check the html in the generated documentation
|
||||||
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
|
* @library /test/langtools/tools/lib ../../doccheck /test/lib ../../../../tools/tester
|
||||||
* @build DocTester toolbox.TestRunner
|
* @build DocTester toolbox.TestRunner jtreg.SkippedException
|
||||||
* @run main/othervm -Ddoccheck.checks=html DocCheck
|
* @run main/othervm -Ddoccheck.checks=html DocCheck
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -38,6 +38,7 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import jtreg.SkippedException;
|
||||||
|
|
||||||
public class TidyChecker implements FileChecker, AutoCloseable {
|
public class TidyChecker implements FileChecker, AutoCloseable {
|
||||||
private final Path TIDY;
|
private final Path TIDY;
|
||||||
@ -164,8 +165,7 @@ public class TidyChecker implements FileChecker, AutoCloseable {
|
|||||||
if (p.isPresent()) {
|
if (p.isPresent()) {
|
||||||
tidyExePath = p.get();
|
tidyExePath = p.get();
|
||||||
} else {
|
} else {
|
||||||
System.err.println("tidy not found on PATH");
|
throw new jtreg.SkippedException("tidy not found on PATH");
|
||||||
return Path.of("tidy"); //non-null placeholder return; exception would be better
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
158
test/hotspot/gtest/utilities/test_packedTable.cpp
Normal file
158
test/hotspot/gtest/utilities/test_packedTable.cpp
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "utilities/packedTable.hpp"
|
||||||
|
#include "unittest.hpp"
|
||||||
|
|
||||||
|
class Supplier: public PackedTableBuilder::Supplier {
|
||||||
|
uint32_t* _keys;
|
||||||
|
uint32_t* _values;
|
||||||
|
size_t _num_keys;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Supplier(uint32_t* keys, uint32_t* values, size_t num_keys):
|
||||||
|
_keys(keys), _values(values), _num_keys(num_keys) {}
|
||||||
|
|
||||||
|
bool next(uint32_t* key, uint32_t* value) override {
|
||||||
|
if (_num_keys == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*key = *_keys;
|
||||||
|
++_keys;
|
||||||
|
if (_values != nullptr) {
|
||||||
|
*value = *_values;
|
||||||
|
++_values;
|
||||||
|
} else {
|
||||||
|
*value = 0;
|
||||||
|
}
|
||||||
|
--_num_keys;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Comparator: public PackedTableLookup::Comparator {
|
||||||
|
uint32_t _current;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int compare_to(uint32_t key) override {
|
||||||
|
return _current < key ? -1 : (_current > key ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(uint32_t key) DEBUG_ONLY(override) {
|
||||||
|
_current = key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test(uint32_t max_key, uint32_t max_value, unsigned int length) {
|
||||||
|
if (length > max_key + 1) {
|
||||||
|
// can't generate more keys, as keys must be unique
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PackedTableBuilder builder(max_key, max_value);
|
||||||
|
size_t table_length = length * builder.element_bytes();
|
||||||
|
u1* table = new u1[table_length];
|
||||||
|
|
||||||
|
uint32_t* keys = new uint32_t[length];
|
||||||
|
uint32_t* values = max_value != 0 ? new uint32_t[length] : nullptr;
|
||||||
|
for (unsigned int i = 0; i < length; ++i) {
|
||||||
|
keys[i] = i;
|
||||||
|
if (values != nullptr) {
|
||||||
|
values[i] = i % max_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Supplier sup(keys, values, length);
|
||||||
|
builder.fill(table, table_length, sup);
|
||||||
|
|
||||||
|
Comparator comparator;
|
||||||
|
PackedTableLookup lookup(max_key, max_value, table, table_length);
|
||||||
|
#ifdef ASSERT
|
||||||
|
lookup.validate_order(comparator);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < length; ++i) {
|
||||||
|
uint32_t key, value;
|
||||||
|
comparator.reset(keys[i]);
|
||||||
|
EXPECT_TRUE(lookup.search(comparator, &key, &value));
|
||||||
|
EXPECT_EQ(key, keys[i]);
|
||||||
|
if (values != nullptr) {
|
||||||
|
EXPECT_EQ(value, values[i]);
|
||||||
|
} else {
|
||||||
|
EXPECT_EQ(value, 0U);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] keys;
|
||||||
|
delete[] values;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_with_bits(uint32_t max_key, uint32_t max_value) {
|
||||||
|
// Some small sizes
|
||||||
|
for (unsigned int i = 0; i <= 100; ++i) {
|
||||||
|
test(max_key, max_value, i);
|
||||||
|
}
|
||||||
|
test(max_key, max_value, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PackedTableLookup, lookup) {
|
||||||
|
for (int key_bits = 1; key_bits <= 32; ++key_bits) {
|
||||||
|
for (int value_bits = 0; value_bits <= 32; ++value_bits) {
|
||||||
|
test_with_bits(static_cast<uint32_t>((1ULL << key_bits) - 1),
|
||||||
|
static_cast<uint32_t>((1ULL << value_bits) - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PackedTableBase, element_bytes) {
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(1, 0);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 1U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(15, 15);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 1U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(15, 16);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 2U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(31, 7);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 1U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(32, 7);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 2U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(-1, 0);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 4U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(-1, 1);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 5U);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PackedTableBuilder builder(-1, -1);
|
||||||
|
EXPECT_EQ(builder.element_bytes(), 8U);
|
||||||
|
}
|
||||||
|
}
|
@ -91,6 +91,8 @@ requires.properties= \
|
|||||||
vm.compiler1.enabled \
|
vm.compiler1.enabled \
|
||||||
vm.compiler2.enabled \
|
vm.compiler2.enabled \
|
||||||
vm.musl \
|
vm.musl \
|
||||||
|
vm.asan \
|
||||||
|
vm.ubsan \
|
||||||
vm.flagless \
|
vm.flagless \
|
||||||
container.support \
|
container.support \
|
||||||
systemd.support \
|
systemd.support \
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,9 +25,9 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 8238756
|
* @bug 8238756
|
||||||
* @requires vm.debug == true & vm.flavor == "server"
|
* @requires vm.debug == true & vm.flavor == "server"
|
||||||
* @summary Run with -Xcomp to test -XX:VerifyIterativeGVN=11 in debug builds.
|
* @summary Run with -Xcomp to test -XX:VerifyIterativeGVN=1111 in debug builds.
|
||||||
*
|
*
|
||||||
* @run main/othervm/timeout=300 -Xbatch -Xcomp -XX:VerifyIterativeGVN=11 compiler.c2.TestVerifyIterativeGVN
|
* @run main/othervm/timeout=300 -Xcomp -XX:VerifyIterativeGVN=1111 compiler.c2.TestVerifyIterativeGVN
|
||||||
*/
|
*/
|
||||||
package compiler.c2;
|
package compiler.c2;
|
||||||
|
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 8359121
|
||||||
|
* @summary Region node introduced by ArraysSupport.mismatch must be disconnected,
|
||||||
|
* and not just put aside as dead: when simplifying
|
||||||
|
* Proj -> Region -> If -> ...
|
||||||
|
* into
|
||||||
|
* Proj -> If -> ...
|
||||||
|
* -> Region
|
||||||
|
* the dead Region node must be removed from Proj's outputs.
|
||||||
|
* @modules java.base/jdk.internal.util
|
||||||
|
* @run main/othervm -Xcomp
|
||||||
|
* -XX:CompileCommand=compileonly,compiler.igvn.RemoveDeadRegionFromVectorizedMismatchIntrinsic::test
|
||||||
|
* compiler.igvn.RemoveDeadRegionFromVectorizedMismatchIntrinsic
|
||||||
|
* @run main compiler.igvn.RemoveDeadRegionFromVectorizedMismatchIntrinsic
|
||||||
|
*/
|
||||||
|
package compiler.igvn;
|
||||||
|
|
||||||
|
import jdk.internal.util.ArraysSupport;
|
||||||
|
|
||||||
|
public class RemoveDeadRegionFromVectorizedMismatchIntrinsic {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ArraysSupport.mismatch(new int[0], new int[0], 0); // loads ArraysSupport
|
||||||
|
test(new byte[0], new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int test(byte[] a, byte[] b) {
|
||||||
|
int i = ArraysSupport.vectorizedMismatch(a, 0, b, 0, 0, 0);
|
||||||
|
return i >= 0 ? i : 0;
|
||||||
|
}
|
||||||
|
}
|
@ -122,10 +122,11 @@ public class BMITestRunner {
|
|||||||
List<String> vmOpts = new LinkedList<String>();
|
List<String> vmOpts = new LinkedList<String>();
|
||||||
|
|
||||||
Collections.addAll(vmOpts, additionalVMOpts);
|
Collections.addAll(vmOpts, additionalVMOpts);
|
||||||
// Hide timestamps from warnings (e.g. due to potential CDS
|
// Hide timestamps from warnings (e.g. due to potential AOT
|
||||||
// saved/runtime state mismatch), to avoid false positives when
|
// saved/runtime state mismatch), to avoid false positives when
|
||||||
// comparing output across runs.
|
// comparing output across runs.
|
||||||
vmOpts.add("-Xlog:all=warning:stdout:level,tags");
|
vmOpts.add("-Xlog:all=warning:stdout:level,tags");
|
||||||
|
vmOpts.add("-Xlog:aot=off");
|
||||||
|
|
||||||
//setup mode-specific options
|
//setup mode-specific options
|
||||||
switch (testVMMode) {
|
switch (testVMMode) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -57,6 +57,19 @@ public class AndnTestI extends BmiIntrinsicBase.BmiTestCase {
|
|||||||
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||||
(byte) 0x00,
|
(byte) 0x00,
|
||||||
(byte) 0xF2};
|
(byte) 0xF2};
|
||||||
|
// from intel apx specifications EVEX.128.NP.0F38.W0 F2 /r
|
||||||
|
instrMaskAPX = new byte[]{
|
||||||
|
(byte) 0xFF,
|
||||||
|
(byte) 0x07,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xFF};
|
||||||
|
instrPatternAPX = new byte[]{
|
||||||
|
(byte) 0x62, // fixed prefix byte 0x62 for extended EVEX instruction
|
||||||
|
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xF2};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -59,6 +59,23 @@ public class BlsiTestI extends BmiIntrinsicBase.BmiTestCase {
|
|||||||
(byte) 0x00,
|
(byte) 0x00,
|
||||||
(byte) 0xF3,
|
(byte) 0xF3,
|
||||||
(byte) 0b0001_1000}; // bits 543 == 011 (3)
|
(byte) 0b0001_1000}; // bits 543 == 011 (3)
|
||||||
|
|
||||||
|
// from intel apx specifications EVEX.128.NP.0F38.W0 F3 /3(opcode extension)
|
||||||
|
instrMaskAPX = new byte[]{
|
||||||
|
(byte) 0xFF,
|
||||||
|
(byte) 0x07,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xFF,
|
||||||
|
(byte) 0x38};
|
||||||
|
|
||||||
|
instrPatternAPX = new byte[]{
|
||||||
|
(byte) 0x62, // fixed prefix byte 0x62 for extended EVEX instruction
|
||||||
|
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xF3,
|
||||||
|
(byte) 0b0001_1000}; // bits 543 == 011 (3)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -57,7 +57,24 @@ public class BlsmskTestI extends BmiIntrinsicBase.BmiTestCase {
|
|||||||
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||||
(byte) 0x00,
|
(byte) 0x00,
|
||||||
(byte) 0xF3,
|
(byte) 0xF3,
|
||||||
(byte) 0b0001_0000}; // bits 543 == 011 (3)
|
(byte) 0b0001_0000}; // bits 543 == 010 (2)
|
||||||
|
|
||||||
|
// from intel apx specifications EVEX.128.NP.0F38.W1 F3 /2(opcode extension part of ModRM.REG)
|
||||||
|
instrMaskAPX = new byte[]{
|
||||||
|
(byte) 0xFF,
|
||||||
|
(byte) 0x07,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xFF,
|
||||||
|
(byte) 0x38};
|
||||||
|
|
||||||
|
instrPatternAPX = new byte[]{
|
||||||
|
(byte) 0x62, // fixed prefix byte 0x62 for extended EVEX instruction
|
||||||
|
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xF3,
|
||||||
|
(byte) 0b0001_0000}; // bits 543 == 010 (2)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -58,7 +58,25 @@ public class BlsrTestI extends BmiIntrinsicBase.BmiTestCase {
|
|||||||
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||||
(byte) 0x00,
|
(byte) 0x00,
|
||||||
(byte) 0xF3,
|
(byte) 0xF3,
|
||||||
(byte) 0b0000_1000}; // bits 543 == 011 (3)
|
(byte) 0b0000_1000}; // bits 543 == 001 (1)
|
||||||
|
|
||||||
|
// from intel apx specifications EVEX.128.NP.0F38.W1 F3 /1(opcode extension part of ModRM.REG)
|
||||||
|
instrMaskAPX = new byte[]{
|
||||||
|
(byte) 0xFF,
|
||||||
|
(byte) 0x07,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xFF,
|
||||||
|
(byte) 0x38};
|
||||||
|
|
||||||
|
instrPatternAPX = new byte[]{
|
||||||
|
(byte) 0x62, // fixed prefix byte 0x62 for extended EVEX instruction
|
||||||
|
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xF3,
|
||||||
|
(byte) 0b0000_1000}; // bits 543 == 001 (1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -111,7 +111,8 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest {
|
|||||||
protected void checkEmittedCode(Executable executable) {
|
protected void checkEmittedCode(Executable executable) {
|
||||||
final byte[] nativeCode = NMethod.get(executable, false).insts;
|
final byte[] nativeCode = NMethod.get(executable, false).insts;
|
||||||
final byte[] matchInstrPattern = (((BmiTestCase) testCase).getTestCaseX64() && Platform.isX64()) ? ((BmiTestCase_x64) testCase).getInstrPattern_x64() : ((BmiTestCase) testCase).getInstrPattern();
|
final byte[] matchInstrPattern = (((BmiTestCase) testCase).getTestCaseX64() && Platform.isX64()) ? ((BmiTestCase_x64) testCase).getInstrPattern_x64() : ((BmiTestCase) testCase).getInstrPattern();
|
||||||
if (!((BmiTestCase) testCase).verifyPositive(nativeCode)) {
|
boolean use_apx = CPUInfo.hasFeature("apx_f");
|
||||||
|
if (!((BmiTestCase) testCase).verifyPositive(nativeCode, use_apx)) {
|
||||||
throw new AssertionError(testCase.name() + " " + "CPU instructions expected not found in nativeCode: " + Utils.toHexString(nativeCode) + " ---- Expected instrPattern: " +
|
throw new AssertionError(testCase.name() + " " + "CPU instructions expected not found in nativeCode: " + Utils.toHexString(nativeCode) + " ---- Expected instrPattern: " +
|
||||||
Utils.toHexString(matchInstrPattern));
|
Utils.toHexString(matchInstrPattern));
|
||||||
} else {
|
} else {
|
||||||
@ -124,6 +125,8 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest {
|
|||||||
private final Method method;
|
private final Method method;
|
||||||
protected byte[] instrMask;
|
protected byte[] instrMask;
|
||||||
protected byte[] instrPattern;
|
protected byte[] instrPattern;
|
||||||
|
protected byte[] instrMaskAPX;
|
||||||
|
protected byte[] instrPatternAPX;
|
||||||
protected boolean isLongOperation;
|
protected boolean isLongOperation;
|
||||||
protected String cpuFlag = "bmi1";
|
protected String cpuFlag = "bmi1";
|
||||||
protected String vmFlag = "UseBMI1Instructions";
|
protected String vmFlag = "UseBMI1Instructions";
|
||||||
@ -160,6 +163,13 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest {
|
|||||||
return countCpuInstructions(nativeCode, instrMask, instrPattern);
|
return countCpuInstructions(nativeCode, instrMask, instrPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected int countCpuInstructionsAPX(byte[] nativeCode) {
|
||||||
|
if (instrMaskAPX == null || instrPatternAPX == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return countCpuInstructions(nativeCode, instrMaskAPX, instrPatternAPX);
|
||||||
|
}
|
||||||
|
|
||||||
public static int countCpuInstructions(byte[] nativeCode, byte[] instrMask, byte[] instrPattern) {
|
public static int countCpuInstructions(byte[] nativeCode, byte[] instrMask, byte[] instrPattern) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int patternSize = Math.min(instrMask.length, instrPattern.length);
|
int patternSize = Math.min(instrMask.length, instrPattern.length);
|
||||||
@ -181,8 +191,12 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean verifyPositive(byte[] nativeCode) {
|
public boolean verifyPositive(byte[] nativeCode, boolean use_apx) {
|
||||||
final int cnt = countCpuInstructions(nativeCode);
|
int cnt = countCpuInstructions(nativeCode);
|
||||||
|
if (use_apx) {
|
||||||
|
System.out.println("CHECKING APX INST PATTERNS");
|
||||||
|
cnt += countCpuInstructionsAPX(nativeCode);
|
||||||
|
}
|
||||||
if (Platform.isX86()) {
|
if (Platform.isX86()) {
|
||||||
return cnt >= (isLongOperation ? 2 : 1);
|
return cnt >= (isLongOperation ? 2 : 1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -73,6 +73,21 @@ public class BzhiTestI2L extends BmiIntrinsicBase.BmiTestCase_x64 {
|
|||||||
(byte) 0x62, // 00010 implied 0F 38 leading opcode bytes
|
(byte) 0x62, // 00010 implied 0F 38 leading opcode bytes
|
||||||
(byte) 0xA8,
|
(byte) 0xA8,
|
||||||
(byte) 0xF5};
|
(byte) 0xF5};
|
||||||
|
|
||||||
|
// from intel apx specifications EVEX.128.NP.0F38.W0 F5 /r
|
||||||
|
instrMaskAPX = new byte[]{
|
||||||
|
(byte) 0xFF,
|
||||||
|
(byte) 0x07,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xFF};
|
||||||
|
|
||||||
|
instrPatternAPX = new byte[]{
|
||||||
|
(byte) 0x62, // fixed prefix byte 0x62 for extended EVEX instruction
|
||||||
|
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0x00,
|
||||||
|
(byte) 0xF5};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -51,6 +51,10 @@ public class LZcntTestI extends BmiIntrinsicBase.BmiTestCase_x64 {
|
|||||||
|
|
||||||
instrMask_x64 = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF};
|
instrMask_x64 = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF};
|
||||||
instrPattern_x64 = new byte[]{(byte) 0xF3, (byte) 0x00, (byte) 0x0F, (byte) 0xBD};
|
instrPattern_x64 = new byte[]{(byte) 0xF3, (byte) 0x00, (byte) 0x0F, (byte) 0xBD};
|
||||||
|
|
||||||
|
// REX2 variant
|
||||||
|
instrMaskAPX = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte)0x80, (byte) 0xFF};
|
||||||
|
instrPatternAPX = new byte[]{(byte) 0xF3, (byte) 0xD5, (byte) 0x80, (byte) 0xBD};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -50,6 +50,10 @@ public class TZcntTestI extends BmiIntrinsicBase.BmiTestCase_x64 {
|
|||||||
|
|
||||||
instrMask_x64 = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF};
|
instrMask_x64 = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF};
|
||||||
instrPattern_x64 = new byte[]{(byte) 0xF3, (byte) 0x00, (byte) 0x0F, (byte) 0xBC};
|
instrPattern_x64 = new byte[]{(byte) 0xF3, (byte) 0x00, (byte) 0x0F, (byte) 0xBC};
|
||||||
|
|
||||||
|
// REX2 variant
|
||||||
|
instrMaskAPX = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte)0x80, (byte) 0xFF};
|
||||||
|
instrPatternAPX = new byte[]{(byte) 0xF3, (byte) 0xD5, (byte) 0x80, (byte) 0xBC};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package compiler.lib.template_framework.library;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import compiler.lib.ir_framework.TestFramework;
|
||||||
|
import compiler.lib.compile_framework.CompileFramework;
|
||||||
|
import compiler.lib.template_framework.Template;
|
||||||
|
import compiler.lib.template_framework.TemplateToken;
|
||||||
|
import static compiler.lib.template_framework.Template.body;
|
||||||
|
import static compiler.lib.template_framework.Template.let;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a {@link #render} method that can be used to simplify generating
|
||||||
|
* source code when using the {@link TestFramework} (also known as IR Framework) to run
|
||||||
|
* a list of tests.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The idea is that the user only has to generate the code for the individual tests,
|
||||||
|
* and can then pass the corresponding list of {@link TemplateToken}s to this
|
||||||
|
* provided {@link #render} method which generates the surrounding class and the main
|
||||||
|
* method that invokes the {@link TestFramework}, so that all the generated tests
|
||||||
|
* are run.
|
||||||
|
*/
|
||||||
|
public final class TestFrameworkClass {
|
||||||
|
|
||||||
|
// Ensure there can be no instance, and we do not have to document the constructor.
|
||||||
|
private TestFrameworkClass() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method renders a list of {@code testTemplateTokens} into the body of a class
|
||||||
|
* and generates a {@code main} method which launches the {@link TestFramework}
|
||||||
|
* to run the generated tests.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The generated {@code main} method is to be invoked with a {@code vmFlags} argument,
|
||||||
|
* which must be a {@link String[]}, specifying the VM flags for the Test VM, in which
|
||||||
|
* the tests will be run. Thus, one can generate the test class once, and invoke its
|
||||||
|
* {@code main} method multiple times, each time with a different set of VM flags.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The internal {@link Template} sets the {@link Hooks#CLASS_HOOK} for the scope of
|
||||||
|
* all test methods.
|
||||||
|
*
|
||||||
|
* @param packageName The package name of the test class.
|
||||||
|
* @param className The name of the test class.
|
||||||
|
* @param imports A set of imports.
|
||||||
|
* @param classpath The classpath from {@link CompileFramework#getEscapedClassPathOfCompiledClasses},
|
||||||
|
* so that the Test VM has access to the class files that are compiled from the
|
||||||
|
* generated source code.
|
||||||
|
* @param testTemplateTokens The list of tests to be generated into the test class.
|
||||||
|
* Every test must be annotated with {@code @Test}, so that
|
||||||
|
* the {@link TestFramework} can later find and run them.
|
||||||
|
* @return The generated source code of the test class as a {@link String}.
|
||||||
|
*/
|
||||||
|
public static String render(final String packageName,
|
||||||
|
final String className,
|
||||||
|
final Set<String> imports,
|
||||||
|
final String classpath,
|
||||||
|
final List<TemplateToken> testTemplateTokens) {
|
||||||
|
var template = Template.make(() -> body(
|
||||||
|
let("packageName", packageName),
|
||||||
|
let("className", className),
|
||||||
|
let("classpath", classpath),
|
||||||
|
"""
|
||||||
|
package #packageName;
|
||||||
|
// --- IMPORTS start ---
|
||||||
|
import compiler.lib.ir_framework.*;
|
||||||
|
""",
|
||||||
|
imports.stream().map(i -> "import " + i + ";\n").toList(),
|
||||||
|
"""
|
||||||
|
// --- IMPORTS end ---
|
||||||
|
public class #className {
|
||||||
|
// --- CLASS_HOOK insertions start ---
|
||||||
|
""",
|
||||||
|
Hooks.CLASS_HOOK.anchor(
|
||||||
|
"""
|
||||||
|
// --- CLASS_HOOK insertions end ---
|
||||||
|
public static void main(String[] vmFlags) {
|
||||||
|
TestFramework framework = new TestFramework(#className.class);
|
||||||
|
framework.addFlags("-classpath", "#classpath");
|
||||||
|
framework.addFlags(vmFlags);
|
||||||
|
framework.start();
|
||||||
|
}
|
||||||
|
// --- LIST OF TESTS start ---
|
||||||
|
""",
|
||||||
|
testTemplateTokens
|
||||||
|
),
|
||||||
|
"""
|
||||||
|
// --- LIST OF TESTS end ---
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
));
|
||||||
|
return template.render();
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,8 @@ package gc.arguments;
|
|||||||
* @library /test/lib
|
* @library /test/lib
|
||||||
* @library /
|
* @library /
|
||||||
* @requires vm.bits == "64"
|
* @requires vm.bits == "64"
|
||||||
|
* @comment ulimit clashes with the memory requirements of ASAN
|
||||||
|
* @requires !vm.asan
|
||||||
* @requires os.family == "linux"
|
* @requires os.family == "linux"
|
||||||
* @requires vm.gc != "Z"
|
* @requires vm.gc != "Z"
|
||||||
* @requires vm.opt.UseCompressedOops == null
|
* @requires vm.opt.UseCompressedOops == null
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Red Hat, Inc. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 8358334
|
||||||
|
* @summary C2/Shenandoah: incorrect execution with Unsafe
|
||||||
|
* @requires vm.gc.Shenandoah
|
||||||
|
* @modules java.base/jdk.internal.misc:+open
|
||||||
|
*
|
||||||
|
* @run main/othervm -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:-TieredCompilation -XX:+UseShenandoahGC
|
||||||
|
* TestLostAntiDependencyAtExpansion
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jdk.internal.misc.Unsafe;
|
||||||
|
|
||||||
|
public class TestLostAntiDependencyAtExpansion {
|
||||||
|
static final jdk.internal.misc.Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
long addr = UNSAFE.allocateMemory(8);
|
||||||
|
for (int i = 0; i < 20_000; i++) {
|
||||||
|
UNSAFE.putLong(addr, 42L);
|
||||||
|
long res = test1(addr);
|
||||||
|
if (res != 42L) {
|
||||||
|
throw new RuntimeException("Incorrect result: " + res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class A {
|
||||||
|
long field;
|
||||||
|
}
|
||||||
|
|
||||||
|
static A a = new A();
|
||||||
|
|
||||||
|
private static long test1(long addr) {
|
||||||
|
long tmp = UNSAFE.getLong(addr);
|
||||||
|
|
||||||
|
UNSAFE.putLong(addr, 0L);
|
||||||
|
|
||||||
|
return tmp + a.field;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
140
test/hotspot/jtreg/runtime/FieldStream/LocalFieldLookupTest.java
Normal file
140
test/hotspot/jtreg/runtime/FieldStream/LocalFieldLookupTest.java
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||||
|
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
|
||||||
|
import static org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test id=defaults
|
||||||
|
* @bug 8352075
|
||||||
|
* @library /test/lib
|
||||||
|
* @library /testlibrary/asm
|
||||||
|
* @run main/othervm LocalFieldLookupTest
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* @test id=custom-threshold
|
||||||
|
* @bug 8352075
|
||||||
|
* @library /test/lib
|
||||||
|
* @library /testlibrary/asm
|
||||||
|
* @requires vm.debug == true
|
||||||
|
* @run main/othervm LocalFieldLookupTest
|
||||||
|
* @run main/othervm -XX:BinarySearchThreshold=0 LocalFieldLookupTest
|
||||||
|
* @run main/othervm -XX:BinarySearchThreshold=1 LocalFieldLookupTest
|
||||||
|
* @run main/othervm -XX:BinarySearchThreshold=15 LocalFieldLookupTest
|
||||||
|
* @run main/othervm -XX:BinarySearchThreshold=100000 LocalFieldLookupTest
|
||||||
|
*/
|
||||||
|
public class LocalFieldLookupTest {
|
||||||
|
private static final String TEST_CLASS_NAME = "Test";
|
||||||
|
private static final int MAX_FIELDS_IN_METHOD = 10000;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||||
|
// Test small classes, covering the tested thresholds
|
||||||
|
for (int i = 0; i <= 33; ++i) {
|
||||||
|
makeClass(i).newInstance();
|
||||||
|
}
|
||||||
|
// Test classes around 256 fields (index encoding 1/2 bytes) to check off-by-one errors
|
||||||
|
for (int i = 254; i <= 259; ++i) {
|
||||||
|
makeClass(255).newInstance();
|
||||||
|
}
|
||||||
|
// We would like to test #fields that create have the stream about 65536 bytes long;
|
||||||
|
// this value is not exposed, though, so these are rather experimentally found values,
|
||||||
|
// hence fragile. Moreover, since the stream length is incremented by about 8 bytes
|
||||||
|
// for each field we cannot test for off-by-one errors reliably.
|
||||||
|
for (int i = 8433; i <= 8437; ++i) {
|
||||||
|
makeClass(i).newInstance();
|
||||||
|
}
|
||||||
|
// The largest class we can create - this one has 65533 entries in the constant pool
|
||||||
|
makeClass(26205).newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> makeClass(int fields) throws ClassNotFoundException {
|
||||||
|
ClassWriter writer = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||||
|
writer.visit(49, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, TEST_CLASS_NAME,null, "java/lang/Object", null);
|
||||||
|
|
||||||
|
for (int i = 0; i < fields; i += 2) {
|
||||||
|
writer.visitField(ACC_PUBLIC, "f" + i, "I", null, null);
|
||||||
|
// Let's use duplicate names to confirm search takes signatures into account
|
||||||
|
if (i + 1 < fields) {
|
||||||
|
writer.visitField(ACC_PUBLIC, "f" + i, "J", null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We initialize fields in multiple methods to avoid running into bytecode limit per method
|
||||||
|
MethodVisitor fi = null;
|
||||||
|
for (int i = 0; i < fields; i+= 2) {
|
||||||
|
if (fi == null) {
|
||||||
|
fi = writer.visitMethod(ACC_PRIVATE, "init" + i, "()V", null, null);
|
||||||
|
fi.visitCode();
|
||||||
|
}
|
||||||
|
fi.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
|
fi.visitInsn(Opcodes.ICONST_2);
|
||||||
|
fi.visitFieldInsn(PUTFIELD, TEST_CLASS_NAME, "f" + i, "I");
|
||||||
|
if (i + 1 < fields) {
|
||||||
|
fi.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
|
fi.visitInsn(Opcodes.LCONST_1);
|
||||||
|
fi.visitFieldInsn(PUTFIELD, TEST_CLASS_NAME, "f" + i, "J");
|
||||||
|
}
|
||||||
|
if (i % MAX_FIELDS_IN_METHOD == MAX_FIELDS_IN_METHOD - 2) {
|
||||||
|
fi.visitInsn(Opcodes.RETURN);
|
||||||
|
fi.visitMaxs(0, 0);
|
||||||
|
fi.visitEnd();
|
||||||
|
fi = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fi != null) {
|
||||||
|
fi.visitInsn(Opcodes.RETURN);
|
||||||
|
fi.visitMaxs(0, 0);
|
||||||
|
fi.visitEnd();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
MethodVisitor mv = writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||||
|
mv.visitCode();
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||||
|
for (int i = 0; i < fields; i += MAX_FIELDS_IN_METHOD) {
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitMethodInsn(INVOKESPECIAL, TEST_CLASS_NAME, "init" + i, "()V", false);
|
||||||
|
}
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(0, 0);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
writer.visitEnd();
|
||||||
|
|
||||||
|
byte[] bytecode = writer.toByteArray();
|
||||||
|
ClassLoader cl = new ClassLoader() {
|
||||||
|
@Override
|
||||||
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
|
if (!TEST_CLASS_NAME.equals(name)) {
|
||||||
|
throw new ClassNotFoundException();
|
||||||
|
}
|
||||||
|
return defineClass(TEST_CLASS_NAME, bytecode, 0, bytecode.length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return cl.loadClass(TEST_CLASS_NAME);
|
||||||
|
}
|
||||||
|
}
|
@ -38,6 +38,8 @@
|
|||||||
* @requires os.family != "windows" & os.family != "aix"
|
* @requires os.family != "windows" & os.family != "aix"
|
||||||
* @comment TODO: Decide libjsig support on static JDK with 8351367
|
* @comment TODO: Decide libjsig support on static JDK with 8351367
|
||||||
* @requires !jdk.static
|
* @requires !jdk.static
|
||||||
|
* @comment loading of the jsig lib does currently not work well with ASAN lib
|
||||||
|
* @requires !vm.asan
|
||||||
* @library /vmTestbase
|
* @library /vmTestbase
|
||||||
* /test/lib
|
* /test/lib
|
||||||
* @run driver TestBreakSignalThreadDump load_libjsig
|
* @run driver TestBreakSignalThreadDump load_libjsig
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
* @modules java.base/jdk.internal.misc
|
* @modules java.base/jdk.internal.misc
|
||||||
* java.management
|
* java.management
|
||||||
* @requires os.family == "linux" | os.family == "mac"
|
* @requires os.family == "linux" | os.family == "mac"
|
||||||
|
* @comment loading of the jsig lib does currently not work well with ASAN lib
|
||||||
|
* @requires !vm.asan
|
||||||
* @comment TODO: Decide libjsig support on static JDK with 8351367
|
* @comment TODO: Decide libjsig support on static JDK with 8351367
|
||||||
* @requires !jdk.static
|
* @requires !jdk.static
|
||||||
* @run driver XCheckJSig
|
* @run driver XCheckJSig
|
||||||
|
@ -72,6 +72,7 @@ import java.io.File;
|
|||||||
import java.lang.StackWalker.StackFrame;
|
import java.lang.StackWalker.StackFrame;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -312,12 +313,15 @@ class BulkLoaderTestApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ArrayList<ClassLoader> savedLoaders = new ArrayList<>();
|
||||||
|
|
||||||
static Object initFromCustomLoader() throws Exception {
|
static Object initFromCustomLoader() throws Exception {
|
||||||
String path = "cust.jar";
|
String path = "cust.jar";
|
||||||
URL url = new File(path).toURI().toURL();
|
URL url = new File(path).toURI().toURL();
|
||||||
URL[] urls = new URL[] {url};
|
URL[] urls = new URL[] {url};
|
||||||
URLClassLoader urlClassLoader =
|
URLClassLoader urlClassLoader =
|
||||||
new URLClassLoader("MyLoader", urls, null);
|
new URLClassLoader("MyLoader", urls, null);
|
||||||
|
savedLoaders.add(urlClassLoader);
|
||||||
Class c = Class.forName("SimpleCusty", true, urlClassLoader);
|
Class c = Class.forName("SimpleCusty", true, urlClassLoader);
|
||||||
return c.newInstance();
|
return c.newInstance();
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,8 @@ import java.util.regex.Pattern;
|
|||||||
* @summary Test of diagnostic command System.map
|
* @summary Test of diagnostic command System.map
|
||||||
* @library /test/lib
|
* @library /test/lib
|
||||||
* @requires (os.family == "linux" | os.family == "windows" | os.family == "mac")
|
* @requires (os.family == "linux" | os.family == "windows" | os.family == "mac")
|
||||||
|
* @comment ASAN changes the memory map dump slightly, but the test has rather strict requirements
|
||||||
|
* @requires !vm.asan
|
||||||
* @requires os.arch != "riscv64" | !(vm.cpu.features ~= ".*qemu.*")
|
* @requires os.arch != "riscv64" | !(vm.cpu.features ~= ".*qemu.*")
|
||||||
* @modules java.base/jdk.internal.misc
|
* @modules java.base/jdk.internal.misc
|
||||||
* java.compiler
|
* java.compiler
|
||||||
|
@ -32,6 +32,8 @@ import jdk.test.lib.process.OutputAnalyzer;
|
|||||||
* @summary Test of diagnostic command System.map
|
* @summary Test of diagnostic command System.map
|
||||||
* @library /test/lib
|
* @library /test/lib
|
||||||
* @requires (vm.gc != "Z") & (os.family == "linux" | os.family == "windows" | os.family == "mac")
|
* @requires (vm.gc != "Z") & (os.family == "linux" | os.family == "windows" | os.family == "mac")
|
||||||
|
* @comment ASAN changes the memory map dump slightly, but the test has rather strict requirements
|
||||||
|
* @requires !vm.asan
|
||||||
* @modules java.base/jdk.internal.misc
|
* @modules java.base/jdk.internal.misc
|
||||||
* java.compiler
|
* java.compiler
|
||||||
* java.management
|
* java.management
|
||||||
@ -47,6 +49,8 @@ import jdk.test.lib.process.OutputAnalyzer;
|
|||||||
* @summary Test of diagnostic command System.map using ZGC
|
* @summary Test of diagnostic command System.map using ZGC
|
||||||
* @library /test/lib
|
* @library /test/lib
|
||||||
* @requires vm.gc.Z & (os.family == "linux" | os.family == "windows" | os.family == "mac")
|
* @requires vm.gc.Z & (os.family == "linux" | os.family == "windows" | os.family == "mac")
|
||||||
|
* @comment ASAN changes the memory map dump slightly, but the test has rather strict requirements
|
||||||
|
* @requires !vm.asan
|
||||||
* @modules java.base/jdk.internal.misc
|
* @modules java.base/jdk.internal.misc
|
||||||
* java.compiler
|
* java.compiler
|
||||||
* java.management
|
* java.management
|
||||||
|
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @summary Test TestFrameworkClass.TEMPLATE which allows generating many tests and running them with the IR TestFramework.
|
||||||
|
* @modules java.base/jdk.internal.misc
|
||||||
|
* @library /test/lib /
|
||||||
|
* @compile ../../../compiler/lib/ir_framework/TestFramework.java
|
||||||
|
* @compile ../../../compiler/lib/generators/Generators.java
|
||||||
|
* @compile ../../../compiler/lib/verify/Verify.java
|
||||||
|
* @run driver template_framework.examples.TestWithTestFrameworkClass
|
||||||
|
*/
|
||||||
|
|
||||||
|
package template_framework.examples;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import compiler.lib.compile_framework.CompileFramework;
|
||||||
|
|
||||||
|
import compiler.lib.generators.Generators;
|
||||||
|
|
||||||
|
import compiler.lib.template_framework.Template;
|
||||||
|
import compiler.lib.template_framework.TemplateToken;
|
||||||
|
import static compiler.lib.template_framework.Template.body;
|
||||||
|
import static compiler.lib.template_framework.Template.let;
|
||||||
|
|
||||||
|
import compiler.lib.template_framework.library.Hooks;
|
||||||
|
import compiler.lib.template_framework.library.TestFrameworkClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a basic IR verification test, in combination with Generators for random input generation
|
||||||
|
* and Verify for output verification.
|
||||||
|
* <p>
|
||||||
|
* The "@compile" command for JTREG is required so that the frameworks used in the Template code
|
||||||
|
* are compiled and available for the Test-VM.
|
||||||
|
* <p>
|
||||||
|
* Additionally, we must set the classpath for the Test VM, so that it has access to all compiled
|
||||||
|
* classes (see {@link CompileFramework#getEscapedClassPathOfCompiledClasses}).
|
||||||
|
*/
|
||||||
|
public class TestWithTestFrameworkClass {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Create a new CompileFramework instance.
|
||||||
|
CompileFramework comp = new CompileFramework();
|
||||||
|
|
||||||
|
// Add a java source file.
|
||||||
|
comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp));
|
||||||
|
|
||||||
|
// Compile the source file.
|
||||||
|
comp.compile();
|
||||||
|
|
||||||
|
// p.xyz.InnterTest.main(new String[] {});
|
||||||
|
comp.invoke("p.xyz.InnerTest", "main", new Object[] {new String[] {}});
|
||||||
|
|
||||||
|
// We can also pass VM flags for the Test VM.
|
||||||
|
// p.xyz.InnterTest.main(new String[] {"-Xbatch"});
|
||||||
|
comp.invoke("p.xyz.InnerTest", "main", new Object[] {new String[] {"-Xbatch"}});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a source Java file as String
|
||||||
|
public static String generate(CompileFramework comp) {
|
||||||
|
// A simple template that adds a comment.
|
||||||
|
var commentTemplate = Template.make(() -> body(
|
||||||
|
"""
|
||||||
|
// Comment inserted from test method to class hook.
|
||||||
|
"""
|
||||||
|
));
|
||||||
|
|
||||||
|
// We define a Test-Template:
|
||||||
|
// - static fields for inputs: INPUT_A and INPUT_B
|
||||||
|
// - Data generated with Generators and hashtag replacement #con1.
|
||||||
|
// - GOLD value precomputed with dedicated call to test.
|
||||||
|
// - This ensures that the GOLD value is computed in the interpreter
|
||||||
|
// most likely, since the test method is not yet compiled.
|
||||||
|
// This allows us later to compare to the results of the compiled
|
||||||
|
// code.
|
||||||
|
// The input data is cloned, so that the original INPUT_A is never
|
||||||
|
// modified and can serve as identical input in later calls to test.
|
||||||
|
// - In the Setup method, we clone the input data, since the input data
|
||||||
|
// could be modified inside the test method.
|
||||||
|
// - The test method makes use of hashtag replacements (#con2 and #op).
|
||||||
|
// - The Check method verifies the results of the test method with the
|
||||||
|
// GOLD value.
|
||||||
|
var testTemplate = Template.make("op", (String op) -> body(
|
||||||
|
let("size", Generators.G.safeRestrict(Generators.G.ints(), 10_000, 20_000).next()),
|
||||||
|
let("con1", Generators.G.ints().next()),
|
||||||
|
let("con2", Generators.G.safeRestrict(Generators.G.ints(), 1, Integer.MAX_VALUE).next()),
|
||||||
|
"""
|
||||||
|
// --- $test start ---
|
||||||
|
// $test with size=#size and op=#op
|
||||||
|
private static int[] $INPUT_A = new int[#size];
|
||||||
|
static {
|
||||||
|
Generators.G.fill(Generators.G.ints(), $INPUT_A);
|
||||||
|
}
|
||||||
|
private static int $INPUT_B = #con1;
|
||||||
|
private static Object $GOLD = $test($INPUT_A.clone(), $INPUT_B);
|
||||||
|
|
||||||
|
@Setup
|
||||||
|
public static Object[] $setup() {
|
||||||
|
// Must make sure to clone input arrays, if it is mutated in the test.
|
||||||
|
return new Object[] {$INPUT_A.clone(), $INPUT_B};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Arguments(setup = "$setup")
|
||||||
|
public static Object $test(int[] a, int b) {
|
||||||
|
for (int i = 0; i < a.length; i++) {
|
||||||
|
int con = #con2;
|
||||||
|
a[i] = (a[i] * con) #op b;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Check(test = "$test")
|
||||||
|
public static void $check(Object result) {
|
||||||
|
Verify.checkEQ(result, $GOLD);
|
||||||
|
}
|
||||||
|
// --- $test end ---
|
||||||
|
""",
|
||||||
|
// Good to know: we can insert to the class hook, which is set for the
|
||||||
|
// TestFrameworkClass scope:
|
||||||
|
Hooks.CLASS_HOOK.insert(commentTemplate.asToken())
|
||||||
|
));
|
||||||
|
|
||||||
|
// Create a test for each operator.
|
||||||
|
List<String> ops = List.of("+", "-", "*", "&", "|");
|
||||||
|
List<TemplateToken> testTemplateTokens = ops.stream().map(testTemplate::asToken).toList();
|
||||||
|
|
||||||
|
// Create the test class, which runs all testTemplateTokens.
|
||||||
|
return TestFrameworkClass.render(
|
||||||
|
// package and class name.
|
||||||
|
"p.xyz", "InnerTest",
|
||||||
|
// Set of imports.
|
||||||
|
Set.of("compiler.lib.generators.*",
|
||||||
|
"compiler.lib.verify.*"),
|
||||||
|
// classpath, so the Test VM has access to the compiled class files.
|
||||||
|
comp.getEscapedClassPathOfCompiledClasses(),
|
||||||
|
// The list of tests.
|
||||||
|
testTemplateTokens);
|
||||||
|
}
|
||||||
|
}
|
@ -102,6 +102,8 @@ requires.properties= \
|
|||||||
vm.cds.write.archived.java.heap \
|
vm.cds.write.archived.java.heap \
|
||||||
vm.continuations \
|
vm.continuations \
|
||||||
vm.musl \
|
vm.musl \
|
||||||
|
vm.asan \
|
||||||
|
vm.ubsan \
|
||||||
vm.debug \
|
vm.debug \
|
||||||
vm.hasSA \
|
vm.hasSA \
|
||||||
vm.hasJFR \
|
vm.hasJFR \
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -40,6 +40,8 @@ import java.util.zip.ZipInputStream;
|
|||||||
* @bug 8226346
|
* @bug 8226346
|
||||||
* @summary Check all output files for absolute path fragments
|
* @summary Check all output files for absolute path fragments
|
||||||
* @requires !vm.debug
|
* @requires !vm.debug
|
||||||
|
* @comment ASAN keeps the 'unwanted' paths in the binaries because of its build options
|
||||||
|
* @requires !vm.asan
|
||||||
* @run main/othervm -Xmx900m AbsPathsInImage
|
* @run main/othervm -Xmx900m AbsPathsInImage
|
||||||
*/
|
*/
|
||||||
public class AbsPathsInImage {
|
public class AbsPathsInImage {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,7 +30,7 @@ import jdk.test.lib.Platform;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 6191897
|
* @bug 6191897 8354646
|
||||||
* @summary Verifies that ctrl+left/right does not move word-by-word in a TextField
|
* @summary Verifies that ctrl+left/right does not move word-by-word in a TextField
|
||||||
* with echo character set
|
* with echo character set
|
||||||
* @library /java/awt/regtesthelpers /test/lib
|
* @library /java/awt/regtesthelpers /test/lib
|
||||||
|
@ -24,21 +24,14 @@
|
|||||||
/* @test
|
/* @test
|
||||||
@bug 6481955
|
@bug 6481955
|
||||||
@summary Path length less than MAX_PATH (260) works on Windows
|
@summary Path length less than MAX_PATH (260) works on Windows
|
||||||
@library /test/lib
|
@requires (os.family == "windows")
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import jtreg.SkippedException;
|
|
||||||
|
|
||||||
public class MaxPath {
|
public class MaxPath {
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
String osName = System.getProperty("os.name");
|
|
||||||
if (!osName.startsWith("Windows")) {
|
|
||||||
throw new SkippedException("This test is run only on Windows");
|
|
||||||
}
|
|
||||||
|
|
||||||
int MAX_PATH = 260;
|
int MAX_PATH = 260;
|
||||||
String dir = new File(".").getAbsolutePath() + "\\";
|
String dir = new File(".").getAbsolutePath() + "\\";
|
||||||
String padding = "1234567890123456789012345678901234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890";
|
String padding = "1234567890123456789012345678901234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2001, 2007, 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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,29 +23,60 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @bug 4490253 6535542
|
* @bug 4490253 6535542 8357959
|
||||||
|
* @key randomness
|
||||||
|
* @library /test/lib
|
||||||
|
* @build jdk.test.lib.RandomFactory
|
||||||
* @summary Verify that newly allocated direct buffers are initialized.
|
* @summary Verify that newly allocated direct buffers are initialized.
|
||||||
|
* @run main/othervm AllocateDirectInit
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import jdk.test.lib.RandomFactory;
|
||||||
|
|
||||||
public class AllocateDirectInit {
|
public class AllocateDirectInit {
|
||||||
|
private static final int MAX_BIN_LIMIT = 16 * 1024 * 1024;
|
||||||
|
private static final int MAX_DEC_LIMIT = 10 * 1000 * 1000;
|
||||||
|
private static final int TRIES_PER_LIMIT = 1024;
|
||||||
|
|
||||||
|
private static final Random RND = RandomFactory.getRandom();
|
||||||
|
|
||||||
public static void main(String [] args){
|
public static void main(String [] args){
|
||||||
for (int i = 0; i < 1024; i++) {
|
// Try power of two limits
|
||||||
ByteBuffer bb = ByteBuffer.allocateDirect(1024);
|
for (int limit = 1; limit < MAX_BIN_LIMIT; limit *= 2) {
|
||||||
// printByteBuffer(bb);
|
check(ByteBuffer.allocateDirect(limit - 1));
|
||||||
for (bb.position(0); bb.position() < bb.limit(); ) {
|
check(ByteBuffer.allocateDirect(limit));
|
||||||
if ((bb.get() & 0xff) != 0)
|
check(ByteBuffer.allocateDirect(limit + 1));
|
||||||
throw new RuntimeException("uninitialized buffer, position = "
|
}
|
||||||
+ bb.position());
|
|
||||||
|
// Try power of ten limits
|
||||||
|
for (int limit = 1; limit < MAX_DEC_LIMIT; limit *= 10) {
|
||||||
|
check(ByteBuffer.allocateDirect(limit - 1));
|
||||||
|
check(ByteBuffer.allocateDirect(limit));
|
||||||
|
check(ByteBuffer.allocateDirect(limit + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try random sizes within power of two limits
|
||||||
|
for (int limit = 1; limit < MAX_BIN_LIMIT; limit *= 2) {
|
||||||
|
for (int t = 0; t < TRIES_PER_LIMIT; t++) {
|
||||||
|
check(ByteBuffer.allocateDirect(RND.nextInt(limit)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printByteBuffer(ByteBuffer bb) {
|
private static void check(ByteBuffer bb) {
|
||||||
System.out.print("byte [");
|
while (bb.hasRemaining()) {
|
||||||
for (bb.position(0); bb.position() < bb.limit(); )
|
if (bb.get() != 0) {
|
||||||
System.out.print(" " + Integer.toHexString(bb.get() & 0xff));
|
int mismatchPos = bb.position();
|
||||||
System.out.println(" ]");
|
System.out.print("byte [");
|
||||||
|
for (bb.position(0); bb.position() < bb.limit(); ) {
|
||||||
|
System.out.print(" " + Integer.toHexString(bb.get() & 0xff));
|
||||||
|
}
|
||||||
|
System.out.println(" ]");
|
||||||
|
throw new RuntimeException("uninitialized buffer, position = " + mismatchPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8358764
|
||||||
|
* @summary Test closing a socket while a thread is blocked in read. The connection
|
||||||
|
* should be closed gracefuly so that the peer reads EOF.
|
||||||
|
* @run junit PeerReadsAfterAsyncClose
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class PeerReadsAfterAsyncClose {
|
||||||
|
|
||||||
|
static Stream<ThreadFactory> factories() {
|
||||||
|
return Stream.of(Thread.ofPlatform().factory(), Thread.ofVirtual().factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close SocketChannel while a thread is blocked reading from the channel's socket.
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("factories")
|
||||||
|
void testCloseDuringSocketChannelRead(ThreadFactory factory) throws Exception {
|
||||||
|
var loopback = InetAddress.getLoopbackAddress();
|
||||||
|
try (var listener = new ServerSocket()) {
|
||||||
|
listener.bind(new InetSocketAddress(loopback, 0));
|
||||||
|
|
||||||
|
try (SocketChannel sc = SocketChannel.open(listener.getLocalSocketAddress());
|
||||||
|
Socket peer = listener.accept()) {
|
||||||
|
|
||||||
|
// start thread to read from channel
|
||||||
|
var cceThrown = new AtomicBoolean();
|
||||||
|
Thread thread = factory.newThread(() -> {
|
||||||
|
try {
|
||||||
|
sc.read(ByteBuffer.allocate(1));
|
||||||
|
fail();
|
||||||
|
} catch (ClosedChannelException e) {
|
||||||
|
cceThrown.set(true);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
try {
|
||||||
|
// close SocketChannel when thread sampled in implRead
|
||||||
|
onReach(thread, "sun.nio.ch.SocketChannelImpl.implRead", () -> {
|
||||||
|
try {
|
||||||
|
sc.close();
|
||||||
|
} catch (IOException ignore) { }
|
||||||
|
});
|
||||||
|
|
||||||
|
// peer should read EOF
|
||||||
|
int n = peer.getInputStream().read();
|
||||||
|
assertEquals(-1, n);
|
||||||
|
} finally {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
assertEquals(true, cceThrown.get(), "ClosedChannelException not thrown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close Socket while a thread is blocked reading from the socket.
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("factories")
|
||||||
|
void testCloseDuringSocketUntimedRead(ThreadFactory factory) throws Exception {
|
||||||
|
testCloseDuringSocketRead(factory, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close Socket while a thread is blocked reading from the socket with a timeout.
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("factories")
|
||||||
|
void testCloseDuringSockeTimedRead(ThreadFactory factory) throws Exception {
|
||||||
|
testCloseDuringSocketRead(factory, 60_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testCloseDuringSocketRead(ThreadFactory factory, int timeout) throws Exception {
|
||||||
|
var loopback = InetAddress.getLoopbackAddress();
|
||||||
|
try (var listener = new ServerSocket()) {
|
||||||
|
listener.bind(new InetSocketAddress(loopback, 0));
|
||||||
|
|
||||||
|
try (Socket s = new Socket(loopback, listener.getLocalPort());
|
||||||
|
Socket peer = listener.accept()) {
|
||||||
|
|
||||||
|
// start thread to read from socket
|
||||||
|
var seThrown = new AtomicBoolean();
|
||||||
|
Thread thread = factory.newThread(() -> {
|
||||||
|
try {
|
||||||
|
s.setSoTimeout(timeout);
|
||||||
|
s.getInputStream().read();
|
||||||
|
fail();
|
||||||
|
} catch (SocketException e) {
|
||||||
|
seThrown.set(true);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
try {
|
||||||
|
// close Socket when thread sampled in implRead
|
||||||
|
onReach(thread, "sun.nio.ch.NioSocketImpl.implRead", () -> {
|
||||||
|
try {
|
||||||
|
s.close();
|
||||||
|
} catch (IOException ignore) { }
|
||||||
|
});
|
||||||
|
|
||||||
|
// peer should read EOF
|
||||||
|
int n = peer.getInputStream().read();
|
||||||
|
assertEquals(-1, n);
|
||||||
|
} finally {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
assertEquals(true, seThrown.get(), "SocketException not thrown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given action when the given target thread is sampled at the given
|
||||||
|
* location. The location takes the form "{@code c.m}" where
|
||||||
|
* {@code c} is the fully qualified class name and {@code m} is the method name.
|
||||||
|
*/
|
||||||
|
private void onReach(Thread target, String location, Runnable action) {
|
||||||
|
int index = location.lastIndexOf('.');
|
||||||
|
String className = location.substring(0, index);
|
||||||
|
String methodName = location.substring(index + 1);
|
||||||
|
Thread.ofPlatform().daemon(true).start(() -> {
|
||||||
|
try {
|
||||||
|
boolean found = false;
|
||||||
|
while (!found) {
|
||||||
|
found = contains(target.getStackTrace(), className, methodName);
|
||||||
|
if (!found) {
|
||||||
|
Thread.sleep(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
action.run();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given stack trace contains an element for the given class
|
||||||
|
* and method name.
|
||||||
|
*/
|
||||||
|
private boolean contains(StackTraceElement[] stack, String className, String methodName) {
|
||||||
|
return Arrays.stream(stack)
|
||||||
|
.anyMatch(e -> className.equals(e.getClassName())
|
||||||
|
&& methodName.equals(e.getMethodName()));
|
||||||
|
}
|
||||||
|
}
|
@ -43,8 +43,10 @@ import java.util.Queue;
|
|||||||
import java.util.concurrent.BlockingDeque;
|
import java.util.concurrent.BlockingDeque;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.LinkedBlockingDeque;
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
|
|
||||||
import junit.framework.Test;
|
import junit.framework.Test;
|
||||||
@ -1886,4 +1888,112 @@ public class LinkedBlockingDequeTest extends JSR166TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testInterruptedExceptionThrownInBlockingMethods() throws InterruptedException {
|
||||||
|
// Ensure that putFirst(), putLast(), takeFirst(), and takeLast()
|
||||||
|
// immediately throw an InterruptedException if the thread is
|
||||||
|
// interrupted, to be consistent with other blocking queues such as
|
||||||
|
// ArrayBlockingQueue and LinkedBlockingQueue
|
||||||
|
try (var pool = Executors.newSingleThreadExecutor()) {
|
||||||
|
Future<Void> success = pool.submit(() -> {
|
||||||
|
var queue = new LinkedBlockingDeque<>();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
try {
|
||||||
|
queue.putFirst(42);
|
||||||
|
fail("Expected InterruptedException in putFirst()");
|
||||||
|
} catch (InterruptedException expected) {
|
||||||
|
// good that's what we want
|
||||||
|
assertFalse(Thread.currentThread().isInterrupted());
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
try {
|
||||||
|
queue.putLast(42);
|
||||||
|
fail("Expected InterruptedException in putLast()");
|
||||||
|
} catch (InterruptedException expected) {
|
||||||
|
// good that's what we want
|
||||||
|
assertFalse(Thread.currentThread().isInterrupted());
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.add(42);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
try {
|
||||||
|
queue.takeFirst();
|
||||||
|
fail("Expected InterruptedException in takeFirst()");
|
||||||
|
} catch (InterruptedException expected) {
|
||||||
|
// good that's what we want
|
||||||
|
assertFalse(Thread.currentThread().isInterrupted());
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.add(42);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
try {
|
||||||
|
queue.takeLast();
|
||||||
|
fail("Expected InterruptedException in takeLast()");
|
||||||
|
} catch (InterruptedException expected) {
|
||||||
|
// good that's what we want
|
||||||
|
assertFalse(Thread.currentThread().isInterrupted());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
success.get();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
try {
|
||||||
|
throw e.getCause();
|
||||||
|
} catch (Error | RuntimeException unchecked) {
|
||||||
|
throw unchecked;
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
throw new AssertionError(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWeaklyConsistentIterationWithClear() {
|
||||||
|
final LinkedBlockingDeque<Item> q = new LinkedBlockingDeque<>();
|
||||||
|
q.add(one);
|
||||||
|
q.add(two);
|
||||||
|
q.add(three);
|
||||||
|
final Iterator<Item> it = q.iterator();
|
||||||
|
mustEqual(one, it.next());
|
||||||
|
q.clear();
|
||||||
|
q.add(four);
|
||||||
|
q.add(five);
|
||||||
|
q.add(six);
|
||||||
|
mustEqual(two, it.next());
|
||||||
|
mustEqual(four, it.next());
|
||||||
|
mustEqual(five, it.next());
|
||||||
|
mustEqual(six, it.next());
|
||||||
|
mustEqual(3, q.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWeaklyConsistentIterationWithIteratorRemove() {
|
||||||
|
final LinkedBlockingDeque<Item> q = new LinkedBlockingDeque<>();
|
||||||
|
q.add(one);
|
||||||
|
q.add(two);
|
||||||
|
q.add(three);
|
||||||
|
q.add(four);
|
||||||
|
q.add(five);
|
||||||
|
final Iterator<Item> it1 = q.iterator();
|
||||||
|
final Iterator<Item> it2 = q.iterator();
|
||||||
|
final Iterator<Item> it3 = q.iterator();
|
||||||
|
mustEqual(one, it1.next());
|
||||||
|
mustEqual(two, it1.next());
|
||||||
|
it1.remove(); // removing "two"
|
||||||
|
mustEqual(one, it2.next());
|
||||||
|
it2.remove(); // removing "one"
|
||||||
|
mustEqual(three, it2.next());
|
||||||
|
mustEqual(four, it2.next());
|
||||||
|
it2.remove(); // removing "four"
|
||||||
|
mustEqual(one, it3.next());
|
||||||
|
mustEqual(three, it3.next());
|
||||||
|
mustEqual(five, it3.next());
|
||||||
|
assertFalse(it3.hasNext());
|
||||||
|
mustEqual(three, it1.next());
|
||||||
|
mustEqual(five, it1.next());
|
||||||
|
assertFalse(it1.hasNext());
|
||||||
|
mustEqual(five, it2.next());
|
||||||
|
assertFalse(it2.hasNext());
|
||||||
|
mustEqual(2, q.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @key headful
|
||||||
|
* @bug 4231444 8354646
|
||||||
|
* @summary Password fields' ActionMap needs to replace
|
||||||
|
* DefaultEditorKit.selectWordAction with
|
||||||
|
* DefaultEditorKit.selectLineAction.
|
||||||
|
*
|
||||||
|
* @run main PasswordSelectionWordTest
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JPasswordField;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.UnsupportedLookAndFeelException;
|
||||||
|
import javax.swing.plaf.basic.BasicTextUI;
|
||||||
|
import javax.swing.text.DefaultEditorKit;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
public class PasswordSelectionWordTest {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
for (UIManager.LookAndFeelInfo laf :
|
||||||
|
UIManager.getInstalledLookAndFeels()) {
|
||||||
|
System.out.println("Testing LAF: " + laf.getClassName());
|
||||||
|
SwingUtilities.invokeAndWait(() -> {
|
||||||
|
if (setLookAndFeel(laf)) {
|
||||||
|
runTest();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean setLookAndFeel(UIManager.LookAndFeelInfo laf) {
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel(laf.getClassName());
|
||||||
|
return true;
|
||||||
|
} catch (UnsupportedLookAndFeelException e) {
|
||||||
|
System.err.println("Skipping unsupported look and feel:");
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runTest() {
|
||||||
|
String str = "one two three";
|
||||||
|
JPasswordField field = new JPasswordField(str);
|
||||||
|
if (!(field.getUI() instanceof BasicTextUI)) {
|
||||||
|
throw new RuntimeException("Unexpected condition: JPasswordField UI was " + field.getUI());
|
||||||
|
}
|
||||||
|
System.out.println("Testing " + field.getUI());
|
||||||
|
|
||||||
|
// do something (anything) to initialize the Views:
|
||||||
|
field.setSize(100, 100);
|
||||||
|
field.addNotify();
|
||||||
|
|
||||||
|
Action action = field.getActionMap().get(
|
||||||
|
DefaultEditorKit.selectWordAction);
|
||||||
|
action.actionPerformed(new ActionEvent(field, 0, ""));
|
||||||
|
int selectionStart = field.getSelectionStart();
|
||||||
|
int selectionEnd = field.getSelectionEnd();
|
||||||
|
System.out.println("selectionStart = " + selectionStart);
|
||||||
|
System.out.println("selectionEnd = " + selectionEnd);
|
||||||
|
if (selectionStart != 0 || selectionEnd != str.length()) {
|
||||||
|
throw new RuntimeException("selectionStart = " + selectionStart +
|
||||||
|
" and selectionEnd = " + selectionEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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