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
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# 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.
|
||||
# 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
|
||||
|
||||
|
@ -6687,6 +6687,7 @@ instruct reinterpretL(iRegLdst dst) %{
|
||||
match(Set dst (VectorReinterpret dst));
|
||||
ins_cost(0);
|
||||
format %{ "reinterpret $dst" %}
|
||||
size(0);
|
||||
ins_encode( /*empty*/ );
|
||||
ins_pipe(pipe_class_empty);
|
||||
%}
|
||||
@ -6695,6 +6696,7 @@ instruct reinterpretX(vecX dst) %{
|
||||
match(Set dst (VectorReinterpret dst));
|
||||
ins_cost(0);
|
||||
format %{ "reinterpret $dst" %}
|
||||
size(0);
|
||||
ins_encode( /*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" %}
|
||||
size(4);
|
||||
ins_encode %{
|
||||
// This is a Power7 instruction for which no machine description exists.
|
||||
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
|
||||
%}
|
||||
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" %}
|
||||
size(4);
|
||||
ins_encode %{
|
||||
// This is a Power7 instruction for which no machine description exists.
|
||||
__ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register);
|
||||
%}
|
||||
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" %}
|
||||
size(4);
|
||||
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;
|
||||
__ isel($dst$$Register, $crx$$CondRegister,
|
||||
(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" %}
|
||||
size(4);
|
||||
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;
|
||||
__ isel($dst$$Register, $crx$$CondRegister,
|
||||
(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" %}
|
||||
size(4);
|
||||
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;
|
||||
__ isel($dst$$Register, $crx$$CondRegister,
|
||||
(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" %}
|
||||
size(4);
|
||||
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;
|
||||
__ isel($dst$$Register, $crx$$CondRegister,
|
||||
(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.
|
||||
// int floatToIntBits(float value)
|
||||
// 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) %{
|
||||
match(Set dst (MoveL2D src));
|
||||
@ -12434,6 +12419,7 @@ instruct minI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
|
||||
effect(KILL cr0);
|
||||
ins_cost(DEFAULT_COST*2);
|
||||
|
||||
size(8);
|
||||
ins_encode %{
|
||||
__ cmpw(CR0, $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);
|
||||
ins_cost(DEFAULT_COST*2);
|
||||
|
||||
size(8);
|
||||
ins_encode %{
|
||||
__ cmpw(CR0, $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 ------------------------------------
|
||||
|
||||
// Popcnt for Power7.
|
||||
instruct popCountI(iRegIdst dst, iRegIsrc src) %{
|
||||
match(Set dst (PopCountI src));
|
||||
predicate(UsePopCountInstruction);
|
||||
@ -12470,7 +12456,6 @@ instruct popCountI(iRegIdst dst, iRegIsrc src) %{
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
// Popcnt for Power7.
|
||||
instruct popCountL(iRegIdst dst, iRegLsrc src) %{
|
||||
predicate(UsePopCountInstruction);
|
||||
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);
|
||||
|
||||
format %{ "LI $dst, #0 \t// replicate2F" %}
|
||||
size(4);
|
||||
ins_encode %{
|
||||
__ li($dst$$Register, 0x0);
|
||||
%}
|
||||
@ -13927,6 +13913,7 @@ instruct overflowAddL_reg_reg(flagsRegCR0 cr0, iRegLsrc op1, iRegLsrc op2) %{
|
||||
match(Set cr0 (OverflowAddL op1 op2));
|
||||
|
||||
format %{ "add_ $op1, $op2\t# overflow check long" %}
|
||||
size(12);
|
||||
ins_encode %{
|
||||
__ li(R0, 0);
|
||||
__ 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));
|
||||
|
||||
format %{ "subfo_ R0, $op2, $op1\t# overflow check long" %}
|
||||
size(12);
|
||||
ins_encode %{
|
||||
__ li(R0, 0);
|
||||
__ 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));
|
||||
|
||||
format %{ "nego_ R0, $op2\t# overflow check long" %}
|
||||
size(12);
|
||||
ins_encode %{
|
||||
__ li(R0, 0);
|
||||
__ 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));
|
||||
|
||||
format %{ "mulldo_ R0, $op1, $op2\t# overflow check long" %}
|
||||
size(12);
|
||||
ins_encode %{
|
||||
__ li(R0, 0);
|
||||
__ 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);
|
||||
|
||||
format %{ "XXLXOR $dst, $zero \t// replicate4F" %}
|
||||
size(4);
|
||||
ins_encode %{
|
||||
__ 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)) {
|
||||
FLAG_SET_DEFAULT(UseCRC32Intrinsics, true);
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
@ -465,13 +465,19 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M
|
||||
__ call_VM_leaf0(CAST_FROM_FN_PTR(address, SharedRuntime::dtan));
|
||||
}
|
||||
} else if (kind == Interpreter::java_lang_math_tanh) {
|
||||
assert(StubRoutines::dtanh() != nullptr, "not initialized");
|
||||
if (StubRoutines::dtanh() != nullptr) {
|
||||
__ movdbl(xmm0, Address(rsp, wordSize));
|
||||
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dtanh())));
|
||||
} else {
|
||||
return nullptr; // Fallback to default implementation
|
||||
}
|
||||
} else if (kind == Interpreter::java_lang_math_cbrt) {
|
||||
assert(StubRoutines::dcbrt() != nullptr, "not initialized");
|
||||
__ movdbl(xmm0, Address(rsp, wordSize));
|
||||
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
|
||||
if (StubRoutines::dcbrt() != nullptr) {
|
||||
__ movdbl(xmm0, Address(rsp, wordSize));
|
||||
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
|
||||
} else {
|
||||
return nullptr; // Fallback to default implementation
|
||||
}
|
||||
} else if (kind == Interpreter::java_lang_math_abs) {
|
||||
assert(StubRoutines::x86::double_sign_mask() != nullptr, "not initialized");
|
||||
__ 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
|
||||
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));
|
||||
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);
|
||||
@ -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)
|
||||
%{
|
||||
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));
|
||||
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);
|
||||
@ -10563,6 +10565,7 @@ instruct xorI_rReg_mem_imm_ndd(rRegI dst, memory src1, immI src2, rFlagsReg cr)
|
||||
predicate(UseAPX);
|
||||
match(Set dst (XorI (LoadI src1) src2));
|
||||
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);
|
||||
|
||||
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
|
||||
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));
|
||||
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);
|
||||
@ -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)
|
||||
%{
|
||||
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));
|
||||
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);
|
||||
@ -11238,6 +11243,7 @@ instruct xorL_rReg_mem_imm(rRegL dst, memory src1, immL32 src2, rFlagsReg cr)
|
||||
match(Set dst (XorL (LoadL src1) src2));
|
||||
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);
|
||||
ins_cost(150);
|
||||
|
||||
format %{ "exorq $dst, $src1, $src2\t# long ndd" %}
|
||||
ins_encode %{
|
||||
|
@ -154,7 +154,8 @@ julong os::Bsd::available_memory() {
|
||||
assert(kerr == KERN_SUCCESS,
|
||||
"host_statistics64 failed - check mach_host_self() and count");
|
||||
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
|
||||
return available;
|
||||
|
@ -3740,6 +3740,7 @@ void ClassFileParser::apply_parsed_class_metadata(
|
||||
_cp->set_pool_holder(this_klass);
|
||||
this_klass->set_constants(_cp);
|
||||
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_methods(_methods);
|
||||
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_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
|
||||
// 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
|
||||
@ -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
|
||||
assert(nullptr == _cp, "invariant");
|
||||
assert(nullptr == _fieldinfo_stream, "invariant");
|
||||
assert(nullptr == _fieldinfo_search_table, "invariant");
|
||||
assert(nullptr == _fields_status, "invariant");
|
||||
assert(nullptr == _methods, "invariant");
|
||||
assert(nullptr == _inner_classes, "invariant");
|
||||
@ -5276,6 +5280,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
|
||||
_super_klass(),
|
||||
_cp(nullptr),
|
||||
_fieldinfo_stream(nullptr),
|
||||
_fieldinfo_search_table(nullptr),
|
||||
_fields_status(nullptr),
|
||||
_methods(nullptr),
|
||||
_inner_classes(nullptr),
|
||||
@ -5352,6 +5357,7 @@ void ClassFileParser::clear_class_metadata() {
|
||||
// deallocated if classfile parsing returns an error.
|
||||
_cp = nullptr;
|
||||
_fieldinfo_stream = nullptr;
|
||||
_fieldinfo_search_table = nullptr;
|
||||
_fields_status = nullptr;
|
||||
_methods = nullptr;
|
||||
_inner_classes = nullptr;
|
||||
@ -5374,6 +5380,7 @@ ClassFileParser::~ClassFileParser() {
|
||||
if (_fieldinfo_stream != nullptr) {
|
||||
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_stream);
|
||||
}
|
||||
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_search_table);
|
||||
|
||||
if (_fields_status != nullptr) {
|
||||
MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status);
|
||||
@ -5774,6 +5781,7 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st
|
||||
_fieldinfo_stream =
|
||||
FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
|
||||
injected_fields_count, loader_data(), CHECK);
|
||||
_fieldinfo_search_table = FieldInfoStream::create_search_table(_cp, _fieldinfo_stream, _loader_data, CHECK);
|
||||
_fields_status =
|
||||
MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(),
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -123,6 +123,7 @@ class ClassFileParser {
|
||||
const InstanceKlass* _super_klass;
|
||||
ConstantPool* _cp;
|
||||
Array<u1>* _fieldinfo_stream;
|
||||
Array<u1>* _fieldinfo_search_table;
|
||||
Array<FieldStatus>* _fields_status;
|
||||
Array<Method*>* _methods;
|
||||
Array<u2>* _inner_classes;
|
||||
|
@ -301,7 +301,7 @@ void FieldLayout::reconstruct_layout(const InstanceKlass* ik, bool& has_instance
|
||||
BasicType last_type;
|
||||
int last_offset = -1;
|
||||
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());
|
||||
// distinction between static and non-static fields is missing
|
||||
if (fs.access_flags().is_static()) continue;
|
||||
@ -461,7 +461,7 @@ void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlas
|
||||
bool found = false;
|
||||
const InstanceKlass* ik = super;
|
||||
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()) {
|
||||
output->print_cr(" @%d \"%s\" %s %d/%d %s",
|
||||
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);
|
||||
ik->set_fieldinfo_stream(new_fis);
|
||||
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::_dcos:
|
||||
case vmIntrinsics::_dtan:
|
||||
case vmIntrinsics::_dtanh:
|
||||
case vmIntrinsics::_dcbrt:
|
||||
case vmIntrinsics::_dlog:
|
||||
case vmIntrinsics::_dexp:
|
||||
case vmIntrinsics::_dpow:
|
||||
@ -316,6 +314,13 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
|
||||
case vmIntrinsics::_fmaF:
|
||||
if (!InlineMathNatives || !UseFMA) return true;
|
||||
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::_float16ToFloat:
|
||||
if (!InlineIntrinsics) return true;
|
||||
|
@ -625,6 +625,34 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) {
|
||||
}
|
||||
#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) {
|
||||
// That both nodes have the same control is not sufficient to prove
|
||||
// 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()) {
|
||||
assert(phase->ctrl_or_self(m->in(LoopNode::EntryControl)) != c, "following loop entry should lead to new control");
|
||||
} else {
|
||||
if (m->is_Store() || m->is_LoadStore()) {
|
||||
// Take anti-dependencies into account
|
||||
Node* mem = m->in(MemNode::Memory);
|
||||
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));
|
||||
}
|
||||
}
|
||||
// Take anti-dependencies into account
|
||||
maybe_push_anti_dependent_loads(phase, m, c, wq);
|
||||
push_data_inputs_at_control(phase, m, c, wq);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -1006,7 +1021,20 @@ void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* lo
|
||||
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* 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
|
||||
// its memory is control dependent on the barrier's input control)
|
||||
// must stay above the barrier.
|
||||
uses_to_ignore.clear();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
collect_nodes_above_barrier(nodes_above_barrier, phase, ctrl, init_raw_mem);
|
||||
for (DUIterator_Fast imax, i = ctrl->fast_outs(imax); i < imax; i++) {
|
||||
Node* u = ctrl->fast_out(i);
|
||||
if (u->_idx < last &&
|
||||
u != barrier &&
|
||||
!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())) &&
|
||||
(ctrl->Opcode() != Op_CatchProj || u->Opcode() != Op_CreateEx)) {
|
||||
Node* old_c = phase->ctrl_or_self(u);
|
||||
Node* c = old_c;
|
||||
if (c != ctrl ||
|
||||
if (old_c != ctrl ||
|
||||
is_dominator_same_ctrl(old_c, barrier, u, phase) ||
|
||||
ShenandoahBarrierSetC2::is_shenandoah_state_load(u)) {
|
||||
phase->igvn().rehash_node_delayed(u);
|
||||
@ -1315,7 +1330,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
|
||||
|
||||
// Expand load-reference-barriers
|
||||
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--) {
|
||||
ShenandoahLoadReferenceBarrierNode* lrb = state->load_reference_barrier(i);
|
||||
uint last = phase->C->unique();
|
||||
@ -1410,7 +1425,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
|
||||
Node* out_val = val_phi;
|
||||
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;
|
||||
|
||||
|
@ -62,8 +62,12 @@ private:
|
||||
PhaseIdealLoop* phase, int flags);
|
||||
static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr,
|
||||
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 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);
|
||||
public:
|
||||
@ -76,6 +80,11 @@ public:
|
||||
static bool expand(Compile* C, PhaseIterGVN& igvn);
|
||||
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
|
||||
static void verify(RootNode* root);
|
||||
#endif
|
||||
|
@ -216,6 +216,7 @@ class outputStream;
|
||||
LOG_TAG(valuebasedclasses) \
|
||||
LOG_TAG(verification) \
|
||||
LOG_TAG(verify) \
|
||||
LOG_TAG(vmatree) \
|
||||
LOG_TAG(vmmutex) \
|
||||
LOG_TAG(vmoperation) \
|
||||
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()));
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -43,11 +43,8 @@
|
||||
class NativeCallStackStorage : public CHeapObjBase {
|
||||
public:
|
||||
using StackIndex = int;
|
||||
|
||||
private:
|
||||
constexpr static const StackIndex invalid = std::numeric_limits<StackIndex>::max() - 1;
|
||||
|
||||
public:
|
||||
static bool equals(const StackIndex a, const StackIndex b) {
|
||||
return a == b;
|
||||
}
|
||||
|
@ -28,21 +28,228 @@
|
||||
#include "utilities/globalDefinitions.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] = {
|
||||
"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) {
|
||||
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) {
|
||||
// A 0-sized mapping isn't worth recording.
|
||||
|
||||
if (_A == _B) {
|
||||
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{
|
||||
IntervalState{StateType::Released, empty_regiondata},
|
||||
IntervalState{ state, metadata}
|
||||
@ -51,176 +258,400 @@ VMATree::SummaryDiff VMATree::register_mapping(position A, position B, StateType
|
||||
IntervalState{ state, metadata},
|
||||
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.
|
||||
// Find closest node that is LEQ A
|
||||
bool LEQ_A_found = false;
|
||||
AddressState LEQ_A;
|
||||
TreapNode* leqA_n = _tree.closest_leq(A);
|
||||
if (leqA_n == nullptr) {
|
||||
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) {
|
||||
log_debug(nmt)("Cannot use the tag inplace if no pre-existing tag exists. From: " PTR_FORMAT " To: " PTR_FORMAT, A, B);
|
||||
// nodes: .....X.......Y...Z......W........U
|
||||
// request: A------------------B
|
||||
// X,Y = enclosing_nodes(A)
|
||||
// W,U = enclosing_nodes(B)
|
||||
// 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)
|
||||
// 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)
|
||||
|
||||
// We update regions in 3 sections: 1) X..A..Y, 2) Y....W, 3) W..B..U
|
||||
// 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()) {
|
||||
_tree.upsert(A, stA);
|
||||
};
|
||||
// update region between n1 and n2
|
||||
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 {
|
||||
LEQ_A_found = true;
|
||||
LEQ_A = AddressState{leqA_n->key(), leqA_n->val()};
|
||||
StateType leqA_state = leqA_n->val().out.type();
|
||||
StateType new_state = stA.out.type();
|
||||
// If we specify use_tag_inplace then the new region takes over the current tag instead of the tag in metadata.
|
||||
// This is important because the VirtualMemoryTracker API doesn't require supplying the tag for some operations.
|
||||
if (use_tag_inplace) {
|
||||
assert(leqA_n->val().out.type() != StateType::Released, "Should not use inplace the tag of a released region");
|
||||
MemTag tag = leqA_n->val().out.mem_tag();
|
||||
stA.out.set_tag(tag);
|
||||
stB.in.set_tag(tag);
|
||||
}
|
||||
|
||||
// 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;
|
||||
return false;
|
||||
};
|
||||
GrowableArrayCHeap<position, mtNMT> to_be_removed;
|
||||
// update regions in [Y,W)
|
||||
auto update_loop = [&]() {
|
||||
TreapNode* prev = nullptr;
|
||||
_tree.visit_range_in_order(_A + 1, _B + 1, [&](TreapNode* curr) {
|
||||
if (prev != nullptr) {
|
||||
update_region(prev, curr, req, diff);
|
||||
// during visit, structure of the tree should not be changed
|
||||
// keep the keys to be removed, and remove them later
|
||||
if (prev->val().is_noop()) {
|
||||
to_be_removed.push(prev->key());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The address must be smaller.
|
||||
assert(A > leqA_n->key(), "must be");
|
||||
prev = curr;
|
||||
});
|
||||
};
|
||||
// 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
|
||||
// state change, we just omit the node.
|
||||
// That happens, for example, when reserving within an already reserved region with identical metadata.
|
||||
stA.in = out_state(leqA_n); // .. and the region's prior state is the incoming state
|
||||
if (stA.is_noop()) {
|
||||
// Nothing to do.
|
||||
} else {
|
||||
// Add new node.
|
||||
_tree.upsert(A, stA);
|
||||
}
|
||||
if ( X_exists && !Y_exists && !U_exists) { row = 4; }
|
||||
if ( X_exists && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 5; }
|
||||
if ( X_exists && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 6; }
|
||||
if ( X_exists && Y_exists && W_eq_B && !U_exists) { row = 7; }
|
||||
|
||||
if ( X_eq_A && !Y_exists && !U_exists) { row = 8; }
|
||||
if ( X_eq_A && Y_exists && Y_eq_W && !W_eq_B && !U_exists) { row = 9; }
|
||||
if ( X_eq_A && Y_exists && !Y_eq_W && !W_eq_B && !U_exists) { row = 10; }
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we handle B.
|
||||
// We first search all nodes that are (A, B]. All of these nodes
|
||||
// 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 1: .........A...YW.............B.....
|
||||
case 1: {
|
||||
ShouldNotReachHere();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Insert B node if needed
|
||||
if (B_needs_insert && // Was not already inserted
|
||||
!stB.is_noop()) // The operation is differing
|
||||
{
|
||||
_tree.upsert(B, stB);
|
||||
}
|
||||
|
||||
// We now need to:
|
||||
// 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 2: .........A...Y..........W...B.....
|
||||
case 2: {
|
||||
update_A(Y);
|
||||
upsert_if(A);
|
||||
update_loop();
|
||||
remove_if(Y);
|
||||
update(W, B);
|
||||
remove_if(W);
|
||||
upsert_if(B);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Track the previous node.
|
||||
AddressState prev{A, stA};
|
||||
for (int i = 0; i < to_be_deleted_inbetween_a_b.length(); i++) {
|
||||
const AddressState delete_me = to_be_deleted_inbetween_a_b.at(i);
|
||||
_tree.remove(delete_me.address);
|
||||
|
||||
// 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;
|
||||
// row 3: .........A...Y.............WB.....
|
||||
case 3: {
|
||||
update_A(Y);
|
||||
upsert_if(A);
|
||||
update_loop();
|
||||
remove_if(W);
|
||||
break;
|
||||
}
|
||||
prev = delete_me;
|
||||
}
|
||||
|
||||
if (prev.address != A && prev.out().type() != StateType::Released) {
|
||||
// The last node wasn't released, so it must be connected to a node outside of (A, B)
|
||||
// A - prev - B - (some node >= B)
|
||||
// It might be that prev.address == B == (some node >= B), this is fine.
|
||||
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 4: .....X...A..................B.....
|
||||
case 4: {
|
||||
A->val().in = X->val().out;
|
||||
update_A(B);
|
||||
upsert_if(A);
|
||||
upsert_if(B);
|
||||
break;
|
||||
}
|
||||
// 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.
|
||||
SingleDiff& rescom = diff.tag[NMTUtil::tag_to_index(stA.out.mem_tag())];
|
||||
if (state == StateType::Reserved) {
|
||||
rescom.reserve += B - A;
|
||||
} else if (state == StateType::Committed) {
|
||||
rescom.commit += B - A;
|
||||
rescom.reserve += B - A;
|
||||
// Remove the 'noop' nodes that found inside the loop
|
||||
while(to_be_removed.length() != 0) {
|
||||
_tree.remove(to_be_removed.pop());
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
void VMATree::print_on(outputStream* out) {
|
||||
visit_in_order([&](TreapNode* current) {
|
||||
out->print("%zu (%s) - %s - ", current->key(), NMTUtil::tag_to_name(out_state(current).mem_tag()),
|
||||
statetype_to_string(out_state(current).type()));
|
||||
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()), current->val().out.reserved_stack(), current->val().out.committed_stack());
|
||||
});
|
||||
out->cr();
|
||||
}
|
||||
@ -268,7 +699,7 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con
|
||||
SummaryDiff diff;
|
||||
// Ignore any released ranges, these must be mtNone and have no stack
|
||||
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);
|
||||
diff.add(result);
|
||||
}
|
||||
@ -289,7 +720,7 @@ VMATree::SummaryDiff VMATree::set_tag(const position start, const size size, con
|
||||
StateType type = out.type();
|
||||
|
||||
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);
|
||||
diff.add(result);
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ class VMATree {
|
||||
public:
|
||||
using position = size_t;
|
||||
using size = size_t;
|
||||
using SIndex = NativeCallStackStorage::StackIndex;
|
||||
|
||||
class PositionComparator {
|
||||
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:
|
||||
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.
|
||||
struct RegionData {
|
||||
const NativeCallStackStorage::StackIndex stack_idx;
|
||||
const SIndex stack_idx;
|
||||
const MemTag mem_tag;
|
||||
|
||||
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) {}
|
||||
|
||||
static bool equals(const RegionData& a, const RegionData& b) {
|
||||
@ -91,15 +92,27 @@ private:
|
||||
private:
|
||||
// Store the type and mem_tag as two bytes
|
||||
uint8_t type_tag[2];
|
||||
NativeCallStackStorage::StackIndex sidx;
|
||||
NativeCallStackStorage::StackIndex _reserved_stack;
|
||||
NativeCallStackStorage::StackIndex _committed_stack;
|
||||
|
||||
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) {
|
||||
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[1] = static_cast<uint8_t>(data.mem_tag);
|
||||
sidx = data.stack_idx;
|
||||
_reserved_stack = data.stack_idx;
|
||||
_committed_stack = NativeCallStackStorage::invalid;
|
||||
}
|
||||
|
||||
StateType type() const {
|
||||
@ -110,16 +123,50 @@ private:
|
||||
return static_cast<MemTag>(type_tag[1]);
|
||||
}
|
||||
|
||||
RegionData regiondata() const {
|
||||
return RegionData{sidx, mem_tag()};
|
||||
RegionData reserved_regiondata() const {
|
||||
return RegionData{_reserved_stack, mem_tag()};
|
||||
}
|
||||
RegionData committed_regiondata() const {
|
||||
return RegionData{_committed_stack, mem_tag()};
|
||||
}
|
||||
|
||||
void set_tag(MemTag tag) {
|
||||
type_tag[1] = static_cast<uint8_t>(tag);
|
||||
}
|
||||
|
||||
NativeCallStackStorage::StackIndex stack() const {
|
||||
return sidx;
|
||||
NativeCallStackStorage::StackIndex reserved_stack() const {
|
||||
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;
|
||||
|
||||
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() &&
|
||||
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
|
||||
};
|
||||
|
||||
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:
|
||||
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:
|
||||
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 "runtime/atomic.hpp"
|
||||
#include "utilities/packedTable.hpp"
|
||||
|
||||
void FieldInfo::print(outputStream* os, ConstantPool* cp) {
|
||||
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(),
|
||||
initializer_index(),
|
||||
generic_signature_index(),
|
||||
_field_flags.is_injected() ? lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8(),
|
||||
contended_group());
|
||||
_field_flags.is_generic() ? (_field_flags.is_injected() ?
|
||||
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) {
|
||||
@ -62,13 +67,17 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
|
||||
StreamSizer s;
|
||||
StreamFieldSizer sizer(&s);
|
||||
|
||||
assert(fields->length() == java_fields + injected_fields, "must be");
|
||||
|
||||
sizer.consumer()->accept_uint(java_fields);
|
||||
sizer.consumer()->accept_uint(injected_fields);
|
||||
for (int i = 0; i < fields->length(); i++) {
|
||||
FieldInfo* fi = fields->adr_at(i);
|
||||
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);
|
||||
|
||||
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(injected_fields);
|
||||
for (int i = 0; i < fields->length(); i++) {
|
||||
FieldInfo* fi = fields->adr_at(i);
|
||||
writer.map_field_info(*fi);
|
||||
writer.map_field_info(fields->at(i));
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
FieldInfoReader r(fis);
|
||||
int jfc = r.next_uint();
|
||||
int jfc, ifc;
|
||||
r.read_field_counts(&jfc, &ifc);
|
||||
assert(jfc == java_fields, "Must be");
|
||||
int ifc = r.next_uint();
|
||||
assert(ifc == injected_fields, "Must be");
|
||||
for (int i = 0; i < jfc + ifc; i++) {
|
||||
FieldInfo fi;
|
||||
@ -113,30 +121,221 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
|
||||
return fis;
|
||||
}
|
||||
|
||||
GrowableArray<FieldInfo>* FieldInfoStream::create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count) {
|
||||
int length = FieldInfoStream::num_total_fields(fis);
|
||||
GrowableArray<FieldInfo>* array = new GrowableArray<FieldInfo>(length);
|
||||
int FieldInfoStream::compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2) {
|
||||
int cmp = n1->fast_compare(n2);
|
||||
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);
|
||||
*java_fields_count = r.next_uint();
|
||||
*injected_fields_count = r.next_uint();
|
||||
int java_fields;
|
||||
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()) {
|
||||
FieldInfo fi;
|
||||
r.read_field_info(fi);
|
||||
array->append(fi);
|
||||
}
|
||||
assert(array->length() == length, "Must be");
|
||||
assert(array->length() == *java_fields_count + *injected_fields_count, "Must be");
|
||||
return array;
|
||||
}
|
||||
|
||||
void FieldInfoStream::print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp) {
|
||||
int length = FieldInfoStream::num_total_fields(fis);
|
||||
FieldInfoReader r(fis);
|
||||
int java_field_count = r.next_uint();
|
||||
int injected_fields_count = r.next_uint();
|
||||
int java_fields_count;
|
||||
int injected_fields_count;
|
||||
r.read_field_counts(&java_fields_count, &injected_fields_count);
|
||||
while (r.has_next()) {
|
||||
FieldInfo fi;
|
||||
r.read_field_info(fi);
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
// Gadget for decoding and reading the stream of field records.
|
||||
class FieldInfoReader {
|
||||
friend class FieldInfoStream;
|
||||
friend class ClassFileParser;
|
||||
friend class FieldStreamBase;
|
||||
friend class FieldInfo;
|
||||
|
||||
UNSIGNED5::Reader<const u1*, int> _r;
|
||||
int _next_index;
|
||||
|
||||
public:
|
||||
public:
|
||||
FieldInfoReader(const Array<u1>* fi);
|
||||
|
||||
private:
|
||||
uint32_t next_uint() { return _r.next_uint(); }
|
||||
private:
|
||||
inline uint32_t next_uint() { return _r.next_uint(); }
|
||||
void skip(int n) { int s = _r.try_skip(n); assert(s == n,""); }
|
||||
|
||||
public:
|
||||
int has_next() { return _r.has_next(); }
|
||||
int position() { return _r.position(); }
|
||||
int next_index() { return _next_index; }
|
||||
void read_field_counts(int* java_fields, int* injected_fields);
|
||||
int has_next() const { return _r.position() < _r.limit(); }
|
||||
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);
|
||||
|
||||
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
|
||||
FieldInfoReader& skip_field_info();
|
||||
|
||||
@ -271,6 +270,11 @@ class FieldInfoStream : AllStatic {
|
||||
friend class JavaFieldStream;
|
||||
friend class FieldStreamBase;
|
||||
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:
|
||||
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 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 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 {
|
||||
|
@ -56,16 +56,27 @@ inline Symbol* FieldInfo::lookup_symbol(int symbol_index) const {
|
||||
|
||||
inline int FieldInfoStream::num_injected_java_fields(const Array<u1>* fis) {
|
||||
FieldInfoReader fir(fis);
|
||||
fir.skip(1);
|
||||
return fir.next_uint();
|
||||
int java_fields_count;
|
||||
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) {
|
||||
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>
|
||||
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)
|
||||
: _r(fi->data(), 0),
|
||||
: _r(fi->data(), fi->length()),
|
||||
_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) {
|
||||
fi._index = _next_index++;
|
||||
fi._name_index = checked_cast<u2>(next_uint());
|
||||
fi._signature_index = checked_cast<u2>(next_uint());
|
||||
read_name_and_signature(&fi._name_index, &fi._signature_index);
|
||||
fi._offset = next_uint();
|
||||
fi._access_flags = AccessFlags(checked_cast<u2>(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.
|
||||
*
|
||||
* 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(Array<u1>* fieldinfo_stream, ConstantPool* constants);
|
||||
inline FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants);
|
||||
|
||||
private:
|
||||
private:
|
||||
void initialize() {
|
||||
int java_fields_count = _reader.next_uint();
|
||||
int injected_fields_count = _reader.next_uint();
|
||||
assert( _limit <= java_fields_count + injected_fields_count, "Safety check");
|
||||
int java_fields_count;
|
||||
int injected_fields_count;
|
||||
_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) {
|
||||
_reader.read_field_info(_fi_buf);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
inline FieldStreamBase(InstanceKlass* klass);
|
||||
|
||||
@ -138,8 +144,11 @@ class FieldStreamBase : public StackObj {
|
||||
|
||||
// Iterate over only the Java fields
|
||||
class JavaFieldStream : public FieldStreamBase {
|
||||
Array<u1>* _search_table;
|
||||
|
||||
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 {
|
||||
assert(!field()->field_flags().is_injected(), "regular only");
|
||||
@ -149,7 +158,6 @@ class JavaFieldStream : public FieldStreamBase {
|
||||
u2 signature_index() const {
|
||||
assert(!field()->field_flags().is_injected(), "regular only");
|
||||
return field()->signature_index();
|
||||
return -1;
|
||||
}
|
||||
|
||||
u2 generic_signature_index() const {
|
||||
@ -164,6 +172,10 @@ class JavaFieldStream : public FieldStreamBase {
|
||||
assert(!field()->field_flags().is_injected(), "regular only");
|
||||
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 {
|
||||
public:
|
||||
AllFieldStream(Array<u1>* fieldinfo, ConstantPool* constants): FieldStreamBase(fieldinfo, 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.
|
||||
*
|
||||
* 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) :
|
||||
_fieldinfo_stream(fieldinfo_stream),
|
||||
_reader(FieldInfoReader(_fieldinfo_stream)),
|
||||
_constants(constantPoolHandle(Thread::current(), constants)), _index(start) {
|
||||
_index = start;
|
||||
if (limit < start) {
|
||||
_limit = FieldInfoStream::num_total_fields(_fieldinfo_stream);
|
||||
} else {
|
||||
_limit = limit;
|
||||
}
|
||||
_constants(constantPoolHandle(Thread::current(), constants)),
|
||||
_index(start),
|
||||
_limit(limit) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
FieldStreamBase::FieldStreamBase(Array<u1>* fieldinfo_stream, ConstantPool* constants) :
|
||||
FieldStreamBase::FieldStreamBase(const Array<u1>* fieldinfo_stream, ConstantPool* constants) :
|
||||
_fieldinfo_stream(fieldinfo_stream),
|
||||
_reader(FieldInfoReader(_fieldinfo_stream)),
|
||||
_constants(constantPoolHandle(Thread::current(), constants)),
|
||||
_index(0),
|
||||
_limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
|
||||
_limit(-1) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@ -57,9 +53,28 @@ FieldStreamBase::FieldStreamBase(InstanceKlass* klass) :
|
||||
_reader(FieldInfoReader(_fieldinfo_stream)),
|
||||
_constants(constantPoolHandle(Thread::current(), klass->constants())),
|
||||
_index(0),
|
||||
_limit(FieldInfoStream::num_total_fields(_fieldinfo_stream)) {
|
||||
_limit(-1) {
|
||||
assert(klass == field_holder(), "");
|
||||
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
|
||||
|
@ -686,6 +686,11 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
|
||||
}
|
||||
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()) {
|
||||
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 {
|
||||
for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
|
||||
Symbol* f_name = fs.name();
|
||||
Symbol* f_sig = fs.signature();
|
||||
if (f_name == name && f_sig == sig) {
|
||||
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.to_FieldInfo());
|
||||
return true;
|
||||
}
|
||||
JavaFieldStream fs(this);
|
||||
if (fs.lookup(name, sig)) {
|
||||
assert(fs.name() == name, "name must match");
|
||||
assert(fs.signature() == sig, "signature must match");
|
||||
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.to_FieldInfo());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -2610,6 +2614,7 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
|
||||
}
|
||||
|
||||
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()
|
||||
it->push(&_fields_status, MetaspaceClosure::_writable);
|
||||
|
||||
@ -2710,6 +2715,8 @@ void InstanceKlass::remove_unshareable_info() {
|
||||
DEBUG_ONLY(_shared_class_load_count = 0);
|
||||
|
||||
remove_unshareable_flags();
|
||||
|
||||
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
|
||||
}
|
||||
|
||||
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()) {
|
||||
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.
|
||||
@ -3760,6 +3769,11 @@ void InstanceKlass::print_on(outputStream* st) const {
|
||||
map++;
|
||||
}
|
||||
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 {
|
||||
|
@ -276,6 +276,7 @@ class InstanceKlass: public Klass {
|
||||
|
||||
// Fields information is stored in an UNSIGNED5 encoded stream (see fieldInfo.hpp)
|
||||
Array<u1>* _fieldinfo_stream;
|
||||
Array<u1>* _fieldinfo_search_table;
|
||||
Array<FieldStatus>* _fields_status;
|
||||
|
||||
// embedded Java vtable follows here
|
||||
@ -398,6 +399,9 @@ class InstanceKlass: public Klass {
|
||||
Array<u1>* fieldinfo_stream() const { return _fieldinfo_stream; }
|
||||
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; }
|
||||
void set_fields_status(Array<FieldStatus>* array) { _fields_status = array; }
|
||||
|
||||
|
@ -678,10 +678,12 @@
|
||||
"Print progress during Iterative Global Value Numbering") \
|
||||
\
|
||||
develop(uint, VerifyIterativeGVN, 0, \
|
||||
"Verify Iterative Global Value Numbering" \
|
||||
"=XY, with Y: verify Def-Use modifications during IGVN" \
|
||||
" X: verify that type(n) == n->Value() after IGVN" \
|
||||
"X and Y in 0=off; 1=on") \
|
||||
"Verify Iterative Global Value Numbering =DCBA, with:" \
|
||||
" D: verify Node::Identity did not miss opportunities" \
|
||||
" C: verify Node::Ideal did not miss opportunities" \
|
||||
" 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) \
|
||||
\
|
||||
develop(bool, TraceCISCSpill, false, \
|
||||
|
@ -6580,6 +6580,10 @@ bool LibraryCallKit::inline_vectorizedMismatch() {
|
||||
memory_phi = _gvn.transform(memory_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_all_memory(memory_phi);
|
||||
set_result(result_phi);
|
||||
|
@ -1072,7 +1072,11 @@ void PhaseIterGVN::optimize() {
|
||||
|
||||
#ifdef ASSERT
|
||||
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;
|
||||
Unique_Node_List worklist;
|
||||
bool failure = false;
|
||||
@ -1080,7 +1084,10 @@ void PhaseIterGVN::verify_optimize() {
|
||||
worklist.push(C->root());
|
||||
for (uint j = 0; j < worklist.size(); ++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
|
||||
for (uint i = 0; i < n->req(); i++) {
|
||||
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).
|
||||
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.
|
||||
@ -1104,7 +1132,7 @@ void PhaseIterGVN::verify_optimize() {
|
||||
// (1) Integer "widen" changes, but the range is the same.
|
||||
// (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.
|
||||
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
|
||||
// the node never went through gvn.transform, which would be a bug.
|
||||
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.
|
||||
return false;
|
||||
}
|
||||
tty->cr();
|
||||
tty->print_cr("Missed Value optimization:");
|
||||
n->dump_bfs(1, nullptr, "");
|
||||
tty->print_cr("Current type:");
|
||||
told->dump_on(tty);
|
||||
tty->cr();
|
||||
tty->print_cr("Optimized type:");
|
||||
tnew->dump_on(tty);
|
||||
tty->cr();
|
||||
|
||||
stringStream ss; // Print as a block without tty lock.
|
||||
ss.cr();
|
||||
ss.print_cr("Missed Value optimization:");
|
||||
n->dump_bfs(1, nullptr, "", &ss);
|
||||
ss.print_cr("Current type:");
|
||||
told->dump_on(&ss);
|
||||
ss.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;
|
||||
}
|
||||
#endif
|
||||
@ -1890,12 +2776,12 @@ void PhaseCCP::analyze() {
|
||||
|
||||
#ifdef ASSERT
|
||||
// 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) {
|
||||
bool failure = false;
|
||||
while (worklist_verify.size()) {
|
||||
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.
|
||||
// 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -494,7 +494,10 @@ public:
|
||||
void optimize();
|
||||
#ifdef ASSERT
|
||||
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
|
||||
|
||||
#ifndef PRODUCT
|
||||
@ -593,6 +596,14 @@ public:
|
||||
// '-XX:VerifyIterativeGVN=10'
|
||||
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:
|
||||
// Sub-quadratic implementation of '-XX:VerifyIterativeGVN=1' (Use-Def verification).
|
||||
julong _verify_counter;
|
||||
|
@ -12862,16 +12862,17 @@ myInit() {
|
||||
parameters uniquely identify the current location
|
||||
(where the exception was detected) and allow
|
||||
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>
|
||||
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,
|
||||
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
|
||||
between the throw location and the catch clause, the exception may
|
||||
be reset by one of those native methods.
|
||||
Similarly, exceptions that are reported as uncaught (<code>catch_klass</code>
|
||||
et al. set to 0) may in fact be caught by native code.
|
||||
Similarly, exceptions that are reported as uncaught (<code>catch_method</code>
|
||||
set to null) may in fact be caught by native code.
|
||||
Agents can check for these occurrences by monitoring
|
||||
<eventlink id="ExceptionCatch"></eventlink> events.
|
||||
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
|
||||
<code>exception</code> object identifies the exception object. Exceptions
|
||||
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.
|
||||
</description>
|
||||
<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);
|
||||
scratch_class->set_fieldinfo_stream(new_fis);
|
||||
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
|
||||
|
@ -1075,6 +1075,22 @@ bool WhiteBox::validate_cgroup(bool cgroups_v2_enabled,
|
||||
}
|
||||
#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) {
|
||||
// Screen for unavailable/bad comp level or null method
|
||||
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();
|
||||
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))
|
||||
return (jlong) WhiteBox::get_in_use_monitor_count();
|
||||
WB_END
|
||||
@ -2882,6 +2906,8 @@ static JNINativeMethod methods[] = {
|
||||
(void*)&WB_AddModuleExportsToAll },
|
||||
{CC"deflateIdleMonitors", CC"()Z", (void*)&WB_DeflateIdleMonitors },
|
||||
{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"getLockStackCapacity", CC"()I", (void*)&WB_getLockStackCapacity },
|
||||
{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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -72,6 +72,9 @@ class WhiteBox : public AllStatic {
|
||||
#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);
|
||||
#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
|
||||
|
@ -299,8 +299,9 @@ JVMFlag::Error TypeProfileLevelConstraintFunc(uint value, bool verbose) {
|
||||
}
|
||||
|
||||
JVMFlag::Error VerifyIterativeGVNConstraintFunc(uint value, bool verbose) {
|
||||
const int max_modes = 4;
|
||||
uint original_value = value;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int i = 0; i < max_modes; i++) {
|
||||
if (value % 10 > 1) {
|
||||
JVMFlag::printError(verbose,
|
||||
"Invalid value (" UINT32_FORMAT ") "
|
||||
@ -312,7 +313,7 @@ JVMFlag::Error VerifyIterativeGVNConstraintFunc(uint value, bool verbose) {
|
||||
if (value != 0) {
|
||||
JVMFlag::printError(verbose,
|
||||
"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::SUCCESS;
|
||||
|
@ -2005,6 +2005,10 @@ const int ObjectAlignmentInBytes = 8;
|
||||
product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC, \
|
||||
"Use an extra lock during Thread start and exit to alleviate" \
|
||||
"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
|
||||
|
||||
|
@ -465,9 +465,7 @@ HandshakeState::HandshakeState(JavaThread* target) :
|
||||
_queue(),
|
||||
_lock(Monitor::nosafepoint, "HandshakeState_lock"),
|
||||
_active_handshaker(),
|
||||
_async_exceptions_blocked(false),
|
||||
_suspended(false),
|
||||
_async_suspend_handshake(false) {
|
||||
_async_exceptions_blocked(false) {
|
||||
}
|
||||
|
||||
HandshakeState::~HandshakeState() {
|
||||
@ -699,128 +697,8 @@ HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* ma
|
||||
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() {
|
||||
if (is_suspended()) {
|
||||
if (_handshakee->is_suspended()) {
|
||||
// A suspend handshake was added to the queue after the
|
||||
// unsafe access error. Since the suspender has already
|
||||
// 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,8 +35,6 @@
|
||||
class HandshakeOperation;
|
||||
class AsyncHandshakeOperation;
|
||||
class JavaThread;
|
||||
class SuspendThreadHandshake;
|
||||
class ThreadSelfSuspensionHandshake;
|
||||
class UnsafeAccessErrorHandshake;
|
||||
class ThreadsListHandle;
|
||||
|
||||
@ -88,8 +86,6 @@ class JvmtiRawMonitor;
|
||||
// operation is only done by either VMThread/Handshaker on behalf of the
|
||||
// JavaThread or by the target JavaThread itself.
|
||||
class HandshakeState {
|
||||
friend ThreadSelfSuspensionHandshake;
|
||||
friend SuspendThreadHandshake;
|
||||
friend UnsafeAccessErrorHandshake;
|
||||
friend JavaThread;
|
||||
// This a back reference to the JavaThread,
|
||||
@ -98,7 +94,7 @@ class HandshakeState {
|
||||
// The queue containing handshake operations to be performed on _handshakee.
|
||||
FilterQueue<HandshakeOperation*> _queue;
|
||||
// Provides mutual exclusion to this state and queue. Also used for
|
||||
// JavaThread suspend/resume operations.
|
||||
// JavaThread suspend/resume operations performed by SuspendResumeManager.
|
||||
Monitor _lock;
|
||||
// Set to the thread executing the handshake operation.
|
||||
Thread* volatile _active_handshaker;
|
||||
@ -160,31 +156,5 @@ class HandshakeState {
|
||||
bool async_exceptions_blocked() { return _async_exceptions_blocked; }
|
||||
void set_async_exceptions_blocked(bool b) { _async_exceptions_blocked = b; }
|
||||
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
|
||||
|
@ -498,6 +498,7 @@ JavaThread::JavaThread(MemTag mem_tag) :
|
||||
_pending_interrupted_exception(false),
|
||||
|
||||
_handshake(this),
|
||||
_suspend_resume_manager(this, &_handshake._lock),
|
||||
|
||||
_popframe_preserved_args(nullptr),
|
||||
_popframe_preserved_args_size(0),
|
||||
@ -1200,13 +1201,13 @@ bool JavaThread::java_suspend(bool register_vthread_SR) {
|
||||
|
||||
guarantee(Thread::is_JavaThread_protected(/* target */ this),
|
||||
"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) {
|
||||
guarantee(Thread::is_JavaThread_protected_by_TLH(/* target */ this),
|
||||
"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
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "runtime/safepointMechanism.hpp"
|
||||
#include "runtime/stackWatermarkSet.hpp"
|
||||
#include "runtime/stackOverflow.hpp"
|
||||
#include "runtime/suspendResumeManager.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "runtime/threadHeapSampler.hpp"
|
||||
#include "runtime/threadIdentifier.hpp"
|
||||
@ -694,9 +695,13 @@ private:
|
||||
|
||||
// Suspend/resume support for JavaThread
|
||||
// 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_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.
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -261,7 +261,7 @@ class UNSIGNED5 : AllStatic {
|
||||
ARR _array;
|
||||
OFF _limit;
|
||||
OFF _position;
|
||||
int next_length() {
|
||||
int next_length() const {
|
||||
return UNSIGNED5::check_length(_array, _position, _limit, GET());
|
||||
}
|
||||
public:
|
||||
@ -270,7 +270,7 @@ class UNSIGNED5 : AllStatic {
|
||||
uint32_t next_uint() {
|
||||
return UNSIGNED5::read_uint(_array, _position, _limit, GET());
|
||||
}
|
||||
bool has_next() {
|
||||
bool has_next() const {
|
||||
return next_length() != 0;
|
||||
}
|
||||
// tries to skip count logical entries; returns actual number skipped
|
||||
@ -284,8 +284,9 @@ class UNSIGNED5 : AllStatic {
|
||||
return actual;
|
||||
}
|
||||
ARR array() { return _array; }
|
||||
OFF limit() { return _limit; }
|
||||
OFF position() { return _position; }
|
||||
OFF limit() const { return _limit; }
|
||||
OFF position() const { return _position; }
|
||||
void set_limit(OFF limit) { _limit = limit; }
|
||||
void set_position(OFF position) { _position = position; }
|
||||
|
||||
// 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.
|
||||
*
|
||||
* 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.
|
||||
static final int JNI_COPY_TO_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.
|
||||
*
|
||||
* 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);
|
||||
throw x;
|
||||
}
|
||||
UNSAFE.setMemory(base, size, (byte) 0);
|
||||
Bits.setMemory(base, size, (byte) 0);
|
||||
if (pa && (base % ps != 0)) {
|
||||
// Round up to page boundary
|
||||
address = base + ps - (base & (ps - 1));
|
||||
|
@ -150,7 +150,7 @@ public class LinkedBlockingDeque<E>
|
||||
transient Node<E> last;
|
||||
|
||||
/** Number of items in the deque */
|
||||
private transient int count;
|
||||
private transient volatile int count;
|
||||
|
||||
/** @serial Maximum number of items in the deque */
|
||||
private final int capacity;
|
||||
@ -206,10 +206,13 @@ public class LinkedBlockingDeque<E>
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// assert lock.isHeldByCurrentThread();
|
||||
if (count >= capacity)
|
||||
int c;
|
||||
if ((c = count) >= capacity)
|
||||
return false;
|
||||
Node<E> f = first;
|
||||
node.next = f;
|
||||
@ -218,17 +221,20 @@ public class LinkedBlockingDeque<E>
|
||||
last = node;
|
||||
else
|
||||
f.prev = node;
|
||||
++count;
|
||||
count = c + 1;
|
||||
notEmpty.signal();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// assert lock.isHeldByCurrentThread();
|
||||
if (count >= capacity)
|
||||
int c;
|
||||
if ((c = count) >= capacity)
|
||||
return false;
|
||||
Node<E> l = last;
|
||||
node.prev = l;
|
||||
@ -237,7 +243,7 @@ public class LinkedBlockingDeque<E>
|
||||
first = node;
|
||||
else
|
||||
l.next = node;
|
||||
++count;
|
||||
count = c + 1;
|
||||
notEmpty.signal();
|
||||
return true;
|
||||
}
|
||||
@ -334,6 +340,8 @@ public class LinkedBlockingDeque<E>
|
||||
*/
|
||||
public boolean offerFirst(E e) {
|
||||
if (e == null) throw new NullPointerException();
|
||||
if (count >= capacity)
|
||||
return false;
|
||||
Node<E> node = new Node<E>(e);
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
@ -349,6 +357,8 @@ public class LinkedBlockingDeque<E>
|
||||
*/
|
||||
public boolean offerLast(E e) {
|
||||
if (e == null) throw new NullPointerException();
|
||||
if (count >= capacity)
|
||||
return false;
|
||||
Node<E> node = new Node<E>(e);
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
@ -367,7 +377,7 @@ public class LinkedBlockingDeque<E>
|
||||
if (e == null) throw new NullPointerException();
|
||||
Node<E> node = new Node<E>(e);
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
lock.lockInterruptibly();
|
||||
try {
|
||||
while (!linkFirst(node))
|
||||
notFull.await();
|
||||
@ -384,7 +394,7 @@ public class LinkedBlockingDeque<E>
|
||||
if (e == null) throw new NullPointerException();
|
||||
Node<E> node = new Node<E>(e);
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
lock.lockInterruptibly();
|
||||
try {
|
||||
while (!linkLast(node))
|
||||
notFull.await();
|
||||
@ -458,6 +468,7 @@ public class LinkedBlockingDeque<E>
|
||||
}
|
||||
|
||||
public E pollFirst() {
|
||||
if (count == 0) return null;
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
@ -468,6 +479,7 @@ public class LinkedBlockingDeque<E>
|
||||
}
|
||||
|
||||
public E pollLast() {
|
||||
if (count == 0) return null;
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
@ -479,7 +491,7 @@ public class LinkedBlockingDeque<E>
|
||||
|
||||
public E takeFirst() throws InterruptedException {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
lock.lockInterruptibly();
|
||||
try {
|
||||
E x;
|
||||
while ( (x = unlinkFirst()) == null)
|
||||
@ -492,7 +504,7 @@ public class LinkedBlockingDeque<E>
|
||||
|
||||
public E takeLast() throws InterruptedException {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
lock.lockInterruptibly();
|
||||
try {
|
||||
E x;
|
||||
while ( (x = unlinkLast()) == null)
|
||||
@ -558,6 +570,7 @@ public class LinkedBlockingDeque<E>
|
||||
}
|
||||
|
||||
public E peekFirst() {
|
||||
if (count == 0) return null;
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
@ -568,6 +581,7 @@ public class LinkedBlockingDeque<E>
|
||||
}
|
||||
|
||||
public E peekLast() {
|
||||
if (count == 0) return null;
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
@ -718,13 +732,7 @@ public class LinkedBlockingDeque<E>
|
||||
* insert or remove an element.
|
||||
*/
|
||||
public int remainingCapacity() {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
return capacity - count;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return capacity - count;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -806,13 +814,7 @@ public class LinkedBlockingDeque<E>
|
||||
* @return the number of elements in this deque
|
||||
*/
|
||||
public int size() {
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
return count;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -858,7 +860,7 @@ public class LinkedBlockingDeque<E>
|
||||
|
||||
// Copy c into a private chain of Nodes
|
||||
Node<E> beg = null, end = null;
|
||||
int n = 0;
|
||||
long n = 0;
|
||||
for (E e : c) {
|
||||
Objects.requireNonNull(e);
|
||||
n++;
|
||||
@ -878,14 +880,15 @@ public class LinkedBlockingDeque<E>
|
||||
final ReentrantLock lock = this.lock;
|
||||
lock.lock();
|
||||
try {
|
||||
if (count + n <= capacity) {
|
||||
long cnt;
|
||||
if ((cnt = count + n) <= capacity) {
|
||||
beg.prev = last;
|
||||
if (first == null)
|
||||
first = beg;
|
||||
else
|
||||
last.next = beg;
|
||||
last = end;
|
||||
count += n;
|
||||
count = (int)cnt;
|
||||
notEmpty.signalAll();
|
||||
return true;
|
||||
}
|
||||
@ -894,6 +897,7 @@ public class LinkedBlockingDeque<E>
|
||||
}
|
||||
// Fall back to historic non-atomic implementation, failing
|
||||
// with IllegalStateException when the capacity is exceeded.
|
||||
beg = end = null; // help GC
|
||||
return super.addAll(c);
|
||||
}
|
||||
|
||||
@ -994,8 +998,8 @@ public class LinkedBlockingDeque<E>
|
||||
for (Node<E> f = first; f != null; ) {
|
||||
f.item = null;
|
||||
Node<E> n = f.next;
|
||||
f.prev = null;
|
||||
f.next = null;
|
||||
f.prev = f;
|
||||
f.next = f;
|
||||
f = n;
|
||||
}
|
||||
first = last = null;
|
||||
|
@ -405,8 +405,7 @@ module java.base {
|
||||
uses sun.text.spi.JavaTimeDateTimePatternProvider;
|
||||
uses sun.util.spi.CalendarProvider;
|
||||
uses sun.util.locale.provider.LocaleDataMetaInfo;
|
||||
uses sun.util.resources.LocaleData.CommonResourceBundleProvider;
|
||||
uses sun.util.resources.LocaleData.SupplementaryResourceBundleProvider;
|
||||
uses sun.util.resources.LocaleData.LocaleDataResourceBundleProvider;
|
||||
|
||||
// Built-in service providers that are located via ServiceLoader
|
||||
|
||||
|
@ -94,6 +94,15 @@ public class Net {
|
||||
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
|
||||
* 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 boolean shouldShutdownWriteBeforeClose0();
|
||||
|
||||
private static native boolean shouldSetBothIPv4AndIPv6Options0();
|
||||
|
||||
private static native boolean canIPv6SocketJoinIPv4Group0();
|
||||
|
@ -846,7 +846,7 @@ class SocketChannelImpl
|
||||
/**
|
||||
* Marks the beginning of a connect operation that might block.
|
||||
* @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 AlreadyConnectedException if already connected
|
||||
* @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
|
||||
* channel is not registered with a Selector.
|
||||
* Closes the socket if there are no I/O operations in progress (or no I/O
|
||||
* operations tracked), and the channel is not registered with a Selector.
|
||||
*/
|
||||
private boolean tryClose() throws IOException {
|
||||
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
|
||||
* and the I/O threads signalled, in which case the final close is deferred
|
||||
* until all I/O operations complete.
|
||||
* On Unix systems, 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
|
||||
* 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
|
||||
* This arises when a key is canceled and the channel configured to blocking
|
||||
@ -1112,17 +1122,17 @@ class SocketChannelImpl
|
||||
boolean connected = (state == ST_CONNECTED);
|
||||
state = ST_CLOSING;
|
||||
|
||||
if (!tryClose()) {
|
||||
if (connected && Net.shouldShutdownWriteBeforeClose()) {
|
||||
// shutdown output when linger interval not set to 0
|
||||
if (connected) {
|
||||
try {
|
||||
var SO_LINGER = StandardSocketOptions.SO_LINGER;
|
||||
if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
|
||||
Net.shutdown(fd, Net.SHUT_WR);
|
||||
}
|
||||
} catch (IOException ignore) { }
|
||||
}
|
||||
try {
|
||||
var SO_LINGER = StandardSocketOptions.SO_LINGER;
|
||||
if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) {
|
||||
Net.shutdown(fd, Net.SHUT_WR);
|
||||
}
|
||||
} catch (IOException ignore) { }
|
||||
}
|
||||
|
||||
if (!tryClose()) {
|
||||
// prepare file descriptor for closing
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,9 +23,6 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
/*
|
||||
* (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
|
||||
* (C) Copyright IBM Corp. 1996 - 1999 - All Rights Reserved
|
||||
@ -79,11 +76,11 @@
|
||||
|
||||
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
|
||||
protected final Object[][] getContents() {
|
||||
@ -119,6 +116,109 @@ public class FormatData extends ParallelListResourceBundle {
|
||||
"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[][] {
|
||||
{ "MonthNames",
|
||||
new String[] {
|
||||
@ -138,39 +238,9 @@ public class FormatData extends ParallelListResourceBundle {
|
||||
}
|
||||
},
|
||||
{ "MonthAbbreviations",
|
||||
new String[] {
|
||||
"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
|
||||
}
|
||||
},
|
||||
sharedMonthAbbrs },
|
||||
{ "MonthNarrows",
|
||||
new String[] {
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"",
|
||||
}
|
||||
},
|
||||
sharedMonthNarrows },
|
||||
{ "DayNames",
|
||||
new String[] {
|
||||
"Sunday", // Sunday
|
||||
@ -183,33 +253,11 @@ public class FormatData extends ParallelListResourceBundle {
|
||||
}
|
||||
},
|
||||
{ "DayAbbreviations",
|
||||
new String[] {
|
||||
"Sun", // abb Sunday
|
||||
"Mon", // abb Monday
|
||||
"Tue", // abb Tuesday
|
||||
"Wed", // abb Wednesday
|
||||
"Thu", // abb Thursday
|
||||
"Fri", // abb Friday
|
||||
"Sat" // abb Saturday
|
||||
}
|
||||
},
|
||||
sharedDayAbbrs },
|
||||
{ "DayNarrows",
|
||||
new String[] {
|
||||
"S",
|
||||
"M",
|
||||
"T",
|
||||
"W",
|
||||
"T",
|
||||
"F",
|
||||
"S",
|
||||
}
|
||||
},
|
||||
sharedDayNarrows },
|
||||
{ "AmPmMarkers",
|
||||
new String[] {
|
||||
"AM", // am marker
|
||||
"PM" // pm marker
|
||||
}
|
||||
},
|
||||
sharedAmPmMarkers },
|
||||
{ "narrow.AmPmMarkers",
|
||||
new String[] {
|
||||
"a", // am marker
|
||||
@ -227,22 +275,17 @@ public class FormatData extends ParallelListResourceBundle {
|
||||
}
|
||||
},
|
||||
{ "buddhist.Eras",
|
||||
buddhistEras
|
||||
},
|
||||
buddhistEras },
|
||||
{ "buddhist.short.Eras",
|
||||
buddhistEras
|
||||
},
|
||||
buddhistEras },
|
||||
{ "buddhist.narrow.Eras",
|
||||
buddhistEras
|
||||
},
|
||||
buddhistEras },
|
||||
{ "japanese.Eras",
|
||||
japaneseEras },
|
||||
{ "japanese.short.Eras",
|
||||
japaneseEraAbbrs
|
||||
},
|
||||
japaneseEraAbbrs },
|
||||
{ "japanese.narrow.Eras",
|
||||
japaneseEraAbbrs
|
||||
},
|
||||
japaneseEraAbbrs },
|
||||
{ "japanese.FirstYear",
|
||||
new String[] { // Japanese imperial calendar year name
|
||||
// empty in English
|
||||
@ -898,6 +941,164 @@ public class FormatData extends ParallelListResourceBundle {
|
||||
}
|
||||
},
|
||||
{ "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.
|
||||
*
|
||||
* 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 sun.util.resources.LocaleData;
|
||||
import sun.util.resources.OpenListResourceBundle;
|
||||
import sun.util.resources.ParallelListResourceBundle;
|
||||
import sun.util.resources.TimeZoneNamesBundle;
|
||||
|
||||
/**
|
||||
@ -579,11 +578,7 @@ public class LocaleResources {
|
||||
* resources required by JSR 310.
|
||||
*/
|
||||
public ResourceBundle getJavaTimeFormatData() {
|
||||
ResourceBundle rb = localeData.getDateFormatData(locale);
|
||||
if (rb instanceof ParallelListResourceBundle) {
|
||||
localeData.setSupplementary((ParallelListResourceBundle) rb);
|
||||
}
|
||||
return rb;
|
||||
return localeData.getDateFormatData(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
*
|
||||
* 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.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -143,30 +142,6 @@ public class LocaleData {
|
||||
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
|
||||
* to allow accessing a sun.* package.
|
||||
@ -179,18 +154,7 @@ public class LocaleData {
|
||||
return Bundles.of(baseName, locale, LocaleDataStrategy.INSTANCE);
|
||||
}
|
||||
|
||||
private static OpenListResourceBundle getSupplementary(final String baseName, final Locale locale) {
|
||||
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
|
||||
public abstract static class LocaleDataResourceBundleProvider
|
||||
implements ResourceBundleProvider {
|
||||
/**
|
||||
* 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
|
||||
|
||||
private static class LocaleDataStrategy implements Bundles.Strategy {
|
||||
@ -254,18 +204,20 @@ public class LocaleData {
|
||||
if (candidates == null) {
|
||||
LocaleProviderAdapter.Type type = baseName.contains(DOTCLDR) ? CLDR : JRE;
|
||||
LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type);
|
||||
candidates = adapter instanceof ResourceBundleBasedAdapter ?
|
||||
((ResourceBundleBasedAdapter)adapter).getCandidateLocales(baseName, locale) :
|
||||
candidates = adapter instanceof ResourceBundleBasedAdapter rbba ?
|
||||
rbba.getCandidateLocales(baseName, locale) :
|
||||
defaultControl.getCandidateLocales(baseName, locale);
|
||||
|
||||
// Weed out Locales which are known to have no resource bundles
|
||||
int lastDot = baseName.lastIndexOf('.');
|
||||
String category = (lastDot >= 0) ? baseName.substring(lastDot + 1) : baseName;
|
||||
Set<String> langtags = ((JRELocaleProviderAdapter)adapter).getLanguageTagSet(category);
|
||||
if (!langtags.isEmpty()) {
|
||||
for (Iterator<Locale> itr = candidates.iterator(); itr.hasNext();) {
|
||||
if (!adapter.isSupportedProviderLocale(itr.next(), langtags)) {
|
||||
itr.remove();
|
||||
if (adapter instanceof JRELocaleProviderAdapter jlpa) {
|
||||
var langtags = jlpa.getLanguageTagSet(category);
|
||||
if (!langtags.isEmpty()) {
|
||||
for (Iterator<Locale> itr = candidates.iterator(); itr.hasNext();) {
|
||||
if (!jlpa.isSupportedProviderLocale(itr.next(), langtags)) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -302,36 +254,7 @@ public class LocaleData {
|
||||
public Class<? extends ResourceBundleProvider> getResourceBundleProviderType(String baseName,
|
||||
Locale locale) {
|
||||
return inJavaBaseModule(baseName, locale) ?
|
||||
null : CommonResourceBundleProvider.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);
|
||||
null : LocaleDataResourceBundleProvider.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_nio_ch_Net_shouldShutdownWriteBeforeClose0(JNIEnv *env, jclass clazz) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
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.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_nio_ch_Net_shouldShutdownWriteBeforeClose0(JNIEnv *env, jclass clazz) {
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
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.
|
||||
*
|
||||
* 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) {
|
||||
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.
|
||||
*
|
||||
* 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());
|
||||
map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
* 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) {
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
sun.util.resources.cldr.provider.CLDRLocaleDataMetaInfo,
|
||||
sun.util.resources.provider.NonBaseLocaleDataMetaInfo;
|
||||
provides sun.util.resources.LocaleData.CommonResourceBundleProvider with
|
||||
provides sun.util.resources.LocaleData.LocaleDataResourceBundleProvider with
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,9 +25,9 @@
|
||||
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -76,11 +76,11 @@
|
||||
|
||||
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
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
public class LocaleDataProvider extends LocaleData.CommonResourceBundleProvider {
|
||||
public class LocaleDataProvider extends LocaleData.LocaleDataResourceBundleProvider {
|
||||
@Override
|
||||
public ResourceBundle getBundle(String baseName, Locale 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.
|
||||
*
|
||||
* 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);
|
||||
fileTester.processFiles(baseDir);
|
||||
files = fileTester.getFiles();
|
||||
if (html) {
|
||||
new TidyChecker();
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,6 +26,6 @@
|
||||
* @bug 8337109
|
||||
* @summary Check the html in the generated documentation
|
||||
* @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
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -38,6 +38,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import jtreg.SkippedException;
|
||||
|
||||
public class TidyChecker implements FileChecker, AutoCloseable {
|
||||
private final Path TIDY;
|
||||
@ -164,8 +165,7 @@ public class TidyChecker implements FileChecker, AutoCloseable {
|
||||
if (p.isPresent()) {
|
||||
tidyExePath = p.get();
|
||||
} else {
|
||||
System.err.println("tidy not found on PATH");
|
||||
return Path.of("tidy"); //non-null placeholder return; exception would be better
|
||||
throw new jtreg.SkippedException("tidy not found on PATH");
|
||||
}
|
||||
}
|
||||
|
||||
|
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.compiler2.enabled \
|
||||
vm.musl \
|
||||
vm.asan \
|
||||
vm.ubsan \
|
||||
vm.flagless \
|
||||
container.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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,9 +25,9 @@
|
||||
* @test
|
||||
* @bug 8238756
|
||||
* @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;
|
||||
|
||||
|
@ -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>();
|
||||
|
||||
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
|
||||
// comparing output across runs.
|
||||
vmOpts.add("-Xlog:all=warning:stdout:level,tags");
|
||||
vmOpts.add("-Xlog:aot=off");
|
||||
|
||||
//setup mode-specific options
|
||||
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.
|
||||
*
|
||||
* 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) 0x00,
|
||||
(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 {
|
||||
|
@ -59,6 +59,23 @@ public class BlsiTestI extends BmiIntrinsicBase.BmiTestCase {
|
||||
(byte) 0x00,
|
||||
(byte) 0xF3,
|
||||
(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 {
|
||||
|
@ -57,7 +57,24 @@ public class BlsmskTestI extends BmiIntrinsicBase.BmiTestCase {
|
||||
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||
(byte) 0x00,
|
||||
(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 {
|
||||
|
@ -58,7 +58,25 @@ public class BlsrTestI extends BmiIntrinsicBase.BmiTestCase {
|
||||
(byte) 0x02, // 00010 implied 0F 38 leading opcode bytes
|
||||
(byte) 0x00,
|
||||
(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 {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -111,7 +111,8 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest {
|
||||
protected void checkEmittedCode(Executable executable) {
|
||||
final byte[] nativeCode = NMethod.get(executable, false).insts;
|
||||
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: " +
|
||||
Utils.toHexString(matchInstrPattern));
|
||||
} else {
|
||||
@ -124,6 +125,8 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest {
|
||||
private final Method method;
|
||||
protected byte[] instrMask;
|
||||
protected byte[] instrPattern;
|
||||
protected byte[] instrMaskAPX;
|
||||
protected byte[] instrPatternAPX;
|
||||
protected boolean isLongOperation;
|
||||
protected String cpuFlag = "bmi1";
|
||||
protected String vmFlag = "UseBMI1Instructions";
|
||||
@ -160,6 +163,13 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest {
|
||||
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) {
|
||||
int count = 0;
|
||||
int patternSize = Math.min(instrMask.length, instrPattern.length);
|
||||
@ -181,8 +191,12 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest {
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean verifyPositive(byte[] nativeCode) {
|
||||
final int cnt = countCpuInstructions(nativeCode);
|
||||
public boolean verifyPositive(byte[] nativeCode, boolean use_apx) {
|
||||
int cnt = countCpuInstructions(nativeCode);
|
||||
if (use_apx) {
|
||||
System.out.println("CHECKING APX INST PATTERNS");
|
||||
cnt += countCpuInstructionsAPX(nativeCode);
|
||||
}
|
||||
if (Platform.isX86()) {
|
||||
return cnt >= (isLongOperation ? 2 : 1);
|
||||
} 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.
|
||||
*
|
||||
* 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) 0xA8,
|
||||
(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 {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -51,6 +51,10 @@ public class LZcntTestI extends BmiIntrinsicBase.BmiTestCase_x64 {
|
||||
|
||||
instrMask_x64 = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF};
|
||||
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 {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -50,6 +50,10 @@ public class TZcntTestI extends BmiIntrinsicBase.BmiTestCase_x64 {
|
||||
|
||||
instrMask_x64 = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF};
|
||||
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 {
|
||||
|
@ -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 /
|
||||
* @requires vm.bits == "64"
|
||||
* @comment ulimit clashes with the memory requirements of ASAN
|
||||
* @requires !vm.asan
|
||||
* @requires os.family == "linux"
|
||||
* @requires vm.gc != "Z"
|
||||
* @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"
|
||||
* @comment TODO: Decide libjsig support on static JDK with 8351367
|
||||
* @requires !jdk.static
|
||||
* @comment loading of the jsig lib does currently not work well with ASAN lib
|
||||
* @requires !vm.asan
|
||||
* @library /vmTestbase
|
||||
* /test/lib
|
||||
* @run driver TestBreakSignalThreadDump load_libjsig
|
||||
|
@ -29,6 +29,8 @@
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @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
|
||||
* @requires !jdk.static
|
||||
* @run driver XCheckJSig
|
||||
|
@ -72,6 +72,7 @@ import java.io.File;
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -312,12 +313,15 @@ class BulkLoaderTestApp {
|
||||
}
|
||||
}
|
||||
|
||||
static ArrayList<ClassLoader> savedLoaders = new ArrayList<>();
|
||||
|
||||
static Object initFromCustomLoader() throws Exception {
|
||||
String path = "cust.jar";
|
||||
URL url = new File(path).toURI().toURL();
|
||||
URL[] urls = new URL[] {url};
|
||||
URLClassLoader urlClassLoader =
|
||||
new URLClassLoader("MyLoader", urls, null);
|
||||
savedLoaders.add(urlClassLoader);
|
||||
Class c = Class.forName("SimpleCusty", true, urlClassLoader);
|
||||
return c.newInstance();
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ import java.util.regex.Pattern;
|
||||
* @summary Test of diagnostic command System.map
|
||||
* @library /test/lib
|
||||
* @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.*")
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.compiler
|
||||
|
@ -32,6 +32,8 @@ import jdk.test.lib.process.OutputAnalyzer;
|
||||
* @summary Test of diagnostic command System.map
|
||||
* @library /test/lib
|
||||
* @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
|
||||
* java.compiler
|
||||
* java.management
|
||||
@ -47,6 +49,8 @@ import jdk.test.lib.process.OutputAnalyzer;
|
||||
* @summary Test of diagnostic command System.map using ZGC
|
||||
* @library /test/lib
|
||||
* @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
|
||||
* java.compiler
|
||||
* 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.continuations \
|
||||
vm.musl \
|
||||
vm.asan \
|
||||
vm.ubsan \
|
||||
vm.debug \
|
||||
vm.hasSA \
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -40,6 +40,8 @@ import java.util.zip.ZipInputStream;
|
||||
* @bug 8226346
|
||||
* @summary Check all output files for absolute path fragments
|
||||
* @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
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,7 +30,7 @@ import jdk.test.lib.Platform;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6191897
|
||||
* @bug 6191897 8354646
|
||||
* @summary Verifies that ctrl+left/right does not move word-by-word in a TextField
|
||||
* with echo character set
|
||||
* @library /java/awt/regtesthelpers /test/lib
|
||||
|
@ -24,21 +24,14 @@
|
||||
/* @test
|
||||
@bug 6481955
|
||||
@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.IOException;
|
||||
|
||||
import jtreg.SkippedException;
|
||||
|
||||
public class MaxPath {
|
||||
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;
|
||||
String dir = new File(".").getAbsolutePath() + "\\";
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,29 +23,60 @@
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @run main/othervm AllocateDirectInit
|
||||
*/
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Random;
|
||||
|
||||
import jdk.test.lib.RandomFactory;
|
||||
|
||||
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){
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(1024);
|
||||
// printByteBuffer(bb);
|
||||
for (bb.position(0); bb.position() < bb.limit(); ) {
|
||||
if ((bb.get() & 0xff) != 0)
|
||||
throw new RuntimeException("uninitialized buffer, position = "
|
||||
+ bb.position());
|
||||
// Try power of two limits
|
||||
for (int limit = 1; limit < MAX_BIN_LIMIT; limit *= 2) {
|
||||
check(ByteBuffer.allocateDirect(limit - 1));
|
||||
check(ByteBuffer.allocateDirect(limit));
|
||||
check(ByteBuffer.allocateDirect(limit + 1));
|
||||
}
|
||||
|
||||
// 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) {
|
||||
System.out.print("byte [");
|
||||
for (bb.position(0); bb.position() < bb.limit(); )
|
||||
System.out.print(" " + Integer.toHexString(bb.get() & 0xff));
|
||||
System.out.println(" ]");
|
||||
private static void check(ByteBuffer bb) {
|
||||
while (bb.hasRemaining()) {
|
||||
if (bb.get() != 0) {
|
||||
int mismatchPos = bb.position();
|
||||
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.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
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