mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-27 10:40:29 +00:00
Merge branch 'openjdk:master' into master
This commit is contained in:
commit
d2c4128344
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2020, Red Hat Inc. All rights reserved.
|
||||
* Copyright 2025 Arm Limited and/or its affiliates.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
@ -664,16 +664,52 @@ void VM_Version::initialize() {
|
||||
void VM_Version::insert_features_names(uint64_t features, stringStream& ss) {
|
||||
int i = 0;
|
||||
ss.join([&]() {
|
||||
while (i < MAX_CPU_FEATURES) {
|
||||
if (supports_feature((VM_Version::Feature_Flag)i)) {
|
||||
return _features_names[i++];
|
||||
const char* str = nullptr;
|
||||
while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
|
||||
if (supports_feature(features, (VM_Version::Feature_Flag)i)) {
|
||||
str = _features_names[i];
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return (const char*)nullptr;
|
||||
return str;
|
||||
}, ", ");
|
||||
}
|
||||
|
||||
void VM_Version::get_cpu_features_name(void* features_buffer, stringStream& ss) {
|
||||
uint64_t features = *(uint64_t*)features_buffer;
|
||||
insert_features_names(features, ss);
|
||||
}
|
||||
|
||||
void VM_Version::get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss) {
|
||||
uint64_t vm_features_set1 = *(uint64_t*)features_set1;
|
||||
uint64_t vm_features_set2 = *(uint64_t*)features_set2;
|
||||
int i = 0;
|
||||
ss.join([&]() {
|
||||
const char* str = nullptr;
|
||||
while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
|
||||
Feature_Flag flag = (Feature_Flag)i;
|
||||
if (supports_feature(vm_features_set1, flag) && !supports_feature(vm_features_set2, flag)) {
|
||||
str = _features_names[i];
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return str;
|
||||
}, ", ");
|
||||
}
|
||||
|
||||
int VM_Version::cpu_features_size() {
|
||||
return sizeof(_features);
|
||||
}
|
||||
|
||||
void VM_Version::store_cpu_features(void* buf) {
|
||||
*(uint64_t*)buf = _features;
|
||||
}
|
||||
|
||||
bool VM_Version::supports_features(void* features_buffer) {
|
||||
uint64_t features_to_test = *(uint64_t*)features_buffer;
|
||||
return (_features & features_to_test) == features_to_test;
|
||||
}
|
||||
|
||||
#if defined(LINUX)
|
||||
static bool check_info_file(const char* fpath,
|
||||
const char* virt1, VirtualizationType vt1,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -184,6 +184,9 @@ public:
|
||||
static bool supports_feature(Feature_Flag flag) {
|
||||
return (_features & BIT_MASK(flag)) != 0;
|
||||
}
|
||||
static bool supports_feature(uint64_t features, Feature_Flag flag) {
|
||||
return (features & BIT_MASK(flag)) != 0;
|
||||
}
|
||||
|
||||
static int cpu_family() { return _cpu; }
|
||||
static int cpu_model() { return _model; }
|
||||
@ -244,6 +247,20 @@ public:
|
||||
static bool use_neon_for_vector(int vector_length_in_bytes) {
|
||||
return vector_length_in_bytes <= 16;
|
||||
}
|
||||
|
||||
static void get_cpu_features_name(void* features_buffer, stringStream& ss);
|
||||
|
||||
// Returns names of features present in features_set1 but not in features_set2
|
||||
static void get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss);
|
||||
|
||||
// Returns number of bytes required to store cpu features representation
|
||||
static int cpu_features_size();
|
||||
|
||||
// Stores cpu features representation in the provided buffer. This representation is arch dependent.
|
||||
// Size of the buffer must be same as returned by cpu_features_size()
|
||||
static void store_cpu_features(void* buf);
|
||||
|
||||
static bool supports_features(void* features_to_test);
|
||||
};
|
||||
|
||||
#endif // CPU_AARCH64_VM_VERSION_AARCH64_HPP
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025 SAP SE. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -49,11 +49,6 @@
|
||||
|
||||
#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
|
||||
|
||||
// Workaround for C++ overloading nastiness on '0' for RegisterOrConstant.
|
||||
inline static RegisterOrConstant constant(int value) {
|
||||
return RegisterOrConstant(value);
|
||||
}
|
||||
|
||||
void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg,
|
||||
Register temp_reg, Register temp2_reg) {
|
||||
if (VerifyMethodHandles) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2019 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -29,9 +29,6 @@
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/safepoint.hpp"
|
||||
#ifdef COMPILER2
|
||||
#include "opto/matcher.hpp"
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@ -39,7 +36,6 @@
|
||||
#define __ masm->
|
||||
|
||||
address CompiledDirectCall::emit_to_interp_stub(MacroAssembler *masm, address mark/* = nullptr*/) {
|
||||
#ifdef COMPILER2
|
||||
// Stub is fixed up when the corresponding call is converted from calling
|
||||
// compiled code to calling interpreted code.
|
||||
if (mark == nullptr) {
|
||||
@ -55,7 +51,7 @@ address CompiledDirectCall::emit_to_interp_stub(MacroAssembler *masm, address ma
|
||||
__ relocate(static_stub_Relocation::spec(mark));
|
||||
|
||||
AddressLiteral meta = __ allocate_metadata_address(nullptr);
|
||||
bool success = __ load_const_from_toc(as_Register(Matcher::inline_cache_reg_encode()), meta);
|
||||
bool success = __ load_const_from_toc(Z_inline_cache, meta);
|
||||
|
||||
__ set_inst_mark();
|
||||
AddressLiteral a((address)-1);
|
||||
@ -67,10 +63,6 @@ address CompiledDirectCall::emit_to_interp_stub(MacroAssembler *masm, address ma
|
||||
__ z_br(Z_R1);
|
||||
__ end_a_stub(); // Update current stubs pointer and restore insts_end.
|
||||
return stub;
|
||||
#else
|
||||
ShouldNotReachHere();
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
||||
@ -48,7 +48,7 @@ int VM_Version::_stepping;
|
||||
bool VM_Version::_has_intel_jcc_erratum;
|
||||
VM_Version::CpuidInfo VM_Version::_cpuid_info = { 0, };
|
||||
|
||||
#define DECLARE_CPU_FEATURE_NAME(id, name, bit) name,
|
||||
#define DECLARE_CPU_FEATURE_NAME(id, name, bit) XSTR(name),
|
||||
const char* VM_Version::_features_names[] = { CPU_FEATURE_FLAGS(DECLARE_CPU_FEATURE_NAME)};
|
||||
#undef DECLARE_CPU_FEATURE_NAME
|
||||
|
||||
@ -3297,12 +3297,50 @@ bool VM_Version::is_intrinsic_supported(vmIntrinsicID id) {
|
||||
void VM_Version::insert_features_names(VM_Version::VM_Features features, stringStream& ss) {
|
||||
int i = 0;
|
||||
ss.join([&]() {
|
||||
while (i < MAX_CPU_FEATURES) {
|
||||
if (_features.supports_feature((VM_Version::Feature_Flag)i)) {
|
||||
return _features_names[i++];
|
||||
const char* str = nullptr;
|
||||
while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
|
||||
if (features.supports_feature((VM_Version::Feature_Flag)i)) {
|
||||
str = _features_names[i];
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return (const char*)nullptr;
|
||||
return str;
|
||||
}, ", ");
|
||||
}
|
||||
|
||||
void VM_Version::get_cpu_features_name(void* features_buffer, stringStream& ss) {
|
||||
VM_Features* features = (VM_Features*)features_buffer;
|
||||
insert_features_names(*features, ss);
|
||||
}
|
||||
|
||||
void VM_Version::get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss) {
|
||||
VM_Features* vm_features_set1 = (VM_Features*)features_set1;
|
||||
VM_Features* vm_features_set2 = (VM_Features*)features_set2;
|
||||
int i = 0;
|
||||
ss.join([&]() {
|
||||
const char* str = nullptr;
|
||||
while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
|
||||
Feature_Flag flag = (Feature_Flag)i;
|
||||
if (vm_features_set1->supports_feature(flag) && !vm_features_set2->supports_feature(flag)) {
|
||||
str = _features_names[i];
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return str;
|
||||
}, ", ");
|
||||
}
|
||||
|
||||
int VM_Version::cpu_features_size() {
|
||||
return sizeof(VM_Features);
|
||||
}
|
||||
|
||||
void VM_Version::store_cpu_features(void* buf) {
|
||||
VM_Features copy = _features;
|
||||
copy.clear_feature(CPU_HT); // HT does not result in incompatibility of aot code cache
|
||||
memcpy(buf, ©, sizeof(VM_Features));
|
||||
}
|
||||
|
||||
bool VM_Version::supports_features(void* features_buffer) {
|
||||
VM_Features* features_to_test = (VM_Features*)features_buffer;
|
||||
return _features.supports_features(features_to_test);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -377,84 +377,84 @@ protected:
|
||||
*/
|
||||
enum Feature_Flag {
|
||||
#define CPU_FEATURE_FLAGS(decl) \
|
||||
decl(CX8, "cx8", 0) /* next bits are from cpuid 1 (EDX) */ \
|
||||
decl(CMOV, "cmov", 1) \
|
||||
decl(FXSR, "fxsr", 2) \
|
||||
decl(HT, "ht", 3) \
|
||||
decl(CX8, cx8, 0) /* next bits are from cpuid 1 (EDX) */ \
|
||||
decl(CMOV, cmov, 1) \
|
||||
decl(FXSR, fxsr, 2) \
|
||||
decl(HT, ht, 3) \
|
||||
\
|
||||
decl(MMX, "mmx", 4) \
|
||||
decl(3DNOW_PREFETCH, "3dnowpref", 5) /* Processor supports 3dnow prefetch and prefetchw instructions */ \
|
||||
decl(MMX, mmx, 4) \
|
||||
decl(3DNOW_PREFETCH, 3dnowpref, 5) /* Processor supports 3dnow prefetch and prefetchw instructions */ \
|
||||
/* may not necessarily support other 3dnow instructions */ \
|
||||
decl(SSE, "sse", 6) \
|
||||
decl(SSE2, "sse2", 7) \
|
||||
decl(SSE, sse, 6) \
|
||||
decl(SSE2, sse2, 7) \
|
||||
\
|
||||
decl(SSE3, "sse3", 8 ) /* SSE3 comes from cpuid 1 (ECX) */ \
|
||||
decl(SSSE3, "ssse3", 9 ) \
|
||||
decl(SSE4A, "sse4a", 10) \
|
||||
decl(SSE4_1, "sse4.1", 11) \
|
||||
decl(SSE3, sse3, 8 ) /* SSE3 comes from cpuid 1 (ECX) */ \
|
||||
decl(SSSE3, ssse3, 9 ) \
|
||||
decl(SSE4A, sse4a, 10) \
|
||||
decl(SSE4_1, sse4.1, 11) \
|
||||
\
|
||||
decl(SSE4_2, "sse4.2", 12) \
|
||||
decl(POPCNT, "popcnt", 13) \
|
||||
decl(LZCNT, "lzcnt", 14) \
|
||||
decl(TSC, "tsc", 15) \
|
||||
decl(SSE4_2, sse4.2, 12) \
|
||||
decl(POPCNT, popcnt, 13) \
|
||||
decl(LZCNT, lzcnt, 14) \
|
||||
decl(TSC, tsc, 15) \
|
||||
\
|
||||
decl(TSCINV_BIT, "tscinvbit", 16) \
|
||||
decl(TSCINV, "tscinv", 17) \
|
||||
decl(AVX, "avx", 18) \
|
||||
decl(AVX2, "avx2", 19) \
|
||||
decl(TSCINV_BIT, tscinvbit, 16) \
|
||||
decl(TSCINV, tscinv, 17) \
|
||||
decl(AVX, avx, 18) \
|
||||
decl(AVX2, avx2, 19) \
|
||||
\
|
||||
decl(AES, "aes", 20) \
|
||||
decl(ERMS, "erms", 21) /* enhanced 'rep movsb/stosb' instructions */ \
|
||||
decl(CLMUL, "clmul", 22) /* carryless multiply for CRC */ \
|
||||
decl(BMI1, "bmi1", 23) \
|
||||
decl(AES, aes, 20) \
|
||||
decl(ERMS, erms, 21) /* enhanced 'rep movsb/stosb' instructions */ \
|
||||
decl(CLMUL, clmul, 22) /* carryless multiply for CRC */ \
|
||||
decl(BMI1, bmi1, 23) \
|
||||
\
|
||||
decl(BMI2, "bmi2", 24) \
|
||||
decl(RTM, "rtm", 25) /* Restricted Transactional Memory instructions */ \
|
||||
decl(ADX, "adx", 26) \
|
||||
decl(AVX512F, "avx512f", 27) /* AVX 512bit foundation instructions */ \
|
||||
decl(BMI2, bmi2, 24) \
|
||||
decl(RTM, rtm, 25) /* Restricted Transactional Memory instructions */ \
|
||||
decl(ADX, adx, 26) \
|
||||
decl(AVX512F, avx512f, 27) /* AVX 512bit foundation instructions */ \
|
||||
\
|
||||
decl(AVX512DQ, "avx512dq", 28) \
|
||||
decl(AVX512PF, "avx512pf", 29) \
|
||||
decl(AVX512ER, "avx512er", 30) \
|
||||
decl(AVX512CD, "avx512cd", 31) \
|
||||
decl(AVX512DQ, avx512dq, 28) \
|
||||
decl(AVX512PF, avx512pf, 29) \
|
||||
decl(AVX512ER, avx512er, 30) \
|
||||
decl(AVX512CD, avx512cd, 31) \
|
||||
\
|
||||
decl(AVX512BW, "avx512bw", 32) /* Byte and word vector instructions */ \
|
||||
decl(AVX512VL, "avx512vl", 33) /* EVEX instructions with smaller vector length */ \
|
||||
decl(SHA, "sha", 34) /* SHA instructions */ \
|
||||
decl(FMA, "fma", 35) /* FMA instructions */ \
|
||||
decl(AVX512BW, avx512bw, 32) /* Byte and word vector instructions */ \
|
||||
decl(AVX512VL, avx512vl, 33) /* EVEX instructions with smaller vector length */ \
|
||||
decl(SHA, sha, 34) /* SHA instructions */ \
|
||||
decl(FMA, fma, 35) /* FMA instructions */ \
|
||||
\
|
||||
decl(VZEROUPPER, "vzeroupper", 36) /* Vzeroupper instruction */ \
|
||||
decl(AVX512_VPOPCNTDQ, "avx512_vpopcntdq", 37) /* Vector popcount */ \
|
||||
decl(AVX512_VPCLMULQDQ, "avx512_vpclmulqdq", 38) /* Vector carryless multiplication */ \
|
||||
decl(AVX512_VAES, "avx512_vaes", 39) /* Vector AES instruction */ \
|
||||
decl(VZEROUPPER, vzeroupper, 36) /* Vzeroupper instruction */ \
|
||||
decl(AVX512_VPOPCNTDQ, avx512_vpopcntdq, 37) /* Vector popcount */ \
|
||||
decl(AVX512_VPCLMULQDQ, avx512_vpclmulqdq, 38) /* Vector carryless multiplication */ \
|
||||
decl(AVX512_VAES, avx512_vaes, 39) /* Vector AES instruction */ \
|
||||
\
|
||||
decl(AVX512_VNNI, "avx512_vnni", 40) /* Vector Neural Network Instructions */ \
|
||||
decl(FLUSH, "clflush", 41) /* flush instruction */ \
|
||||
decl(FLUSHOPT, "clflushopt", 42) /* flusopth instruction */ \
|
||||
decl(CLWB, "clwb", 43) /* clwb instruction */ \
|
||||
decl(AVX512_VNNI, avx512_vnni, 40) /* Vector Neural Network Instructions */ \
|
||||
decl(FLUSH, clflush, 41) /* flush instruction */ \
|
||||
decl(FLUSHOPT, clflushopt, 42) /* flusopth instruction */ \
|
||||
decl(CLWB, clwb, 43) /* clwb instruction */ \
|
||||
\
|
||||
decl(AVX512_VBMI2, "avx512_vbmi2", 44) /* VBMI2 shift left double instructions */ \
|
||||
decl(AVX512_VBMI, "avx512_vbmi", 45) /* Vector BMI instructions */ \
|
||||
decl(HV, "hv", 46) /* Hypervisor instructions */ \
|
||||
decl(SERIALIZE, "serialize", 47) /* CPU SERIALIZE */ \
|
||||
decl(RDTSCP, "rdtscp", 48) /* RDTSCP instruction */ \
|
||||
decl(RDPID, "rdpid", 49) /* RDPID instruction */ \
|
||||
decl(FSRM, "fsrm", 50) /* Fast Short REP MOV */ \
|
||||
decl(GFNI, "gfni", 51) /* Vector GFNI instructions */ \
|
||||
decl(AVX512_BITALG, "avx512_bitalg", 52) /* Vector sub-word popcount and bit gather instructions */\
|
||||
decl(F16C, "f16c", 53) /* Half-precision and single precision FP conversion instructions*/ \
|
||||
decl(PKU, "pku", 54) /* Protection keys for user-mode pages */ \
|
||||
decl(OSPKE, "ospke", 55) /* OS enables protection keys */ \
|
||||
decl(CET_IBT, "cet_ibt", 56) /* Control Flow Enforcement - Indirect Branch Tracking */ \
|
||||
decl(CET_SS, "cet_ss", 57) /* Control Flow Enforcement - Shadow Stack */ \
|
||||
decl(AVX512_IFMA, "avx512_ifma", 58) /* Integer Vector FMA instructions*/ \
|
||||
decl(AVX_IFMA, "avx_ifma", 59) /* 256-bit VEX-coded variant of AVX512-IFMA*/ \
|
||||
decl(APX_F, "apx_f", 60) /* Intel Advanced Performance Extensions*/ \
|
||||
decl(SHA512, "sha512", 61) /* SHA512 instructions*/ \
|
||||
decl(AVX512_FP16, "avx512_fp16", 62) /* AVX512 FP16 ISA support*/ \
|
||||
decl(AVX10_1, "avx10_1", 63) /* AVX10 512 bit vector ISA Version 1 support*/ \
|
||||
decl(AVX10_2, "avx10_2", 64) /* AVX10 512 bit vector ISA Version 2 support*/ \
|
||||
decl(HYBRID, "hybrid", 65) /* Hybrid architecture */
|
||||
decl(AVX512_VBMI2, avx512_vbmi2, 44) /* VBMI2 shift left double instructions */ \
|
||||
decl(AVX512_VBMI, avx512_vbmi, 45) /* Vector BMI instructions */ \
|
||||
decl(HV, hv, 46) /* Hypervisor instructions */ \
|
||||
decl(SERIALIZE, serialize, 47) /* CPU SERIALIZE */ \
|
||||
decl(RDTSCP, rdtscp, 48) /* RDTSCP instruction */ \
|
||||
decl(RDPID, rdpid, 49) /* RDPID instruction */ \
|
||||
decl(FSRM, fsrm, 50) /* Fast Short REP MOV */ \
|
||||
decl(GFNI, gfni, 51) /* Vector GFNI instructions */ \
|
||||
decl(AVX512_BITALG, avx512_bitalg, 52) /* Vector sub-word popcount and bit gather instructions */\
|
||||
decl(F16C, f16c, 53) /* Half-precision and single precision FP conversion instructions*/ \
|
||||
decl(PKU, pku, 54) /* Protection keys for user-mode pages */ \
|
||||
decl(OSPKE, ospke, 55) /* OS enables protection keys */ \
|
||||
decl(CET_IBT, cet_ibt, 56) /* Control Flow Enforcement - Indirect Branch Tracking */ \
|
||||
decl(CET_SS, cet_ss, 57) /* Control Flow Enforcement - Shadow Stack */ \
|
||||
decl(AVX512_IFMA, avx512_ifma, 58) /* Integer Vector FMA instructions*/ \
|
||||
decl(AVX_IFMA, avx_ifma, 59) /* 256-bit VEX-coded variant of AVX512-IFMA*/ \
|
||||
decl(APX_F, apx_f, 60) /* Intel Advanced Performance Extensions*/ \
|
||||
decl(SHA512, sha512, 61) /* SHA512 instructions*/ \
|
||||
decl(AVX512_FP16, avx512_fp16, 62) /* AVX512 FP16 ISA support*/ \
|
||||
decl(AVX10_1, avx10_1, 63) /* AVX10 512 bit vector ISA Version 1 support*/ \
|
||||
decl(AVX10_2, avx10_2, 64) /* AVX10 512 bit vector ISA Version 2 support*/ \
|
||||
decl(HYBRID, hybrid, 65) /* Hybrid architecture */
|
||||
|
||||
#define DECLARE_CPU_FEATURE_FLAG(id, name, bit) CPU_##id = (bit),
|
||||
CPU_FEATURE_FLAGS(DECLARE_CPU_FEATURE_FLAG)
|
||||
@ -516,6 +516,15 @@ protected:
|
||||
int idx = index(feature);
|
||||
return (_features_bitmap[idx] & bit_mask(feature)) != 0;
|
||||
}
|
||||
|
||||
bool supports_features(VM_Features* features_to_test) {
|
||||
for (int i = 0; i < features_bitmap_element_count(); i++) {
|
||||
if ((_features_bitmap[i] & features_to_test->_features_bitmap[i]) != features_to_test->_features_bitmap[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// CPU feature flags vector, can be affected by VM settings.
|
||||
@ -1103,6 +1112,20 @@ public:
|
||||
static bool supports_tscinv_ext(void);
|
||||
|
||||
static void initialize_cpu_information(void);
|
||||
|
||||
static void get_cpu_features_name(void* features_buffer, stringStream& ss);
|
||||
|
||||
// Returns names of features present in features_set1 but not in features_set2
|
||||
static void get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss);
|
||||
|
||||
// Returns number of bytes required to store cpu features representation
|
||||
static int cpu_features_size();
|
||||
|
||||
// Stores cpu features representation in the provided buffer. This representation is arch dependent.
|
||||
// Size of the buffer must be same as returned by cpu_features_size()
|
||||
static void store_cpu_features(void* buf);
|
||||
|
||||
static bool supports_features(void* features_to_test);
|
||||
};
|
||||
|
||||
#endif // CPU_X86_VM_VERSION_X86_HPP
|
||||
|
||||
@ -143,12 +143,6 @@ static OSReturn get_jvm_load(double* jvm_uload, double* jvm_sload) {
|
||||
return OS_OK;
|
||||
}
|
||||
|
||||
static void update_prev_time(jvm_time_store_t* from, jvm_time_store_t* to) {
|
||||
if (from && to) {
|
||||
memcpy(to, from, sizeof(jvm_time_store_t));
|
||||
}
|
||||
}
|
||||
|
||||
static void update_prev_ticks(cpu_tick_store_t* from, cpu_tick_store_t* to) {
|
||||
if (from && to) {
|
||||
memcpy(to, from, sizeof(cpu_tick_store_t));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, 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
|
||||
@ -426,7 +426,8 @@ bool AOTClassLocation::check(const char* runtime_path, bool has_aot_linked_class
|
||||
bool size_differs = _filesize != st.st_size;
|
||||
bool time_differs = _check_time && (_timestamp != st.st_mtime);
|
||||
if (size_differs || time_differs) {
|
||||
aot_log_warning(aot)("This file is not the one used while building the shared archive file: '%s'%s%s",
|
||||
aot_log_warning(aot)("This file is not the one used while building the %s: '%s'%s%s",
|
||||
CDSConfig::type_of_archive_being_loaded(),
|
||||
runtime_path,
|
||||
time_differs ? ", timestamp has changed" : "",
|
||||
size_differs ? ", size has changed" : "");
|
||||
@ -448,6 +449,13 @@ void AOTClassLocationConfig::dumptime_init(JavaThread* current) {
|
||||
java_lang_Throwable::print(current->pending_exception(), tty);
|
||||
vm_exit_during_initialization("AOTClassLocationConfig::dumptime_init_helper() failed unexpectedly");
|
||||
}
|
||||
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
// The _max_used_index is usually updated by ClassLoader::record_result(). However,
|
||||
// when dumping the final archive, the classes are loaded from their images in
|
||||
// the AOT config file, so we don't go through ClassLoader::record_result().
|
||||
dumptime_update_max_used_index(runtime()->_max_used_index); // Same value as recorded in the training run.
|
||||
}
|
||||
}
|
||||
|
||||
void AOTClassLocationConfig::dumptime_init_helper(TRAPS) {
|
||||
|
||||
@ -1148,7 +1148,7 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
|
||||
if (CDSConfig::is_dumping_full_module_graph()) {
|
||||
ClassLoaderDataShared::ensure_module_entry_tables_exist();
|
||||
ClassLoaderDataShared::build_tables(CHECK);
|
||||
HeapShared::reset_archived_object_states(CHECK);
|
||||
HeapShared::prepare_for_archiving(CHECK);
|
||||
}
|
||||
|
||||
AOTReferenceObjSupport::initialize(CHECK);
|
||||
|
||||
@ -247,6 +247,28 @@ void HeapShared::reset_archived_object_states(TRAPS) {
|
||||
reset_states(boot_loader(), CHECK);
|
||||
}
|
||||
|
||||
void HeapShared::ensure_determinism(TRAPS) {
|
||||
TempNewSymbol class_name = SymbolTable::new_symbol("jdk/internal/util/WeakReferenceKey");
|
||||
TempNewSymbol method_name = SymbolTable::new_symbol("ensureDeterministicAOTCache");
|
||||
|
||||
Klass* weak_ref_key_class = SystemDictionary::resolve_or_fail(class_name, true, CHECK);
|
||||
precond(weak_ref_key_class != nullptr);
|
||||
|
||||
log_debug(aot)("Calling WeakReferenceKey::ensureDeterministicAOTCache(Object.class)");
|
||||
JavaValue result(T_BOOLEAN);
|
||||
JavaCalls::call_static(&result,
|
||||
weak_ref_key_class,
|
||||
method_name,
|
||||
vmSymbols::void_boolean_signature(),
|
||||
CHECK);
|
||||
assert(result.get_jboolean() == false, "sanity");
|
||||
}
|
||||
|
||||
void HeapShared::prepare_for_archiving(TRAPS) {
|
||||
reset_archived_object_states(CHECK);
|
||||
ensure_determinism(CHECK);
|
||||
}
|
||||
|
||||
HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = nullptr;
|
||||
|
||||
bool HeapShared::is_archived_heap_in_use() {
|
||||
|
||||
@ -382,8 +382,10 @@ private:
|
||||
static bool walk_one_object(PendingOopStack* stack, int level, KlassSubGraphInfo* subgraph_info,
|
||||
oop orig_obj, oop referrer);
|
||||
|
||||
public:
|
||||
static void reset_archived_object_states(TRAPS);
|
||||
static void ensure_determinism(TRAPS);
|
||||
public:
|
||||
static void prepare_for_archiving(TRAPS);
|
||||
static void create_archived_object_cache() {
|
||||
_archived_object_cache =
|
||||
new (mtClass)ArchivedObjectCache(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE);
|
||||
|
||||
@ -399,7 +399,7 @@ AOTCodeCache::~AOTCodeCache() {
|
||||
}
|
||||
}
|
||||
|
||||
void AOTCodeCache::Config::record() {
|
||||
void AOTCodeCache::Config::record(uint cpu_features_offset) {
|
||||
_flags = 0;
|
||||
#ifdef ASSERT
|
||||
_flags |= debugVM;
|
||||
@ -430,9 +430,50 @@ void AOTCodeCache::Config::record() {
|
||||
_compressedKlassShift = CompressedKlassPointers::shift();
|
||||
_contendedPaddingWidth = ContendedPaddingWidth;
|
||||
_gc = (uint)Universe::heap()->kind();
|
||||
_cpu_features_offset = cpu_features_offset;
|
||||
}
|
||||
|
||||
bool AOTCodeCache::Config::verify() const {
|
||||
bool AOTCodeCache::Config::verify_cpu_features(AOTCodeCache* cache) const {
|
||||
LogStreamHandle(Debug, aot, codecache, init) log;
|
||||
uint offset = _cpu_features_offset;
|
||||
uint cpu_features_size = *(uint *)cache->addr(offset);
|
||||
assert(cpu_features_size == (uint)VM_Version::cpu_features_size(), "must be");
|
||||
offset += sizeof(uint);
|
||||
|
||||
void* cached_cpu_features_buffer = (void *)cache->addr(offset);
|
||||
if (log.is_enabled()) {
|
||||
ResourceMark rm; // required for stringStream::as_string()
|
||||
stringStream ss;
|
||||
VM_Version::get_cpu_features_name(cached_cpu_features_buffer, ss);
|
||||
log.print_cr("CPU features recorded in AOTCodeCache: %s", ss.as_string());
|
||||
}
|
||||
|
||||
if (VM_Version::supports_features(cached_cpu_features_buffer)) {
|
||||
if (log.is_enabled()) {
|
||||
ResourceMark rm; // required for stringStream::as_string()
|
||||
stringStream ss;
|
||||
char* runtime_cpu_features = NEW_RESOURCE_ARRAY(char, VM_Version::cpu_features_size());
|
||||
VM_Version::store_cpu_features(runtime_cpu_features);
|
||||
VM_Version::get_missing_features_name(runtime_cpu_features, cached_cpu_features_buffer, ss);
|
||||
if (!ss.is_empty()) {
|
||||
log.print_cr("Additional runtime CPU features: %s", ss.as_string());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (log.is_enabled()) {
|
||||
ResourceMark rm; // required for stringStream::as_string()
|
||||
stringStream ss;
|
||||
char* runtime_cpu_features = NEW_RESOURCE_ARRAY(char, VM_Version::cpu_features_size());
|
||||
VM_Version::store_cpu_features(runtime_cpu_features);
|
||||
VM_Version::get_missing_features_name(cached_cpu_features_buffer, runtime_cpu_features, ss);
|
||||
log.print_cr("AOT Code Cache disabled: required cpu features are missing: %s", ss.as_string());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
|
||||
// First checks affect all cached AOT code
|
||||
#ifdef ASSERT
|
||||
if ((_flags & debugVM) == 0) {
|
||||
@ -478,6 +519,9 @@ bool AOTCodeCache::Config::verify() const {
|
||||
AOTStubCaching = false;
|
||||
}
|
||||
|
||||
if (!verify_cpu_features(cache)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -679,6 +723,17 @@ extern "C" {
|
||||
}
|
||||
}
|
||||
|
||||
void AOTCodeCache::store_cpu_features(char*& buffer, uint buffer_size) {
|
||||
uint* size_ptr = (uint *)buffer;
|
||||
*size_ptr = buffer_size;
|
||||
buffer += sizeof(uint);
|
||||
|
||||
VM_Version::store_cpu_features(buffer);
|
||||
log_debug(aot, codecache, exit)("CPU features recorded in AOTCodeCache: %s", VM_Version::features_string());
|
||||
buffer += buffer_size;
|
||||
buffer = align_up(buffer, DATA_ALIGNMENT);
|
||||
}
|
||||
|
||||
bool AOTCodeCache::finish_write() {
|
||||
if (!align_write()) {
|
||||
return false;
|
||||
@ -698,23 +753,32 @@ bool AOTCodeCache::finish_write() {
|
||||
|
||||
uint store_count = _store_entries_cnt;
|
||||
if (store_count > 0) {
|
||||
uint header_size = (uint)align_up(sizeof(AOTCodeCache::Header), DATA_ALIGNMENT);
|
||||
uint header_size = (uint)align_up(sizeof(AOTCodeCache::Header), DATA_ALIGNMENT);
|
||||
uint code_count = store_count;
|
||||
uint search_count = code_count * 2;
|
||||
uint search_size = search_count * sizeof(uint);
|
||||
uint entries_size = (uint)align_up(code_count * sizeof(AOTCodeEntry), DATA_ALIGNMENT); // In bytes
|
||||
// _write_position includes size of code and strings
|
||||
uint code_alignment = code_count * DATA_ALIGNMENT; // We align_up code size when storing it.
|
||||
uint total_size = header_size + _write_position + code_alignment + search_size + entries_size;
|
||||
uint cpu_features_size = VM_Version::cpu_features_size();
|
||||
uint total_cpu_features_size = sizeof(uint) + cpu_features_size; // sizeof(uint) to store cpu_features_size
|
||||
uint total_size = header_size + _write_position + code_alignment + search_size + entries_size +
|
||||
align_up(total_cpu_features_size, DATA_ALIGNMENT);
|
||||
assert(total_size < max_aot_code_size(), "AOT Code size (" UINT32_FORMAT " bytes) is greater than AOTCodeMaxSize(" UINT32_FORMAT " bytes).", total_size, max_aot_code_size());
|
||||
|
||||
// Create ordered search table for entries [id, index];
|
||||
uint* search = NEW_C_HEAP_ARRAY(uint, search_count, mtCode);
|
||||
// Allocate in AOT Cache buffer
|
||||
char* buffer = (char *)AOTCacheAccess::allocate_aot_code_region(total_size + DATA_ALIGNMENT);
|
||||
char* start = align_up(buffer, DATA_ALIGNMENT);
|
||||
char* current = start + header_size; // Skip header
|
||||
|
||||
uint cpu_features_offset = current - start;
|
||||
store_cpu_features(current, cpu_features_size);
|
||||
assert(is_aligned(current, DATA_ALIGNMENT), "sanity check");
|
||||
assert(current < start + total_size, "sanity check");
|
||||
|
||||
// Create ordered search table for entries [id, index];
|
||||
uint* search = NEW_C_HEAP_ARRAY(uint, search_count, mtCode);
|
||||
|
||||
AOTCodeEntry* entries_address = _store_entries; // Pointer to latest entry
|
||||
uint adapters_count = 0;
|
||||
uint shared_blobs_count = 0;
|
||||
@ -790,7 +854,7 @@ bool AOTCodeCache::finish_write() {
|
||||
header->init(size, (uint)strings_count, strings_offset,
|
||||
entries_count, new_entries_offset,
|
||||
adapters_count, shared_blobs_count,
|
||||
C1_blobs_count, C2_blobs_count);
|
||||
C1_blobs_count, C2_blobs_count, cpu_features_offset);
|
||||
|
||||
log_info(aot, codecache, exit)("Wrote %d AOT code entries to AOT Code Cache", entries_count);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2026, 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
|
||||
@ -185,10 +185,12 @@ protected:
|
||||
restrictContendedPadding = 128
|
||||
};
|
||||
uint _flags;
|
||||
uint _cpu_features_offset; // offset in the cache where cpu features are stored
|
||||
|
||||
public:
|
||||
void record();
|
||||
bool verify() const;
|
||||
void record(uint cpu_features_offset);
|
||||
bool verify_cpu_features(AOTCodeCache* cache) const;
|
||||
bool verify(AOTCodeCache* cache) const;
|
||||
};
|
||||
|
||||
class Header : public CHeapObj<mtCode> {
|
||||
@ -206,14 +208,15 @@ protected:
|
||||
uint _shared_blobs_count;
|
||||
uint _C1_blobs_count;
|
||||
uint _C2_blobs_count;
|
||||
Config _config;
|
||||
Config _config; // must be the last element as there is trailing data stored immediately after Config
|
||||
|
||||
public:
|
||||
void init(uint cache_size,
|
||||
uint strings_count, uint strings_offset,
|
||||
uint entries_count, uint entries_offset,
|
||||
uint adapters_count, uint shared_blobs_count,
|
||||
uint C1_blobs_count, uint C2_blobs_count) {
|
||||
uint C1_blobs_count, uint C2_blobs_count,
|
||||
uint cpu_features_offset) {
|
||||
_version = AOT_CODE_VERSION;
|
||||
_cache_size = cache_size;
|
||||
_strings_count = strings_count;
|
||||
@ -224,7 +227,7 @@ protected:
|
||||
_shared_blobs_count = shared_blobs_count;
|
||||
_C1_blobs_count = C1_blobs_count;
|
||||
_C2_blobs_count = C2_blobs_count;
|
||||
_config.record();
|
||||
_config.record(cpu_features_offset);
|
||||
}
|
||||
|
||||
|
||||
@ -239,8 +242,8 @@ protected:
|
||||
uint C2_blobs_count() const { return _C2_blobs_count; }
|
||||
|
||||
bool verify(uint load_size) const;
|
||||
bool verify_config() const { // Called after Universe initialized
|
||||
return _config.verify();
|
||||
bool verify_config(AOTCodeCache* cache) const { // Called after Universe initialized
|
||||
return _config.verify(cache);
|
||||
}
|
||||
};
|
||||
|
||||
@ -320,6 +323,8 @@ public:
|
||||
|
||||
AOTCodeEntry* find_entry(AOTCodeEntry::Kind kind, uint id);
|
||||
|
||||
void store_cpu_features(char*& buffer, uint buffer_size);
|
||||
|
||||
bool finish_write();
|
||||
|
||||
bool write_relocations(CodeBlob& code_blob);
|
||||
@ -361,7 +366,7 @@ private:
|
||||
static bool open_cache(bool is_dumping, bool is_using);
|
||||
bool verify_config() {
|
||||
if (for_use()) {
|
||||
return _load_header->verify_config();
|
||||
return _load_header->verify_config(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, 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
|
||||
@ -336,6 +336,7 @@ RuntimeBlob::RuntimeBlob(
|
||||
|
||||
void RuntimeBlob::free(RuntimeBlob* blob) {
|
||||
assert(blob != nullptr, "caller must check for nullptr");
|
||||
MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
|
||||
ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock
|
||||
blob->purge();
|
||||
{
|
||||
|
||||
@ -24,10 +24,9 @@
|
||||
|
||||
#include "gc/g1/g1BlockOffsetTable.inline.hpp"
|
||||
#include "gc/g1/g1CollectedHeap.inline.hpp"
|
||||
#include "gc/g1/g1HeapRegion.inline.hpp"
|
||||
#include "gc/g1/g1RegionToSpaceMapper.hpp"
|
||||
#include "gc/shared/memset_with_concurrent_readers.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/java.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
|
||||
size_t G1BlockOffsetTable::compute_size(size_t mem_region_words) {
|
||||
@ -52,6 +51,12 @@ void G1BlockOffsetTable::set_offset_array(Atomic<uint8_t>* addr, uint8_t offset)
|
||||
addr->store_relaxed(offset);
|
||||
}
|
||||
|
||||
static void check_offset(size_t offset, const char* msg) {
|
||||
assert(offset < CardTable::card_size_in_words(),
|
||||
"%s - offset: %zu, N_words: %u",
|
||||
msg, offset, CardTable::card_size_in_words());
|
||||
}
|
||||
|
||||
void G1BlockOffsetTable::set_offset_array(Atomic<uint8_t>* addr, HeapWord* high, HeapWord* low) {
|
||||
assert(high >= low, "addresses out of order");
|
||||
size_t offset = pointer_delta(high, low);
|
||||
|
||||
@ -37,19 +37,12 @@
|
||||
// for each such subregion indicates how far back one must go to find the
|
||||
// start of the chunk that includes the first word of the subregion.
|
||||
class G1BlockOffsetTable : public CHeapObj<mtGC> {
|
||||
private:
|
||||
// The reserved region covered by the table.
|
||||
MemRegion _reserved;
|
||||
|
||||
// Biased array-start of BOT array for fast BOT entry translation
|
||||
Atomic<uint8_t>* _offset_base;
|
||||
|
||||
void check_offset(size_t offset, const char* msg) const {
|
||||
assert(offset < CardTable::card_size_in_words(),
|
||||
"%s - offset: %zu, N_words: %u",
|
||||
msg, offset, CardTable::card_size_in_words());
|
||||
}
|
||||
|
||||
// Bounds checking accessors:
|
||||
// For performance these have to devolve to array accesses in product builds.
|
||||
inline uint8_t offset_array(Atomic<uint8_t>* addr) const;
|
||||
@ -85,7 +78,6 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// Return the number of slots needed for an offset array
|
||||
// that covers mem_region_words words.
|
||||
static size_t compute_size(size_t mem_region_words);
|
||||
@ -99,22 +91,14 @@ public:
|
||||
// in the heap parameter.
|
||||
G1BlockOffsetTable(MemRegion heap, G1RegionToSpaceMapper* storage);
|
||||
|
||||
static bool is_crossing_card_boundary(HeapWord* const obj_start,
|
||||
HeapWord* const obj_end) {
|
||||
HeapWord* cur_card_boundary = align_up_by_card_size(obj_start);
|
||||
// strictly greater-than
|
||||
return obj_end > cur_card_boundary;
|
||||
}
|
||||
inline static bool is_crossing_card_boundary(HeapWord* const obj_start,
|
||||
HeapWord* const obj_end);
|
||||
|
||||
// Returns the address of the start of the block reaching into the card containing
|
||||
// "addr".
|
||||
inline HeapWord* block_start_reaching_into_card(const void* addr) const;
|
||||
|
||||
void update_for_block(HeapWord* blk_start, HeapWord* blk_end) {
|
||||
if (is_crossing_card_boundary(blk_start, blk_end)) {
|
||||
update_for_block_work(blk_start, blk_end);
|
||||
}
|
||||
}
|
||||
inline void update_for_block(HeapWord* blk_start, HeapWord* blk_end);
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_G1_G1BLOCKOFFSETTABLE_HPP
|
||||
|
||||
@ -27,10 +27,7 @@
|
||||
|
||||
#include "gc/g1/g1BlockOffsetTable.hpp"
|
||||
|
||||
#include "gc/g1/g1HeapRegion.hpp"
|
||||
#include "gc/shared/cardTable.hpp"
|
||||
#include "gc/shared/memset_with_concurrent_readers.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
|
||||
inline HeapWord* G1BlockOffsetTable::block_start_reaching_into_card(const void* addr) const {
|
||||
assert(_reserved.contains(addr), "invalid address");
|
||||
@ -70,4 +67,17 @@ inline HeapWord* G1BlockOffsetTable::addr_for_entry(const Atomic<uint8_t>* const
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool G1BlockOffsetTable::is_crossing_card_boundary(HeapWord* const obj_start,
|
||||
HeapWord* const obj_end) {
|
||||
HeapWord* cur_card_boundary = align_up_by_card_size(obj_start);
|
||||
// strictly greater-than
|
||||
return obj_end > cur_card_boundary;
|
||||
}
|
||||
|
||||
inline void G1BlockOffsetTable::update_for_block(HeapWord* blk_start, HeapWord* blk_end) {
|
||||
if (is_crossing_card_boundary(blk_start, blk_end)) {
|
||||
update_for_block_work(blk_start, blk_end);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SHARE_GC_G1_G1BLOCKOFFSETTABLE_INLINE_HPP
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "classfile/classLoaderData.hpp"
|
||||
#include "classfile/classLoaderDataGraph.hpp"
|
||||
#include "cppstdlib/new.hpp"
|
||||
#include "gc/g1/g1BarrierSet.hpp"
|
||||
#include "gc/g1/g1BatchedTask.hpp"
|
||||
#include "gc/g1/g1CardSetMemory.hpp"
|
||||
@ -519,8 +520,8 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h,
|
||||
_max_concurrent_workers(0),
|
||||
|
||||
_region_mark_stats(NEW_C_HEAP_ARRAY(G1RegionMarkStats, _g1h->max_num_regions(), mtGC)),
|
||||
_top_at_mark_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_num_regions(), mtGC)),
|
||||
_top_at_rebuild_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_num_regions(), mtGC)),
|
||||
_top_at_mark_starts(NEW_C_HEAP_ARRAY(Atomic<HeapWord*>, _g1h->max_num_regions(), mtGC)),
|
||||
_top_at_rebuild_starts(NEW_C_HEAP_ARRAY(Atomic<HeapWord*>, _g1h->max_num_regions(), mtGC)),
|
||||
_needs_remembered_set_rebuild(false)
|
||||
{
|
||||
assert(G1CGC_lock != nullptr, "CGC_lock must be initialized");
|
||||
@ -564,6 +565,12 @@ void G1ConcurrentMark::fully_initialize() {
|
||||
_tasks[i] = new G1CMTask(i, this, task_queue, _region_mark_stats);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < _g1h->max_num_regions(); i++) {
|
||||
::new (&_region_mark_stats[i]) G1RegionMarkStats{};
|
||||
::new (&_top_at_mark_starts[i]) Atomic<HeapWord*>{};
|
||||
::new (&_top_at_rebuild_starts[i]) Atomic<HeapWord*>{};
|
||||
}
|
||||
|
||||
reset_at_marking_complete();
|
||||
}
|
||||
|
||||
@ -576,7 +583,7 @@ PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const
|
||||
}
|
||||
|
||||
void G1ConcurrentMark::reset() {
|
||||
_has_aborted = false;
|
||||
_has_aborted.store_relaxed(false);
|
||||
|
||||
reset_marking_for_restart();
|
||||
|
||||
@ -588,7 +595,7 @@ void G1ConcurrentMark::reset() {
|
||||
|
||||
uint max_num_regions = _g1h->max_num_regions();
|
||||
for (uint i = 0; i < max_num_regions; i++) {
|
||||
_top_at_rebuild_starts[i] = nullptr;
|
||||
_top_at_rebuild_starts[i].store_relaxed(nullptr);
|
||||
_region_mark_stats[i].clear();
|
||||
}
|
||||
|
||||
@ -600,7 +607,7 @@ void G1ConcurrentMark::clear_statistics(G1HeapRegion* r) {
|
||||
for (uint j = 0; j < _max_num_tasks; ++j) {
|
||||
_tasks[j]->clear_mark_stats_cache(region_idx);
|
||||
}
|
||||
_top_at_rebuild_starts[region_idx] = nullptr;
|
||||
_top_at_rebuild_starts[region_idx].store_relaxed(nullptr);
|
||||
_region_mark_stats[region_idx].clear();
|
||||
}
|
||||
|
||||
@ -636,7 +643,7 @@ void G1ConcurrentMark::reset_marking_for_restart() {
|
||||
}
|
||||
|
||||
clear_has_overflown();
|
||||
_finger = _heap.start();
|
||||
_finger.store_relaxed(_heap.start());
|
||||
|
||||
for (uint i = 0; i < _max_num_tasks; ++i) {
|
||||
_tasks[i]->reset_for_restart();
|
||||
@ -657,14 +664,14 @@ void G1ConcurrentMark::set_concurrency(uint active_tasks) {
|
||||
void G1ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurrent) {
|
||||
set_concurrency(active_tasks);
|
||||
|
||||
_concurrent = concurrent;
|
||||
_concurrent.store_relaxed(concurrent);
|
||||
|
||||
if (!concurrent) {
|
||||
// At this point we should be in a STW phase, and completed marking.
|
||||
assert_at_safepoint_on_vm_thread();
|
||||
assert(out_of_regions(),
|
||||
"only way to get here: _finger: " PTR_FORMAT ", _heap_end: " PTR_FORMAT,
|
||||
p2i(_finger), p2i(_heap.end()));
|
||||
p2i(finger()), p2i(_heap.end()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -695,8 +702,8 @@ void G1ConcurrentMark::reset_at_marking_complete() {
|
||||
}
|
||||
|
||||
G1ConcurrentMark::~G1ConcurrentMark() {
|
||||
FREE_C_HEAP_ARRAY(HeapWord*, _top_at_mark_starts);
|
||||
FREE_C_HEAP_ARRAY(HeapWord*, _top_at_rebuild_starts);
|
||||
FREE_C_HEAP_ARRAY(Atomic<HeapWord*>, _top_at_mark_starts);
|
||||
FREE_C_HEAP_ARRAY(Atomic<HeapWord*>, _top_at_rebuild_starts);
|
||||
FREE_C_HEAP_ARRAY(G1RegionMarkStats, _region_mark_stats);
|
||||
// The G1ConcurrentMark instance is never freed.
|
||||
ShouldNotReachHere();
|
||||
@ -921,6 +928,8 @@ public:
|
||||
bool do_heap_region(G1HeapRegion* r) override {
|
||||
if (r->is_old_or_humongous() && !r->is_collection_set_candidate() && !r->in_collection_set()) {
|
||||
_cm->update_top_at_mark_start(r);
|
||||
} else {
|
||||
_cm->reset_top_at_mark_start(r);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1163,7 +1172,7 @@ void G1ConcurrentMark::concurrent_cycle_start() {
|
||||
}
|
||||
|
||||
uint G1ConcurrentMark::completed_mark_cycles() const {
|
||||
return AtomicAccess::load(&_completed_mark_cycles);
|
||||
return _completed_mark_cycles.load_relaxed();
|
||||
}
|
||||
|
||||
void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) {
|
||||
@ -1172,7 +1181,7 @@ void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) {
|
||||
_g1h->trace_heap_after_gc(_gc_tracer_cm);
|
||||
|
||||
if (mark_cycle_completed) {
|
||||
AtomicAccess::inc(&_completed_mark_cycles, memory_order_relaxed);
|
||||
_completed_mark_cycles.add_then_fetch(1u, memory_order_relaxed);
|
||||
}
|
||||
|
||||
if (has_aborted()) {
|
||||
@ -1186,7 +1195,7 @@ void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) {
|
||||
}
|
||||
|
||||
void G1ConcurrentMark::mark_from_roots() {
|
||||
_restart_for_overflow = false;
|
||||
_restart_for_overflow.store_relaxed(false);
|
||||
|
||||
uint active_workers = calc_active_marking_workers();
|
||||
|
||||
@ -1355,7 +1364,7 @@ void G1ConcurrentMark::remark() {
|
||||
}
|
||||
} else {
|
||||
// We overflowed. Restart concurrent marking.
|
||||
_restart_for_overflow = true;
|
||||
_restart_for_overflow.store_relaxed(true);
|
||||
|
||||
verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyLocation::RemarkOverflow);
|
||||
|
||||
@ -1784,44 +1793,45 @@ void G1ConcurrentMark::clear_bitmap_for_region(G1HeapRegion* hr) {
|
||||
}
|
||||
|
||||
G1HeapRegion* G1ConcurrentMark::claim_region(uint worker_id) {
|
||||
// "checkpoint" the finger
|
||||
HeapWord* finger = _finger;
|
||||
// "Checkpoint" the finger.
|
||||
HeapWord* local_finger = finger();
|
||||
|
||||
while (finger < _heap.end()) {
|
||||
assert(_g1h->is_in_reserved(finger), "invariant");
|
||||
while (local_finger < _heap.end()) {
|
||||
assert(_g1h->is_in_reserved(local_finger), "invariant");
|
||||
|
||||
G1HeapRegion* curr_region = _g1h->heap_region_containing_or_null(finger);
|
||||
G1HeapRegion* curr_region = _g1h->heap_region_containing_or_null(local_finger);
|
||||
// Make sure that the reads below do not float before loading curr_region.
|
||||
OrderAccess::loadload();
|
||||
// Above heap_region_containing may return null as we always scan claim
|
||||
// until the end of the heap. In this case, just jump to the next region.
|
||||
HeapWord* end = curr_region != nullptr ? curr_region->end() : finger + G1HeapRegion::GrainWords;
|
||||
HeapWord* end = curr_region != nullptr ? curr_region->end() : local_finger + G1HeapRegion::GrainWords;
|
||||
|
||||
// Is the gap between reading the finger and doing the CAS too long?
|
||||
HeapWord* res = AtomicAccess::cmpxchg(&_finger, finger, end);
|
||||
if (res == finger && curr_region != nullptr) {
|
||||
// we succeeded
|
||||
HeapWord* res = _finger.compare_exchange(local_finger, end);
|
||||
if (res == local_finger && curr_region != nullptr) {
|
||||
// We succeeded.
|
||||
HeapWord* bottom = curr_region->bottom();
|
||||
HeapWord* limit = top_at_mark_start(curr_region);
|
||||
|
||||
log_trace(gc, marking)("Claim region %u bottom " PTR_FORMAT " tams " PTR_FORMAT, curr_region->hrm_index(), p2i(curr_region->bottom()), p2i(top_at_mark_start(curr_region)));
|
||||
// notice that _finger == end cannot be guaranteed here since,
|
||||
// someone else might have moved the finger even further
|
||||
assert(_finger >= end, "the finger should have moved forward");
|
||||
// Notice that _finger == end cannot be guaranteed here since,
|
||||
// someone else might have moved the finger even further.
|
||||
assert(finger() >= end, "The finger should have moved forward");
|
||||
|
||||
if (limit > bottom) {
|
||||
return curr_region;
|
||||
} else {
|
||||
assert(limit == bottom,
|
||||
"the region limit should be at bottom");
|
||||
"The region limit should be at bottom");
|
||||
// We return null and the caller should try calling
|
||||
// claim_region() again.
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
assert(_finger > finger, "the finger should have moved forward");
|
||||
// read it again
|
||||
finger = _finger;
|
||||
// Read the finger again.
|
||||
HeapWord* next_finger = finger();
|
||||
assert(next_finger > local_finger, "The finger should have moved forward " PTR_FORMAT " " PTR_FORMAT, p2i(local_finger), p2i(next_finger));
|
||||
local_finger = next_finger;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1957,7 +1967,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() {
|
||||
|
||||
void G1ConcurrentMark::abort_marking_threads() {
|
||||
assert(!_root_regions.scan_in_progress(), "still doing root region scan");
|
||||
_has_aborted = true;
|
||||
_has_aborted.store_relaxed(true);
|
||||
_first_overflow_barrier_sync.abort();
|
||||
_second_overflow_barrier_sync.abort();
|
||||
}
|
||||
|
||||
@ -368,7 +368,7 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
|
||||
|
||||
// For grey objects
|
||||
G1CMMarkStack _global_mark_stack; // Grey objects behind global finger
|
||||
HeapWord* volatile _finger; // The global finger, region aligned,
|
||||
Atomic<HeapWord*> _finger; // The global finger, region aligned,
|
||||
// always pointing to the end of the
|
||||
// last claimed region
|
||||
|
||||
@ -395,19 +395,19 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
|
||||
WorkerThreadsBarrierSync _second_overflow_barrier_sync;
|
||||
|
||||
// Number of completed mark cycles.
|
||||
volatile uint _completed_mark_cycles;
|
||||
Atomic<uint> _completed_mark_cycles;
|
||||
|
||||
// This is set by any task, when an overflow on the global data
|
||||
// structures is detected
|
||||
volatile bool _has_overflown;
|
||||
Atomic<bool> _has_overflown;
|
||||
// True: marking is concurrent, false: we're in remark
|
||||
volatile bool _concurrent;
|
||||
Atomic<bool> _concurrent;
|
||||
// Set at the end of a Full GC so that marking aborts
|
||||
volatile bool _has_aborted;
|
||||
Atomic<bool> _has_aborted;
|
||||
|
||||
// Used when remark aborts due to an overflow to indicate that
|
||||
// another concurrent marking phase should start
|
||||
volatile bool _restart_for_overflow;
|
||||
Atomic<bool> _restart_for_overflow;
|
||||
|
||||
ConcurrentGCTimer* _gc_timer_cm;
|
||||
|
||||
@ -461,8 +461,8 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
|
||||
|
||||
void print_and_reset_taskqueue_stats();
|
||||
|
||||
HeapWord* finger() { return _finger; }
|
||||
bool concurrent() { return _concurrent; }
|
||||
HeapWord* finger() { return _finger.load_relaxed(); }
|
||||
bool concurrent() { return _concurrent.load_relaxed(); }
|
||||
uint active_tasks() { return _num_active_tasks; }
|
||||
TaskTerminator* terminator() { return &_terminator; }
|
||||
|
||||
@ -487,7 +487,7 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
|
||||
// to satisfy an allocation without doing a GC. This is fine, because all
|
||||
// objects in those regions will be considered live anyway because of
|
||||
// SATB guarantees (i.e. their TAMS will be equal to bottom).
|
||||
bool out_of_regions() { return _finger >= _heap.end(); }
|
||||
bool out_of_regions() { return finger() >= _heap.end(); }
|
||||
|
||||
// Returns the task with the given id
|
||||
G1CMTask* task(uint id) {
|
||||
@ -499,10 +499,10 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
|
||||
|
||||
// Access / manipulation of the overflow flag which is set to
|
||||
// indicate that the global stack has overflown
|
||||
bool has_overflown() { return _has_overflown; }
|
||||
void set_has_overflown() { _has_overflown = true; }
|
||||
void clear_has_overflown() { _has_overflown = false; }
|
||||
bool restart_for_overflow() { return _restart_for_overflow; }
|
||||
bool has_overflown() { return _has_overflown.load_relaxed(); }
|
||||
void set_has_overflown() { _has_overflown.store_relaxed(true); }
|
||||
void clear_has_overflown() { _has_overflown.store_relaxed(false); }
|
||||
bool restart_for_overflow() { return _restart_for_overflow.load_relaxed(); }
|
||||
|
||||
// Methods to enter the two overflow sync barriers
|
||||
void enter_first_sync_barrier(uint worker_id);
|
||||
@ -516,12 +516,12 @@ class G1ConcurrentMark : public CHeapObj<mtGC> {
|
||||
G1RegionMarkStats* _region_mark_stats;
|
||||
// Top pointer for each region at the start of marking. Must be valid for all committed
|
||||
// regions.
|
||||
HeapWord* volatile* _top_at_mark_starts;
|
||||
Atomic<HeapWord*>* _top_at_mark_starts;
|
||||
// Top pointer for each region at the start of the rebuild remembered set process
|
||||
// for regions which remembered sets need to be rebuilt. A null for a given region
|
||||
// means that this region does not be scanned during the rebuilding remembered
|
||||
// set phase at all.
|
||||
HeapWord* volatile* _top_at_rebuild_starts;
|
||||
Atomic<HeapWord*>* _top_at_rebuild_starts;
|
||||
// True when Remark pause selected regions for rebuilding.
|
||||
bool _needs_remembered_set_rebuild;
|
||||
public:
|
||||
@ -679,7 +679,7 @@ public:
|
||||
|
||||
uint completed_mark_cycles() const;
|
||||
|
||||
bool has_aborted() { return _has_aborted; }
|
||||
bool has_aborted() { return _has_aborted.load_relaxed(); }
|
||||
|
||||
void print_summary_info();
|
||||
|
||||
|
||||
@ -194,11 +194,11 @@ inline void G1CMTask::process_array_chunk(objArrayOop obj, size_t start, size_t
|
||||
inline void G1ConcurrentMark::update_top_at_mark_start(G1HeapRegion* r) {
|
||||
uint const region = r->hrm_index();
|
||||
assert(region < _g1h->max_num_regions(), "Tried to access TAMS for region %u out of bounds", region);
|
||||
_top_at_mark_starts[region] = r->top();
|
||||
_top_at_mark_starts[region].store_relaxed(r->top());
|
||||
}
|
||||
|
||||
inline void G1ConcurrentMark::reset_top_at_mark_start(G1HeapRegion* r) {
|
||||
_top_at_mark_starts[r->hrm_index()] = r->bottom();
|
||||
_top_at_mark_starts[r->hrm_index()].store_relaxed(r->bottom());
|
||||
}
|
||||
|
||||
inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) const {
|
||||
@ -207,7 +207,7 @@ inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) cons
|
||||
|
||||
inline HeapWord* G1ConcurrentMark::top_at_mark_start(uint region) const {
|
||||
assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region);
|
||||
return _top_at_mark_starts[region];
|
||||
return _top_at_mark_starts[region].load_relaxed();
|
||||
}
|
||||
|
||||
inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const {
|
||||
@ -217,7 +217,7 @@ inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const {
|
||||
}
|
||||
|
||||
inline HeapWord* G1ConcurrentMark::top_at_rebuild_start(G1HeapRegion* r) const {
|
||||
return _top_at_rebuild_starts[r->hrm_index()];
|
||||
return _top_at_rebuild_starts[r->hrm_index()].load_relaxed();
|
||||
}
|
||||
|
||||
inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) {
|
||||
@ -225,10 +225,10 @@ inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) {
|
||||
|
||||
uint const region = r->hrm_index();
|
||||
assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region);
|
||||
assert(_top_at_rebuild_starts[region] == nullptr,
|
||||
assert(top_at_rebuild_start(r) == nullptr,
|
||||
"TARS for region %u has already been set to " PTR_FORMAT " should be null",
|
||||
region, p2i(_top_at_rebuild_starts[region]));
|
||||
_top_at_rebuild_starts[region] = r->top();
|
||||
region, p2i(top_at_rebuild_start(r)));
|
||||
_top_at_rebuild_starts[region].store_relaxed(r->top());
|
||||
}
|
||||
|
||||
inline void G1CMTask::update_liveness(oop const obj, const size_t obj_size) {
|
||||
|
||||
@ -44,6 +44,8 @@ struct G1RegionMarkStats {
|
||||
Atomic<size_t> _live_words;
|
||||
Atomic<size_t> _incoming_refs;
|
||||
|
||||
G1RegionMarkStats() : _live_words(0), _incoming_refs(0) { }
|
||||
|
||||
// Clear all members.
|
||||
void clear() {
|
||||
_live_words.store_relaxed(0);
|
||||
|
||||
@ -497,10 +497,6 @@ class G1PostEvacuateCollectionSetCleanupTask2::ProcessEvacuationFailedRegionsTas
|
||||
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||
G1ConcurrentMark* cm = g1h->concurrent_mark();
|
||||
|
||||
HeapWord* top_at_mark_start = cm->top_at_mark_start(r);
|
||||
assert(top_at_mark_start == r->bottom(), "TAMS must not have been set for region %u", r->hrm_index());
|
||||
assert(cm->live_bytes(r->hrm_index()) == 0, "Marking live bytes must not be set for region %u", r->hrm_index());
|
||||
|
||||
// Concurrent mark does not mark through regions that we retain (they are root
|
||||
// regions wrt to marking), so we must clear their mark data (tams, bitmap, ...)
|
||||
// set eagerly or during evacuation failure.
|
||||
|
||||
@ -30,8 +30,11 @@
|
||||
#include "gc/parallel/psScavenge.hpp"
|
||||
|
||||
inline bool ParallelScavengeHeap::should_alloc_in_eden(const size_t size) const {
|
||||
const size_t eden_size = young_gen()->eden_space()->capacity_in_words();
|
||||
return size < eden_size / 2;
|
||||
const size_t max_young_gen_bytes = young_gen()->max_gen_size();
|
||||
const size_t survivor_size_bytes = young_gen()->from_space()->capacity_in_bytes();
|
||||
const size_t max_eden_size_bytes = max_young_gen_bytes - survivor_size_bytes * 2;
|
||||
const size_t max_eden_size_words = max_eden_size_bytes / HeapWordSize;
|
||||
return size < max_eden_size_words / 2;
|
||||
}
|
||||
|
||||
inline bool ParallelScavengeHeap::is_in_young(const void* p) const {
|
||||
|
||||
@ -1108,6 +1108,10 @@ void ShenandoahGenerationalHeap::complete_degenerated_cycle() {
|
||||
ShenandoahGCPhase phase(ShenandoahPhaseTimings::degen_gc_coalesce_and_fill);
|
||||
coalesce_and_fill_old_regions(false);
|
||||
}
|
||||
|
||||
log_info(gc, cset)("Degenerated cycle complete, promotions reserved: %zu, promotions expended: %zu, failed count: %zu, failed bytes: %zu",
|
||||
old_generation()->get_promoted_reserve(), old_generation()->get_promoted_expended(),
|
||||
old_generation()->get_promotion_failed_count(), old_generation()->get_promotion_failed_words() * HeapWordSize);
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalHeap::complete_concurrent_cycle() {
|
||||
@ -1121,6 +1125,10 @@ void ShenandoahGenerationalHeap::complete_concurrent_cycle() {
|
||||
// throw off the heuristics.
|
||||
entry_global_coalesce_and_fill();
|
||||
}
|
||||
|
||||
log_info(gc, cset)("Concurrent cycle complete, promotions reserved: %zu, promotions expended: %zu, failed count: %zu, failed bytes: %zu",
|
||||
old_generation()->get_promoted_reserve(), old_generation()->get_promoted_expended(),
|
||||
old_generation()->get_promotion_failed_count(), old_generation()->get_promotion_failed_words() * HeapWordSize);
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalHeap::entry_global_coalesce_and_fill() {
|
||||
|
||||
@ -86,6 +86,7 @@
|
||||
#include "nmt/memTracker.hpp"
|
||||
#include "oops/compressedOops.inline.hpp"
|
||||
#include "prims/jvmtiTagMap.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/atomicAccess.hpp"
|
||||
#include "runtime/globals.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
@ -201,9 +202,9 @@ jint ShenandoahHeap::initialize() {
|
||||
assert(num_min_regions <= _num_regions, "sanity");
|
||||
_minimum_size = num_min_regions * reg_size_bytes;
|
||||
|
||||
_soft_max_size = clamp(SoftMaxHeapSize, min_capacity(), max_capacity());
|
||||
_soft_max_size.store_relaxed(clamp(SoftMaxHeapSize, min_capacity(), max_capacity()));
|
||||
|
||||
_committed = _initial_size;
|
||||
_committed.store_relaxed(_initial_size);
|
||||
|
||||
size_t heap_page_size = UseLargePages ? os::large_page_size() : os::vm_page_size();
|
||||
size_t bitmap_page_size = UseLargePages ? os::large_page_size() : os::vm_page_size();
|
||||
@ -725,17 +726,17 @@ size_t ShenandoahHeap::used() const {
|
||||
}
|
||||
|
||||
size_t ShenandoahHeap::committed() const {
|
||||
return AtomicAccess::load(&_committed);
|
||||
return _committed.load_relaxed();
|
||||
}
|
||||
|
||||
void ShenandoahHeap::increase_committed(size_t bytes) {
|
||||
shenandoah_assert_heaplocked_or_safepoint();
|
||||
_committed += bytes;
|
||||
_committed.fetch_then_add(bytes, memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ShenandoahHeap::decrease_committed(size_t bytes) {
|
||||
shenandoah_assert_heaplocked_or_safepoint();
|
||||
_committed -= bytes;
|
||||
_committed.fetch_then_sub(bytes, memory_order_relaxed);
|
||||
}
|
||||
|
||||
size_t ShenandoahHeap::capacity() const {
|
||||
@ -747,7 +748,7 @@ size_t ShenandoahHeap::max_capacity() const {
|
||||
}
|
||||
|
||||
size_t ShenandoahHeap::soft_max_capacity() const {
|
||||
size_t v = AtomicAccess::load(&_soft_max_size);
|
||||
size_t v = _soft_max_size.load_relaxed();
|
||||
assert(min_capacity() <= v && v <= max_capacity(),
|
||||
"Should be in bounds: %zu <= %zu <= %zu",
|
||||
min_capacity(), v, max_capacity());
|
||||
@ -758,7 +759,7 @@ void ShenandoahHeap::set_soft_max_capacity(size_t v) {
|
||||
assert(min_capacity() <= v && v <= max_capacity(),
|
||||
"Should be in bounds: %zu <= %zu <= %zu",
|
||||
min_capacity(), v, max_capacity());
|
||||
AtomicAccess::store(&_soft_max_size, v);
|
||||
_soft_max_size.store_relaxed(v);
|
||||
}
|
||||
|
||||
size_t ShenandoahHeap::min_capacity() const {
|
||||
@ -1941,7 +1942,7 @@ private:
|
||||
size_t const _stride;
|
||||
|
||||
shenandoah_padding(0);
|
||||
volatile size_t _index;
|
||||
Atomic<size_t> _index;
|
||||
shenandoah_padding(1);
|
||||
|
||||
public:
|
||||
@ -1954,8 +1955,8 @@ public:
|
||||
size_t stride = _stride;
|
||||
|
||||
size_t max = _heap->num_regions();
|
||||
while (AtomicAccess::load(&_index) < max) {
|
||||
size_t cur = AtomicAccess::fetch_then_add(&_index, stride, memory_order_relaxed);
|
||||
while (_index.load_relaxed() < max) {
|
||||
size_t cur = _index.fetch_then_add(stride, memory_order_relaxed);
|
||||
size_t start = cur;
|
||||
size_t end = MIN2(cur + stride, max);
|
||||
if (start >= max) break;
|
||||
@ -2703,11 +2704,11 @@ ShenandoahRegionIterator::ShenandoahRegionIterator(ShenandoahHeap* heap) :
|
||||
_index(0) {}
|
||||
|
||||
void ShenandoahRegionIterator::reset() {
|
||||
_index = 0;
|
||||
_index.store_relaxed(0);
|
||||
}
|
||||
|
||||
bool ShenandoahRegionIterator::has_next() const {
|
||||
return _index < _heap->num_regions();
|
||||
return _index.load_relaxed() < _heap->num_regions();
|
||||
}
|
||||
|
||||
ShenandoahLiveData* ShenandoahHeap::get_liveness_cache(uint worker_id) {
|
||||
|
||||
@ -88,7 +88,7 @@ private:
|
||||
ShenandoahHeap* _heap;
|
||||
|
||||
shenandoah_padding(0);
|
||||
volatile size_t _index;
|
||||
Atomic<size_t> _index;
|
||||
shenandoah_padding(1);
|
||||
|
||||
// No implicit copying: iterators should be passed by reference to capture the state
|
||||
@ -208,9 +208,9 @@ private:
|
||||
size_t _initial_size;
|
||||
size_t _minimum_size;
|
||||
|
||||
volatile size_t _soft_max_size;
|
||||
Atomic<size_t> _soft_max_size;
|
||||
shenandoah_padding(0);
|
||||
volatile size_t _committed;
|
||||
Atomic<size_t> _committed;
|
||||
shenandoah_padding(1);
|
||||
|
||||
public:
|
||||
@ -340,7 +340,7 @@ private:
|
||||
ShenandoahSharedFlag _full_gc_move_in_progress;
|
||||
ShenandoahSharedFlag _concurrent_strong_root_in_progress;
|
||||
|
||||
size_t _gc_no_progress_count;
|
||||
Atomic<size_t> _gc_no_progress_count;
|
||||
|
||||
// This updates the singular, global gc state. This call must happen on a safepoint.
|
||||
void set_gc_state_at_safepoint(uint mask, bool value);
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
#include "gc/shenandoah/shenandoahWorkGroup.hpp"
|
||||
#include "oops/compressedOops.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/atomicAccess.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
#include "runtime/prefetch.inline.hpp"
|
||||
@ -61,7 +61,7 @@ inline ShenandoahHeap* ShenandoahHeap::heap() {
|
||||
}
|
||||
|
||||
inline ShenandoahHeapRegion* ShenandoahRegionIterator::next() {
|
||||
size_t new_index = AtomicAccess::add(&_index, (size_t) 1, memory_order_relaxed);
|
||||
size_t new_index = _index.add_then_fetch((size_t) 1, memory_order_relaxed);
|
||||
// get_region() provides the bounds-check and returns null on OOB.
|
||||
return _heap->get_region(new_index - 1);
|
||||
}
|
||||
@ -75,15 +75,15 @@ inline WorkerThreads* ShenandoahHeap::safepoint_workers() {
|
||||
}
|
||||
|
||||
inline void ShenandoahHeap::notify_gc_progress() {
|
||||
AtomicAccess::store(&_gc_no_progress_count, (size_t) 0);
|
||||
_gc_no_progress_count.store_relaxed((size_t) 0);
|
||||
|
||||
}
|
||||
inline void ShenandoahHeap::notify_gc_no_progress() {
|
||||
AtomicAccess::inc(&_gc_no_progress_count);
|
||||
_gc_no_progress_count.add_then_fetch((size_t) 1);
|
||||
}
|
||||
|
||||
inline size_t ShenandoahHeap::get_gc_no_progress_count() const {
|
||||
return AtomicAccess::load(&_gc_no_progress_count);
|
||||
return _gc_no_progress_count.load_relaxed();
|
||||
}
|
||||
|
||||
inline size_t ShenandoahHeap::heap_region_index_containing(const void* addr) const {
|
||||
|
||||
@ -433,8 +433,8 @@ void ShenandoahNMethodTableSnapshot::parallel_nmethods_do(NMethodClosure *f) {
|
||||
ShenandoahNMethod** const list = _list->list();
|
||||
|
||||
size_t max = (size_t)_limit;
|
||||
while (_claimed < max) {
|
||||
size_t cur = AtomicAccess::fetch_then_add(&_claimed, stride, memory_order_relaxed);
|
||||
while (_claimed.load_relaxed() < max) {
|
||||
size_t cur = _claimed.fetch_then_add(stride, memory_order_relaxed);
|
||||
size_t start = cur;
|
||||
size_t end = MIN2(cur + stride, max);
|
||||
if (start >= max) break;
|
||||
@ -457,8 +457,8 @@ void ShenandoahNMethodTableSnapshot::concurrent_nmethods_do(NMethodClosure* cl)
|
||||
|
||||
ShenandoahNMethod** list = _list->list();
|
||||
size_t max = (size_t)_limit;
|
||||
while (_claimed < max) {
|
||||
size_t cur = AtomicAccess::fetch_then_add(&_claimed, stride, memory_order_relaxed);
|
||||
while (_claimed.load_relaxed() < max) {
|
||||
size_t cur = _claimed.fetch_then_add(stride, memory_order_relaxed);
|
||||
size_t start = cur;
|
||||
size_t end = MIN2(cur + stride, max);
|
||||
if (start >= max) break;
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "gc/shenandoah/shenandoahLock.hpp"
|
||||
#include "gc/shenandoah/shenandoahPadding.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
// ShenandoahNMethod tuple records the internal locations of oop slots within reclocation stream in
|
||||
@ -115,7 +116,7 @@ private:
|
||||
int _limit;
|
||||
|
||||
shenandoah_padding(0);
|
||||
volatile size_t _claimed;
|
||||
Atomic<size_t> _claimed;
|
||||
shenandoah_padding(1);
|
||||
|
||||
public:
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
nonstatic_field(ShenandoahHeap, _regions, ShenandoahHeapRegion**) \
|
||||
nonstatic_field(ShenandoahHeap, _log_min_obj_alignment_in_bytes, int) \
|
||||
nonstatic_field(ShenandoahHeap, _free_set, ShenandoahFreeSet*) \
|
||||
volatile_nonstatic_field(ShenandoahHeap, _committed, size_t) \
|
||||
volatile_nonstatic_field(ShenandoahHeap, _committed, Atomic<size_t>) \
|
||||
static_field(ShenandoahHeapRegion, RegionSizeBytes, size_t) \
|
||||
static_field(ShenandoahHeapRegion, RegionSizeBytesShift, size_t) \
|
||||
nonstatic_field(ShenandoahHeapRegion, _state, Atomic<ShenandoahHeapRegion::RegionState>) \
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -120,7 +120,6 @@ public:
|
||||
virtual int Opcode() const;
|
||||
virtual bool is_CFG() const { return true; }
|
||||
virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual uint ideal_reg() const { return NotAMachineReg; }
|
||||
@ -141,7 +140,6 @@ class RethrowNode : public Node {
|
||||
virtual int Opcode() const;
|
||||
virtual bool is_CFG() const { return true; }
|
||||
virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual uint match_edge(uint idx) const;
|
||||
|
||||
@ -207,6 +207,11 @@ bool ConstraintCastNode::higher_equal_types(PhaseGVN* phase, const Node* other)
|
||||
return true;
|
||||
}
|
||||
|
||||
Node* ConstraintCastNode::pin_node_under_control_impl() const {
|
||||
assert(_dependency.is_floating(), "already pinned");
|
||||
return make_cast_for_type(in(0), in(1), bottom_type(), _dependency.with_pinned_dependency(), _extra_types);
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
void ConstraintCastNode::dump_spec(outputStream *st) const {
|
||||
TypeNode::dump_spec(st);
|
||||
@ -277,12 +282,9 @@ void CastIINode::dump_spec(outputStream* st) const {
|
||||
}
|
||||
#endif
|
||||
|
||||
CastIINode* CastIINode::pin_array_access_node() const {
|
||||
CastIINode* CastIINode::pin_node_under_control_impl() const {
|
||||
assert(_dependency.is_floating(), "already pinned");
|
||||
if (has_range_check()) {
|
||||
return new CastIINode(in(0), in(1), bottom_type(), _dependency.with_pinned_dependency(), has_range_check());
|
||||
}
|
||||
return nullptr;
|
||||
return new CastIINode(in(0), in(1), bottom_type(), _dependency.with_pinned_dependency(), _range_check_dependency, _extra_types);
|
||||
}
|
||||
|
||||
void CastIINode::remove_range_check_cast(Compile* C) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2026, 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
|
||||
@ -166,8 +166,6 @@ protected:
|
||||
virtual int Opcode() const;
|
||||
virtual uint ideal_reg() const = 0;
|
||||
bool carry_dependency() const { return !_dependency.cmp(DependencyType::FloatingNarrowing); }
|
||||
// A cast node depends_only_on_test if and only if it is floating
|
||||
virtual bool depends_only_on_test() const { return _dependency.is_floating(); }
|
||||
const DependencyType& dependency() const { return _dependency; }
|
||||
TypeNode* dominating_cast(PhaseGVN* gvn, PhaseTransform* pt) const;
|
||||
static Node* make_cast_for_basic_type(Node* c, Node* n, const Type* t, const DependencyType& dependency, BasicType bt);
|
||||
@ -191,6 +189,12 @@ protected:
|
||||
const Type* extra_type_at(int i) const {
|
||||
return _extra_types->field_at(i);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool depends_only_on_test_impl() const { return _dependency.is_floating(); }
|
||||
|
||||
private:
|
||||
virtual Node* pin_node_under_control_impl() const;
|
||||
};
|
||||
|
||||
//------------------------------CastIINode-------------------------------------
|
||||
@ -222,13 +226,15 @@ class CastIINode: public ConstraintCastNode {
|
||||
#endif
|
||||
}
|
||||
|
||||
CastIINode* pin_array_access_node() const;
|
||||
CastIINode* make_with(Node* parent, const TypeInteger* type, const DependencyType& dependency) const;
|
||||
void remove_range_check_cast(Compile* C);
|
||||
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream* st) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
virtual CastIINode* pin_node_under_control_impl() const;
|
||||
};
|
||||
|
||||
class CastLLNode: public ConstraintCastNode {
|
||||
@ -320,8 +326,10 @@ class CheckCastPPNode: public ConstraintCastNode {
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual int Opcode() const;
|
||||
virtual uint ideal_reg() const { return Op_RegP; }
|
||||
bool depends_only_on_test() const { return !type()->isa_rawptr() && ConstraintCastNode::depends_only_on_test(); }
|
||||
};
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return !type()->isa_rawptr() && ConstraintCastNode::depends_only_on_test_impl(); }
|
||||
};
|
||||
|
||||
|
||||
//------------------------------CastX2PNode-------------------------------------
|
||||
@ -349,8 +357,10 @@ class CastP2XNode : public Node {
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual uint ideal_reg() const { return Op_RegX; }
|
||||
virtual const Type *bottom_type() const { return TypeX_X; }
|
||||
|
||||
private:
|
||||
// Return false to keep node from moving away from an associated card mark.
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -124,7 +124,6 @@ public:
|
||||
virtual bool pinned() const { return (const Node*)in(0) == this; }
|
||||
virtual bool is_CFG() const { return true; }
|
||||
virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual const Type* bottom_type() const { return Type::CONTROL; }
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
@ -287,7 +286,6 @@ public:
|
||||
virtual bool is_CFG() const { return true; }
|
||||
virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash
|
||||
virtual const Node *is_block_proj() const { return this; }
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual const Type *bottom_type() const { return Type::CONTROL; }
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
@ -462,7 +460,7 @@ public:
|
||||
Node* fold_compares(PhaseIterGVN* phase);
|
||||
static Node* up_one_dom(Node* curr, bool linear_only = false);
|
||||
bool is_zero_trip_guard() const;
|
||||
Node* dominated_by(Node* prev_dom, PhaseIterGVN* igvn, bool pin_array_access_nodes);
|
||||
Node* dominated_by(Node* prev_dom, PhaseIterGVN* igvn, bool prev_dom_not_imply_this);
|
||||
ProjNode* uncommon_trap_proj(CallStaticJavaNode*& call, Deoptimization::DeoptReason reason = Deoptimization::Reason_none) const;
|
||||
|
||||
// Takes the type of val and filters it through the test represented
|
||||
@ -565,7 +563,7 @@ public:
|
||||
return in(0)->as_If()->proj_out(1 - _con)->as_IfProj();
|
||||
}
|
||||
|
||||
void pin_array_access_nodes(PhaseIterGVN* igvn);
|
||||
void pin_dependent_nodes(PhaseIterGVN* igvn);
|
||||
|
||||
protected:
|
||||
// Type of If input when this branch is always taken
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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,15 +35,33 @@
|
||||
|
||||
// Optimization - Graph Style
|
||||
|
||||
class DivModIntegerNode : public Node {
|
||||
private:
|
||||
bool _pinned;
|
||||
|
||||
protected:
|
||||
DivModIntegerNode(Node* c, Node* dividend, Node* divisor) : Node(c, dividend, divisor), _pinned(false) {}
|
||||
|
||||
private:
|
||||
virtual uint size_of() const override { return sizeof(DivModIntegerNode); }
|
||||
virtual uint hash() const override { return Node::hash() + _pinned; }
|
||||
virtual bool cmp(const Node& o) const override { return Node::cmp(o) && _pinned == static_cast<const DivModIntegerNode&>(o)._pinned; }
|
||||
virtual bool depends_only_on_test_impl() const override { return !_pinned; }
|
||||
virtual DivModIntegerNode* pin_node_under_control_impl() const override {
|
||||
DivModIntegerNode* res = static_cast<DivModIntegerNode*>(clone());
|
||||
res->_pinned = true;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------DivINode---------------------------------------
|
||||
// Integer division
|
||||
// Note: this is division as defined by JVMS, i.e., MinInt/-1 == MinInt.
|
||||
// On processors which don't naturally support this special case (e.g., x86),
|
||||
// the matcher or runtime system must take care of this.
|
||||
class DivINode : public Node {
|
||||
class DivINode : public DivModIntegerNode {
|
||||
public:
|
||||
DivINode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {}
|
||||
DivINode(Node* c, Node* dividend, Node* divisor) : DivModIntegerNode(c, dividend, divisor) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
@ -54,9 +72,9 @@ public:
|
||||
|
||||
//------------------------------DivLNode---------------------------------------
|
||||
// Long division
|
||||
class DivLNode : public Node {
|
||||
class DivLNode : public DivModIntegerNode {
|
||||
public:
|
||||
DivLNode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {}
|
||||
DivLNode(Node* c, Node* dividend, Node* divisor) : DivModIntegerNode(c, dividend, divisor) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
@ -107,9 +125,9 @@ public:
|
||||
|
||||
//------------------------------UDivINode---------------------------------------
|
||||
// Unsigned integer division
|
||||
class UDivINode : public Node {
|
||||
class UDivINode : public DivModIntegerNode {
|
||||
public:
|
||||
UDivINode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {}
|
||||
UDivINode(Node* c, Node* dividend, Node* divisor) : DivModIntegerNode(c, dividend, divisor) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
@ -120,9 +138,9 @@ public:
|
||||
|
||||
//------------------------------UDivLNode---------------------------------------
|
||||
// Unsigned long division
|
||||
class UDivLNode : public Node {
|
||||
class UDivLNode : public DivModIntegerNode {
|
||||
public:
|
||||
UDivLNode( Node *c, Node *dividend, Node *divisor ) : Node(c, dividend, divisor ) {}
|
||||
UDivLNode(Node* c, Node* dividend, Node* divisor) : DivModIntegerNode(c, dividend, divisor) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
@ -133,9 +151,9 @@ public:
|
||||
|
||||
//------------------------------ModINode---------------------------------------
|
||||
// Integer modulus
|
||||
class ModINode : public Node {
|
||||
class ModINode : public DivModIntegerNode {
|
||||
public:
|
||||
ModINode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {}
|
||||
ModINode(Node* c, Node* in1, Node* in2) : DivModIntegerNode(c, in1, in2) {}
|
||||
virtual int Opcode() const;
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
@ -145,9 +163,9 @@ public:
|
||||
|
||||
//------------------------------ModLNode---------------------------------------
|
||||
// Long modulus
|
||||
class ModLNode : public Node {
|
||||
class ModLNode : public DivModIntegerNode {
|
||||
public:
|
||||
ModLNode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {}
|
||||
ModLNode(Node* c, Node* in1, Node* in2) : DivModIntegerNode(c, in1, in2) {}
|
||||
virtual int Opcode() const;
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
@ -199,9 +217,9 @@ public:
|
||||
|
||||
//------------------------------UModINode---------------------------------------
|
||||
// Unsigned integer modulus
|
||||
class UModINode : public Node {
|
||||
class UModINode : public DivModIntegerNode {
|
||||
public:
|
||||
UModINode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {}
|
||||
UModINode(Node* c, Node* in1, Node* in2) : DivModIntegerNode(c, in1, in2) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
virtual const Type *bottom_type() const { return TypeInt::INT; }
|
||||
@ -211,9 +229,9 @@ public:
|
||||
|
||||
//------------------------------UModLNode---------------------------------------
|
||||
// Unsigned long modulus
|
||||
class UModLNode : public Node {
|
||||
class UModLNode : public DivModIntegerNode {
|
||||
public:
|
||||
UModLNode( Node *c, Node *in1, Node *in2 ) : Node(c,in1, in2) {}
|
||||
UModLNode(Node* c, Node* in1, Node* in2) : DivModIntegerNode(c, in1, in2) {}
|
||||
virtual int Opcode() const;
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
virtual const Type *bottom_type() const { return TypeLong::LONG; }
|
||||
@ -243,6 +261,9 @@ public:
|
||||
|
||||
ProjNode* div_proj() { return proj_out_or_null(div_proj_num); }
|
||||
ProjNode* mod_proj() { return proj_out_or_null(mod_proj_num); }
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
};
|
||||
|
||||
//------------------------------DivModINode---------------------------------------
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2026, 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
|
||||
@ -604,7 +604,7 @@ static void adjust_check(IfProjNode* proj, Node* range, Node* index,
|
||||
// at the lowest/nearest dominating check in the graph. To ensure that these Loads/Casts do not float above any of the
|
||||
// dominating checks (even when the lowest dominating check is later replaced by yet another dominating check), we
|
||||
// need to pin them at the lowest dominating check.
|
||||
proj->pin_array_access_nodes(igvn);
|
||||
proj->pin_dependent_nodes(igvn);
|
||||
}
|
||||
|
||||
//------------------------------up_one_dom-------------------------------------
|
||||
@ -1539,7 +1539,7 @@ Node* IfNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
//------------------------------dominated_by-----------------------------------
|
||||
Node* IfNode::dominated_by(Node* prev_dom, PhaseIterGVN* igvn, bool pin_array_access_nodes) {
|
||||
Node* IfNode::dominated_by(Node* prev_dom, PhaseIterGVN* igvn, bool prev_dom_not_imply_this) {
|
||||
#ifndef PRODUCT
|
||||
if (TraceIterativeGVN) {
|
||||
tty->print(" Removing IfNode: "); this->dump();
|
||||
@ -1570,20 +1570,16 @@ Node* IfNode::dominated_by(Node* prev_dom, PhaseIterGVN* igvn, bool pin_array_ac
|
||||
// Loop ends when projection has no more uses.
|
||||
for (DUIterator_Last jmin, j = ifp->last_outs(jmin); j >= jmin; --j) {
|
||||
Node* s = ifp->last_out(j); // Get child of IfTrue/IfFalse
|
||||
if (s->depends_only_on_test() && igvn->no_dependent_zero_check(s)) {
|
||||
// For control producers.
|
||||
// Do not rewire Div and Mod nodes which could have a zero divisor to avoid skipping their zero check.
|
||||
if (s->depends_only_on_test()) {
|
||||
// For control producers
|
||||
igvn->replace_input_of(s, 0, data_target); // Move child to data-target
|
||||
if (pin_array_access_nodes && data_target != top) {
|
||||
// As a result of range check smearing, Loads and range check Cast nodes that are control dependent on this
|
||||
// range check (that is about to be removed) now depend on multiple dominating range checks. After the removal
|
||||
// of this range check, these control dependent nodes end up at the lowest/nearest dominating check in the
|
||||
// graph. To ensure that these Loads/Casts do not float above any of the dominating checks (even when the
|
||||
// lowest dominating check is later replaced by yet another dominating check), we need to pin them at the
|
||||
// lowest dominating check.
|
||||
Node* clone = s->pin_array_access_node();
|
||||
if (prev_dom_not_imply_this && data_target != top) {
|
||||
// If prev_dom_not_imply_this, s now depends on multiple tests with prev_dom being the
|
||||
// lowest dominating one. As a result, it must be pinned there. Otherwise, it can be
|
||||
// incorrectly moved to a dominating test equivalent to the lowest one here.
|
||||
Node* clone = s->pin_node_under_control();
|
||||
if (clone != nullptr) {
|
||||
clone = igvn->transform(clone);
|
||||
igvn->register_new_node_with_optimizer(clone, s);
|
||||
igvn->replace_node(s, clone);
|
||||
}
|
||||
}
|
||||
@ -1831,16 +1827,15 @@ bool IfNode::is_zero_trip_guard() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void IfProjNode::pin_array_access_nodes(PhaseIterGVN* igvn) {
|
||||
void IfProjNode::pin_dependent_nodes(PhaseIterGVN* igvn) {
|
||||
for (DUIterator i = outs(); has_out(i); i++) {
|
||||
Node* u = out(i);
|
||||
if (!u->depends_only_on_test()) {
|
||||
continue;
|
||||
}
|
||||
Node* clone = u->pin_array_access_node();
|
||||
Node* clone = u->pin_node_under_control();
|
||||
if (clone != nullptr) {
|
||||
clone = igvn->transform(clone);
|
||||
assert(clone != u, "shouldn't common");
|
||||
igvn->register_new_node_with_optimizer(clone, u);
|
||||
igvn->replace_node(u, clone);
|
||||
--i;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -41,6 +41,9 @@ class PartialSubtypeCheckNode : public Node {
|
||||
virtual int Opcode() const;
|
||||
virtual const Type* bottom_type() const { return TypeRawPtr::BOTTOM; }
|
||||
virtual uint ideal_reg() const { return Op_RegP; }
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
//------------------------------StrIntrinsic-------------------------------
|
||||
@ -74,13 +77,15 @@ class StrIntrinsicNode: public Node {
|
||||
Node(control, char_array_mem, s1, s2), _encoding(encoding) {
|
||||
}
|
||||
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual const TypePtr* adr_type() const { return TypeAryPtr::BYTES; }
|
||||
virtual uint match_edge(uint idx) const;
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
ArgEncoding encoding() const { return _encoding; }
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
//------------------------------StrComp-------------------------------------
|
||||
@ -172,13 +177,15 @@ class VectorizedHashCodeNode: public Node {
|
||||
VectorizedHashCodeNode(Node* control, Node* ary_mem, Node* arg1, Node* cnt1, Node* result, Node* basic_type)
|
||||
: Node(control, ary_mem, arg1, cnt1, result, basic_type) {};
|
||||
virtual int Opcode() const;
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual const Type* bottom_type() const { return TypeInt::INT; }
|
||||
virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; }
|
||||
virtual uint match_edge(uint idx) const;
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
//------------------------------EncodeISOArray--------------------------------
|
||||
@ -191,7 +198,6 @@ class EncodeISOArrayNode: public Node {
|
||||
|
||||
bool is_ascii() { return _ascii; }
|
||||
virtual int Opcode() const;
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual const Type* bottom_type() const { return TypeInt::INT; }
|
||||
virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; }
|
||||
virtual uint match_edge(uint idx) const;
|
||||
@ -203,6 +209,9 @@ class EncodeISOArrayNode: public Node {
|
||||
virtual bool cmp(const Node& n) const {
|
||||
return Node::cmp(n) && _ascii == ((EncodeISOArrayNode&)n).is_ascii();
|
||||
}
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
//-------------------------------DigitNode----------------------------------------
|
||||
|
||||
@ -147,6 +147,9 @@ public:
|
||||
virtual int Opcode() const;
|
||||
virtual const Type* Value(PhaseGVN* phase) const { return TypeInt::CC; }
|
||||
const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;}
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
|
||||
@ -169,6 +172,8 @@ public:
|
||||
virtual const Type* Value(PhaseGVN* phase) const { return TypeInt::CC; }
|
||||
const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;}
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
#endif // SHARE_OPTO_LOCKNODE_HPP
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2026, 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
|
||||
@ -334,7 +334,7 @@ class Invariance : public StackObj {
|
||||
// loop, it was marked invariant but n is only invariant if
|
||||
// it depends only on that test. Otherwise, unless that test
|
||||
// is out of the loop, it's not invariant.
|
||||
if (n->is_CFG() || (n->depends_only_on_test() && _phase->igvn().no_dependent_zero_check(n)) || n->in(0) == nullptr || !_phase->is_member(_lpt, n->in(0))) {
|
||||
if (n->is_CFG() || n->in(0) == nullptr || n->depends_only_on_test() || !_phase->is_member(_lpt, n->in(0))) {
|
||||
_invariant.set(n->_idx); // I am a invariant too
|
||||
}
|
||||
}
|
||||
|
||||
@ -1676,8 +1676,8 @@ public:
|
||||
Node *has_local_phi_input( Node *n );
|
||||
// Mark an IfNode as being dominated by a prior test,
|
||||
// without actually altering the CFG (and hence IDOM info).
|
||||
void dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip = false, bool pin_array_access_nodes = false);
|
||||
void rewire_safe_outputs_to_dominator(Node* source, Node* dominator, bool pin_array_access_nodes);
|
||||
void dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip = false, bool prev_dom_not_imply_this = false);
|
||||
void rewire_safe_outputs_to_dominator(Node* source, Node* dominator, bool dominator_not_imply_source);
|
||||
|
||||
// Split Node 'n' through merge point
|
||||
RegionNode* split_thru_region(Node* n, RegionNode* region);
|
||||
@ -1960,7 +1960,7 @@ public:
|
||||
|
||||
bool can_move_to_inner_loop(Node* n, LoopNode* n_loop, Node* x);
|
||||
|
||||
void pin_array_access_nodes_dependent_on(Node* ctrl);
|
||||
void pin_nodes_dependent_on(Node* ctrl, bool old_iff_is_rangecheck);
|
||||
|
||||
Node* ensure_node_and_inputs_are_above_pre_end(CountedLoopEndNode* pre_end, Node* node);
|
||||
|
||||
|
||||
@ -345,7 +345,7 @@ bool PhaseIdealLoop::loop_phi_backedge_type_contains_zero(const Node* phi_diviso
|
||||
// Replace the dominated test with an obvious true or false. Place it on the
|
||||
// IGVN worklist for later cleanup. Move control-dependent data Nodes on the
|
||||
// live path up to the dominating control.
|
||||
void PhaseIdealLoop::dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip, bool pin_array_access_nodes) {
|
||||
void PhaseIdealLoop::dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip, bool prevdom_not_imply_this) {
|
||||
if (VerifyLoopOptimizations && PrintOpto) { tty->print_cr("dominating test"); }
|
||||
|
||||
// prevdom is the dominating projection of the dominating test.
|
||||
@ -386,26 +386,25 @@ void PhaseIdealLoop::dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip, b
|
||||
return;
|
||||
}
|
||||
|
||||
rewire_safe_outputs_to_dominator(dp, prevdom, pin_array_access_nodes);
|
||||
rewire_safe_outputs_to_dominator(dp, prevdom, prevdom_not_imply_this);
|
||||
}
|
||||
|
||||
void PhaseIdealLoop::rewire_safe_outputs_to_dominator(Node* source, Node* dominator, const bool pin_array_access_nodes) {
|
||||
void PhaseIdealLoop::rewire_safe_outputs_to_dominator(Node* source, Node* dominator, const bool dominator_not_imply_source) {
|
||||
IdealLoopTree* old_loop = get_loop(source);
|
||||
|
||||
for (DUIterator_Fast imax, i = source->fast_outs(imax); i < imax; i++) {
|
||||
Node* out = source->fast_out(i); // Control-dependent node
|
||||
// Do not rewire Div and Mod nodes which could have a zero divisor to avoid skipping their zero check.
|
||||
if (out->depends_only_on_test() && _igvn.no_dependent_zero_check(out)) {
|
||||
if (out->depends_only_on_test()) {
|
||||
assert(out->in(0) == source, "must be control dependent on source");
|
||||
_igvn.replace_input_of(out, 0, dominator);
|
||||
if (pin_array_access_nodes) {
|
||||
if (dominator_not_imply_source) {
|
||||
// Because of Loop Predication, Loads and range check Cast nodes that are control dependent on this range
|
||||
// check (that is about to be removed) now depend on multiple dominating Hoisted Check Predicates. After the
|
||||
// removal of this range check, these control dependent nodes end up at the lowest/nearest dominating predicate
|
||||
// in the graph. To ensure that these Loads/Casts do not float above any of the dominating checks (even when the
|
||||
// lowest dominating check is later replaced by yet another dominating check), we need to pin them at the lowest
|
||||
// dominating check.
|
||||
Node* clone = out->pin_array_access_node();
|
||||
Node* clone = out->pin_node_under_control();
|
||||
if (clone != nullptr) {
|
||||
clone = _igvn.register_new_node_with_optimizer(clone, out);
|
||||
_igvn.replace_node(out, clone);
|
||||
@ -1644,7 +1643,7 @@ bool PhaseIdealLoop::try_merge_identical_ifs(Node* n) {
|
||||
void PhaseIdealLoop::push_pinned_nodes_thru_region(IfNode* dom_if, Node* region) {
|
||||
for (DUIterator i = region->outs(); region->has_out(i); i++) {
|
||||
Node* u = region->out(i);
|
||||
if (!has_ctrl(u) || u->is_Phi() || !u->depends_only_on_test() || !_igvn.no_dependent_zero_check(u)) {
|
||||
if (!has_ctrl(u) || u->is_Phi() || !u->depends_only_on_test()) {
|
||||
continue;
|
||||
}
|
||||
assert(u->in(0) == region, "not a control dependent node?");
|
||||
@ -1724,11 +1723,11 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) {
|
||||
Node* outside_ctrl = place_outside_loop(n_ctrl, loop_ctrl);
|
||||
if (!would_sink_below_pre_loop_exit(loop_ctrl, outside_ctrl)) {
|
||||
if (n->depends_only_on_test()) {
|
||||
Node* pinned_clone = n->pin_array_access_node();
|
||||
// If this node depends_only_on_test, it will be rewired to a control input that is not
|
||||
// the correct test. As a result, it must be pinned otherwise it can be incorrectly
|
||||
// rewired to a dominating test equivalent to the new control.
|
||||
Node* pinned_clone = n->pin_node_under_control();
|
||||
if (pinned_clone != nullptr) {
|
||||
// Pin array access nodes: if this is an array load, it's going to be dependent on a condition that's not a
|
||||
// range check for that access. If that condition is replaced by an identical dominating one, then an
|
||||
// unpinned load would risk floating above its range check.
|
||||
register_new_node(pinned_clone, n_ctrl);
|
||||
maybe_pinned_n = pinned_clone;
|
||||
_igvn.replace_node(n, pinned_clone);
|
||||
@ -1754,11 +1753,11 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) {
|
||||
Node* u = n->last_out(j); // Clone private computation per use
|
||||
_igvn.rehash_node_delayed(u);
|
||||
Node* x = nullptr;
|
||||
if (n->depends_only_on_test()) {
|
||||
// Pin array access nodes: if this is an array load, it's going to be dependent on a condition that's not a
|
||||
// range check for that access. If that condition is replaced by an identical dominating one, then an
|
||||
// unpinned load would risk floating above its range check.
|
||||
x = n->pin_array_access_node();
|
||||
if (n->in(0) != nullptr && n->depends_only_on_test()) {
|
||||
// If this node depends_only_on_test, it will be rewired to a control input that is not
|
||||
// the correct test. As a result, it must be pinned otherwise it can be incorrectly
|
||||
// rewired to a dominating test equivalent to the new control.
|
||||
x = n->pin_node_under_control();
|
||||
}
|
||||
if (x == nullptr) {
|
||||
x = n->clone();
|
||||
@ -2328,14 +2327,12 @@ void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new,
|
||||
// We notify all uses of old, including use, and the indirect uses,
|
||||
// that may now be optimized because we have replaced old with phi.
|
||||
_igvn.add_users_to_worklist(old);
|
||||
if (idx == 0 &&
|
||||
use->depends_only_on_test()) {
|
||||
Node* pinned_clone = use->pin_array_access_node();
|
||||
if (idx == 0 && use->depends_only_on_test()) {
|
||||
// If this node depends_only_on_test, it will be rewired to a control input that is not the
|
||||
// correct test. As a result, it must be pinned otherwise it can be incorrectly rewired to
|
||||
// a dominating test equivalent to the new control.
|
||||
Node* pinned_clone = use->pin_node_under_control();
|
||||
if (pinned_clone != nullptr) {
|
||||
// Pin array access nodes: control is updated here to a region. If, after some transformations, only one path
|
||||
// into the region is left, an array load could become dependent on a condition that's not a range check for
|
||||
// that access. If that condition is replaced by an identical dominating one, then an unpinned load would risk
|
||||
// floating above its range check.
|
||||
pinned_clone->set_req(0, phi);
|
||||
register_new_node_with_ctrl_of(pinned_clone, use);
|
||||
_igvn.replace_node(use, pinned_clone);
|
||||
@ -4102,11 +4099,9 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) {
|
||||
not_peel.test(n->_idx) && peel.test(n->in(0)->_idx)) {
|
||||
Node* n_clone = old_new[n->_idx];
|
||||
if (n_clone->depends_only_on_test()) {
|
||||
// Pin array access nodes: control is updated here to the loop head. If, after some transformations, the
|
||||
// backedge is removed, an array load could become dependent on a condition that's not a range check for that
|
||||
// access. If that condition is replaced by an identical dominating one, then an unpinned load would risk
|
||||
// floating above its range check.
|
||||
Node* pinned_clone = n_clone->pin_array_access_node();
|
||||
// If this node depends_only_on_test, it will be rewire to the loop head, which is not the
|
||||
// correct test
|
||||
Node* pinned_clone = n_clone->pin_node_under_control();
|
||||
if (pinned_clone != nullptr) {
|
||||
register_new_node_with_ctrl_of(pinned_clone, n_clone);
|
||||
old_new.map(n->_idx, pinned_clone);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, Alibaba Group Holding Limited. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -900,7 +900,7 @@ void LoadNode::dump_spec(outputStream *st) const {
|
||||
// standard dump does this in Verbose and WizardMode
|
||||
st->print(" #"); _type->dump_on(st);
|
||||
}
|
||||
if (!depends_only_on_test()) {
|
||||
if (in(0) != nullptr && !depends_only_on_test()) {
|
||||
st->print(" (does not depend only on test, ");
|
||||
if (control_dependency() == UnknownControl) {
|
||||
st->print("unknown control");
|
||||
@ -1025,14 +1025,6 @@ static bool skip_through_membars(Compile::AliasType* atp, const TypeInstPtr* tp,
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadNode* LoadNode::pin_array_access_node() const {
|
||||
const TypePtr* adr_type = this->adr_type();
|
||||
if (adr_type != nullptr && adr_type->isa_aryptr()) {
|
||||
return clone_pinned();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Is the value loaded previously stored by an arraycopy? If so return
|
||||
// a load node that reads from the source array so we may be able to
|
||||
// optimize out the ArrayCopy node later.
|
||||
@ -2585,6 +2577,21 @@ LoadNode* LoadNode::clone_pinned() const {
|
||||
return ld;
|
||||
}
|
||||
|
||||
// Pin a LoadNode if it carries a dependency on its control input. There are cases when the node
|
||||
// does not actually have any dependency on its control input. For example, if we have a LoadNode
|
||||
// being used only outside a loop but it must be scheduled inside the loop, we can clone the node
|
||||
// for each of its use so that all the clones can be scheduled outside the loop. Then, to prevent
|
||||
// the clones from being GVN-ed again, we add a control input for each of them at the loop exit. In
|
||||
// those case, since there is not a dependency between the node and its control input, we do not
|
||||
// need to pin it.
|
||||
LoadNode* LoadNode::pin_node_under_control_impl() const {
|
||||
const TypePtr* adr_type = this->adr_type();
|
||||
if (adr_type != nullptr && adr_type->isa_aryptr()) {
|
||||
// Only array accesses have dependencies on their control input
|
||||
return clone_pinned();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//------------------------------Value------------------------------------------
|
||||
const Type* LoadNKlassNode::Value(PhaseGVN* phase) const {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, Alibaba Group Holding Limited. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -299,8 +299,6 @@ public:
|
||||
bool has_unknown_control_dependency() const { return _control_dependency == UnknownControl; }
|
||||
bool has_pinned_control_dependency() const { return _control_dependency == Pinned; }
|
||||
|
||||
LoadNode* pin_array_access_node() const;
|
||||
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream *st) const;
|
||||
#endif
|
||||
@ -314,6 +312,7 @@ protected:
|
||||
|
||||
Node* can_see_arraycopy_value(Node* st, PhaseGVN* phase) const;
|
||||
|
||||
private:
|
||||
// depends_only_on_test is almost always true, and needs to be almost always
|
||||
// true to enable key hoisting & commoning optimizations. However, for the
|
||||
// special case of RawPtr loads from TLS top & end, and other loads performed by
|
||||
@ -323,11 +322,12 @@ protected:
|
||||
// which produce results (new raw memory state) inside of loops preventing all
|
||||
// manner of other optimizations). Basically, it's ugly but so is the alternative.
|
||||
// See comment in macro.cpp, around line 125 expand_allocate_common().
|
||||
virtual bool depends_only_on_test() const {
|
||||
virtual bool depends_only_on_test_impl() const {
|
||||
return adr_type() != TypeRawPtr::BOTTOM && _control_dependency == DependsOnlyOnTest;
|
||||
}
|
||||
|
||||
LoadNode* clone_pinned() const;
|
||||
virtual LoadNode* pin_node_under_control_impl() const;
|
||||
};
|
||||
|
||||
//------------------------------LoadBNode--------------------------------------
|
||||
@ -534,7 +534,6 @@ public:
|
||||
virtual int Opcode() const;
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual bool depends_only_on_test() const { return true; }
|
||||
|
||||
// Polymorphic factory method:
|
||||
static Node* make(PhaseGVN& gvn, Node* mem, Node* adr, const TypePtr* at,
|
||||
@ -563,7 +562,6 @@ public:
|
||||
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual bool depends_only_on_test() const { return true; }
|
||||
};
|
||||
|
||||
|
||||
@ -580,7 +578,6 @@ private:
|
||||
virtual uint size_of() const { return sizeof(*this); }
|
||||
protected:
|
||||
virtual bool cmp( const Node &n ) const;
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
|
||||
Node *Ideal_masked_input (PhaseGVN *phase, uint mask);
|
||||
Node* Ideal_sign_extended_input(PhaseGVN* phase, int num_rejected_bits);
|
||||
@ -660,6 +657,9 @@ public:
|
||||
Node* convert_to_reinterpret_store(PhaseGVN& gvn, Node* val, const Type* vt);
|
||||
|
||||
MemBarNode* trailing_membar() const;
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
//------------------------------StoreBNode-------------------------------------
|
||||
@ -816,7 +816,6 @@ private:
|
||||
#endif // ASSERT
|
||||
public:
|
||||
LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required );
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual uint match_edge(uint idx) const { return idx == MemNode::Address || idx == MemNode::ValueIn; }
|
||||
|
||||
virtual const Type *bottom_type() const { return _type; }
|
||||
@ -829,6 +828,9 @@ public:
|
||||
|
||||
uint8_t barrier_data() { return _barrier_data; }
|
||||
void set_barrier_data(uint8_t barrier_data) { _barrier_data = barrier_data; }
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
class LoadStoreConditionalNode : public LoadStoreNode {
|
||||
@ -1115,6 +1117,9 @@ public:
|
||||
// Return allocation input memory edge if it is different instance
|
||||
// or itself if it is the one we are looking for.
|
||||
static bool step_through(Node** np, uint instance_id, PhaseValues* phase);
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
//------------------------------MemBar-----------------------------------------
|
||||
@ -1677,6 +1682,9 @@ public:
|
||||
virtual uint match_edge(uint idx) const { return (idx == 2); }
|
||||
virtual const TypePtr *adr_type() const { return TypePtr::BOTTOM; }
|
||||
virtual const Type *bottom_type() const { return Type::MEMORY; }
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
// cachewb pre sync node for ensuring that writebacks are serialised
|
||||
@ -1689,6 +1697,9 @@ public:
|
||||
virtual uint match_edge(uint idx) const { return false; }
|
||||
virtual const TypePtr *adr_type() const { return TypePtr::BOTTOM; }
|
||||
virtual const Type *bottom_type() const { return Type::MEMORY; }
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
// cachewb pre sync node for ensuring that writebacks are serialised
|
||||
@ -1701,6 +1712,9 @@ public:
|
||||
virtual uint match_edge(uint idx) const { return false; }
|
||||
virtual const TypePtr *adr_type() const { return TypePtr::BOTTOM; }
|
||||
virtual const Type *bottom_type() const { return Type::MEMORY; }
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
//------------------------------Prefetch---------------------------------------
|
||||
@ -1713,6 +1727,9 @@ public:
|
||||
virtual uint ideal_reg() const { return NotAMachineReg; }
|
||||
virtual uint match_edge(uint idx) const { return idx==2; }
|
||||
virtual const Type *bottom_type() const { return ( AllocatePrefetchStyle == 3 ) ? Type::MEMORY : Type::ABIO; }
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
#endif // SHARE_OPTO_MEMNODE_HPP
|
||||
|
||||
@ -1238,20 +1238,26 @@ Node* RShiftNode::IdentityIL(PhaseGVN* phase, BasicType bt) {
|
||||
return in(1);
|
||||
}
|
||||
// Check for useless sign-masking
|
||||
int lshift_count = 0;
|
||||
if (in(1)->Opcode() == Op_LShift(bt) &&
|
||||
in(1)->req() == 3 &&
|
||||
in(1)->in(2) == in(2)) {
|
||||
// Compare shift counts by value, not by node pointer, to also match a not-yet-normalized
|
||||
// negative constant (e.g. -1 vs 31)
|
||||
const_shift_count(phase, in(1), &lshift_count)) {
|
||||
count &= bits_per_java_integer(bt) - 1; // semantics of Java shifts
|
||||
// Compute masks for which this shifting doesn't change
|
||||
jlong lo = (CONST64(-1) << (bits_per_java_integer(bt) - ((uint)count)-1)); // FFFF8000
|
||||
jlong hi = ~lo; // 00007FFF
|
||||
const TypeInteger* t11 = phase->type(in(1)->in(1))->isa_integer(bt);
|
||||
if (t11 == nullptr) {
|
||||
return this;
|
||||
}
|
||||
// Does actual value fit inside of mask?
|
||||
if (lo <= t11->lo_as_long() && t11->hi_as_long() <= hi) {
|
||||
return in(1)->in(1); // Then shifting is a nop
|
||||
lshift_count &= bits_per_java_integer(bt) - 1;
|
||||
if (count == lshift_count) {
|
||||
// Compute masks for which this shifting doesn't change
|
||||
jlong lo = (CONST64(-1) << (bits_per_java_integer(bt) - ((uint)count)-1)); // FFFF8000
|
||||
jlong hi = ~lo; // 00007FFF
|
||||
const TypeInteger* t11 = phase->type(in(1)->in(1))->isa_integer(bt);
|
||||
if (t11 == nullptr) {
|
||||
return this;
|
||||
}
|
||||
// Does actual value fit inside of mask?
|
||||
if (lo <= t11->lo_as_long() && t11->hi_as_long() <= hi) {
|
||||
return in(1)->in(1); // Then shifting is a nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1524,11 +1530,14 @@ Node* URShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
// If Q is "X << z" the rounding is useless. Look for patterns like
|
||||
// ((X<<Z) + Y) >>> Z and replace with (X + Y>>>Z) & Z-mask.
|
||||
Node *add = in(1);
|
||||
const TypeInt *t2 = phase->type(in(2))->isa_int();
|
||||
if (in1_op == Op_AddI) {
|
||||
Node *lshl = add->in(1);
|
||||
if( lshl->Opcode() == Op_LShiftI &&
|
||||
phase->type(lshl->in(2)) == t2 ) {
|
||||
// Compare shift counts by value, not by node pointer, to also match a not-yet-normalized
|
||||
// negative constant (e.g. -1 vs 31)
|
||||
int lshl_con = 0;
|
||||
if (lshl->Opcode() == Op_LShiftI &&
|
||||
const_shift_count(phase, lshl, &lshl_con) &&
|
||||
(lshl_con & (BitsPerJavaInteger - 1)) == con) {
|
||||
Node *y_z = phase->transform( new URShiftINode(add->in(2),in(2)) );
|
||||
Node *sum = phase->transform( new AddINode( lshl->in(1), y_z ) );
|
||||
return new AndINode( sum, phase->intcon(mask) );
|
||||
@ -1555,11 +1564,16 @@ Node* URShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
|
||||
// Check for "(X << z ) >>> z" which simply zero-extends
|
||||
Node *shl = in(1);
|
||||
if( in1_op == Op_LShiftI &&
|
||||
phase->type(shl->in(2)) == t2 )
|
||||
return new AndINode( shl->in(1), phase->intcon(mask) );
|
||||
// Compare shift counts by value, not by node pointer, to also match a not-yet-normalized
|
||||
// negative constant (e.g. -1 vs 31)
|
||||
int shl_con = 0;
|
||||
if (in1_op == Op_LShiftI &&
|
||||
const_shift_count(phase, shl, &shl_con) &&
|
||||
(shl_con & (BitsPerJavaInteger - 1)) == con)
|
||||
return new AndINode(shl->in(1), phase->intcon(mask));
|
||||
|
||||
// Check for (x >> n) >>> 31. Replace with (x >>> 31)
|
||||
const TypeInt* t2 = phase->type(in(2))->isa_int();
|
||||
Node *shr = in(1);
|
||||
if ( in1_op == Op_RShiftI ) {
|
||||
Node *in11 = shr->in(1);
|
||||
@ -1677,11 +1691,15 @@ Node* URShiftLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
const TypeInt *t2 = phase->type(in(2))->isa_int();
|
||||
if (add->Opcode() == Op_AddL) {
|
||||
Node *lshl = add->in(1);
|
||||
if( lshl->Opcode() == Op_LShiftL &&
|
||||
phase->type(lshl->in(2)) == t2 ) {
|
||||
Node *y_z = phase->transform( new URShiftLNode(add->in(2),in(2)) );
|
||||
Node *sum = phase->transform( new AddLNode( lshl->in(1), y_z ) );
|
||||
return new AndLNode( sum, phase->longcon(mask) );
|
||||
// Compare shift counts by value, not by node pointer, to also match a not-yet-normalized
|
||||
// negative constant (e.g. -1 vs 63)
|
||||
int lshl_con = 0;
|
||||
if (lshl->Opcode() == Op_LShiftL &&
|
||||
const_shift_count(phase, lshl, &lshl_con) &&
|
||||
(lshl_con & (BitsPerJavaLong - 1)) == con) {
|
||||
Node* y_z = phase->transform(new URShiftLNode(add->in(2), in(2)));
|
||||
Node* sum = phase->transform(new AddLNode(lshl->in(1), y_z));
|
||||
return new AndLNode(sum, phase->longcon(mask));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1701,9 +1719,14 @@ Node* URShiftLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
|
||||
// Check for "(X << z ) >>> z" which simply zero-extends
|
||||
Node *shl = in(1);
|
||||
if( shl->Opcode() == Op_LShiftL &&
|
||||
phase->type(shl->in(2)) == t2 )
|
||||
return new AndLNode( shl->in(1), phase->longcon(mask) );
|
||||
// Compare shift counts by value, not by node pointer, to also match a not-yet-normalized
|
||||
// negative constant (e.g. -1 vs 63)
|
||||
int shl_con = 0;
|
||||
if (shl->Opcode() == Op_LShiftL &&
|
||||
const_shift_count(phase, shl, &shl_con) &&
|
||||
(shl_con & (BitsPerJavaLong - 1)) == con) {
|
||||
return new AndLNode(shl->in(1), phase->longcon(mask));
|
||||
}
|
||||
|
||||
// Check for (x >> n) >>> 63. Replace with (x >>> 63)
|
||||
Node *shr = in(1);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -42,7 +42,6 @@ public:
|
||||
virtual const Type *bottom_type() const = 0;
|
||||
virtual bool is_CFG() const { return true; }
|
||||
virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual const RegMask &out_RegMask() const;
|
||||
virtual Node *match( const ProjNode *proj, const Matcher *m );
|
||||
virtual uint ideal_reg() const { return NotAMachineReg; }
|
||||
@ -176,8 +175,7 @@ public:
|
||||
const bool _is_io_use; // Used to distinguish between the projections
|
||||
// used on the control and io paths from a macro node
|
||||
virtual int Opcode() const;
|
||||
virtual bool is_CFG() const;
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual bool is_CFG() const;
|
||||
virtual const Type *bottom_type() const;
|
||||
virtual const TypePtr *adr_type() const;
|
||||
virtual bool pinned() const;
|
||||
|
||||
@ -1059,14 +1059,135 @@ public:
|
||||
|
||||
virtual bool is_CFG() const { return false; }
|
||||
|
||||
// If this node is control-dependent on a test, can it be
|
||||
// rerouted to a dominating equivalent test? This is usually
|
||||
// true of non-CFG nodes, but can be false for operations which
|
||||
// depend for their correct sequencing on more than one test.
|
||||
// (In that case, hoisting to a dominating test may silently
|
||||
// skip some other important test.)
|
||||
virtual bool depends_only_on_test() const { assert(!is_CFG(), ""); return true; };
|
||||
// If this node is control-dependent on a test, can it be rerouted to a dominating equivalent
|
||||
// test? This means that the node can be executed safely as long as it happens after the test
|
||||
// that is its control input without worrying about the whole control flow. On the contrary, if
|
||||
// the node depends on a test that is not its control input, or if it depends on more than one
|
||||
// tests, then this method must return false.
|
||||
//
|
||||
// Pseudocode examples:
|
||||
// 1. if (y != 0) {
|
||||
// x / y;
|
||||
// }
|
||||
// The division depends only on the test y != 0 and can be executed anywhere y != 0 holds true.
|
||||
// As a result, depends_only_on_test returns true.
|
||||
// 2. if (y != 0) {
|
||||
// if (x > 1) {
|
||||
// x / y;
|
||||
// }
|
||||
// }
|
||||
// If the division x / y has its control input being the IfTrueNode of the test y != 0, then
|
||||
// depends_only_on_test returns true. Otherwise, if the division has its control input being the
|
||||
// IfTrueNode of the test x > 1, then depends_only_on_test returns false.
|
||||
// 3. if (y > z) {
|
||||
// if (z > 0) {
|
||||
// x / y
|
||||
// }
|
||||
// }
|
||||
// The division depends on both tests y > z and z > 0. As a result, depends_only_on_test returns
|
||||
// false.
|
||||
//
|
||||
// This method allows more freedom in certain nodes with regards to scheduling, for example it
|
||||
// allows nodes to float out of loops together with its test.
|
||||
//
|
||||
// This method is pessimistic, this means that it may return false even if the node satisfy the
|
||||
// requirements. However, it must return false if the node does not satisfy the requirements.
|
||||
// When a test is decomposed into multiple tests, all nodes that depend on the decomposed test
|
||||
// must be pinned at the lowest dominating test of those. For example, when a zero check of a
|
||||
// division is split through a region but the division itself is not, it must be pinned at the
|
||||
// merge point by returning false when calling this method.
|
||||
bool depends_only_on_test() const {
|
||||
if (is_CFG() || pinned()) {
|
||||
return false;
|
||||
}
|
||||
assert(in(0) != nullptr, "must have a control input");
|
||||
return depends_only_on_test_impl();
|
||||
}
|
||||
|
||||
// Return a clone of the current node that's pinned. The current node must return true for
|
||||
// depends_only_on_test, and the retuned node must return false. This method is called when the
|
||||
// node is disconnected from its test.
|
||||
//
|
||||
// Examples:
|
||||
// 1. for (int i = start; i <= limit; i++) {
|
||||
// if (!rangecheck(i, a)) {
|
||||
// trap;
|
||||
// }
|
||||
// a[i];
|
||||
// }
|
||||
// Loop predication can then hoist the range check out of the loop:
|
||||
// if (!rangecheck(start, a)) {
|
||||
// trap;
|
||||
// }
|
||||
// if (!rangecheck(limit, a)) {
|
||||
// trap;
|
||||
// }
|
||||
// for (int i = start; i <= limit; i++) {
|
||||
// a[i];
|
||||
// }
|
||||
// As the load a[i] now depends on both tests rangecheck(start, a) and rangecheck(limit, a), it
|
||||
// must be pinned at the lowest dominating test of those.
|
||||
//
|
||||
// 2. if (y > x) {
|
||||
// if (x >= 0) {
|
||||
// if (y != 0) {
|
||||
// x / y;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// The test (y != 0) == true can be deduced from (y > x) == true and (x >= 0) == true, so we may
|
||||
// choose to elide it. In such cases, the division x / y now depends on both tests
|
||||
// (y > x) == true and (x >= 0) == true, so it must be pinned at the lowest dominating test of
|
||||
// those.
|
||||
//
|
||||
// 3. if (b) {
|
||||
// ...
|
||||
// } else {
|
||||
// ...
|
||||
// }
|
||||
// if (y == 0) {
|
||||
// trap;
|
||||
// }
|
||||
// x / y;
|
||||
// The division x / y depends only on the test (y == 0) == false, but if we split the test
|
||||
// through the merge point but not the division:
|
||||
// if (b) {
|
||||
// ...
|
||||
// if (y == 0) {
|
||||
// trap;
|
||||
// }
|
||||
// } else {
|
||||
// ...
|
||||
// if (y == 0) {
|
||||
// trap;
|
||||
// }
|
||||
// }
|
||||
// x / y;
|
||||
// The division now has the control input being the RegionNode merge the branches of if(b)
|
||||
// instead of a test that proves y != 0. As a result, it must be pinned at that node.
|
||||
//
|
||||
// There are cases where the node does not actually have a dependency on its control input. For
|
||||
// example, when we try to sink a LoadNode out of a loop in PhaseIdealLoop::try_sink_out_of_loop,
|
||||
// we clone the node so that all of the clones can be scheduled out of the loop. To prevent the
|
||||
// clones from being GVN-ed again, we add a control input for the node at the loop exit. For the
|
||||
// cases when the node does provably not depend on its control input, this method can return
|
||||
// nullptr.
|
||||
Node* pin_node_under_control() const {
|
||||
assert(depends_only_on_test(), "must be a depends_only_on_test node");
|
||||
Node* res = pin_node_under_control_impl();
|
||||
if (res == nullptr) {
|
||||
assert(is_Load(), "unexpected failure to pin for %s", Name());
|
||||
return nullptr;
|
||||
}
|
||||
assert(!res->depends_only_on_test(), "the result must not depends_only_on_test");
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { assert(false, "%s", Name()); return false; }
|
||||
virtual Node* pin_node_under_control_impl() const { assert(false, "%s", Name()); return nullptr; }
|
||||
|
||||
public:
|
||||
// When building basic blocks, I need to have a notion of block beginning
|
||||
// Nodes, next block selector Nodes (block enders), and next block
|
||||
// projections. These calls need to work on their machine equivalents. The
|
||||
@ -1201,13 +1322,6 @@ public:
|
||||
template <typename Callback, typename Check>
|
||||
void visit_uses(Callback callback, Check is_boundary) const;
|
||||
|
||||
// Returns a clone of the current node that's pinned (if the current node is not) for nodes found in array accesses
|
||||
// (Load and range check CastII nodes).
|
||||
// This is used when an array access is made dependent on 2 or more range checks (range check smearing or Loop Predication).
|
||||
virtual Node* pin_array_access_node() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//----------------- Code Generation
|
||||
|
||||
// Ideal register class for Matching. Zero means unmatched instruction
|
||||
|
||||
@ -1722,11 +1722,6 @@ void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) {
|
||||
case Op_MergeMem:
|
||||
return;
|
||||
|
||||
// URShiftINode::Ideal
|
||||
// Found in tier1-3. Did not investigate further yet.
|
||||
case Op_URShiftI:
|
||||
return;
|
||||
|
||||
// CMoveINode::Ideal
|
||||
// Found in tier1-3. Did not investigate further yet.
|
||||
case Op_CMoveI:
|
||||
@ -2594,12 +2589,15 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_
|
||||
auto is_boundary = [](Node* n){ return !n->is_ConstraintCast(); };
|
||||
use->visit_uses(push_the_uses_to_worklist, is_boundary);
|
||||
}
|
||||
// If changed LShift inputs, check RShift users for useless sign-ext
|
||||
// If changed LShift inputs, check RShift/URShift users for
|
||||
// "(X << C) >> C" sign-ext and "(X << C) >>> C" zero-ext optimizations.
|
||||
if (use_op == Op_LShiftI || use_op == Op_LShiftL) {
|
||||
for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
|
||||
Node* u = use->fast_out(i2);
|
||||
if (u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL)
|
||||
if (u->Opcode() == Op_RShiftI || u->Opcode() == Op_RShiftL ||
|
||||
u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL) {
|
||||
worklist.push(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If changed LShift inputs, check And users for shift and mask (And) operation
|
||||
@ -2796,37 +2794,6 @@ void PhaseIterGVN::remove_speculative_types() {
|
||||
_table.check_no_speculative_types();
|
||||
}
|
||||
|
||||
// Check if the type of a divisor of a Div or Mod node includes zero.
|
||||
bool PhaseIterGVN::no_dependent_zero_check(Node* n) const {
|
||||
switch (n->Opcode()) {
|
||||
case Op_DivI:
|
||||
case Op_ModI:
|
||||
case Op_UDivI:
|
||||
case Op_UModI: {
|
||||
// Type of divisor includes 0?
|
||||
if (type(n->in(2)) == Type::TOP) {
|
||||
// 'n' is dead. Treat as if zero check is still there to avoid any further optimizations.
|
||||
return false;
|
||||
}
|
||||
const TypeInt* type_divisor = type(n->in(2))->is_int();
|
||||
return (type_divisor->_hi < 0 || type_divisor->_lo > 0);
|
||||
}
|
||||
case Op_DivL:
|
||||
case Op_ModL:
|
||||
case Op_UDivL:
|
||||
case Op_UModL: {
|
||||
// Type of divisor includes 0?
|
||||
if (type(n->in(2)) == Type::TOP) {
|
||||
// 'n' is dead. Treat as if zero check is still there to avoid any further optimizations.
|
||||
return false;
|
||||
}
|
||||
const TypeLong* type_divisor = type(n->in(2))->is_long();
|
||||
return (type_divisor->_hi < 0 || type_divisor->_lo > 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
#ifndef PRODUCT
|
||||
uint PhaseCCP::_total_invokes = 0;
|
||||
|
||||
@ -604,7 +604,6 @@ public:
|
||||
}
|
||||
|
||||
bool is_dominator(Node *d, Node *n) { return is_dominator_helper(d, n, false); }
|
||||
bool no_dependent_zero_check(Node* n) const;
|
||||
|
||||
#ifndef PRODUCT
|
||||
static bool is_verify_def_use() {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -63,7 +63,6 @@ public:
|
||||
virtual const Type *bottom_type() const;
|
||||
virtual bool is_CFG() const { return true; }
|
||||
virtual uint hash() const { return NO_HASH; } // CFG nodes do not hash
|
||||
virtual bool depends_only_on_test() const { return false; }
|
||||
virtual const Node *is_block_proj() const { return this; }
|
||||
virtual const RegMask &out_RegMask() const;
|
||||
virtual uint ideal_reg() const { return NotAMachineReg; }
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "opto/movenode.hpp"
|
||||
#include "opto/node.hpp"
|
||||
#include "opto/opaquenode.hpp"
|
||||
#include "opto/opcodes.hpp"
|
||||
#include "opto/predicates.hpp"
|
||||
|
||||
//------------------------------split_thru_region------------------------------
|
||||
@ -716,14 +717,11 @@ void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, Regio
|
||||
} // End of while merge point has phis
|
||||
|
||||
_igvn.remove_dead_node(region);
|
||||
if (iff->Opcode() == Op_RangeCheck) {
|
||||
// Pin array access nodes: control is updated here to a region. If, after some transformations, only one path
|
||||
// into the region is left, an array load could become dependent on a condition that's not a range check for
|
||||
// that access. If that condition is replaced by an identical dominating one, then an unpinned load would risk
|
||||
// floating above its range check.
|
||||
pin_array_access_nodes_dependent_on(new_true);
|
||||
pin_array_access_nodes_dependent_on(new_false);
|
||||
}
|
||||
|
||||
// Control is updated here to a region, which is not a test, so any node that
|
||||
// depends_only_on_test must be pinned
|
||||
pin_nodes_dependent_on(new_true, iff->Opcode() == Op_RangeCheck);
|
||||
pin_nodes_dependent_on(new_false, iff->Opcode() == Op_RangeCheck);
|
||||
|
||||
if (new_false_region != nullptr) {
|
||||
*new_false_region = new_false;
|
||||
@ -735,13 +733,22 @@ void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, Regio
|
||||
DEBUG_ONLY( if (VerifyLoopOptimizations) { verify(); } );
|
||||
}
|
||||
|
||||
void PhaseIdealLoop::pin_array_access_nodes_dependent_on(Node* ctrl) {
|
||||
void PhaseIdealLoop::pin_nodes_dependent_on(Node* ctrl, bool old_iff_is_rangecheck) {
|
||||
for (DUIterator i = ctrl->outs(); ctrl->has_out(i); i++) {
|
||||
Node* use = ctrl->out(i);
|
||||
if (!use->depends_only_on_test()) {
|
||||
continue;
|
||||
}
|
||||
Node* pinned_clone = use->pin_array_access_node();
|
||||
|
||||
|
||||
// When a RangeCheckNode is folded because its condition is a constant, IfProjNode::Identity
|
||||
// returns the control input of the RangeCheckNode. As a result, when the old IfNode is not a
|
||||
// RangeCheckNode, and a Load output of it depends_only_on_test, we don't need to pin the Load.
|
||||
if (use->is_Load() && !old_iff_is_rangecheck) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Node* pinned_clone = use->pin_node_under_control();
|
||||
if (pinned_clone != nullptr) {
|
||||
register_new_node_with_ctrl_of(pinned_clone, use);
|
||||
_igvn.replace_node(use, pinned_clone);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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
|
||||
@ -507,6 +507,9 @@ public:
|
||||
virtual int Opcode() const;
|
||||
const Type *bottom_type() const { return Type::DOUBLE; }
|
||||
virtual uint ideal_reg() const { return Op_RegD; }
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
|
||||
@ -522,6 +525,9 @@ public:
|
||||
const Type *bottom_type() const { return Type::DOUBLE; }
|
||||
virtual uint ideal_reg() const { return Op_RegD; }
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
//------------------------------SqrtFNode--------------------------------------
|
||||
@ -541,6 +547,9 @@ public:
|
||||
const Type *bottom_type() const { return Type::FLOAT; }
|
||||
virtual uint ideal_reg() const { return Op_RegF; }
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
|
||||
private:
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
};
|
||||
|
||||
//------------------------------SqrtHFNode-------------------------------------
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -48,7 +48,6 @@ public:
|
||||
|
||||
virtual int Opcode() const;
|
||||
const Type* bottom_type() const { return TypeInt::CC; }
|
||||
bool depends_only_on_test() const { return false; }
|
||||
|
||||
ciMethod* method() const { return _method; }
|
||||
int bci() const { return _bci; }
|
||||
@ -71,6 +70,8 @@ private:
|
||||
static bool is_oop(PhaseGVN* phase, Node* n);
|
||||
|
||||
Node* load_klass(PhaseGVN* phase) const;
|
||||
|
||||
virtual bool depends_only_on_test_impl() const { return false; }
|
||||
#endif // ASSERT
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -42,6 +42,7 @@ typedef enum {
|
||||
} VirtualizationType;
|
||||
|
||||
class outputStream;
|
||||
class stringStream;
|
||||
enum class vmIntrinsicID;
|
||||
|
||||
// Abstract_VM_Version provides information about the VM.
|
||||
@ -226,6 +227,21 @@ class Abstract_VM_Version: AllStatic {
|
||||
|
||||
static const char* cpu_name(void);
|
||||
static const char* cpu_description(void);
|
||||
|
||||
static void get_cpu_features_name(void* features_buffer, stringStream& ss) { return; }
|
||||
|
||||
// Returns names of features present in features_set1 but not in features_set2
|
||||
static void get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss) { return; }
|
||||
|
||||
// Returns number of bytes required to store cpu features representation
|
||||
static int cpu_features_size() { return 0; }
|
||||
|
||||
// Stores arch dependent cpu features representation in the provided buffer.
|
||||
// Size of the buffer must be same as returned by cpu_features_size()
|
||||
static void store_cpu_features(void* buf) { return; }
|
||||
|
||||
// features_to_test is an opaque object that stores arch specific representation of cpu features
|
||||
static bool supports_features(void* features_to_test) { return false; };
|
||||
};
|
||||
|
||||
#endif // SHARE_RUNTIME_ABSTRACT_VM_VERSION_HPP
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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,7 @@
|
||||
#include "runtime/threadSMR.inline.hpp"
|
||||
#include "runtime/vmOperations.hpp"
|
||||
#include "services/threadService.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/ticks.hpp"
|
||||
|
||||
#define VM_OP_NAME_INITIALIZE(name) #name,
|
||||
@ -286,42 +287,27 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView {
|
||||
}
|
||||
|
||||
private:
|
||||
class ObjectMonitorLinkedList :
|
||||
public LinkedListImpl<ObjectMonitor*,
|
||||
AnyObj::C_HEAP, mtThread,
|
||||
AllocFailStrategy::RETURN_NULL> {};
|
||||
using ObjectMonitorList = GrowableArrayCHeap<ObjectMonitor*, mtThread>;
|
||||
|
||||
// HashTable SIZE is specified at compile time so we
|
||||
// use 1031 which is the first prime after 1024.
|
||||
typedef HashTable<int64_t, ObjectMonitorLinkedList*, 1031, AnyObj::C_HEAP, mtThread,
|
||||
typedef HashTable<int64_t, ObjectMonitorList, 1031, AnyObj::C_HEAP, mtThread,
|
||||
&ObjectMonitorsDump::ptr_hash> PtrTable;
|
||||
PtrTable* _ptrs;
|
||||
size_t _key_count;
|
||||
size_t _om_count;
|
||||
|
||||
void add_list(int64_t key, ObjectMonitorLinkedList* list) {
|
||||
_ptrs->put(key, list);
|
||||
_key_count++;
|
||||
}
|
||||
|
||||
ObjectMonitorLinkedList* get_list(int64_t key) {
|
||||
ObjectMonitorLinkedList** listpp = _ptrs->get(key);
|
||||
return (listpp == nullptr) ? nullptr : *listpp;
|
||||
}
|
||||
|
||||
void add(ObjectMonitor* monitor) {
|
||||
int64_t key = monitor->owner();
|
||||
|
||||
ObjectMonitorLinkedList* list = get_list(key);
|
||||
if (list == nullptr) {
|
||||
// Create new list and add it to the hash table:
|
||||
list = new (mtThread) ObjectMonitorLinkedList;
|
||||
_ptrs->put(key, list);
|
||||
bool created = false;
|
||||
ObjectMonitorList* list = _ptrs->put_if_absent(key, &created);
|
||||
if (created) {
|
||||
_key_count++;
|
||||
}
|
||||
|
||||
assert(list->find(monitor) == nullptr, "Should not contain duplicates");
|
||||
list->add(monitor); // Add the ObjectMonitor to the list.
|
||||
assert(list->find(monitor) == -1, "Should not contain duplicates");
|
||||
list->push(monitor); // Add the ObjectMonitor to the list.
|
||||
_om_count++;
|
||||
}
|
||||
|
||||
@ -332,17 +318,7 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView {
|
||||
ObjectMonitorsDump() : _ptrs(new (mtThread) PtrTable), _key_count(0), _om_count(0) {}
|
||||
|
||||
~ObjectMonitorsDump() {
|
||||
class CleanupObjectMonitorsDump: StackObj {
|
||||
public:
|
||||
bool do_entry(int64_t& key, ObjectMonitorLinkedList*& list) {
|
||||
list->clear(); // clear the LinkListNodes
|
||||
delete list; // then delete the LinkedList
|
||||
return true;
|
||||
}
|
||||
} cleanup;
|
||||
|
||||
_ptrs->unlink(&cleanup); // cleanup the LinkedLists
|
||||
delete _ptrs; // then delete the hash table
|
||||
delete _ptrs;
|
||||
}
|
||||
|
||||
// Implements MonitorClosure used to collect all owned monitors in the system
|
||||
@ -368,11 +344,12 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView {
|
||||
// Implements the ObjectMonitorsView interface
|
||||
void visit(MonitorClosure* closure, JavaThread* thread) override {
|
||||
int64_t key = ObjectMonitor::owner_id_from(thread);
|
||||
ObjectMonitorLinkedList* list = get_list(key);
|
||||
LinkedListIterator<ObjectMonitor*> iter(list != nullptr ? list->head() : nullptr);
|
||||
while (!iter.is_empty()) {
|
||||
ObjectMonitor* monitor = *iter.next();
|
||||
closure->do_monitor(monitor);
|
||||
ObjectMonitorList* list = _ptrs->get(key);
|
||||
if (list == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < list->length(); i++) {
|
||||
closure->do_monitor(list->at(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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,9 +26,8 @@
|
||||
#define SHARE_UTILITIES_RBTREE_HPP
|
||||
|
||||
#include "cppstdlib/type_traits.hpp"
|
||||
#include "metaprogramming/enableIf.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "nmt/memTag.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
// An intrusive red-black tree is constructed with two template parameters:
|
||||
@ -64,8 +63,9 @@ enum class RBTreeOrdering : int { LT, EQ, GT };
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
class AbstractRBTree;
|
||||
|
||||
class Arena;
|
||||
class outputStream;
|
||||
class ResourceArea;
|
||||
|
||||
class IntrusiveRBNode {
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
@ -81,7 +81,7 @@ class IntrusiveRBNode {
|
||||
DEBUG_ONLY(mutable bool _visited);
|
||||
|
||||
public:
|
||||
IntrusiveRBNode() : _parent(0), _left(nullptr), _right(nullptr) DEBUG_ONLY(COMMA _visited(false)) {}
|
||||
IntrusiveRBNode();
|
||||
|
||||
// Gets the previous in-order node in the tree.
|
||||
// nullptr is returned if there is no previous node.
|
||||
@ -96,22 +96,18 @@ public:
|
||||
void print_on(outputStream* st, int depth = 0) const;
|
||||
|
||||
private:
|
||||
bool is_black() const { return (_parent & 0x1) != 0; }
|
||||
bool is_red() const { return (_parent & 0x1) == 0; }
|
||||
bool is_black() const;
|
||||
bool is_red() const;
|
||||
|
||||
void set_black() { _parent |= 0x1; }
|
||||
void set_red() { _parent &= ~0x1; }
|
||||
void set_black();
|
||||
void set_red();
|
||||
|
||||
IntrusiveRBNode* parent() const { return (IntrusiveRBNode*)(_parent & ~0x1); }
|
||||
void set_parent(IntrusiveRBNode* new_parent) { _parent = (_parent & 0x1) | (uintptr_t)new_parent; }
|
||||
IntrusiveRBNode* parent() const;
|
||||
void set_parent(IntrusiveRBNode* new_parent);
|
||||
|
||||
bool is_right_child() const {
|
||||
return parent() != nullptr && parent()->_right == this;
|
||||
}
|
||||
bool is_right_child() const;
|
||||
|
||||
bool is_left_child() const {
|
||||
return parent() != nullptr && parent()->_left == this;
|
||||
}
|
||||
bool is_left_child() const;
|
||||
|
||||
void replace_child(IntrusiveRBNode* old_child, IntrusiveRBNode* new_child);
|
||||
|
||||
@ -142,20 +138,20 @@ private:
|
||||
V _value;
|
||||
|
||||
public:
|
||||
const K& key() const { return _key; }
|
||||
const K& key() const;
|
||||
|
||||
V& val() { return _value; }
|
||||
const V& val() const { return _value; }
|
||||
void set_val(const V& v) { _value = v; }
|
||||
V& val();
|
||||
const V& val() const;
|
||||
void set_val(const V& v);
|
||||
|
||||
RBNode() {}
|
||||
RBNode(const K& key) : IntrusiveRBNode(), _key(key) {}
|
||||
RBNode(const K& key, const V& val) : IntrusiveRBNode(), _key(key), _value(val) {}
|
||||
RBNode();
|
||||
RBNode(const K& key);
|
||||
RBNode(const K& key, const V& val);
|
||||
|
||||
const RBNode<K, V>* prev() const { return (RBNode<K, V>*)IntrusiveRBNode::prev(); }
|
||||
const RBNode<K, V>* next() const { return (RBNode<K, V>*)IntrusiveRBNode::next(); }
|
||||
RBNode<K, V>* prev() { return (RBNode<K, V>*)IntrusiveRBNode::prev(); }
|
||||
RBNode<K, V>* next() { return (RBNode<K, V>*)IntrusiveRBNode::next(); }
|
||||
const RBNode<K, V>* prev() const;
|
||||
const RBNode<K, V>* next() const;
|
||||
RBNode<K, V>* prev();
|
||||
RBNode<K, V>* next();
|
||||
|
||||
void print_on(outputStream* st, int depth = 0) const;
|
||||
|
||||
@ -176,17 +172,15 @@ public:
|
||||
friend AbstractRBTree<K, NodeType, COMPARATOR>;
|
||||
NodeType** _insert_location;
|
||||
NodeType* _parent;
|
||||
Cursor() : _insert_location(nullptr), _parent(nullptr) {}
|
||||
Cursor(NodeType** insert_location, NodeType* parent)
|
||||
: _insert_location(insert_location), _parent(parent) {}
|
||||
Cursor(NodeType* const* insert_location, NodeType* parent)
|
||||
: _insert_location((NodeType**)insert_location), _parent(parent) {}
|
||||
Cursor();
|
||||
Cursor(NodeType** insert_location, NodeType* parent);
|
||||
Cursor(NodeType* const* insert_location, NodeType* parent);
|
||||
|
||||
public:
|
||||
bool valid() const { return _insert_location != nullptr; }
|
||||
bool found() const { return *_insert_location != nullptr; }
|
||||
NodeType* node() { return _insert_location == nullptr ? nullptr : *_insert_location; }
|
||||
NodeType* node() const { return _insert_location == nullptr ? nullptr : *_insert_location; }
|
||||
bool valid() const;
|
||||
bool found() const;
|
||||
NodeType* node();
|
||||
NodeType* node() const;
|
||||
};
|
||||
|
||||
protected:
|
||||
@ -212,36 +206,16 @@ private:
|
||||
|
||||
static constexpr bool HasNodeVerifier = HasNodeVerifierImpl<COMPARATOR>::value;
|
||||
|
||||
RBTreeOrdering cmp(const K& a, const NodeType* b) const {
|
||||
if constexpr (HasNodeComparator) {
|
||||
return COMPARATOR::cmp(a, b);
|
||||
} else if constexpr (HasKeyComparator) {
|
||||
return COMPARATOR::cmp(a, b->key());
|
||||
}
|
||||
}
|
||||
RBTreeOrdering cmp(const K& a, const NodeType* b) const;
|
||||
|
||||
bool less_than(const NodeType* a, const NodeType* b) const {
|
||||
if constexpr (HasNodeVerifier) {
|
||||
return COMPARATOR::less_than(a, b);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bool less_than(const NodeType* a, const NodeType* b) const;
|
||||
|
||||
void assert_key_leq(K a, K b) const {
|
||||
if constexpr (HasKeyComparator) { // Cannot assert if no key comparator exist.
|
||||
assert(COMPARATOR::cmp(a, b) != RBTreeOrdering::GT, "key a must be less or equal to key b");
|
||||
}
|
||||
}
|
||||
void assert_key_leq(K a, K b) const;
|
||||
|
||||
// True if node is black (nil nodes count as black)
|
||||
static inline bool is_black(const IntrusiveRBNode* node) {
|
||||
return node == nullptr || node->is_black();
|
||||
}
|
||||
static inline bool is_black(const IntrusiveRBNode* node);
|
||||
|
||||
static inline bool is_red(const IntrusiveRBNode* node) {
|
||||
return node != nullptr && node->is_red();
|
||||
}
|
||||
static inline bool is_red(const IntrusiveRBNode* node);
|
||||
|
||||
void fix_insert_violations(IntrusiveRBNode* node);
|
||||
|
||||
@ -251,18 +225,14 @@ private:
|
||||
void remove_from_tree(IntrusiveRBNode* node);
|
||||
|
||||
struct empty_verifier {
|
||||
bool operator()(const NodeType* n) const {
|
||||
return true;
|
||||
}
|
||||
bool operator()(const NodeType* n) const;
|
||||
};
|
||||
|
||||
template <typename NODE_VERIFIER, typename USER_VERIFIER>
|
||||
void verify_self(NODE_VERIFIER verifier, const USER_VERIFIER& extra_verifier) const;
|
||||
|
||||
struct default_printer {
|
||||
void operator()(outputStream* st, const NodeType* n, int depth) const {
|
||||
n->print_on(st, depth);
|
||||
}
|
||||
void operator()(outputStream* st, const NodeType* n, int depth) const;
|
||||
};
|
||||
|
||||
template <typename PRINTER>
|
||||
@ -271,12 +241,9 @@ private:
|
||||
public:
|
||||
NONCOPYABLE(AbstractRBTree);
|
||||
|
||||
AbstractRBTree() : _num_nodes(0), _root(nullptr) DEBUG_ONLY(COMMA _expected_visited(false)) {
|
||||
static_assert(std::is_trivially_destructible<K>::value, "key type must be trivially destructable");
|
||||
static_assert(HasKeyComparator || HasNodeComparator, "comparator must be of correct type");
|
||||
}
|
||||
AbstractRBTree();
|
||||
|
||||
size_t size() const { return _num_nodes; }
|
||||
size_t size() const;
|
||||
|
||||
// Gets the cursor associated with the given node or key.
|
||||
Cursor cursor(const K& key, const NodeType* hint_node = nullptr);
|
||||
@ -311,87 +278,39 @@ public:
|
||||
void replace_at_cursor(NodeType* new_node, const Cursor& node_cursor);
|
||||
|
||||
// Finds the node associated with the given key.
|
||||
NodeType* find_node(const K& key, const NodeType* hint_node = nullptr) const {
|
||||
Cursor node_cursor = cursor(key, hint_node);
|
||||
return node_cursor.node();
|
||||
}
|
||||
|
||||
NodeType* find_node(const K& key, const NodeType* hint_node = nullptr) {
|
||||
Cursor node_cursor = cursor(key, hint_node);
|
||||
return node_cursor.node();
|
||||
}
|
||||
NodeType* find_node(const K& key, const NodeType* hint_node = nullptr);
|
||||
NodeType* find_node(const K& key, const NodeType* hint_node = nullptr) const;
|
||||
|
||||
// Inserts the given node into the tree.
|
||||
void insert(const K& key, NodeType* node, const NodeType* hint_node = nullptr) {
|
||||
Cursor node_cursor = cursor(key, hint_node);
|
||||
insert_at_cursor(node, node_cursor);
|
||||
}
|
||||
void insert(const K& key, NodeType* node, const NodeType* hint_node = nullptr);
|
||||
|
||||
void remove(NodeType* node) {
|
||||
Cursor node_cursor = cursor(node);
|
||||
remove_at_cursor(node_cursor);
|
||||
}
|
||||
// Removes the given node from the tree.
|
||||
void remove(NodeType* node);
|
||||
|
||||
// Finds the node with the closest key <= the given key.
|
||||
// If no node is found, null is returned instead.
|
||||
NodeType* closest_leq(const K& key) const {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? node_cursor.node() : prev(node_cursor).node();
|
||||
}
|
||||
|
||||
NodeType* closest_leq(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? node_cursor.node() : prev(node_cursor).node();
|
||||
}
|
||||
NodeType* closest_leq(const K& key);
|
||||
NodeType* closest_leq(const K& key) const;
|
||||
|
||||
// Finds the node with the closest key > the given key.
|
||||
// If no node is found, null is returned instead.
|
||||
NodeType* closest_gt(const K& key) const {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return next(node_cursor).node();
|
||||
}
|
||||
|
||||
NodeType* closest_gt(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return next(node_cursor).node();
|
||||
}
|
||||
NodeType* closest_gt(const K& key);
|
||||
NodeType* closest_gt(const K& key) const;
|
||||
|
||||
// Finds the node with the closest key >= the given key.
|
||||
// If no node is found, null is returned instead.
|
||||
NodeType* closest_ge(const K& key) const {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? node_cursor.node() : next(node_cursor).node();
|
||||
}
|
||||
|
||||
NodeType* closest_ge(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? node_cursor.node() : next(node_cursor).node();
|
||||
}
|
||||
NodeType* closest_ge(const K& key);
|
||||
NodeType* closest_ge(const K& key) const;
|
||||
|
||||
// Returns leftmost node, nullptr if tree is empty.
|
||||
// If COMPARATOR::cmp(a, b) behaves canonically (positive value for a > b), this will the smallest key value.
|
||||
const NodeType* leftmost() const {
|
||||
IntrusiveRBNode* n = _root, *n2 = nullptr;
|
||||
while (n != nullptr) {
|
||||
n2 = n;
|
||||
n = n->_left;
|
||||
}
|
||||
return (NodeType*)n2;
|
||||
}
|
||||
NodeType* leftmost();
|
||||
const NodeType* leftmost() const;
|
||||
|
||||
// Returns rightmost node, nullptr if tree is empty.
|
||||
// If COMPARATOR::cmp(a, b) behaves canonically (positive value for a > b), this will the largest key value.
|
||||
const NodeType* rightmost() const {
|
||||
IntrusiveRBNode* n = _root, *n2 = nullptr;
|
||||
while (n != nullptr) {
|
||||
n2 = n;
|
||||
n = n->_right;
|
||||
}
|
||||
return (NodeType*)n2;
|
||||
}
|
||||
|
||||
NodeType* leftmost() { return const_cast<NodeType*>(static_cast<const TreeType*>(this)->leftmost()); }
|
||||
NodeType* rightmost() { return const_cast<NodeType*>(static_cast<const TreeType*>(this)->rightmost()); }
|
||||
NodeType* rightmost();
|
||||
const NodeType* rightmost() const;
|
||||
|
||||
struct Range {
|
||||
NodeType* start;
|
||||
@ -403,11 +322,7 @@ public:
|
||||
// Return the range [start, end)
|
||||
// where start->key() <= addr < end->key().
|
||||
// Failure to find the range leads to start and/or end being null.
|
||||
Range find_enclosing_range(K key) const {
|
||||
NodeType* start = closest_leq(key);
|
||||
NodeType* end = closest_gt(key);
|
||||
return Range(start, end);
|
||||
}
|
||||
Range find_enclosing_range(K key) const;
|
||||
|
||||
// Visit all RBNodes in ascending order, calling f on each node.
|
||||
// If f returns `true` the iteration continues, otherwise it is stopped at the current node.
|
||||
@ -417,7 +332,6 @@ public:
|
||||
template <typename F>
|
||||
void visit_in_order(F f);
|
||||
|
||||
|
||||
// Visit all RBNodes in ascending order whose keys are in range [from, to], calling f on each node.
|
||||
// If f returns `true` the iteration continues, otherwise it is stopped at the current node.
|
||||
template <typename F>
|
||||
@ -433,15 +347,7 @@ public:
|
||||
// This should return true if the node is valid.
|
||||
// If provided, each node is also verified through this callable.
|
||||
template <typename USER_VERIFIER = empty_verifier>
|
||||
void verify_self(const USER_VERIFIER& extra_verifier = USER_VERIFIER()) const {
|
||||
if constexpr (HasNodeVerifier) {
|
||||
verify_self([](const NodeType* a, const NodeType* b){ return COMPARATOR::less_than(a, b);}, extra_verifier);
|
||||
} else if constexpr (HasKeyComparator) {
|
||||
verify_self([](const NodeType* a, const NodeType* b){ return COMPARATOR::cmp(a->key(), b->key()) == RBTreeOrdering::LT; }, extra_verifier);
|
||||
} else {
|
||||
verify_self([](const NodeType*, const NodeType*){ return true;}, extra_verifier);
|
||||
}
|
||||
}
|
||||
void verify_self(const USER_VERIFIER& extra_verifier = USER_VERIFIER()) const;
|
||||
|
||||
// Accepts an optional printing callable `void node_printer(outputStream* st, const Node* n, int depth)`.
|
||||
// If provided, each node is printed through this callable rather than the default `print_on`.
|
||||
@ -458,9 +364,10 @@ class RBTree : public AbstractRBTree<K, RBNode<K, V>, COMPARATOR> {
|
||||
ALLOCATOR _allocator;
|
||||
|
||||
public:
|
||||
RBTree() : BaseType(), _allocator() {}
|
||||
template<typename... AllocArgs>
|
||||
RBTree(AllocArgs... alloc_args);
|
||||
~RBTree();
|
||||
NONCOPYABLE(RBTree);
|
||||
~RBTree() { remove_all(); }
|
||||
|
||||
bool copy_into(RBTree& other) const;
|
||||
|
||||
@ -471,118 +378,68 @@ public:
|
||||
using BaseType::next;
|
||||
using BaseType::prev;
|
||||
|
||||
void replace_at_cursor(RBNode<K, V>* new_node, const Cursor& node_cursor) {
|
||||
RBNode<K, V>* old_node = node_cursor.node();
|
||||
BaseType::replace_at_cursor(new_node, node_cursor);
|
||||
free_node(old_node);
|
||||
}
|
||||
void replace_at_cursor(RBNode<K, V>* new_node, const Cursor& node_cursor);
|
||||
|
||||
RBNode<K, V>* allocate_node(const K& key) {
|
||||
void* node_place = _allocator.allocate(sizeof(RBNode<K, V>));
|
||||
if (node_place == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new (node_place) RBNode<K, V>(key);
|
||||
}
|
||||
RBNode<K, V>* allocate_node(const K& key);
|
||||
RBNode<K, V>* allocate_node(const K& key, const V& val);
|
||||
|
||||
RBNode<K, V>* allocate_node(const K& key, const V& val) {
|
||||
void* node_place = _allocator.allocate(sizeof(RBNode<K, V>));
|
||||
if (node_place == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new (node_place) RBNode<K, V>(key, val);
|
||||
}
|
||||
|
||||
void free_node(RBNode<K, V>* node) {
|
||||
node->_value.~V();
|
||||
_allocator.free(node);
|
||||
}
|
||||
void free_node(RBNode<K, V>* node);
|
||||
|
||||
// Inserts a node with the given key/value into the tree,
|
||||
// if the key already exist, the value is updated instead.
|
||||
// Returns false if and only if allocation of a new node failed.
|
||||
bool upsert(const K& key, const V& val, const RBNode<K, V>* hint_node = nullptr) {
|
||||
Cursor node_cursor = cursor(key, hint_node);
|
||||
RBNode<K, V>* node = node_cursor.node();
|
||||
if (node != nullptr) {
|
||||
node->set_val(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
node = allocate_node(key, val);
|
||||
if (node == nullptr) {
|
||||
return false;
|
||||
}
|
||||
insert_at_cursor(node, node_cursor);
|
||||
return true;
|
||||
}
|
||||
bool upsert(const K& key, const V& val, const RBNode<K, V>* hint_node = nullptr);
|
||||
|
||||
// Finds the value of the node associated with the given key.
|
||||
V* find(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? &node_cursor.node()->_value : nullptr;
|
||||
}
|
||||
V* find(const K& key);
|
||||
V* find(const K& key) const;
|
||||
|
||||
V* find(const K& key) const {
|
||||
const Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? &node_cursor.node()->_value : nullptr;
|
||||
}
|
||||
|
||||
void remove(RBNode<K, V>* node) {
|
||||
Cursor node_cursor = cursor(node);
|
||||
remove_at_cursor(node_cursor);
|
||||
free_node(node);
|
||||
}
|
||||
void remove(RBNode<K, V>* node);
|
||||
|
||||
// Removes the node with the given key from the tree if it exists.
|
||||
// Returns true if the node was successfully removed, false otherwise.
|
||||
bool remove(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
if (!node_cursor.found()) {
|
||||
return false;
|
||||
}
|
||||
RBNode<K, V>* node = node_cursor.node();
|
||||
remove_at_cursor(node_cursor);
|
||||
free_node((RBNode<K, V>*)node);
|
||||
return true;
|
||||
}
|
||||
bool remove(const K& key);
|
||||
|
||||
// Removes all existing nodes from the tree.
|
||||
void remove_all() {
|
||||
IntrusiveRBNode* to_delete[64];
|
||||
int stack_idx = 0;
|
||||
to_delete[stack_idx++] = BaseType::_root;
|
||||
|
||||
while (stack_idx > 0) {
|
||||
IntrusiveRBNode* head = to_delete[--stack_idx];
|
||||
if (head == nullptr) continue;
|
||||
to_delete[stack_idx++] = head->_left;
|
||||
to_delete[stack_idx++] = head->_right;
|
||||
free_node((RBNode<K, V>*)head);
|
||||
}
|
||||
BaseType::_num_nodes = 0;
|
||||
BaseType::_root = nullptr;
|
||||
}
|
||||
void remove_all();
|
||||
};
|
||||
|
||||
template <MemTag mem_tag, AllocFailType strategy>
|
||||
class RBTreeCHeapAllocator {
|
||||
public:
|
||||
void* allocate(size_t sz) {
|
||||
void* allocation = os::malloc(sz, mem_tag);
|
||||
if (allocation == nullptr && strategy == AllocFailStrategy::EXIT_OOM) {
|
||||
vm_exit_out_of_memory(sz, OOM_MALLOC_ERROR,
|
||||
"red-black tree failed allocation");
|
||||
}
|
||||
return allocation;
|
||||
}
|
||||
|
||||
void free(void* ptr) { os::free(ptr); }
|
||||
void* allocate(size_t sz);
|
||||
void free(void* ptr);
|
||||
};
|
||||
|
||||
template <AllocFailType strategy>
|
||||
class RBTreeArenaAllocator {
|
||||
Arena* _arena;
|
||||
public:
|
||||
RBTreeArenaAllocator(Arena* arena);
|
||||
void* allocate(size_t sz);
|
||||
void free(void* ptr);
|
||||
};
|
||||
|
||||
template <AllocFailType strategy>
|
||||
class RBTreeResourceAreaAllocator {
|
||||
ResourceArea* _rarea;
|
||||
public:
|
||||
RBTreeResourceAreaAllocator(ResourceArea* rarea);
|
||||
void* allocate(size_t sz);
|
||||
void free(void* ptr);
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, MemTag mem_tag, AllocFailType strategy = AllocFailStrategy::EXIT_OOM>
|
||||
using RBTreeCHeap = RBTree<K, V, COMPARATOR, RBTreeCHeapAllocator<mem_tag, strategy>>;
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, AllocFailType strategy = AllocFailStrategy::EXIT_OOM>
|
||||
using RBTreeArena = RBTree<K, V, COMPARATOR, RBTreeArenaAllocator<strategy>>;
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, AllocFailType strategy = AllocFailStrategy::EXIT_OOM>
|
||||
using RBTreeResourceArea = RBTree<K, V, COMPARATOR, RBTreeResourceAreaAllocator<strategy>>;
|
||||
|
||||
template <typename K, typename COMPARATOR>
|
||||
using IntrusiveRBTree = AbstractRBTree<K, IntrusiveRBNode, COMPARATOR>;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,12 +27,49 @@
|
||||
|
||||
#include "utilities/rbTree.hpp"
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/arena.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "metaprogramming/enableIf.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
#include "utilities/powerOfTwo.hpp"
|
||||
|
||||
inline IntrusiveRBNode::IntrusiveRBNode()
|
||||
: _parent(0), _left(nullptr), _right(nullptr) DEBUG_ONLY(COMMA _visited(false)) {}
|
||||
|
||||
inline bool IntrusiveRBNode::is_black() const {
|
||||
return (_parent & 0x1) != 0;
|
||||
}
|
||||
|
||||
inline bool IntrusiveRBNode::is_red() const {
|
||||
return (_parent & 0x1) == 0;
|
||||
}
|
||||
|
||||
inline void IntrusiveRBNode::set_black() {
|
||||
_parent |= 0x1;
|
||||
}
|
||||
inline void IntrusiveRBNode::set_red() {
|
||||
_parent &= ~0x1;
|
||||
}
|
||||
|
||||
inline IntrusiveRBNode* IntrusiveRBNode::parent() const {
|
||||
return reinterpret_cast<IntrusiveRBNode*>(_parent & ~0x1);
|
||||
}
|
||||
|
||||
inline void IntrusiveRBNode::set_parent(IntrusiveRBNode* new_parent) {
|
||||
_parent = (_parent & 0x1) | reinterpret_cast<uintptr_t>(new_parent);
|
||||
}
|
||||
|
||||
inline bool IntrusiveRBNode::is_right_child() const {
|
||||
return parent() != nullptr && parent()->_right == this;
|
||||
}
|
||||
|
||||
inline bool IntrusiveRBNode::is_left_child() const {
|
||||
return parent() != nullptr && parent()->_left == this;
|
||||
}
|
||||
|
||||
inline void IntrusiveRBNode::replace_child(IntrusiveRBNode* old_child, IntrusiveRBNode* new_child) {
|
||||
if (_left == old_child) {
|
||||
_left = new_child;
|
||||
@ -185,6 +222,144 @@ inline void IntrusiveRBNode::verify(
|
||||
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline const K& RBNode<K, V>::key() const {
|
||||
return _key;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline V& RBNode<K, V>::val() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline const V& RBNode<K, V>::val() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline void RBNode<K, V>::set_val(const V& v) {
|
||||
_value = v;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline RBNode<K, V>::RBNode() {}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline RBNode<K, V>::RBNode(const K& key) : IntrusiveRBNode(), _key(key) {}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline RBNode<K, V>::RBNode(const K& key, const V& val) : IntrusiveRBNode(), _key(key), _value(val) {}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline const RBNode<K, V>* RBNode<K, V>::prev() const {
|
||||
return static_cast<const RBNode<K, V>*>(IntrusiveRBNode::prev());
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline const RBNode<K, V>* RBNode<K, V>::next() const {
|
||||
return static_cast<const RBNode<K, V>*>(IntrusiveRBNode::next());
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline RBNode<K, V>* RBNode<K, V>::prev() {
|
||||
return static_cast<RBNode<K, V>*>(IntrusiveRBNode::prev());
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline RBNode<K, V>* RBNode<K, V>::next() {
|
||||
return static_cast<RBNode<K, V>*>(IntrusiveRBNode::next());
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline AbstractRBTree<K, NodeType, COMPARATOR>::Cursor::Cursor()
|
||||
: _insert_location(nullptr), _parent(nullptr) {}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline AbstractRBTree<K, NodeType, COMPARATOR>::Cursor::Cursor(NodeType** insert_location, NodeType* parent)
|
||||
: _insert_location(insert_location), _parent(parent) {}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline AbstractRBTree<K, NodeType, COMPARATOR>::Cursor::Cursor(NodeType* const* insert_location, NodeType* parent)
|
||||
: _insert_location(const_cast<NodeType**>(insert_location)), _parent(parent) {}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline bool AbstractRBTree<K, NodeType, COMPARATOR>::Cursor::valid() const {
|
||||
return _insert_location != nullptr;
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline bool AbstractRBTree<K, NodeType, COMPARATOR>::Cursor::found() const {
|
||||
return *_insert_location != nullptr;
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::Cursor::node() {
|
||||
return _insert_location == nullptr ? nullptr : *_insert_location;
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::Cursor::node() const {
|
||||
return _insert_location == nullptr ? nullptr : *_insert_location;
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline RBTreeOrdering AbstractRBTree<K, NodeType, COMPARATOR>::cmp(const K& a, const NodeType* b) const {
|
||||
if constexpr (HasNodeComparator) {
|
||||
return COMPARATOR::cmp(a, b);
|
||||
} else if constexpr (HasKeyComparator) {
|
||||
return COMPARATOR::cmp(a, b->key());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline bool AbstractRBTree<K, NodeType, COMPARATOR>::less_than(const NodeType* a, const NodeType* b) const {
|
||||
if constexpr (HasNodeVerifier) {
|
||||
return COMPARATOR::less_than(a, b);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline void AbstractRBTree<K, NodeType, COMPARATOR>::assert_key_leq(K a, K b) const {
|
||||
if constexpr (HasKeyComparator) { // Cannot assert if no key comparator exist.
|
||||
assert(COMPARATOR::cmp(a, b) != RBTreeOrdering::GT, "key a must be less or equal to key b");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline bool AbstractRBTree<K, NodeType, COMPARATOR>::is_black(const IntrusiveRBNode* node) {
|
||||
return node == nullptr || node->is_black();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline bool AbstractRBTree<K, NodeType, COMPARATOR>::is_red(const IntrusiveRBNode* node) {
|
||||
return node != nullptr && node->is_red();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline bool AbstractRBTree<K, NodeType, COMPARATOR>::empty_verifier::operator()(const NodeType* n) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline void AbstractRBTree<K, NodeType, COMPARATOR>::default_printer::operator()(outputStream* st, const NodeType* n, int depth) const {
|
||||
n->print_on(st, depth);
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline AbstractRBTree<K, NodeType, COMPARATOR>::AbstractRBTree()
|
||||
: _num_nodes(0), _root(nullptr) DEBUG_ONLY(COMMA _expected_visited(false)) {
|
||||
static_assert(std::is_trivially_destructible<K>::value, "key type must be trivially destructable");
|
||||
static_assert(HasKeyComparator || HasNodeComparator, "comparator must be of correct type");
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline size_t AbstractRBTree<K, NodeType, COMPARATOR>::size() const {
|
||||
return _num_nodes;
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline const typename AbstractRBTree<K, NodeType, COMPARATOR>::Cursor
|
||||
AbstractRBTree<K, NodeType, COMPARATOR>::cursor(const K& key, const NodeType* hint_node) const {
|
||||
@ -596,6 +771,104 @@ AbstractRBTree<K, NodeType, COMPARATOR>::prev(const Cursor& node_cursor) {
|
||||
return static_cast<const AbstractRBTree<K, NodeType, COMPARATOR>*>(this)->prev(node_cursor);
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::find_node(const K& key, const NodeType* hint_node) const {
|
||||
Cursor node_cursor = cursor(key, hint_node);
|
||||
return node_cursor.node();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::find_node(const K& key, const NodeType* hint_node) {
|
||||
Cursor node_cursor = cursor(key, hint_node);
|
||||
return node_cursor.node();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline void AbstractRBTree<K, NodeType, COMPARATOR>::insert(const K& key, NodeType* node, const NodeType* hint_node) {
|
||||
Cursor node_cursor = cursor(key, hint_node);
|
||||
insert_at_cursor(node, node_cursor);
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline void AbstractRBTree<K, NodeType, COMPARATOR>::remove(NodeType* node) {
|
||||
Cursor node_cursor = cursor(node);
|
||||
remove_at_cursor(node_cursor);
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::closest_leq(const K& key) const {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? node_cursor.node() : prev(node_cursor).node();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::closest_leq(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? node_cursor.node() : prev(node_cursor).node();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::closest_gt(const K& key) const {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return next(node_cursor).node();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::closest_gt(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return next(node_cursor).node();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::closest_ge(const K& key) const {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? node_cursor.node() : next(node_cursor).node();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::closest_ge(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? node_cursor.node() : next(node_cursor).node();
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline const NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::leftmost() const {
|
||||
IntrusiveRBNode* n = _root, *n2 = nullptr;
|
||||
while (n != nullptr) {
|
||||
n2 = n;
|
||||
n = n->_left;
|
||||
}
|
||||
return static_cast<const NodeType*>(n2);
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline const NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::rightmost() const {
|
||||
IntrusiveRBNode* n = _root, *n2 = nullptr;
|
||||
while (n != nullptr) {
|
||||
n2 = n;
|
||||
n = n->_right;
|
||||
}
|
||||
return static_cast<const NodeType*>(n2);
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::leftmost() {
|
||||
return const_cast<NodeType*>(static_cast<const AbstractRBTree<K, NodeType, COMPARATOR>*>(this)->leftmost());
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline NodeType* AbstractRBTree<K, NodeType, COMPARATOR>::rightmost() {
|
||||
return const_cast<NodeType*>(static_cast<const AbstractRBTree<K, NodeType, COMPARATOR>*>(this)->rightmost());
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
inline typename AbstractRBTree<K, NodeType, COMPARATOR>::Range
|
||||
AbstractRBTree<K, NodeType, COMPARATOR>::find_enclosing_range(K key) const {
|
||||
NodeType* start = closest_leq(key);
|
||||
NodeType* end = closest_gt(key);
|
||||
return Range(start, end);
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
template <typename F>
|
||||
inline void AbstractRBTree<K, NodeType, COMPARATOR>::visit_in_order(F f) const {
|
||||
@ -662,6 +935,18 @@ inline void AbstractRBTree<K, NodeType, COMPARATOR>::visit_range_in_order(const
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
template <typename USER_VERIFIER>
|
||||
inline void AbstractRBTree<K, NodeType, COMPARATOR>::verify_self(const USER_VERIFIER& extra_verifier) const {
|
||||
if constexpr (HasNodeVerifier) {
|
||||
verify_self([](const NodeType* a, const NodeType* b){ return COMPARATOR::less_than(a, b);}, extra_verifier);
|
||||
} else if constexpr (HasKeyComparator) {
|
||||
verify_self([](const NodeType* a, const NodeType* b){ return COMPARATOR::cmp(a->key(), b->key()) == RBTreeOrdering::LT; }, extra_verifier);
|
||||
} else {
|
||||
verify_self([](const NodeType*, const NodeType*){ return true;}, extra_verifier);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename NodeType, typename COMPARATOR>
|
||||
template <typename NODE_VERIFIER, typename USER_VERIFIER>
|
||||
inline void AbstractRBTree<K, NodeType, COMPARATOR>::verify_self(NODE_VERIFIER verifier, const USER_VERIFIER& extra_verifier) const {
|
||||
@ -753,6 +1038,15 @@ void AbstractRBTree<K, NodeType, COMPARATOR>::print_on(outputStream* st, const P
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
template<typename... AllocArgs>
|
||||
inline RBTree<K, V, COMPARATOR, ALLOCATOR>::RBTree(AllocArgs... alloc_args) : BaseType(), _allocator(alloc_args...) {}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline RBTree<K, V, COMPARATOR, ALLOCATOR>::~RBTree() {
|
||||
remove_all();
|
||||
}
|
||||
|
||||
template<typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
bool RBTree<K, V, COMPARATOR, ALLOCATOR>::copy_into(RBTree& other) const {
|
||||
assert(other.size() == 0, "You can only copy into an empty RBTree");
|
||||
@ -802,4 +1096,132 @@ bool RBTree<K, V, COMPARATOR, ALLOCATOR>::copy_into(RBTree& other) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::replace_at_cursor(RBNode<K, V>* new_node, const Cursor& node_cursor) {
|
||||
RBNode<K, V>* old_node = node_cursor.node();
|
||||
BaseType::replace_at_cursor(new_node, node_cursor);
|
||||
free_node(old_node);
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline RBNode<K, V>* RBTree<K, V, COMPARATOR, ALLOCATOR>::allocate_node(const K& key) {
|
||||
void* node_place = _allocator.allocate(sizeof(RBNode<K, V>));
|
||||
if (node_place == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new (node_place) RBNode<K, V>(key);
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline RBNode<K, V>* RBTree<K, V, COMPARATOR, ALLOCATOR>::allocate_node(const K& key, const V& val) {
|
||||
void* node_place = _allocator.allocate(sizeof(RBNode<K, V>));
|
||||
if (node_place == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new (node_place) RBNode<K, V>(key, val);
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::free_node(RBNode<K, V>* node) {
|
||||
node->_value.~V();
|
||||
_allocator.free(node);
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline bool RBTree<K, V, COMPARATOR, ALLOCATOR>::upsert(const K& key, const V& val, const RBNode<K, V>* hint_node) {
|
||||
Cursor node_cursor = cursor(key, hint_node);
|
||||
RBNode<K, V>* node = node_cursor.node();
|
||||
if (node != nullptr) {
|
||||
node->set_val(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
node = allocate_node(key, val);
|
||||
if (node == nullptr) {
|
||||
return false;
|
||||
}
|
||||
insert_at_cursor(node, node_cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline V* RBTree<K, V, COMPARATOR, ALLOCATOR>::find(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? &node_cursor.node()->_value : nullptr;
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline V* RBTree<K, V, COMPARATOR, ALLOCATOR>::find(const K& key) const {
|
||||
const Cursor node_cursor = cursor(key);
|
||||
return node_cursor.found() ? &node_cursor.node()->_value : nullptr;
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::remove(RBNode<K, V>* node) {
|
||||
Cursor node_cursor = cursor(node);
|
||||
remove_at_cursor(node_cursor);
|
||||
free_node(node);
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline bool RBTree<K, V, COMPARATOR, ALLOCATOR>::remove(const K& key) {
|
||||
Cursor node_cursor = cursor(key);
|
||||
if (!node_cursor.found()) {
|
||||
return false;
|
||||
}
|
||||
RBNode<K, V>* node = node_cursor.node();
|
||||
remove_at_cursor(node_cursor);
|
||||
free_node((RBNode<K, V>*)node);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename COMPARATOR, typename ALLOCATOR>
|
||||
inline void RBTree<K, V, COMPARATOR, ALLOCATOR>::remove_all() {
|
||||
IntrusiveRBNode* to_delete[64];
|
||||
int stack_idx = 0;
|
||||
to_delete[stack_idx++] = BaseType::_root;
|
||||
|
||||
while (stack_idx > 0) {
|
||||
IntrusiveRBNode* head = to_delete[--stack_idx];
|
||||
if (head == nullptr) continue;
|
||||
to_delete[stack_idx++] = head->_left;
|
||||
to_delete[stack_idx++] = head->_right;
|
||||
free_node((RBNode<K, V>*)head);
|
||||
}
|
||||
BaseType::_num_nodes = 0;
|
||||
BaseType::_root = nullptr;
|
||||
}
|
||||
|
||||
template <MemTag mem_tag, AllocFailType strategy>
|
||||
inline void* RBTreeCHeapAllocator<mem_tag, strategy>::allocate(size_t sz) {
|
||||
return AllocateHeap(sz, mem_tag, strategy);
|
||||
}
|
||||
|
||||
template <MemTag mem_tag, AllocFailType strategy>
|
||||
inline void RBTreeCHeapAllocator<mem_tag, strategy>::free(void* ptr) {
|
||||
FreeHeap(ptr);
|
||||
}
|
||||
|
||||
template <AllocFailType strategy>
|
||||
inline RBTreeArenaAllocator<strategy>::RBTreeArenaAllocator(Arena* arena) : _arena(arena) {}
|
||||
|
||||
template <AllocFailType strategy>
|
||||
inline void* RBTreeArenaAllocator<strategy>::allocate(size_t sz) {
|
||||
return _arena->Amalloc(sz, strategy);
|
||||
}
|
||||
|
||||
template <AllocFailType strategy>
|
||||
inline void RBTreeArenaAllocator<strategy>::free(void* ptr) { /* NOP */ }
|
||||
|
||||
template <AllocFailType strategy>
|
||||
inline RBTreeResourceAreaAllocator<strategy>::RBTreeResourceAreaAllocator(ResourceArea* rarea) : _rarea(rarea) {}
|
||||
|
||||
template <AllocFailType strategy>
|
||||
inline void* RBTreeResourceAreaAllocator<strategy>::allocate(size_t sz) {
|
||||
return _rarea->Amalloc(sz, strategy);
|
||||
}
|
||||
|
||||
template <AllocFailType strategy>
|
||||
inline void RBTreeResourceAreaAllocator<strategy>::free(void* ptr) { /* NOP */ }
|
||||
|
||||
#endif // SHARE_UTILITIES_RBTREE_INLINE_HPP
|
||||
|
||||
@ -3561,4 +3561,50 @@ final class FdLibm {
|
||||
return hx > 0 ? w : -w;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Inverse Hyperbolic Cosine of x
|
||||
*
|
||||
* Method :
|
||||
*
|
||||
*
|
||||
* acosh(x) is defined so that acosh(cosh(alpha)) = alpha, -∞ < alpha < ∞
|
||||
* and cosh(acosh(x)) = x, 1 <= x < ∞.
|
||||
* It can be written as acosh(x) = log(x + sqrt(x^2 - 1)), 1 <= x < ∞.
|
||||
* acosh(x) := log(x)+ln2, if x is large; else
|
||||
* := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else
|
||||
* := log1p(t+sqrt(2.0*t+t*t)); where t=x-1.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Special cases:
|
||||
* acosh(x) is NaN with signal if x < 1.
|
||||
* acosh(NaN) is NaN without signal.
|
||||
*/
|
||||
static final class Acosh {
|
||||
private static final double ln2 = 6.93147180559945286227e-01;
|
||||
|
||||
static double compute(double x) {
|
||||
double t;
|
||||
int hx;
|
||||
hx = __HI(x);
|
||||
if (hx < 0x3ff0_0000) { // x < 1 */
|
||||
return (x - x) / (x - x);
|
||||
} else if (hx >= 0x41b0_0000) { // x > 2**28
|
||||
if (hx >= 0x7ff0_0000) { // x is inf of NaN
|
||||
return x + x;
|
||||
} else {
|
||||
return Log.compute(x) + ln2; // acosh(huge) = log(2x)
|
||||
}
|
||||
} else if (((hx - 0x3ff0_0000) | __LO(x)) == 0) {
|
||||
return 0.0; // acosh(1) = 0
|
||||
} else if (hx > 0x4000_0000) { // 2**28 > x > 2
|
||||
t = x * x;
|
||||
return Log.compute(2.0 * x - 1.0 / (x + Sqrt.compute(t - 1.0)));
|
||||
} else { // 1< x <2
|
||||
t = x - 1.0;
|
||||
return Log1p.compute(t + Sqrt.compute(2.0 * t + t * t));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,10 +109,10 @@ import static java.lang.Double.*;
|
||||
* acos acos}, {@link atan atan}, {@link exp exp}, {@link expm1
|
||||
* expm1}, {@link log log}, {@link log10 log10}, {@link log1p log1p},
|
||||
* {@link sinh sinh}, {@link cosh cosh}, {@link tanh tanh}, {@link asinh asinh},
|
||||
* {@link hypot hypot}, and {@link pow pow}. (The {@link sqrt sqrt}
|
||||
* operation is a required part of IEEE 754 from a different section
|
||||
* of the standard.) The special case behavior of the recommended
|
||||
* operations generally follows the guidance of the IEEE 754
|
||||
* {@link acosh acosh}, {@link hypot hypot}, and {@link pow pow}.
|
||||
* (The {@link sqrt sqrt} operation is a required part of IEEE 754
|
||||
* from a different section of the standard.) The special case behavior
|
||||
* of the recommended operations generally follows the guidance of the IEEE 754
|
||||
* standard. However, the {@code pow} method defines different
|
||||
* behavior for some arguments, as noted in its {@linkplain pow
|
||||
* specification}. The IEEE 754 standard defines its operations to be
|
||||
@ -2785,6 +2785,35 @@ public final class Math {
|
||||
return StrictMath.asinh(x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the inverse hyperbolic cosine of a {@code double} value.
|
||||
* The inverse hyperbolic cosine of <i>x</i> is defined to be the function such that
|
||||
* acosh({@linkplain Math#cosh cosh(<i>x</i>)}) = <i>x</i> for any <i>x</i> >= 0.
|
||||
* Note that range of the exact acosh(x) is >= 0.
|
||||
* <p>Special cases:
|
||||
* <ul>
|
||||
*
|
||||
* <li>If the argument is positive infinity, then the result is
|
||||
* positive infinity
|
||||
*
|
||||
* <li>If the argument less than 1, then the result is NaN.
|
||||
*
|
||||
* <li>If the argument is NaN, then the result is NaN.
|
||||
*
|
||||
* <li>If the argument is {@code 1.0}, then the result is positive zero.
|
||||
*
|
||||
* </ul>
|
||||
* <p>The computed result must be within 2.5 ulps of the exact result.
|
||||
* @param x The number whose inverse hyperbolic cosine is to be returned.
|
||||
* @return The inverse hyperbolic cosine of {@code x}.
|
||||
* @since 27
|
||||
*/
|
||||
public static double acosh(double x) {
|
||||
return StrictMath.acosh(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sqrt(<i>x</i><sup>2</sup> +<i>y</i><sup>2</sup>)
|
||||
* without intermediate overflow or underflow.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2026, 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
|
||||
@ -930,6 +930,12 @@ public final class ProcessBuilder
|
||||
* command interpreters, or the standard C library function
|
||||
* {@code system()}.
|
||||
*
|
||||
* @implNote
|
||||
* When the process is {@link #start started},
|
||||
* if {#code System.out} and/or {#code System.err} have been
|
||||
* closed in the current process, the corresponding output
|
||||
* in the subprocess will be discarded.
|
||||
*
|
||||
* @return this process builder
|
||||
* @since 1.7
|
||||
*/
|
||||
|
||||
@ -76,8 +76,8 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
* {@code exp}, {@code log}, {@code log10},
|
||||
* {@code cbrt}, {@code atan2}, {@code pow},
|
||||
* {@code sinh}, {@code cosh}, {@code tanh},
|
||||
* {@code asinh}, {@code hypot}, {@code expm1},
|
||||
* and {@code log1p}.
|
||||
* {@code asinh}, {@code acosh}, {@code hypot},
|
||||
* {@code expm1}, and {@code log1p}.
|
||||
*
|
||||
* <p>
|
||||
* The platform uses signed two's complement integer arithmetic with
|
||||
@ -2196,6 +2196,32 @@ public final class StrictMath {
|
||||
return FdLibm.Asinh.compute(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inverse hyperbolic cosine of a {@code double} value.
|
||||
* The inverse hyperbolic cosine of <i>x</i> is defined to be the function such that
|
||||
* acosh({@linkplain Math#cosh cosh(<i>x</i>)}) = <i>x</i> for any <i>x</i> >= 0.
|
||||
* Note that range of the exact acosh(x) is >= 0.
|
||||
* <p>Special cases:
|
||||
* <ul>
|
||||
*
|
||||
* <li>If the argument is positive infinity, then the result is
|
||||
* positive infinity
|
||||
*
|
||||
* <li>If the argument less than {@code 1.0}, then the result is NaN.
|
||||
*
|
||||
* <li>If the argument is NaN, then the result is NaN.
|
||||
*
|
||||
* <li>If the argument is {@code 1.0}, then the result is positive zero.
|
||||
*
|
||||
* </ul>
|
||||
* @param x The number whose inverse hyperbolic cosine is to be returned.
|
||||
* @return The inverse hyperbolic cosine of {@code x}.
|
||||
* @since 27
|
||||
*/
|
||||
public static double acosh(double x) {
|
||||
return FdLibm.Acosh.compute(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sqrt(<i>x</i><sup>2</sup> +<i>y</i><sup>2</sup>)
|
||||
* without intermediate overflow or underflow.
|
||||
|
||||
@ -195,10 +195,9 @@ public class BasicImageReader implements AutoCloseable {
|
||||
}
|
||||
|
||||
if (result.getMajorVersion() != ImageHeader.MAJOR_VERSION ||
|
||||
result.getMinorVersion() != ImageHeader.MINOR_VERSION) {
|
||||
throw new IOException("The image file \"" + name + "\" is not " +
|
||||
"the correct version. Major: " + result.getMajorVersion() +
|
||||
". Minor: " + result.getMinorVersion());
|
||||
result.getMinorVersion() != ImageHeader.MINOR_VERSION) {
|
||||
throw new ImageVersionMismatchException(
|
||||
name, result.getMajorVersion(), result.getMinorVersion());
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -447,4 +446,14 @@ public class BasicImageReader implements AutoCloseable {
|
||||
|
||||
return new ByteArrayInputStream(bytes);
|
||||
}
|
||||
|
||||
public static final class ImageVersionMismatchException extends IOException {
|
||||
@Deprecated
|
||||
private static final long serialVersionUID = 1L;
|
||||
// If needed we could capture major/minor version for use by JImageTask.
|
||||
ImageVersionMismatchException(String name, int majorVersion, int minorVersion) {
|
||||
super("The image file \"" + name + "\" is not the correct version. " +
|
||||
"Major: " + majorVersion + ". Minor: " + minorVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2026, 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
|
||||
@ -88,4 +88,18 @@ final class WeakReferenceKey<T> extends WeakReference<T> implements ReferenceKey
|
||||
public String toString() {
|
||||
return this.getClass().getCanonicalName() + "#" + System.identityHashCode(this);
|
||||
}
|
||||
|
||||
// WeakReferenceKey.equals() is usually executed in the AOT assembly phase. However,
|
||||
// in some rare occasions, it's not executed (due to peculiarity of hash code and
|
||||
// memory addressing??). As a result, the constant pool entries used by
|
||||
// equals() are not resolved.
|
||||
//
|
||||
// The JVM calls ensureDeterministicAOTCache() during the AOT assembly phase to ensure
|
||||
// that the constant pool entries used by equals() are resolved, so that the
|
||||
// the JDK's default CDS archives have deterministic contents.
|
||||
private static boolean ensureDeterministicAOTCache() {
|
||||
WeakReferenceKey<String> k1 = new WeakReferenceKey<>("1", null);
|
||||
WeakReferenceKey<String> k2 = new WeakReferenceKey<>("2", null);
|
||||
return k1.equals(k2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2026, 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
|
||||
@ -119,6 +119,12 @@ final class ProcessImpl extends Process {
|
||||
stdHandles[1] = -1L;
|
||||
} else if (redirects[1] == Redirect.INHERIT) {
|
||||
stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
|
||||
if (stdHandles[1] == -1L) {
|
||||
// FileDescriptor.out has been closed.
|
||||
f1 = newFileOutputStream(Redirect.DISCARD.file(),
|
||||
Redirect.DISCARD.append());
|
||||
stdHandles[1] = fdAccess.getHandle(f1.getFD());
|
||||
}
|
||||
} else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
|
||||
stdHandles[1] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd());
|
||||
// Force getInputStream to return a null stream,
|
||||
@ -134,6 +140,12 @@ final class ProcessImpl extends Process {
|
||||
stdHandles[2] = -1L;
|
||||
} else if (redirects[2] == Redirect.INHERIT) {
|
||||
stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
|
||||
if (stdHandles[2] == -1L) {
|
||||
// FileDescriptor.err has been closed.
|
||||
f2 = newFileOutputStream(Redirect.DISCARD.file(),
|
||||
Redirect.DISCARD.append());
|
||||
stdHandles[2] = fdAccess.getHandle(f2.getFD());
|
||||
}
|
||||
} else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
|
||||
stdHandles[2] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd());
|
||||
} else {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2026, 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
|
||||
@ -64,7 +64,6 @@ import javax.swing.JComponent;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.AWTAccessor.ComponentAccessor;
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.CGraphicsDevice;
|
||||
import sun.awt.DisplayChangedListener;
|
||||
import sun.awt.ExtendedKeyCodes;
|
||||
@ -1236,9 +1235,7 @@ public class LWWindowPeer
|
||||
return false;
|
||||
}
|
||||
|
||||
AppContext targetAppContext = AWTAccessor.getComponentAccessor().getAppContext(getTarget());
|
||||
KeyboardFocusManager kfm = AWTAccessor.getKeyboardFocusManagerAccessor()
|
||||
.getCurrentKeyboardFocusManager(targetAppContext);
|
||||
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||
Window currentActive = kfm.getActiveWindow();
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2026, 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
|
||||
@ -1030,13 +1030,19 @@ final class CAccessibility implements PropertyChangeListener {
|
||||
}
|
||||
|
||||
if (!allowIgnored) {
|
||||
final AccessibleRole role = context.getAccessibleRole();
|
||||
if (role != null && ignoredRoles != null && ignoredRoles.contains(roleKey(role))) {
|
||||
// Get the child's unignored children.
|
||||
_addChildren(child, whichChildren, false, childrenAndRoles, ChildrenOperations.COMMON);
|
||||
} else {
|
||||
childrenAndRoles.add(child);
|
||||
childrenAndRoles.add(getAccessibleRole(child));
|
||||
// If a Component isn't showing then it should be classified as
|
||||
// "ignored", and we should skip it and its descendants
|
||||
if (isShowing(context)) {
|
||||
final AccessibleRole role = context.getAccessibleRole();
|
||||
if (role != null && ignoredRoles != null &&
|
||||
ignoredRoles.contains(roleKey(role))) {
|
||||
// Get the child's unignored children.
|
||||
_addChildren(child, whichChildren, false,
|
||||
childrenAndRoles, ChildrenOperations.COMMON);
|
||||
} else {
|
||||
childrenAndRoles.add(child);
|
||||
childrenAndRoles.add(getAccessibleRole(child));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
childrenAndRoles.add(child);
|
||||
@ -1050,6 +1056,46 @@ final class CAccessibility implements PropertyChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false if an AccessibleContext is not showing
|
||||
* <p>
|
||||
* This first checks {@link AccessibleComponent#isShowing()}, if possible.
|
||||
* If there is no AccessibleComponent then this checks the
|
||||
* AccessibleStateSet for {@link AccessibleState#SHOWING}. If there is no
|
||||
* AccessibleStateSet then we assume (given the lack of information) the
|
||||
* AccessibleContext may be visible, and we recursive check its parent if
|
||||
* possible.
|
||||
*
|
||||
* Return false if an AccessibleContext is not showing
|
||||
*/
|
||||
private static boolean isShowing(final AccessibleContext context) {
|
||||
AccessibleComponent c = context.getAccessibleComponent();
|
||||
if (c != null) {
|
||||
return c.isShowing();
|
||||
}
|
||||
|
||||
AccessibleStateSet ass = context.getAccessibleStateSet();
|
||||
if (ass != null && ass.contains((AccessibleState.SHOWING))) {
|
||||
return true;
|
||||
} else {
|
||||
// We don't have an AccessibleComponent. And either we don't
|
||||
// have an AccessibleStateSet OR it doesn't include useful
|
||||
// info to determine visibility/showing. So our status is
|
||||
// unknown. When in doubt: assume we're showing and ask our
|
||||
// parent if it is visible/showing.
|
||||
}
|
||||
|
||||
Accessible parent = context.getAccessibleParent();
|
||||
if (parent == null) {
|
||||
return true;
|
||||
}
|
||||
AccessibleContext parentContext = parent.getAccessibleContext();
|
||||
if (parentContext == null) {
|
||||
return true;
|
||||
}
|
||||
return isShowing(parentContext);
|
||||
}
|
||||
|
||||
private static native String roleKey(AccessibleRole aRole);
|
||||
|
||||
public static Object[] getChildren(final Accessible a, final Component c) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2026, 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
|
||||
@ -7938,7 +7938,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
|
||||
(this, temporary, focusedWindowChangeAllowed, time, cause);
|
||||
if (!success) {
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager
|
||||
(appContext).dequeueKeyEvents(time, this);
|
||||
().dequeueKeyEvents(time, this);
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) {
|
||||
focusLog.finest("Peer request failed");
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -38,7 +38,6 @@ import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.awt.TimedWindowEvent;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
@ -231,9 +230,8 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -2924743257508701758L;
|
||||
|
||||
public DefaultKeyboardFocusManagerSentEvent(AWTEvent nested,
|
||||
AppContext toNotify) {
|
||||
super(nested, toNotify);
|
||||
public DefaultKeyboardFocusManagerSentEvent(AWTEvent nested) {
|
||||
super(nested);
|
||||
}
|
||||
public final void dispatch() {
|
||||
KeyboardFocusManager manager =
|
||||
@ -260,76 +258,12 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a synthetic AWTEvent to a Component. If the Component is in
|
||||
* the current AppContext, then the event is immediately dispatched.
|
||||
* If the Component is in a different AppContext, then the event is
|
||||
* posted to the other AppContext's EventQueue, and this method blocks
|
||||
* until the event is handled or target AppContext is disposed.
|
||||
* Returns true if successfully dispatched event, false if failed
|
||||
* to dispatch.
|
||||
* Sends a synthetic AWTEvent to a Component.
|
||||
*/
|
||||
static boolean sendMessage(Component target, AWTEvent e) {
|
||||
e.isPosted = true;
|
||||
AppContext myAppContext = AppContext.getAppContext();
|
||||
final AppContext targetAppContext = target.appContext;
|
||||
final SentEvent se =
|
||||
new DefaultKeyboardFocusManagerSentEvent(e, myAppContext);
|
||||
|
||||
if (myAppContext == targetAppContext) {
|
||||
se.dispatch();
|
||||
} else {
|
||||
if (targetAppContext.isDisposed()) {
|
||||
return false;
|
||||
}
|
||||
SunToolkit.postEvent(targetAppContext, se);
|
||||
if (EventQueue.isDispatchThread()) {
|
||||
if (Thread.currentThread() instanceof EventDispatchThread) {
|
||||
EventDispatchThread edt = (EventDispatchThread)
|
||||
Thread.currentThread();
|
||||
edt.pumpEvents(SentEvent.ID, new Conditional() {
|
||||
public boolean evaluate() {
|
||||
return !se.dispatched && !targetAppContext.isDisposed();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (fxAppThreadIsDispatchThread) {
|
||||
Thread fxCheckDispatchThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!se.dispatched && !targetAppContext.isDisposed()) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
fxCheckDispatchThread.start();
|
||||
try {
|
||||
// check if event is dispatched or disposed
|
||||
// but since this user app thread is same as
|
||||
// dispatch thread in fx when run with
|
||||
// javafx.embed.singleThread=true
|
||||
// we do not wait infinitely to avoid deadlock
|
||||
// as dispatch will ultimately be done by this thread
|
||||
fxCheckDispatchThread.join(500);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
synchronized (se) {
|
||||
while (!se.dispatched && !targetAppContext.isDisposed()) {
|
||||
try {
|
||||
se.wait(1000);
|
||||
} catch (InterruptedException ie) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final SentEvent se = new DefaultKeyboardFocusManagerSentEvent(e);
|
||||
se.dispatch();
|
||||
return se.dispatched;
|
||||
}
|
||||
|
||||
@ -356,7 +290,7 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
|
||||
// Check that the component awaiting focus belongs to
|
||||
// the current focused window. See 8015454.
|
||||
if (toplevel != null && toplevel.isFocused()) {
|
||||
SunToolkit.postEvent(AppContext.getAppContext(), new SequencedEvent(e));
|
||||
SunToolkit.postEvent(new SequencedEvent(e));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2026, 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,7 +50,6 @@ import java.util.WeakHashMap;
|
||||
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.awt.KeyboardFocusManagerPeerProvider;
|
||||
import sun.awt.AWTAccessor;
|
||||
@ -127,9 +126,6 @@ public abstract class KeyboardFocusManager
|
||||
public void setMostRecentFocusOwner(Window window, Component component) {
|
||||
KeyboardFocusManager.setMostRecentFocusOwner(window, component);
|
||||
}
|
||||
public KeyboardFocusManager getCurrentKeyboardFocusManager(AppContext ctx) {
|
||||
return KeyboardFocusManager.getCurrentKeyboardFocusManager(ctx);
|
||||
}
|
||||
public Container getCurrentFocusCycleRoot() {
|
||||
return KeyboardFocusManager.currentFocusCycleRoot;
|
||||
}
|
||||
@ -183,53 +179,40 @@ public abstract class KeyboardFocusManager
|
||||
|
||||
static final int TRAVERSAL_KEY_LENGTH = DOWN_CYCLE_TRAVERSAL_KEYS + 1;
|
||||
|
||||
private static KeyboardFocusManager manager;
|
||||
|
||||
/**
|
||||
* Returns the current KeyboardFocusManager instance for the calling
|
||||
* thread's context.
|
||||
* Returns the current KeyboardFocusManager instance
|
||||
*
|
||||
* @return this thread's context's KeyboardFocusManager
|
||||
* @return the current KeyboardFocusManager
|
||||
* @see #setCurrentKeyboardFocusManager
|
||||
*/
|
||||
public static KeyboardFocusManager getCurrentKeyboardFocusManager() {
|
||||
return getCurrentKeyboardFocusManager(AppContext.getAppContext());
|
||||
}
|
||||
|
||||
static synchronized KeyboardFocusManager
|
||||
getCurrentKeyboardFocusManager(AppContext appcontext)
|
||||
{
|
||||
KeyboardFocusManager manager = (KeyboardFocusManager)
|
||||
appcontext.get(KeyboardFocusManager.class);
|
||||
public static synchronized KeyboardFocusManager getCurrentKeyboardFocusManager() {
|
||||
if (manager == null) {
|
||||
manager = new DefaultKeyboardFocusManager();
|
||||
appcontext.put(KeyboardFocusManager.class, manager);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current KeyboardFocusManager instance for the calling thread's
|
||||
* context. If null is specified, then the current KeyboardFocusManager
|
||||
* Sets the current KeyboardFocusManager instance.
|
||||
* If null is specified, then the current KeyboardFocusManager
|
||||
* is replaced with a new instance of DefaultKeyboardFocusManager.
|
||||
*
|
||||
* @param newManager the new KeyboardFocusManager for this thread's context
|
||||
* @param newManager the new KeyboardFocusManager
|
||||
* @see #getCurrentKeyboardFocusManager
|
||||
* @see DefaultKeyboardFocusManager
|
||||
*/
|
||||
public static void setCurrentKeyboardFocusManager(KeyboardFocusManager newManager) {
|
||||
|
||||
KeyboardFocusManager oldManager = null;
|
||||
KeyboardFocusManager oldManager = manager;
|
||||
|
||||
if (newManager == null) {
|
||||
newManager = new DefaultKeyboardFocusManager();
|
||||
}
|
||||
|
||||
synchronized (KeyboardFocusManager.class) {
|
||||
AppContext appcontext = AppContext.getAppContext();
|
||||
|
||||
if (newManager != null) {
|
||||
oldManager = getCurrentKeyboardFocusManager(appcontext);
|
||||
|
||||
appcontext.put(KeyboardFocusManager.class, newManager);
|
||||
} else {
|
||||
oldManager = getCurrentKeyboardFocusManager(appcontext);
|
||||
appcontext.remove(KeyboardFocusManager.class);
|
||||
}
|
||||
manager = newManager;
|
||||
}
|
||||
|
||||
if (oldManager != null) {
|
||||
@ -344,7 +327,7 @@ public abstract class KeyboardFocusManager
|
||||
private static java.util.Map<Window, WeakReference<Component>> mostRecentFocusOwners = new WeakHashMap<>();
|
||||
|
||||
/*
|
||||
* SequencedEvent which is currently dispatched in AppContext.
|
||||
* SequencedEvent which is currently dispatched.
|
||||
*/
|
||||
transient SequencedEvent currentSequencedEvent = null;
|
||||
|
||||
@ -431,13 +414,7 @@ public abstract class KeyboardFocusManager
|
||||
*/
|
||||
public Component getFocusOwner() {
|
||||
synchronized (KeyboardFocusManager.class) {
|
||||
if (focusOwner == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (focusOwner.appContext == AppContext.getAppContext())
|
||||
? focusOwner
|
||||
: null;
|
||||
return focusOwner;
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,42 +576,32 @@ public abstract class KeyboardFocusManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permanent focus owner, if the permanent focus owner is in
|
||||
* the same context as the calling thread. The permanent focus owner is
|
||||
* Returns the permanent focus owner. The permanent focus owner is
|
||||
* defined as the last Component in an application to receive a permanent
|
||||
* FOCUS_GAINED event. The focus owner and permanent focus owner are
|
||||
* equivalent unless a temporary focus change is currently in effect. In
|
||||
* such a situation, the permanent focus owner will again be the focus
|
||||
* owner when the temporary focus change ends.
|
||||
*
|
||||
* @return the permanent focus owner, or null if the permanent focus owner
|
||||
* is not a member of the calling thread's context
|
||||
* @return the permanent focus owner, or null if there is none
|
||||
* @see #getGlobalPermanentFocusOwner
|
||||
* @see #setGlobalPermanentFocusOwner
|
||||
*/
|
||||
public Component getPermanentFocusOwner() {
|
||||
synchronized (KeyboardFocusManager.class) {
|
||||
if (permanentFocusOwner == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (permanentFocusOwner.appContext ==
|
||||
AppContext.getAppContext())
|
||||
? permanentFocusOwner
|
||||
: null;
|
||||
return permanentFocusOwner;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permanent focus owner, even if the calling thread is in a
|
||||
* different context than the permanent focus owner. The permanent focus
|
||||
* Returns the permanent focus owner. The permanent focus
|
||||
* owner is defined as the last Component in an application to receive a
|
||||
* permanent FOCUS_GAINED event. The focus owner and permanent focus owner
|
||||
* are equivalent unless a temporary focus change is currently in effect.
|
||||
* In such a situation, the permanent focus owner will again be the focus
|
||||
* owner when the temporary focus change ends.
|
||||
*
|
||||
* @return the permanent focus owner
|
||||
* @return the permanent focus owner, or null if there is none
|
||||
* @see #getPermanentFocusOwner
|
||||
* @see #setGlobalPermanentFocusOwner
|
||||
*/
|
||||
@ -701,24 +668,16 @@ public abstract class KeyboardFocusManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the focused Window, if the focused Window is in the same context
|
||||
* as the calling thread. The focused Window is the Window that is or
|
||||
* contains the focus owner.
|
||||
* Returns the focused Window.
|
||||
* The focused Window is the Window that is or contains the focus owner.
|
||||
*
|
||||
* @return the focused Window, or null if the focused Window is not a
|
||||
* member of the calling thread's context
|
||||
* @return the focused Window, or null if there is none
|
||||
* @see #getGlobalFocusedWindow
|
||||
* @see #setGlobalFocusedWindow
|
||||
*/
|
||||
public Window getFocusedWindow() {
|
||||
synchronized (KeyboardFocusManager.class) {
|
||||
if (focusedWindow == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (focusedWindow.appContext == AppContext.getAppContext())
|
||||
? focusedWindow
|
||||
: null;
|
||||
return focusedWindow;
|
||||
}
|
||||
}
|
||||
|
||||
@ -785,27 +744,19 @@ public abstract class KeyboardFocusManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active Window, if the active Window is in the same context
|
||||
* as the calling thread. Only a Frame or a Dialog can be the active
|
||||
* Returns the active Window. Only a Frame or a Dialog can be the active
|
||||
* Window. The native windowing system may denote the active Window or its
|
||||
* children with special decorations, such as a highlighted title bar.
|
||||
* The active Window is always either the focused Window, or the first
|
||||
* Frame or Dialog that is an owner of the focused Window.
|
||||
*
|
||||
* @return the active Window, or null if the active Window is not a member
|
||||
* of the calling thread's context
|
||||
* @return the active Window, or null if there is none
|
||||
* @see #getGlobalActiveWindow
|
||||
* @see #setGlobalActiveWindow
|
||||
*/
|
||||
public Window getActiveWindow() {
|
||||
synchronized (KeyboardFocusManager.class) {
|
||||
if (activeWindow == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (activeWindow.appContext == AppContext.getAppContext())
|
||||
? activeWindow
|
||||
: null;
|
||||
return activeWindow;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1100,14 +1051,7 @@ public abstract class KeyboardFocusManager
|
||||
*/
|
||||
public Container getCurrentFocusCycleRoot() {
|
||||
synchronized (KeyboardFocusManager.class) {
|
||||
if (currentFocusCycleRoot == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (currentFocusCycleRoot.appContext ==
|
||||
AppContext.getAppContext())
|
||||
? currentFocusCycleRoot
|
||||
: null;
|
||||
return currentFocusCycleRoot;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2159,7 +2103,7 @@ public abstract class KeyboardFocusManager
|
||||
descendant = heavyweight;
|
||||
}
|
||||
|
||||
KeyboardFocusManager manager = getCurrentKeyboardFocusManager(SunToolkit.targetToAppContext(descendant));
|
||||
KeyboardFocusManager manager = getCurrentKeyboardFocusManager();
|
||||
|
||||
FocusEvent currentFocusOwnerEvent = null;
|
||||
FocusEvent newFocusOwnerEvent = null;
|
||||
@ -2268,8 +2212,7 @@ public abstract class KeyboardFocusManager
|
||||
descendant = heavyweight;
|
||||
}
|
||||
|
||||
KeyboardFocusManager manager =
|
||||
getCurrentKeyboardFocusManager(SunToolkit.targetToAppContext(descendant));
|
||||
KeyboardFocusManager manager = getCurrentKeyboardFocusManager();
|
||||
KeyboardFocusManager thisManager = getCurrentKeyboardFocusManager();
|
||||
Component currentFocusOwner = thisManager.getGlobalFocusOwner();
|
||||
Component nativeFocusOwner = thisManager.getNativeFocusOwner();
|
||||
@ -2484,16 +2427,6 @@ public abstract class KeyboardFocusManager
|
||||
KeyboardFocusManager manager = getCurrentKeyboardFocusManager();
|
||||
LinkedList<LightweightFocusRequest> localLightweightRequests = null;
|
||||
|
||||
Component globalFocusOwner = manager.getGlobalFocusOwner();
|
||||
if ((globalFocusOwner != null) &&
|
||||
(globalFocusOwner.appContext != AppContext.getAppContext()))
|
||||
{
|
||||
// The current app context differs from the app context of a focus
|
||||
// owner (and all pending lightweight requests), so we do nothing
|
||||
// now and wait for a next event.
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized(heavyweightRequests) {
|
||||
if (currentLightweightRequests != null) {
|
||||
clearingCurrentLightweightRequests = true;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, 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,6 @@ import java.beans.PropertyChangeSupport;
|
||||
import java.util.Vector;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.HeadlessToolkit;
|
||||
import sun.awt.SunToolkit;
|
||||
|
||||
@ -213,6 +212,8 @@ public class SystemTray {
|
||||
}
|
||||
}
|
||||
|
||||
private Vector<TrayIcon> icons;
|
||||
|
||||
/**
|
||||
* Adds a {@code TrayIcon} to the {@code SystemTray}.
|
||||
* The tray icon becomes visible in the system tray once it is
|
||||
@ -240,15 +241,10 @@ public class SystemTray {
|
||||
}
|
||||
TrayIcon[] oldArray;
|
||||
TrayIcon[] newArray;
|
||||
Vector<TrayIcon> icons;
|
||||
synchronized (this) {
|
||||
oldArray = systemTray.getTrayIcons();
|
||||
@SuppressWarnings("unchecked")
|
||||
Vector<TrayIcon> tmp = (Vector<TrayIcon>)AppContext.getAppContext().get(TrayIcon.class);
|
||||
icons = tmp;
|
||||
if (icons == null) {
|
||||
icons = new Vector<>(3);
|
||||
AppContext.getAppContext().put(TrayIcon.class, icons);
|
||||
|
||||
} else if (icons.contains(trayIcon)) {
|
||||
throw new IllegalArgumentException("adding TrayIcon that is already added");
|
||||
@ -291,8 +287,6 @@ public class SystemTray {
|
||||
TrayIcon[] newArray;
|
||||
synchronized (this) {
|
||||
oldArray = systemTray.getTrayIcons();
|
||||
@SuppressWarnings("unchecked")
|
||||
Vector<TrayIcon> icons = (Vector<TrayIcon>)AppContext.getAppContext().get(TrayIcon.class);
|
||||
// TrayIcon with no peer is not contained in the array.
|
||||
if (icons == null || !icons.remove(trayIcon)) {
|
||||
return;
|
||||
@ -320,12 +314,12 @@ public class SystemTray {
|
||||
* @see TrayIcon
|
||||
*/
|
||||
public TrayIcon[] getTrayIcons() {
|
||||
@SuppressWarnings("unchecked")
|
||||
Vector<TrayIcon> icons = (Vector<TrayIcon>)AppContext.getAppContext().get(TrayIcon.class);
|
||||
if (icons != null) {
|
||||
return icons.toArray(EMPTY_TRAY_ARRAY);
|
||||
synchronized (this) {
|
||||
if (icons != null) {
|
||||
return icons.toArray(EMPTY_TRAY_ARRAY);
|
||||
}
|
||||
return EMPTY_TRAY_ARRAY;
|
||||
}
|
||||
return EMPTY_TRAY_ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -374,8 +368,6 @@ public class SystemTray {
|
||||
* </tbody>
|
||||
* </table>
|
||||
* <p>
|
||||
* The {@code listener} listens to property changes only in this context.
|
||||
* <p>
|
||||
* If {@code listener} is {@code null}, no exception is thrown
|
||||
* and no action is performed.
|
||||
*
|
||||
@ -398,8 +390,6 @@ public class SystemTray {
|
||||
* Removes a {@code PropertyChangeListener} from the listener list
|
||||
* for a specific property.
|
||||
* <p>
|
||||
* The {@code PropertyChangeListener} must be from this context.
|
||||
* <p>
|
||||
* If {@code propertyName} or {@code listener} is {@code null} or invalid,
|
||||
* no exception is thrown and no action is taken.
|
||||
*
|
||||
@ -421,8 +411,6 @@ public class SystemTray {
|
||||
/**
|
||||
* Returns an array of all the listeners that have been associated
|
||||
* with the named property.
|
||||
* <p>
|
||||
* Only the listeners in this context are returned.
|
||||
*
|
||||
* @param propertyName the specified property
|
||||
* @return all of the {@code PropertyChangeListener}s associated with
|
||||
@ -461,19 +449,16 @@ public class SystemTray {
|
||||
getCurrentChangeSupport().firePropertyChange(propertyName, oldValue, newValue);
|
||||
}
|
||||
|
||||
private PropertyChangeSupport changeSupport;
|
||||
|
||||
/**
|
||||
* Returns the current PropertyChangeSupport instance for the
|
||||
* calling thread's context.
|
||||
* Returns the current PropertyChangeSupport instance
|
||||
*
|
||||
* @return this thread's context's PropertyChangeSupport
|
||||
* @return the current PropertyChangeSupport for this {@code SystemTray}
|
||||
*/
|
||||
private synchronized PropertyChangeSupport getCurrentChangeSupport() {
|
||||
PropertyChangeSupport changeSupport =
|
||||
(PropertyChangeSupport)AppContext.getAppContext().get(SystemTray.class);
|
||||
|
||||
if (changeSupport == null) {
|
||||
changeSupport = new PropertyChangeSupport(this);
|
||||
AppContext.getAppContext().put(SystemTray.class, changeSupport);
|
||||
}
|
||||
return changeSupport;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,7 +27,6 @@ package java.awt;
|
||||
|
||||
import java.awt.event.*;
|
||||
import java.awt.peer.TrayIconPeer;
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.HeadlessToolkit;
|
||||
@ -126,7 +125,7 @@ public class TrayIcon {
|
||||
if (!SystemTray.isSupported()) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
SunToolkit.insertTargetMapping(this, AppContext.getAppContext());
|
||||
SunToolkit.insertTargetMapping(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, 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,6 +25,7 @@
|
||||
|
||||
package java.awt.geom;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.io.IOException;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
@ -1052,19 +1053,7 @@ public abstract class Arc2D extends RectangularShape {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the high-precision framing rectangle of the arc. The framing
|
||||
* rectangle contains only the part of this {@code Arc2D} that is
|
||||
* in between the starting and ending angles and contains the pie
|
||||
* wedge, if this {@code Arc2D} has a {@code PIE} closure type.
|
||||
* <p>
|
||||
* This method differs from the
|
||||
* {@link RectangularShape#getBounds() getBounds} in that the
|
||||
* {@code getBounds} method only returns the bounds of the
|
||||
* enclosing ellipse of this {@code Arc2D} without considering
|
||||
* the starting and ending angles of this {@code Arc2D}.
|
||||
*
|
||||
* @return the {@code Rectangle2D} that represents the arc's
|
||||
* framing rectangle.
|
||||
* {@inheritDoc java.awt.Shape}
|
||||
* @since 1.2
|
||||
*/
|
||||
public Rectangle2D getBounds2D() {
|
||||
@ -1110,6 +1099,15 @@ public abstract class Arc2D extends RectangularShape {
|
||||
return makeBounds(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc java.awt.Shape}
|
||||
* @since 1.2
|
||||
*/
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
return getBounds2D().getBounds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code Rectangle2D} of the appropriate precision
|
||||
* to hold the parameters calculated to be the framing rectangle
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,7 +29,6 @@ import java.util.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.AWTAccessor.MouseEventAccessor;
|
||||
import sun.swing.SwingUtilities2;
|
||||
@ -48,8 +47,7 @@ public class MenuSelectionManager {
|
||||
private static final boolean VERBOSE = false; // show reuse hits/misses
|
||||
private static final boolean DEBUG = false; // show bad params, misc.
|
||||
|
||||
private static final StringBuilder MENU_SELECTION_MANAGER_KEY =
|
||||
new StringBuilder("javax.swing.MenuSelectionManager");
|
||||
private static final MenuSelectionManager DEFAULT_MSM = new MenuSelectionManager();
|
||||
|
||||
/**
|
||||
* Constructs a {@code MenuSelectionManager}.
|
||||
@ -62,23 +60,7 @@ public class MenuSelectionManager {
|
||||
* @return a MenuSelectionManager object
|
||||
*/
|
||||
public static MenuSelectionManager defaultManager() {
|
||||
synchronized (MENU_SELECTION_MANAGER_KEY) {
|
||||
AppContext context = AppContext.getAppContext();
|
||||
MenuSelectionManager msm = (MenuSelectionManager)context.get(
|
||||
MENU_SELECTION_MANAGER_KEY);
|
||||
if (msm == null) {
|
||||
msm = new MenuSelectionManager();
|
||||
context.put(MENU_SELECTION_MANAGER_KEY, msm);
|
||||
|
||||
// installing additional listener if found in the AppContext
|
||||
Object o = context.get(SwingUtilities2.MENU_SELECTION_MANAGER_LISTENER_KEY);
|
||||
if (o instanceof ChangeListener listener) {
|
||||
msm.addChangeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
return msm;
|
||||
}
|
||||
return DEFAULT_MSM;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2026, 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
|
||||
@ -441,11 +441,6 @@ public final class AWTAccessor {
|
||||
*/
|
||||
void setMostRecentFocusOwner(Window window, Component component);
|
||||
|
||||
/**
|
||||
* Returns current KFM of the specified AppContext.
|
||||
*/
|
||||
KeyboardFocusManager getCurrentKeyboardFocusManager(AppContext ctx);
|
||||
|
||||
/**
|
||||
* Return the current focus cycle root
|
||||
*/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2026, 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
|
||||
@ -218,7 +218,7 @@ public abstract class EmbeddedFrame extends Frame
|
||||
*/
|
||||
public boolean dispatchKeyEvent(KeyEvent e) {
|
||||
|
||||
Container currentRoot = AWTAccessor.getKeyboardFocusManagerAccessor()
|
||||
Container currentRoot = KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.getCurrentFocusCycleRoot();
|
||||
|
||||
// if we are not in EmbeddedFrame's cycle, we should not try to leave.
|
||||
|
||||
@ -414,6 +414,11 @@ public abstract class SunToolkit extends Toolkit
|
||||
cont.setFocusTraversalPolicy(defaultPolicy);
|
||||
}
|
||||
|
||||
/* This method should be removed at the same time as targetToAppContext() */
|
||||
public static void insertTargetMapping(Object target) {
|
||||
insertTargetMapping(target, AppContext.getAppContext());
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a mapping from target to AppContext, for later retrieval
|
||||
* via targetToAppContext() above.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2026, 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
|
||||
@ -87,7 +87,6 @@ import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.ComponentFactory;
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.awt.image.ImageRepresentation;
|
||||
@ -154,9 +153,9 @@ public abstract class DataTransferer {
|
||||
Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
/**
|
||||
* The key used to store pending data conversion requests for an AppContext.
|
||||
* The Runnable for pending data conversion requests.
|
||||
*/
|
||||
private static final String DATA_CONVERTER_KEY = "DATA_CONVERTER_KEY";
|
||||
private static volatile Runnable dataConverterInstance;
|
||||
|
||||
static {
|
||||
DataFlavor tJavaTextEncodingFlavor = null;
|
||||
@ -1769,13 +1768,9 @@ search:
|
||||
}
|
||||
};
|
||||
|
||||
final AppContext appContext = SunToolkit.targetToAppContext(source);
|
||||
|
||||
getToolkitThreadBlockedHandler().lock();
|
||||
|
||||
if (appContext != null) {
|
||||
appContext.put(DATA_CONVERTER_KEY, dataConverter);
|
||||
}
|
||||
dataConverterInstance = dataConverter;
|
||||
|
||||
SunToolkit.executeOnEventHandlerThread(source, dataConverter);
|
||||
|
||||
@ -1783,9 +1778,7 @@ search:
|
||||
getToolkitThreadBlockedHandler().enter();
|
||||
}
|
||||
|
||||
if (appContext != null) {
|
||||
appContext.remove(DATA_CONVERTER_KEY);
|
||||
}
|
||||
dataConverterInstance = null;
|
||||
|
||||
ret = stack.pop();
|
||||
} finally {
|
||||
@ -1802,14 +1795,12 @@ search:
|
||||
|
||||
public void processDataConversionRequests() {
|
||||
if (EventQueue.isDispatchThread()) {
|
||||
AppContext appContext = AppContext.getAppContext();
|
||||
getToolkitThreadBlockedHandler().lock();
|
||||
try {
|
||||
Runnable dataConverter =
|
||||
(Runnable)appContext.get(DATA_CONVERTER_KEY);
|
||||
Runnable dataConverter = dataConverterInstance;
|
||||
if (dataConverter != null) {
|
||||
dataConverter.run();
|
||||
appContext.remove(DATA_CONVERTER_KEY);
|
||||
dataConverterInstance = null;
|
||||
}
|
||||
} finally {
|
||||
getToolkitThreadBlockedHandler().unlock();
|
||||
|
||||
@ -121,9 +121,6 @@ import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;
|
||||
*/
|
||||
public class SwingUtilities2 {
|
||||
|
||||
public static final Object MENU_SELECTION_MANAGER_LISTENER_KEY =
|
||||
new StringBuffer("MenuSelectionManager listener key");
|
||||
|
||||
// Maintain a cache of CACHE_SIZE fonts and the left side bearing
|
||||
// of the characters falling into the range MIN_CHAR_INDEX to
|
||||
// MAX_CHAR_INDEX. The values in fontCache are created as needed.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,9 +35,10 @@ import java.io.OutputStream;
|
||||
class SaslOutputStream extends FilterOutputStream {
|
||||
private static final boolean debug = false;
|
||||
|
||||
private byte[] lenBuf = new byte[4]; // buffer for storing length
|
||||
private final byte[] lenBuf = new byte[4]; // buffer for storing length
|
||||
private int rawSendSize = 65536;
|
||||
private SaslClient sc;
|
||||
private final SaslClient sc;
|
||||
private boolean closed;
|
||||
|
||||
SaslOutputStream(SaslClient sc, OutputStream out) throws SaslException {
|
||||
super(out);
|
||||
@ -60,8 +61,9 @@ class SaslOutputStream extends FilterOutputStream {
|
||||
|
||||
// Override this method to call write(byte[], int, int) counterpart
|
||||
// super.write(int) simply calls out.write(int)
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
ensureOpen();
|
||||
byte[] buffer = new byte[1];
|
||||
buffer[0] = (byte)b;
|
||||
write(buffer, 0, 1);
|
||||
@ -71,7 +73,9 @@ class SaslOutputStream extends FilterOutputStream {
|
||||
* Override this method to "wrap" the outgoing buffer before
|
||||
* writing it to the underlying output stream.
|
||||
*/
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int total) throws IOException {
|
||||
ensureOpen();
|
||||
int count;
|
||||
byte[] wrappedToken;
|
||||
|
||||
@ -101,7 +105,12 @@ class SaslOutputStream extends FilterOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
SaslException save = null;
|
||||
try {
|
||||
sc.dispose(); // Dispose of SaslClient's state
|
||||
@ -121,8 +130,7 @@ class SaslOutputStream extends FilterOutputStream {
|
||||
* Encodes an integer into 4 bytes in network byte order in the buffer
|
||||
* supplied.
|
||||
*/
|
||||
private static void intToNetworkByteOrder(int num, byte[] buf, int start,
|
||||
int count) {
|
||||
private static void intToNetworkByteOrder(int num, byte[] buf, int start, int count) {
|
||||
if (count > 4) {
|
||||
throw new IllegalArgumentException("Cannot handle more than 4 bytes");
|
||||
}
|
||||
@ -132,4 +140,10 @@ class SaslOutputStream extends FilterOutputStream {
|
||||
num >>>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureOpen() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("stream closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2026, 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,7 +117,7 @@ import com.sun.naming.internal.ResourceManager;
|
||||
* @see Context
|
||||
* @see NamingManager#setInitialContextFactoryBuilder
|
||||
* NamingManager.setInitialContextFactoryBuilder
|
||||
* @since 1.3, JNDI 1.1
|
||||
* @since 1.3
|
||||
*/
|
||||
|
||||
public class InitialContext implements Context {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, 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
|
||||
@ -68,6 +68,8 @@
|
||||
* <p> Unless otherwise stated, {@code null} parameter values will cause methods
|
||||
* of all classes in this package to throw {@code NullPointerException}.
|
||||
*
|
||||
* @see java.net.http/
|
||||
*
|
||||
* @spec https://www.rfc-editor.org/info/rfc9114
|
||||
* RFC 9114: HTTP/3
|
||||
* @spec https://www.rfc-editor.org/info/rfc7540
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, 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
|
||||
@ -473,11 +473,10 @@ class Http1Exchange<T> extends ExchangeImpl<T> {
|
||||
|
||||
@Override
|
||||
Http1ResponseBodySubscriber<T> createResponseSubscriber(BodyHandler<T> handler, ResponseInfo response) {
|
||||
BodySubscriber<T> subscriber = handler.apply(response);
|
||||
var cancelTimerOnTermination =
|
||||
cancelTimerOnResponseBodySubscriberTermination(
|
||||
exchange.request().isWebSocket(), response.statusCode());
|
||||
return new Http1ResponseBodySubscriber<>(subscriber, cancelTimerOnTermination, this);
|
||||
return new Http1ResponseBodySubscriber<>(handler.apply(response), cancelTimerOnTermination, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, 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
|
||||
@ -384,6 +384,7 @@ class MultiExchange<T> implements Cancelable {
|
||||
private CompletableFuture<HttpResponse<T>> handleNoBody(Response r, Exchange<T> exch) {
|
||||
BodySubscriber<T> bs = responseHandler.apply(new ResponseInfoImpl(r.statusCode(),
|
||||
r.headers(), r.version()));
|
||||
Objects.requireNonNull(bs, "BodyHandler returned a null BodySubscriber");
|
||||
bs.onSubscribe(new NullSubscription());
|
||||
bs.onComplete();
|
||||
CompletionStage<T> cs = ResponseSubscribers.getBodyAsync(executor, bs);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2026, 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
|
||||
@ -70,6 +70,7 @@ public class HttpBodySubscriberWrapper<T> implements TrustedSubscriber<T> {
|
||||
volatile SubscriptionWrapper subscription;
|
||||
volatile Throwable withError;
|
||||
public HttpBodySubscriberWrapper(BodySubscriber<T> userSubscriber) {
|
||||
Objects.requireNonNull(userSubscriber, "BodySubscriber");
|
||||
this.userSubscriber = userSubscriber;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,7 +24,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the HTTP Client and WebSocket APIs.
|
||||
* Defines the {@linkplain java.net.http HTTP Client and WebSocket APIs}.
|
||||
* <p>
|
||||
* <b id="httpclientprops">System properties used by the java.net.http API</b>
|
||||
* <p>
|
||||
@ -144,15 +144,21 @@
|
||||
* The value for HTTP/2 and HTTP/3 can be overridden with the
|
||||
* {@code jdk.httpclient.keepalive.timeout.h2} and {@code jdk.httpclient.keepalive.timeout.h3}
|
||||
* properties respectively. The value specified for HTTP/2 acts as default value for HTTP/3.
|
||||
* If the provided value is negative, the default value is used.
|
||||
* A value of 0 is valid and has no special meaning other than the connection is closed
|
||||
* when it becomes idle.
|
||||
* </li>
|
||||
* <li><p><b>{@systemProperty jdk.httpclient.keepalive.timeout.h2}</b> (default: see
|
||||
* below)<br>The number of seconds to keep idle HTTP/2 connections alive. If not set, then the
|
||||
* {@code jdk.httpclient.keepalive.timeout} setting is used.
|
||||
* below)<br>The number of seconds to keep idle HTTP/2 connections alive. If not set, or negative,
|
||||
* then the {@code jdk.httpclient.keepalive.timeout} setting is used.
|
||||
* A value of 0 is valid and has no special meaning other than the connection is closed
|
||||
* when it becomes idle.
|
||||
* </li>
|
||||
* <li><p><b>{@systemProperty jdk.httpclient.keepalive.timeout.h3}</b> (default: see
|
||||
* below)<br>The number of seconds to keep idle HTTP/3 connections alive. If not set, then the
|
||||
* {@code jdk.httpclient.keepalive.timeout.h2} setting is used.
|
||||
* </li>
|
||||
* below)<br>The number of seconds to keep idle HTTP/3 connections alive. If not set,
|
||||
* or negative, then the {@code jdk.httpclient.keepalive.timeout.h2} setting is used.
|
||||
* A value of 0 is valid and has no special meaning other than the connection is closed
|
||||
* when it becomes idle.
|
||||
* <li><p><b>{@systemProperty jdk.httpclient.maxframesize}</b> (default: 16384 or 16kB)<br>
|
||||
* The HTTP/2 client maximum frame size in bytes. The server is not permitted to send a frame
|
||||
* larger than this.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2026, 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
|
||||
@ -181,17 +181,17 @@ public final class SourceLauncher {
|
||||
ProgramDescriptor program = context.getProgramDescriptor();
|
||||
|
||||
// 1. Find a main method in the first class and if there is one - invoke it
|
||||
Class<?> firstClass;
|
||||
Class<?> mainClass;
|
||||
String firstClassName = program.qualifiedTypeNames().getFirst();
|
||||
ClassLoader loader = context.newClassLoaderFor(parentLoader, firstClassName);
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
try {
|
||||
firstClass = Class.forName(firstClassName, false, loader);
|
||||
mainClass = Class.forName(firstClassName, false, loader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new Fault(Errors.CantFindClass(firstClassName));
|
||||
}
|
||||
|
||||
Method mainMethod = MethodFinder.findMainMethod(firstClass);
|
||||
Method mainMethod = MethodFinder.findMainMethod(mainClass);
|
||||
if (mainMethod == null) {
|
||||
// 2. If the first class doesn't have a main method, look for a class with a matching name
|
||||
var compilationUnitName = program.fileObject().getFile().getFileName().toString();
|
||||
@ -206,22 +206,18 @@ public final class SourceLauncher {
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new Fault(Errors.CantFindClass(expectedName)));
|
||||
|
||||
Class<?> actualClass;
|
||||
try {
|
||||
actualClass = Class.forName(actualName, false, firstClass.getClassLoader());
|
||||
mainClass = Class.forName(actualName, false, mainClass.getClassLoader());
|
||||
} catch (ClassNotFoundException ignore) {
|
||||
throw new Fault(Errors.CantFindClass(actualName));
|
||||
}
|
||||
mainMethod = MethodFinder.findMainMethod(actualClass);
|
||||
mainMethod = MethodFinder.findMainMethod(mainClass);
|
||||
if (mainMethod == null) {
|
||||
throw new Fault(Errors.CantFindMainMethod(actualName));
|
||||
}
|
||||
}
|
||||
|
||||
// selected main method instance points back to its declaring class
|
||||
Class<?> mainClass = mainMethod.getDeclaringClass();
|
||||
String mainClassName = mainClass.getName();
|
||||
|
||||
var isStatic = Modifier.isStatic(mainMethod.getModifiers());
|
||||
|
||||
Object instance = null;
|
||||
|
||||
@ -96,7 +96,7 @@ struct core_data {
|
||||
int classes_jsa_fd; // file descriptor of class share archive
|
||||
uintptr_t dynamic_addr; // address of dynamic section of a.out
|
||||
uintptr_t vdso_addr; // address of vDSO
|
||||
off64_t vdso_offset; // offset of vDSO in core
|
||||
off_t vdso_offset; // offset of vDSO in core
|
||||
size_t vdso_size; // size of vDSO
|
||||
uintptr_t ld_base_addr; // base address of ld.so
|
||||
size_t num_maps; // number of maps.
|
||||
|
||||
@ -635,8 +635,8 @@ static int handle_vdso(struct ps_prochandle* ph, char* lib_name, size_t lib_name
|
||||
lib_fd = -1;
|
||||
} else {
|
||||
lib_fd = fileno(tmpf);
|
||||
off64_t ofs = ph->core->vdso_offset;
|
||||
if (sendfile64(lib_fd, ph->core->core_fd, &ofs, ph->core->vdso_size) == -1) {
|
||||
off_t ofs = ph->core->vdso_offset;
|
||||
if (sendfile(lib_fd, ph->core->core_fd, &ofs, ph->core->vdso_size) == -1) {
|
||||
print_debug("can't copy vDSO (%d)\n", errno);
|
||||
fclose(tmpf);
|
||||
lib_fd = -1;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, 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
|
||||
@ -154,9 +154,7 @@ class ChunkedOutputStream extends FilterOutputStream
|
||||
} finally {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
Event e = new Event.WriteFinished(t);
|
||||
t.getHttpContext().getServerImpl().addEvent(e);
|
||||
t.postExchangeFinished(true);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -47,13 +47,15 @@ abstract sealed class Event {
|
||||
}
|
||||
|
||||
/**
|
||||
* Event indicating that writing is finished for a given exchange.
|
||||
* Event indicating that the exchange is finished,
|
||||
* without having necessarily read the complete
|
||||
* request or sent the complete response.
|
||||
* Typically, this event is posted when invoking
|
||||
* the filter chain throws an exception.
|
||||
*/
|
||||
static final class WriteFinished extends Event {
|
||||
WriteFinished(ExchangeImpl t) {
|
||||
static final class ExchangeFinished extends Event {
|
||||
ExchangeFinished(ExchangeImpl t) {
|
||||
super(Objects.requireNonNull(t));
|
||||
assert !t.writefinished;
|
||||
t.writefinished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, 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,10 +32,10 @@ import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.text.*;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Stream;
|
||||
import com.sun.net.httpserver.*;
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
@ -46,7 +46,7 @@ class ExchangeImpl {
|
||||
Headers reqHdrs, rspHdrs;
|
||||
Request req;
|
||||
String method;
|
||||
boolean writefinished;
|
||||
private boolean writefinished;
|
||||
URI uri;
|
||||
HttpConnection connection;
|
||||
long reqContentLen;
|
||||
@ -87,6 +87,19 @@ class ExchangeImpl {
|
||||
HttpPrincipal principal;
|
||||
ServerImpl server;
|
||||
|
||||
// Used to control that ServerImpl::endExchange is called
|
||||
// exactly once for this exchange. ServerImpl::endExchange decrements
|
||||
// the refcount that was incremented by calling ServerImpl::startExchange
|
||||
// in this ExchangeImpl constructor.
|
||||
private final AtomicBoolean ended = new AtomicBoolean();
|
||||
|
||||
// Used to ensure that the Event.ExchangeFinished is posted only
|
||||
// once for this exchange. The Event.ExchangeFinished is what will
|
||||
// eventually cause the ServerImpl::finishedLatch to be triggered,
|
||||
// once the number of active exchanges reaches 0 and ServerImpl::stop
|
||||
// has been requested.
|
||||
private final AtomicBoolean finished = new AtomicBoolean();
|
||||
|
||||
ExchangeImpl(
|
||||
String m, URI u, Request req, long len, HttpConnection connection
|
||||
) throws IOException {
|
||||
@ -107,6 +120,55 @@ class ExchangeImpl {
|
||||
server.startExchange();
|
||||
}
|
||||
|
||||
/**
|
||||
* When true, writefinished indicates that all bytes expected
|
||||
* by the client have been written to the response body
|
||||
* outputstream, and that the response body outputstream has
|
||||
* been closed. When all bytes have also been pulled from
|
||||
* the request body input stream, this makes it possible to
|
||||
* reuse the connection for the next request.
|
||||
*/
|
||||
synchronized boolean writefinished() {
|
||||
return writefinished;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls ServerImpl::endExchange if not already called for this
|
||||
* exchange. ServerImpl::endExchange must be called exactly once
|
||||
* per exchange, and this method ensures that it is not called
|
||||
* more than once for this exchange.
|
||||
* @return the new (or current) value of the exchange count.
|
||||
*/
|
||||
int endExchange() {
|
||||
// only call server.endExchange(); once per exchange
|
||||
if (ended.compareAndSet(false, true)) {
|
||||
return server.endExchange();
|
||||
}
|
||||
return server.getExchangeCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts the ExchangeFinished event if not already posted.
|
||||
* If `writefinished` is true, marks the exchange as {@link
|
||||
* #writefinished()} so that the connection can be reused.
|
||||
* @param writefinished whether all bytes expected by the
|
||||
* client have been writen out to the
|
||||
* response body output stream.
|
||||
*/
|
||||
void postExchangeFinished(boolean writefinished) {
|
||||
// only post ExchangeFinished once per exchange
|
||||
if (finished.compareAndSet(false, true)) {
|
||||
if (writefinished) {
|
||||
synchronized (this) {
|
||||
assert this.writefinished == false;
|
||||
this.writefinished = true;
|
||||
}
|
||||
}
|
||||
Event e = new Event.ExchangeFinished(this);
|
||||
getHttpContext().getServerImpl().addEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Headers getRequestHeaders() {
|
||||
return reqHdrs;
|
||||
}
|
||||
@ -140,7 +202,7 @@ class ExchangeImpl {
|
||||
/* close the underlying connection if,
|
||||
* a) the streams not set up yet, no response can be sent, or
|
||||
* b) if the wrapper output stream is not set up, or
|
||||
* c) if the close of the input/outpu stream fails
|
||||
* c) if the close of the input/output stream fails
|
||||
*/
|
||||
try {
|
||||
if (uis_orig == null || uos == null) {
|
||||
@ -157,6 +219,8 @@ class ExchangeImpl {
|
||||
uos.close();
|
||||
} catch (IOException e) {
|
||||
connection.close();
|
||||
} finally {
|
||||
postExchangeFinished(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, 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
|
||||
@ -94,8 +94,7 @@ class FixedLengthOutputStream extends FilterOutputStream
|
||||
is.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
Event e = new Event.WriteFinished(t);
|
||||
t.getHttpContext().getServerImpl().addEvent(e);
|
||||
t.postExchangeFinished(true);
|
||||
}
|
||||
|
||||
// flush is a pass-through
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, 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
|
||||
@ -419,7 +419,8 @@ class ServerImpl {
|
||||
// Stopping marking the state as finished if stop is requested,
|
||||
// termination is in progress and exchange count is 0
|
||||
if (r instanceof Event.StopRequested) {
|
||||
logger.log(Level.TRACE, "Handling Stop Requested Event");
|
||||
logger.log(Level.TRACE, "Handling {0} event",
|
||||
r.getClass().getSimpleName());
|
||||
|
||||
// checking if terminating is set to true
|
||||
final boolean terminatingCopy = terminating;
|
||||
@ -437,10 +438,11 @@ class ServerImpl {
|
||||
HttpConnection c = t.getConnection();
|
||||
|
||||
try {
|
||||
if (r instanceof Event.WriteFinished) {
|
||||
if (r instanceof Event.ExchangeFinished) {
|
||||
|
||||
logger.log(Level.TRACE, "Write Finished");
|
||||
int exchanges = endExchange();
|
||||
logger.log(Level.TRACE, "Handling {0} event",
|
||||
r.getClass().getSimpleName());
|
||||
int exchanges = t.endExchange();
|
||||
if (terminating && exchanges == 0 && reqConnections.isEmpty()) {
|
||||
finishedLatch.countDown();
|
||||
}
|
||||
@ -842,68 +844,77 @@ class ServerImpl {
|
||||
tx = new ExchangeImpl(
|
||||
method, uri, req, clen, connection
|
||||
);
|
||||
String chdr = headers.getFirst("Connection");
|
||||
Headers rheaders = tx.getResponseHeaders();
|
||||
try {
|
||||
|
||||
if (chdr != null && chdr.equalsIgnoreCase("close")) {
|
||||
tx.close = true;
|
||||
}
|
||||
if (version.equalsIgnoreCase("http/1.0")) {
|
||||
tx.http10 = true;
|
||||
if (chdr == null) {
|
||||
String chdr = headers.getFirst("Connection");
|
||||
Headers rheaders = tx.getResponseHeaders();
|
||||
|
||||
if (chdr != null && chdr.equalsIgnoreCase("close")) {
|
||||
tx.close = true;
|
||||
rheaders.set("Connection", "close");
|
||||
} else if (chdr.equalsIgnoreCase("keep-alive")) {
|
||||
rheaders.set("Connection", "keep-alive");
|
||||
int idleSeconds = (int) (ServerConfig.getIdleIntervalMillis() / 1000);
|
||||
String val = "timeout=" + idleSeconds;
|
||||
rheaders.set("Keep-Alive", val);
|
||||
}
|
||||
}
|
||||
if (version.equalsIgnoreCase("http/1.0")) {
|
||||
tx.http10 = true;
|
||||
if (chdr == null) {
|
||||
tx.close = true;
|
||||
rheaders.set("Connection", "close");
|
||||
} else if (chdr.equalsIgnoreCase("keep-alive")) {
|
||||
rheaders.set("Connection", "keep-alive");
|
||||
int idleSeconds = (int) (ServerConfig.getIdleIntervalMillis() / 1000);
|
||||
String val = "timeout=" + idleSeconds;
|
||||
rheaders.set("Keep-Alive", val);
|
||||
}
|
||||
}
|
||||
|
||||
if (newconnection) {
|
||||
connection.setParameters(
|
||||
rawin, rawout, chan, engine, sslStreams,
|
||||
sslContext, protocol, ctx, rawin
|
||||
);
|
||||
}
|
||||
/* check if client sent an Expect 100 Continue.
|
||||
* In that case, need to send an interim response.
|
||||
* In future API may be modified to allow app to
|
||||
* be involved in this process.
|
||||
*/
|
||||
String exp = headers.getFirst("Expect");
|
||||
if (exp != null && exp.equalsIgnoreCase("100-continue")) {
|
||||
logReply(100, requestLine, null);
|
||||
sendReply(
|
||||
Code.HTTP_CONTINUE, false, null
|
||||
);
|
||||
}
|
||||
/* uf is the list of filters seen/set by the user.
|
||||
* sf is the list of filters established internally
|
||||
* and which are not visible to the user. uc and sc
|
||||
* are the corresponding Filter.Chains.
|
||||
* They are linked together by a LinkHandler
|
||||
* so that they can both be invoked in one call.
|
||||
*/
|
||||
final List<Filter> sf = ctx.getSystemFilters();
|
||||
final List<Filter> uf = ctx.getFilters();
|
||||
if (newconnection) {
|
||||
connection.setParameters(
|
||||
rawin, rawout, chan, engine, sslStreams,
|
||||
sslContext, protocol, ctx, rawin
|
||||
);
|
||||
}
|
||||
/* check if client sent an Expect 100 Continue.
|
||||
* In that case, need to send an interim response.
|
||||
* In future API may be modified to allow app to
|
||||
* be involved in this process.
|
||||
*/
|
||||
String exp = headers.getFirst("Expect");
|
||||
if (exp != null && exp.equalsIgnoreCase("100-continue")) {
|
||||
logReply(100, requestLine, null);
|
||||
sendReply(
|
||||
Code.HTTP_CONTINUE, false, null
|
||||
);
|
||||
}
|
||||
/* uf is the list of filters seen/set by the user.
|
||||
* sf is the list of filters established internally
|
||||
* and which are not visible to the user. uc and sc
|
||||
* are the corresponding Filter.Chains.
|
||||
* They are linked together by a LinkHandler
|
||||
* so that they can both be invoked in one call.
|
||||
*/
|
||||
final List<Filter> sf = ctx.getSystemFilters();
|
||||
final List<Filter> uf = ctx.getFilters();
|
||||
|
||||
final Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler());
|
||||
final Filter.Chain uc = new Filter.Chain(uf, new LinkHandler(sc));
|
||||
final Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler());
|
||||
final Filter.Chain uc = new Filter.Chain(uf, new LinkHandler(sc));
|
||||
|
||||
/* set up the two stream references */
|
||||
tx.getRequestBody();
|
||||
tx.getResponseBody();
|
||||
if (https) {
|
||||
uc.doFilter(new HttpsExchangeImpl(tx));
|
||||
} else {
|
||||
uc.doFilter(new HttpExchangeImpl(tx));
|
||||
/* set up the two stream references */
|
||||
tx.getRequestBody();
|
||||
tx.getResponseBody();
|
||||
if (https) {
|
||||
uc.doFilter(new HttpsExchangeImpl(tx));
|
||||
} else {
|
||||
uc.doFilter(new HttpExchangeImpl(tx));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// release the exchange.
|
||||
logger.log(Level.TRACE, "ServerImpl.Exchange", t);
|
||||
if (!tx.writefinished()) {
|
||||
closeConnection(connection);
|
||||
}
|
||||
tx.postExchangeFinished(false);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.TRACE, "ServerImpl.Exchange", e);
|
||||
if (tx == null || !tx.writefinished) {
|
||||
if (tx == null || !tx.writefinished()) {
|
||||
closeConnection(connection);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2007, 2026, 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
|
||||
@ -75,8 +75,7 @@ class UndefLengthOutputStream extends FilterOutputStream
|
||||
is.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
Event e = new Event.WriteFinished(t);
|
||||
t.getHttpContext().getServerImpl().addEvent(e);
|
||||
t.postExchangeFinished(true);
|
||||
}
|
||||
|
||||
// flush is a pass-through
|
||||
|
||||
@ -435,7 +435,10 @@ class JImageTask {
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw TASK_HELPER.newBadArgs("err.invalid.jimage", file, ioe.getMessage());
|
||||
boolean isVersionMismatch = ioe instanceof BasicImageReader.ImageVersionMismatchException;
|
||||
// Both messages take the file name and underlying message.
|
||||
String msgKey = isVersionMismatch ? "err.wrong.version" : "err.invalid.jimage";
|
||||
throw TASK_HELPER.newBadArgs(msgKey, file, ioe.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,15 +89,20 @@ main.opt.footer=\
|
||||
\ glob:<glob-pattern>\n\
|
||||
\ regex:<regex-pattern>
|
||||
|
||||
|
||||
|
||||
err.not.a.task=task must be one of <extract | info | list | verify>: {0}
|
||||
err.missing.arg=no value given for {0}
|
||||
err.ambiguous.arg=value for option {0} starts with \"--\" should use {0}=<value> format
|
||||
err.not.a.dir=not a directory: {0}
|
||||
err.not.a.jimage=not a jimage file: {0}
|
||||
err.invalid.jimage=Unable to open {0}: {1}
|
||||
err.no.jimage=no jimage provided
|
||||
err.option.unsupported={0} not supported: {1}
|
||||
err.unknown.option=unknown option: {0}
|
||||
err.cannot.create.dir=cannot create directory {0}
|
||||
|
||||
# General failure to open a jimage file.
|
||||
# {0} = path of jimage file, {1} = underlying error message
|
||||
err.invalid.jimage=Unable to open {0}: {1}
|
||||
# More specific alternative for cases of version mismatch
|
||||
err.wrong.version=Unable to open {0}: mismatched file and tool version\n\
|
||||
Use ''<JAVA_HOME>/bin/jimage'' for the JDK associated with the jimage file:\n\
|
||||
{1}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2009, 2026, 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
|
||||
@ -1233,7 +1233,7 @@ class ZipFileSystem extends FileSystem {
|
||||
|
||||
private volatile boolean isOpen = true;
|
||||
private final SeekableByteChannel ch; // channel to the zipfile
|
||||
final byte[] cen; // CEN & ENDHDR
|
||||
final byte[] cen; // CEN
|
||||
private END end;
|
||||
private long locpos; // position of first LOC header (usually 0)
|
||||
|
||||
@ -1585,15 +1585,15 @@ class ZipFileSystem extends FileSystem {
|
||||
if (locpos < 0)
|
||||
throw new ZipException("invalid END header (bad central directory offset)");
|
||||
|
||||
// read in the CEN and END
|
||||
byte[] cen = new byte[(int)(end.cenlen + ENDHDR)];
|
||||
if (readNBytesAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) {
|
||||
// read in the CEN
|
||||
byte[] cen = new byte[(int)(end.cenlen)];
|
||||
if (readNBytesAt(cen, 0, cen.length, cenpos) != end.cenlen) {
|
||||
throw new ZipException("read CEN tables failed");
|
||||
}
|
||||
// Iterate through the entries in the central directory
|
||||
inodes = LinkedHashMap.newLinkedHashMap(end.centot + 1);
|
||||
int pos = 0;
|
||||
int limit = cen.length - ENDHDR;
|
||||
int limit = cen.length;
|
||||
while (pos < limit) {
|
||||
if (!cenSigAt(cen, pos))
|
||||
throw new ZipException("invalid CEN header (bad signature)");
|
||||
@ -1641,7 +1641,7 @@ class ZipFileSystem extends FileSystem {
|
||||
// skip ext and comment
|
||||
pos += (CENHDR + nlen + elen + clen);
|
||||
}
|
||||
if (pos + ENDHDR != cen.length) {
|
||||
if (pos != cen.length) {
|
||||
throw new ZipException("invalid CEN header (bad header size)");
|
||||
}
|
||||
buildNodeTree();
|
||||
@ -1671,7 +1671,7 @@ class ZipFileSystem extends FileSystem {
|
||||
}
|
||||
// CEN Offset where this Extra field ends
|
||||
int extraEndOffset = startingOffset + extraFieldLen;
|
||||
if (extraEndOffset > cen.length - ENDHDR) {
|
||||
if (extraEndOffset > cen.length) {
|
||||
zerror("Invalid CEN header (extra data field size too long)");
|
||||
}
|
||||
int currentOffset = startingOffset;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2009, 2026, 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
|
||||
@ -55,6 +55,8 @@ import static java.nio.file.StandardOpenOption.WRITE;
|
||||
*/
|
||||
final class ZipPath implements Path {
|
||||
|
||||
private static final byte[] EMPTY_PATH = new byte[0];
|
||||
|
||||
private final ZipFileSystem zfs;
|
||||
private final byte[] path;
|
||||
private volatile int[] offsets;
|
||||
@ -93,8 +95,15 @@ final class ZipPath implements Path {
|
||||
@Override
|
||||
public ZipPath getFileName() {
|
||||
int off = path.length;
|
||||
if (off == 0 || off == 1 && path[0] == '/')
|
||||
if (off == 0) {
|
||||
// empty path, which is defined as consisting solely of
|
||||
// one name element that is empty
|
||||
return new ZipPath(getFileSystem(), EMPTY_PATH, true);
|
||||
}
|
||||
if (off == 1 && path[0] == '/') {
|
||||
// root path, which is defined as having 0 name elements
|
||||
return null;
|
||||
}
|
||||
while (--off >= 0 && path[off] != '/') {}
|
||||
if (off < 0)
|
||||
return this;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -1252,3 +1252,17 @@ TEST_VM_F(RBTreeTest, AllocatorMayReturnNull) {
|
||||
EXPECT_EQ(false, success);
|
||||
// The test didn't exit the VM, so it was succesful.
|
||||
}
|
||||
|
||||
TEST_VM_F(RBTreeTest, ArenaAllocator) {
|
||||
Arena arena(mtTest);
|
||||
RBTreeArena<int, int, Cmp> rbtree(&arena);
|
||||
bool success = rbtree.upsert(5, 5);
|
||||
ASSERT_EQ(true, success);
|
||||
}
|
||||
|
||||
TEST_VM_F(RBTreeTest, ResourceAreaAllocator) {
|
||||
ResourceArea area(mtTest);
|
||||
RBTreeResourceArea<int, int, Cmp> rbtree(&area);
|
||||
bool success = rbtree.upsert(5, 5);
|
||||
ASSERT_EQ(true, success);
|
||||
}
|
||||
|
||||
@ -59,6 +59,10 @@ compiler/codecache/jmx/PoolsIndependenceTest.java 8264632 macosx-all
|
||||
compiler/vectorapi/reshape/TestVectorReinterpret.java 8320897,8348519 aix-ppc64,linux-ppc64le,linux-s390x
|
||||
compiler/vectorapi/VectorRebracket128Test.java 8330538 generic-all
|
||||
|
||||
compiler/vectorization/TestVectorAlgorithms.java#noSuperWord 8376803 aix-ppc64,linux-s390x
|
||||
compiler/vectorization/TestVectorAlgorithms.java#vanilla 8376803 aix-ppc64,linux-s390x
|
||||
compiler/vectorization/TestVectorAlgorithms.java#noOptimizeFill 8376803 aix-ppc64,linux-s390x
|
||||
|
||||
compiler/jvmci/TestUncaughtErrorInCompileMethod.java 8309073 generic-all
|
||||
compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/DataPatchTest.java 8331704 linux-riscv64
|
||||
compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/MaxOopMapStackOffsetTest.java 8331704 linux-riscv64
|
||||
|
||||
@ -52,7 +52,7 @@ public class TestUseSHA3IntrinsicsWithUseSHADisabledOnSupportedCPU {
|
||||
private static final String UNLOCK_DIAGNOSTIC = "-XX:+UnlockDiagnosticVMOptions";
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
if (!IntrinsicPredicates.isSHA3IntrinsicAvailable().getAsBoolean()) {
|
||||
if (!IntrinsicPredicates.SHA3_INSTRUCTION_AVAILABLE.getAsBoolean()) {
|
||||
throw new SkippedException("Skipping... SHA3 intrinsics are not available on this platform.");
|
||||
}
|
||||
|
||||
|
||||
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