diff --git a/bin/idea.sh b/bin/idea.sh index a184884b61a2..d9a18956e3b5 100644 --- a/bin/idea.sh +++ b/bin/idea.sh @@ -187,14 +187,18 @@ fi SOURCE_PREFIX="" +# SOURCES is a single string containing embeded newlines. for root in $MODULE_ROOTS; do if [ "x$CYGPATH" != "x" ]; then root=`$CYGPATH -am $root` elif [ "x$WSL_DISTRO_NAME" != "x" ]; then root=`wslpath -am $root` fi - - SOURCES=$SOURCES" $SOURCE_PREFIX""$root""$SOURCE_POSTFIX" + # Add line termination/indentation for everything after the first entry. + if [ "x$SOURCES" != "x" ]; then + SOURCES="${SOURCES}\n " + fi + SOURCES="${SOURCES}${SOURCE_PREFIX}${root}${SOURCE_POSTFIX}" done add_replacement "###SOURCE_ROOTS###" "$SOURCES" diff --git a/make/Hsdis.gmk b/make/Hsdis.gmk index 469cc488f16c..76695fc8dde9 100644 --- a/make/Hsdis.gmk +++ b/make/Hsdis.gmk @@ -44,6 +44,9 @@ ifeq ($(HSDIS_BACKEND), capstone) else ifeq ($(call isTargetCpuArch, aarch64), true) CAPSTONE_ARCH := CS_ARCH_$(CAPSTONE_ARCH_AARCH64_NAME) CAPSTONE_MODE := CS_MODE_ARM + else ifeq ($(call isTargetCpuArch, arm), true) + CAPSTONE_ARCH := CS_ARCH_ARM + CAPSTONE_MODE := CS_MODE_ARM else $(error No support for Capstone on this platform) endif diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index ab9cd8be19bc..423654cd50ad 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -544,12 +544,9 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER], TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -fstack-protector" TOOLCHAIN_CFLAGS_JDK="-fvisibility=hidden -pipe -fstack-protector" # reduce lib size on linux in link step, this needs also special compile flags - # do this on s390x also for libjvm (where serviceability agent is not supported) if test "x$ENABLE_LINKTIME_GC" = xtrue; then TOOLCHAIN_CFLAGS_JDK="$TOOLCHAIN_CFLAGS_JDK -ffunction-sections -fdata-sections" - if test "x$OPENJDK_TARGET_CPU" = xs390x && test "x$DEBUG_LEVEL" == xrelease; then - TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections" - fi + TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections" fi # technically NOT for CXX (but since this gives *worse* performance, use # no-strict-aliasing everywhere!) diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4 index 7782735be25c..ff10828731ec 100644 --- a/make/autoconf/flags-ldflags.m4 +++ b/make/autoconf/flags-ldflags.m4 @@ -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 @@ -53,16 +53,15 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER], # add --icf=all (Identical Code Folding — merges identical functions) BASIC_LDFLAGS="-Wl,-z,defs -Wl,-z,relro -Wl,-z,now -Wl,--no-as-needed -Wl,--exclude-libs,ALL" + BASIC_LDFLAGS_JVM_ONLY="" # Linux : remove unused code+data in link step if test "x$ENABLE_LINKTIME_GC" = xtrue; then - if test "x$OPENJDK_TARGET_CPU" = xs390x; then - BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,--gc-sections" - else - BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,--gc-sections" - fi + # keep vtables : -Wl,--undefined-glob=_ZTV* (but this seems not to work with gold ld) + # so keep at least the Metadata vtable that is used in the serviceability agent + BASIC_LDFLAGS_JVM_ONLY="$BASIC_LDFLAGS_JVM_ONLY -Wl,--gc-sections -Wl,--undefined=_ZTV8Metadata" + BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,--gc-sections" fi - BASIC_LDFLAGS_JVM_ONLY="" LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing $DEBUG_PREFIX_CFLAGS" LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r" diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java index de496b3f606b..9f42326ef095 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java @@ -801,10 +801,7 @@ private static Map extractZoneNames(Map map, Str String tzKey = Optional.ofNullable((String)handlerSupplMeta.get(tzid)) .orElse(tzid); // Follow link, if needed - String tzLink = null; - for (var k = tzKey; tzdbLinks.containsKey(k);) { - k = tzLink = tzdbLinks.get(k); - } + String tzLink = getTZDBLink(tzKey); if (tzLink == null && tzdbLinks.containsValue(tzKey)) { // reverse link search // this is needed as in tzdb, "America/Buenos_Aires" links to @@ -833,7 +830,7 @@ private static Map extractZoneNames(Map map, Str } else { // TZDB short names tznames = Arrays.copyOf(tznames, tznames.length); - fillTZDBShortNames(tzid, tznames); + fillTZDBShortNames(tzKey, tznames); names.put(tzid, tznames); } } else { @@ -846,11 +843,13 @@ private static Map extractZoneNames(Map map, Str String metaKey = METAZONE_ID_PREFIX + meta; data = map.get(metaKey); if (data instanceof String[] tznames) { - // TZDB short names - tznames = Arrays.copyOf((String[])names.getOrDefault(metaKey, tznames), 6); - fillTZDBShortNames(tzid, tznames); - // Keep the metazone prefix here. - names.putIfAbsent(metaKey, tznames); + if (isDefaultZone(meta, tzKey)) { + // Record the metazone names only from the default + // (001) zone, with short names filled from TZDB + tznames = Arrays.copyOf(tznames, tznames.length); + fillTZDBShortNames(tzKey, tznames); + names.put(metaKey, tznames); + } names.put(tzid, meta); if (tzLink != null && availableIds.contains(tzLink)) { names.put(tzLink, meta); @@ -1504,12 +1503,12 @@ private static String flipIfNeeded(boolean inVanguard, String format) { * Fill the TZDB short names if there is no name provided by the CLDR */ private static void fillTZDBShortNames(String tzid, String[] names) { - var val = tzdbShortNamesMap.get(tzdbLinks.getOrDefault(tzid, tzid)); + var val = tzdbShortNamesMap.getOrDefault(tzid, tzdbShortNamesMap.get(getTZDBLink(tzid))); if (val != null) { var format = val.split(NBSP)[0]; var rule = val.split(NBSP)[1]; IntStream.of(1, 3, 5).forEach(i -> { - if (names[i] == null) { + if (names[i] == null || names[i].isEmpty()) { if (format.contains("%s")) { names[i] = switch (i) { case 1 -> format.formatted(tzdbSubstLetters.get(rule + NBSP + STD)); @@ -1531,6 +1530,21 @@ private static void fillTZDBShortNames(String tzid, String[] names) { } } + private static boolean isDefaultZone(String meta, String tzid) { + String zone001 = handlerMetaZones.zidMap().get(meta); + var tzLink = getTZDBLink(tzid); + return canonicalTZMap.getOrDefault(tzid, tzid).equals(zone001) || + tzLink != null && canonicalTZMap.getOrDefault(tzLink, tzLink).equals(zone001); + } + + private static String getTZDBLink(String tzid) { + String tzLink = null; + for (var k = tzid; tzdbLinks.containsKey(k);) { + k = tzLink = tzdbLinks.get(k); + } + return tzLink; + } + /* * Convert TZDB offsets to JDK's offsets, eg, "-08" to "GMT-08:00". * If it cannot recognize the pattern, return the argument as is. diff --git a/make/modules/jdk.hotspot.agent/Lib.gmk b/make/modules/jdk.hotspot.agent/Lib.gmk index ed8de631dc35..da02e0dab394 100644 --- a/make/modules/jdk.hotspot.agent/Lib.gmk +++ b/make/modules/jdk.hotspot.agent/Lib.gmk @@ -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 @@ -55,6 +55,12 @@ else LIBSAPROC_LINK_TYPE := C endif +# DWARF related sources would be included on supported platforms only. +LIBSAPROC_EXCLUDE_FILES := +ifneq ($(call And, $(call isTargetOs, linux) $(call isTargetCpu, x86_64 aarch64)), true) + LIBSAPROC_EXCLUDE_FILES := DwarfParser.cpp dwarf.cpp +endif + $(eval $(call SetupJdkLibrary, BUILD_LIBSAPROC, \ NAME := saproc, \ LINK_TYPE := $(LIBSAPROC_LINK_TYPE), \ @@ -70,6 +76,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBSAPROC, \ CFLAGS := $(LIBSAPROC_CFLAGS), \ CXXFLAGS := $(LIBSAPROC_CFLAGS) $(LIBSAPROC_CXXFLAGS), \ EXTRA_SRC := $(LIBSAPROC_EXTRA_SRC), \ + EXCLUDE_FILES := $(LIBSAPROC_EXCLUDE_FILES), \ JDK_LIBS := java.base:libjava, \ LIBS_linux := $(LIBDL), \ LIBS_macosx := \ diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index 19f03d97a72e..4c854913e638 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -1,6 +1,6 @@ // // Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. -// Copyright (c) 2020, 2025, Arm Limited. All rights reserved. +// Copyright (c) 2020, 2026, Arm Limited. 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 @@ -247,10 +247,39 @@ source %{ case Op_MinVHF: case Op_MaxVHF: case Op_SqrtVHF: + if (UseSVE == 0 && !is_feat_fp16_supported()) { + return false; + } + break; + // At the time of writing this, the Vector API has no half-float (FP16) species. + // Consequently, AddReductionVHF and MulReductionVHF are only produced by the + // auto-vectorizer, which requires strictly ordered semantics for FP reductions. + // + // There is no direct Neon instruction that performs strictly ordered floating + // point add reduction. Hence, on Neon only machines, the add reduction operation + // is implemented as a scalarized sequence using half-precision scalar instruction + // FADD which requires FEAT_FP16 and ASIMDHP to be available on the target. + // On SVE machines (UseSVE > 0) however, there is a direct instruction (FADDA) which + // implements strictly ordered floating point add reduction which does not require + // the FEAT_FP16 and ASIMDHP checks as SVE supports half-precision floats by default. + case Op_AddReductionVHF: // FEAT_FP16 is enabled if both "fphp" and "asimdhp" features are supported. // Only the Neon instructions need this check. SVE supports half-precision floats // by default. - if (UseSVE == 0 && !is_feat_fp16_supported()) { + if (length_in_bytes < 8 || (UseSVE == 0 && !is_feat_fp16_supported())) { + return false; + } + break; + case Op_MulReductionVHF: + // There are no direct Neon/SVE instructions that perform strictly ordered + // floating point multiply reduction. + // For vector length ≤ 16 bytes, the reduction is implemented as a scalarized + // sequence using half-precision scalar instruction FMUL. This path requires + // FEAT_FP16 and ASIMDHP to be available on the target. + // For vector length > 16 bytes, this operation is disabled because there is no + // direct SVE instruction that performs a strictly ordered FP16 multiply + // reduction. + if (length_in_bytes < 8 || length_in_bytes > 16 || !is_feat_fp16_supported()) { return false; } break; @@ -300,6 +329,7 @@ source %{ case Op_VectorRearrange: case Op_MulReductionVD: case Op_MulReductionVF: + case Op_MulReductionVHF: case Op_MulReductionVI: case Op_MulReductionVL: case Op_CompressBitsV: @@ -364,6 +394,7 @@ source %{ case Op_VectorMaskCmp: case Op_LoadVectorGather: case Op_StoreVectorScatter: + case Op_AddReductionVHF: case Op_AddReductionVF: case Op_AddReductionVD: case Op_AndReductionV: @@ -597,13 +628,9 @@ instruct vloadcon(vReg dst, immI0 src) %{ BasicType bt = Matcher::vector_element_basic_type(this); if (UseSVE == 0) { uint length_in_bytes = Matcher::vector_length_in_bytes(this); + int entry_idx = __ vector_iota_entry_index(bt); assert(length_in_bytes <= 16, "must be"); - // The iota indices are ordered by type B/S/I/L/F/D, and the offset between two types is 16. - int offset = exact_log2(type2aelembytes(bt)) << 4; - if (is_floating_point_type(bt)) { - offset += 32; - } - __ lea(rscratch1, ExternalAddress(StubRoutines::aarch64::vector_iota_indices() + offset)); + __ lea(rscratch1, ExternalAddress(StubRoutines::aarch64::vector_iota_indices(entry_idx))); if (length_in_bytes == 16) { __ ldrq($dst$$FloatRegister, rscratch1); } else { @@ -3406,6 +3433,44 @@ instruct reduce_non_strict_order_add4F_neon(vRegF dst, vRegF fsrc, vReg vsrc, vR ins_pipe(pipe_slow); %} +// Add Reduction for Half floats (FP16). +// Neon does not provide direct instructions for strictly ordered floating-point add reductions. +// On Neon-only targets (UseSVE = 0), this operation is implemented as a sequence of scalar additions: +// values equal to the vector width are loaded into a vector register, each lane is extracted, +// and its value is accumulated into the running sum, producing a final scalar result. +instruct reduce_addHF_neon(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ + predicate(UseSVE == 0); + match(Set dst (AddReductionVHF fsrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "reduce_addHF $dst, $fsrc, $vsrc\t# 4HF/8HF. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + __ neon_reduce_add_fp16($dst$$FloatRegister, $fsrc$$FloatRegister, + $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// This rule calculates the reduction result in strict order. Two cases will +// reach here: +// 1. Non strictly-ordered AddReductionVHF when vector size > 128-bits. For example - +// AddReductionVHF generated by Vector API. For vector size > 128-bits, it is more +// beneficial performance-wise to generate direct SVE instruction even if it is +// strictly ordered. +// 2. Strictly-ordered AddReductionVHF. For example - AddReductionVHF generated by +// auto-vectorization on SVE machine. +instruct reduce_addHF_sve(vRegF dst_src1, vReg src2) %{ + predicate(UseSVE > 0); + match(Set dst_src1 (AddReductionVHF dst_src1 src2)); + format %{ "reduce_addHF_sve $dst_src1, $dst_src1, $src2" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src2); + assert(length_in_bytes == MaxVectorSize, "invalid vector length"); + __ sve_fadda($dst_src1$$FloatRegister, __ H, ptrue, $src2$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + // This rule calculates the reduction result in strict order. Two cases will // reach here: // 1. Non strictly-ordered AddReductionVF when vector size > 128-bits. For example - @@ -3496,12 +3561,14 @@ instruct reduce_addL_masked(iRegLNoSp dst, iRegL isrc, vReg vsrc, pRegGov pg, vR ins_pipe(pipe_slow); %} -instruct reduce_addF_masked(vRegF dst_src1, vReg src2, pRegGov pg) %{ +instruct reduce_addFHF_masked(vRegF dst_src1, vReg src2, pRegGov pg) %{ predicate(UseSVE > 0); + match(Set dst_src1 (AddReductionVHF (Binary dst_src1 src2) pg)); match(Set dst_src1 (AddReductionVF (Binary dst_src1 src2) pg)); - format %{ "reduce_addF_masked $dst_src1, $pg, $dst_src1, $src2" %} + format %{ "reduce_addFHF_masked $dst_src1, $pg, $dst_src1, $src2" %} ins_encode %{ - __ sve_fadda($dst_src1$$FloatRegister, __ S, + BasicType bt = Matcher::vector_element_basic_type(this, $src2); + __ sve_fadda($dst_src1$$FloatRegister, __ elemType_to_regVariant(bt), $pg$$PRegister, $src2$$FloatRegister); %} ins_pipe(pipe_slow); @@ -3549,14 +3616,17 @@ instruct reduce_mulL(iRegLNoSp dst, iRegL isrc, vReg vsrc) %{ ins_pipe(pipe_slow); %} -instruct reduce_mulF(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ + +instruct reduce_mulFHF(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ predicate(Matcher::vector_length_in_bytes(n->in(2)) <= 16); + match(Set dst (MulReductionVHF fsrc vsrc)); match(Set dst (MulReductionVF fsrc vsrc)); effect(TEMP_DEF dst, TEMP tmp); - format %{ "reduce_mulF $dst, $fsrc, $vsrc\t# 2F/4F. KILL $tmp" %} + format %{ "reduce_mulFHF $dst, $fsrc, $vsrc\t# 2F/4F/4HF/8HF. KILL $tmp" %} ins_encode %{ uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); - __ neon_reduce_mul_fp($dst$$FloatRegister, T_FLOAT, $fsrc$$FloatRegister, + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + __ neon_reduce_mul_fp($dst$$FloatRegister, bt, $fsrc$$FloatRegister, $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister); %} ins_pipe(pipe_slow); diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index 48bffb3cf358..58ed234194ab 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -1,6 +1,6 @@ // // Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. -// Copyright (c) 2020, 2025, Arm Limited. All rights reserved. +// Copyright (c) 2020, 2026, Arm Limited. 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 @@ -237,10 +237,39 @@ source %{ case Op_MinVHF: case Op_MaxVHF: case Op_SqrtVHF: + if (UseSVE == 0 && !is_feat_fp16_supported()) { + return false; + } + break; + // At the time of writing this, the Vector API has no half-float (FP16) species. + // Consequently, AddReductionVHF and MulReductionVHF are only produced by the + // auto-vectorizer, which requires strictly ordered semantics for FP reductions. + // + // There is no direct Neon instruction that performs strictly ordered floating + // point add reduction. Hence, on Neon only machines, the add reduction operation + // is implemented as a scalarized sequence using half-precision scalar instruction + // FADD which requires FEAT_FP16 and ASIMDHP to be available on the target. + // On SVE machines (UseSVE > 0) however, there is a direct instruction (FADDA) which + // implements strictly ordered floating point add reduction which does not require + // the FEAT_FP16 and ASIMDHP checks as SVE supports half-precision floats by default. + case Op_AddReductionVHF: // FEAT_FP16 is enabled if both "fphp" and "asimdhp" features are supported. // Only the Neon instructions need this check. SVE supports half-precision floats // by default. - if (UseSVE == 0 && !is_feat_fp16_supported()) { + if (length_in_bytes < 8 || (UseSVE == 0 && !is_feat_fp16_supported())) { + return false; + } + break; + case Op_MulReductionVHF: + // There are no direct Neon/SVE instructions that perform strictly ordered + // floating point multiply reduction. + // For vector length ≤ 16 bytes, the reduction is implemented as a scalarized + // sequence using half-precision scalar instruction FMUL. This path requires + // FEAT_FP16 and ASIMDHP to be available on the target. + // For vector length > 16 bytes, this operation is disabled because there is no + // direct SVE instruction that performs a strictly ordered FP16 multiply + // reduction. + if (length_in_bytes < 8 || length_in_bytes > 16 || !is_feat_fp16_supported()) { return false; } break; @@ -290,6 +319,7 @@ source %{ case Op_VectorRearrange: case Op_MulReductionVD: case Op_MulReductionVF: + case Op_MulReductionVHF: case Op_MulReductionVI: case Op_MulReductionVL: case Op_CompressBitsV: @@ -354,6 +384,7 @@ source %{ case Op_VectorMaskCmp: case Op_LoadVectorGather: case Op_StoreVectorScatter: + case Op_AddReductionVHF: case Op_AddReductionVF: case Op_AddReductionVD: case Op_AndReductionV: @@ -2063,6 +2094,25 @@ instruct reduce_non_strict_order_add4F_neon(vRegF dst, vRegF fsrc, vReg vsrc, vR ins_pipe(pipe_slow); %} dnl + +// Add Reduction for Half floats (FP16). +// Neon does not provide direct instructions for strictly ordered floating-point add reductions. +// On Neon-only targets (UseSVE = 0), this operation is implemented as a sequence of scalar additions: +// values equal to the vector width are loaded into a vector register, each lane is extracted, +// and its value is accumulated into the running sum, producing a final scalar result. +instruct reduce_addHF_neon(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ + predicate(UseSVE == 0); + match(Set dst (AddReductionVHF fsrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp); + format %{ "reduce_addHF $dst, $fsrc, $vsrc\t# 4HF/8HF. KILL $tmp" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + __ neon_reduce_add_fp16($dst$$FloatRegister, $fsrc$$FloatRegister, + $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} +dnl dnl REDUCE_ADD_FP_SVE($1, $2 ) dnl REDUCE_ADD_FP_SVE(type, size) define(`REDUCE_ADD_FP_SVE', ` @@ -2074,21 +2124,26 @@ define(`REDUCE_ADD_FP_SVE', ` // strictly ordered. // 2. Strictly-ordered AddReductionV$1. For example - AddReductionV$1 generated by // auto-vectorization on SVE machine. -instruct reduce_add$1_sve(vReg$1 dst_src1, vReg src2) %{ - predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) || - n->as_Reduction()->requires_strict_order()); +instruct reduce_add$1_sve(vReg`'ifelse($1, HF, F, $1) dst_src1, vReg src2) %{ + ifelse($1, HF, + `predicate(UseSVE > 0);', + `predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) || + n->as_Reduction()->requires_strict_order());') match(Set dst_src1 (AddReductionV$1 dst_src1 src2)); format %{ "reduce_add$1_sve $dst_src1, $dst_src1, $src2" %} ins_encode %{ - assert(UseSVE > 0, "must be sve"); - uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src2); + ifelse($1, HF, `', + `assert(UseSVE > 0, "must be sve"); + ')dnl +uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src2); assert(length_in_bytes == MaxVectorSize, "invalid vector length"); __ sve_fadda($dst_src1$$FloatRegister, __ $2, ptrue, $src2$$FloatRegister); %} ins_pipe(pipe_slow); %}')dnl dnl -REDUCE_ADD_FP_SVE(F, S) +REDUCE_ADD_FP_SVE(HF, H) +REDUCE_ADD_FP_SVE(F, S) // reduction addD @@ -2129,21 +2184,30 @@ dnl dnl REDUCE_ADD_FP_PREDICATE($1, $2 ) dnl REDUCE_ADD_FP_PREDICATE(insn_name, op_name) define(`REDUCE_ADD_FP_PREDICATE', ` -instruct reduce_add$1_masked(vReg$1 dst_src1, vReg src2, pRegGov pg) %{ +instruct reduce_add$1_masked(vReg$2 dst_src1, vReg src2, pRegGov pg) %{ predicate(UseSVE > 0); - match(Set dst_src1 (AddReductionV$1 (Binary dst_src1 src2) pg)); + ifelse($2, F, + `match(Set dst_src1 (AddReductionVHF (Binary dst_src1 src2) pg)); + match(Set dst_src1 (AddReductionV$2 (Binary dst_src1 src2) pg));', + `match(Set dst_src1 (AddReductionV$2 (Binary dst_src1 src2) pg));') format %{ "reduce_add$1_masked $dst_src1, $pg, $dst_src1, $src2" %} ins_encode %{ - __ sve_fadda($dst_src1$$FloatRegister, __ $2, - $pg$$PRegister, $src2$$FloatRegister); + ifelse($2, F, + `BasicType bt = Matcher::vector_element_basic_type(this, $src2); + ',)dnl +ifelse($2, F, + `__ sve_fadda($dst_src1$$FloatRegister, __ elemType_to_regVariant(bt), + $pg$$PRegister, $src2$$FloatRegister);', + `__ sve_fadda($dst_src1$$FloatRegister, __ $2, + $pg$$PRegister, $src2$$FloatRegister);') %} ins_pipe(pipe_slow); %}')dnl dnl REDUCE_ADD_INT_PREDICATE(I, iRegIorL2I) REDUCE_ADD_INT_PREDICATE(L, iRegL) -REDUCE_ADD_FP_PREDICATE(F, S) -REDUCE_ADD_FP_PREDICATE(D, D) +REDUCE_ADD_FP_PREDICATE(FHF, F) +REDUCE_ADD_FP_PREDICATE(D, D) // ------------------------------ Vector reduction mul ------------------------- @@ -2176,30 +2240,37 @@ instruct reduce_mulL(iRegLNoSp dst, iRegL isrc, vReg vsrc) %{ ins_pipe(pipe_slow); %} -instruct reduce_mulF(vRegF dst, vRegF fsrc, vReg vsrc, vReg tmp) %{ - predicate(Matcher::vector_length_in_bytes(n->in(2)) <= 16); - match(Set dst (MulReductionVF fsrc vsrc)); +dnl REDUCE_MUL_FP($1, $2 ) +dnl REDUCE_MUL_FP(insn_name, op_name) +define(`REDUCE_MUL_FP', ` +instruct reduce_mul$1(vReg$2 dst, vReg$2 ifelse($2, F, fsrc, dsrc), vReg vsrc, vReg tmp) %{ + predicate(Matcher::vector_length_in_bytes(n->in(2)) ifelse($2, F, <=, ==) 16); + ifelse($2, F, + `match(Set dst (MulReductionVHF fsrc vsrc)); + match(Set dst (MulReductionV$2 fsrc vsrc));', + `match(Set dst (MulReductionV$2 dsrc vsrc));') effect(TEMP_DEF dst, TEMP tmp); - format %{ "reduce_mulF $dst, $fsrc, $vsrc\t# 2F/4F. KILL $tmp" %} - ins_encode %{ - uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); - __ neon_reduce_mul_fp($dst$$FloatRegister, T_FLOAT, $fsrc$$FloatRegister, - $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister); + ifelse($2, F, + `format %{ "reduce_mul$1 $dst, $fsrc, $vsrc\t# 2F/4F/4HF/8HF. KILL $tmp" %}', + `format %{ "reduce_mul$1 $dst, $dsrc, $vsrc\t# 2D. KILL $tmp" %}') + ins_encode %{ + ifelse($2, F, + `uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + ',)dnl +ifelse($2, F, + `BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + ',)dnl +ifelse($2, F, + `__ neon_reduce_mul_fp($dst$$FloatRegister, bt, $fsrc$$FloatRegister, + $vsrc$$FloatRegister, length_in_bytes, $tmp$$FloatRegister);', + `__ neon_reduce_mul_fp($dst$$FloatRegister, T_DOUBLE, $dsrc$$FloatRegister, + $vsrc$$FloatRegister, 16, $tmp$$FloatRegister);') %} ins_pipe(pipe_slow); -%} - -instruct reduce_mulD(vRegD dst, vRegD dsrc, vReg vsrc, vReg tmp) %{ - predicate(Matcher::vector_length_in_bytes(n->in(2)) == 16); - match(Set dst (MulReductionVD dsrc vsrc)); - effect(TEMP_DEF dst, TEMP tmp); - format %{ "reduce_mulD $dst, $dsrc, $vsrc\t# 2D. KILL $tmp" %} - ins_encode %{ - __ neon_reduce_mul_fp($dst$$FloatRegister, T_DOUBLE, $dsrc$$FloatRegister, - $vsrc$$FloatRegister, 16, $tmp$$FloatRegister); - %} - ins_pipe(pipe_slow); -%} +%}')dnl +dnl +REDUCE_MUL_FP(FHF, F) +REDUCE_MUL_FP(D, D) dnl dnl REDUCE_BITWISE_OP_NEON($1, $2 $3 $4 ) diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index ebd8f3a9e03c..4c1c8d9bbc80 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -1000,30 +1000,6 @@ class Assembler : public AbstractAssembler { f(0b0101010, 31, 25), f(0, 24), sf(offset, 23, 5), f(0, 4), f(cond, 3, 0); } -#define INSN(NAME, cond) \ - void NAME(address dest) { \ - br(cond, dest); \ - } - - INSN(beq, EQ); - INSN(bne, NE); - INSN(bhs, HS); - INSN(bcs, CS); - INSN(blo, LO); - INSN(bcc, CC); - INSN(bmi, MI); - INSN(bpl, PL); - INSN(bvs, VS); - INSN(bvc, VC); - INSN(bhi, HI); - INSN(bls, LS); - INSN(bge, GE); - INSN(blt, LT); - INSN(bgt, GT); - INSN(ble, LE); - INSN(bal, AL); - INSN(bnv, NV); - void br(Condition cc, Label &L); #undef INSN diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 7aab7d389e1b..3c179f21c149 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright 2026 Arm Limited and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1883,6 +1884,27 @@ void C2_MacroAssembler::neon_reduce_mul_fp(FloatRegister dst, BasicType bt, BLOCK_COMMENT("neon_reduce_mul_fp {"); switch(bt) { + // The T_SHORT type below is for Float16 type which also uses floating-point + // instructions. + case T_SHORT: + fmulh(dst, fsrc, vsrc); + ext(vtmp, T8B, vsrc, vsrc, 2); + fmulh(dst, dst, vtmp); + ext(vtmp, T8B, vsrc, vsrc, 4); + fmulh(dst, dst, vtmp); + ext(vtmp, T8B, vsrc, vsrc, 6); + fmulh(dst, dst, vtmp); + if (isQ) { + ext(vtmp, T16B, vsrc, vsrc, 8); + fmulh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 10); + fmulh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 12); + fmulh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 14); + fmulh(dst, dst, vtmp); + } + break; case T_FLOAT: fmuls(dst, fsrc, vsrc); ins(vtmp, S, vsrc, 0, 1); @@ -1907,6 +1929,33 @@ void C2_MacroAssembler::neon_reduce_mul_fp(FloatRegister dst, BasicType bt, BLOCK_COMMENT("} neon_reduce_mul_fp"); } +// Vector reduction add for half float type with ASIMD instructions. +void C2_MacroAssembler::neon_reduce_add_fp16(FloatRegister dst, FloatRegister fsrc, FloatRegister vsrc, + unsigned vector_length_in_bytes, FloatRegister vtmp) { + assert(vector_length_in_bytes == 8 || vector_length_in_bytes == 16, "unsupported"); + bool isQ = vector_length_in_bytes == 16; + + BLOCK_COMMENT("neon_reduce_add_fp16 {"); + faddh(dst, fsrc, vsrc); + ext(vtmp, T8B, vsrc, vsrc, 2); + faddh(dst, dst, vtmp); + ext(vtmp, T8B, vsrc, vsrc, 4); + faddh(dst, dst, vtmp); + ext(vtmp, T8B, vsrc, vsrc, 6); + faddh(dst, dst, vtmp); + if (isQ) { + ext(vtmp, T16B, vsrc, vsrc, 8); + faddh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 10); + faddh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 12); + faddh(dst, dst, vtmp); + ext(vtmp, T16B, vsrc, vsrc, 14); + faddh(dst, dst, vtmp); + } + BLOCK_COMMENT("} neon_reduce_add_fp16"); +} + // Helper to select logical instruction void C2_MacroAssembler::neon_reduce_logical_helper(int opc, bool is64, Register Rd, Register Rn, Register Rm, @@ -2414,17 +2463,17 @@ void C2_MacroAssembler::neon_rearrange_hsd(FloatRegister dst, FloatRegister src, break; case T_LONG: case T_DOUBLE: - // Load the iota indices for Long type. The indices are ordered by - // type B/S/I/L/F/D, and the offset between two types is 16; Hence - // the offset for L is 48. - lea(rscratch1, - ExternalAddress(StubRoutines::aarch64::vector_iota_indices() + 48)); - ldrq(tmp, rscratch1); - // Check whether the input "shuffle" is the same with iota indices. - // Return "src" if true, otherwise swap the two elements of "src". - cm(EQ, dst, size2, shuffle, tmp); - ext(tmp, size1, src, src, 8); - bsl(dst, size1, src, tmp); + { + int idx = vector_iota_entry_index(T_LONG); + lea(rscratch1, + ExternalAddress(StubRoutines::aarch64::vector_iota_indices(idx))); + ldrq(tmp, rscratch1); + // Check whether the input "shuffle" is the same with iota indices. + // Return "src" if true, otherwise swap the two elements of "src". + cm(EQ, dst, size2, shuffle, tmp); + ext(tmp, size1, src, src, 8); + bsl(dst, size1, src, tmp); + } break; default: assert(false, "unsupported element type"); @@ -2896,3 +2945,24 @@ void C2_MacroAssembler::sve_cpy(FloatRegister dst, SIMD_RegVariant T, } Assembler::sve_cpy(dst, T, pg, imm8, isMerge); } + +int C2_MacroAssembler::vector_iota_entry_index(BasicType bt) { + // The vector iota entries array is ordered by type B/S/I/L/F/D, and + // the offset between two types is 16. + switch(bt) { + case T_BYTE: + return 0; + case T_SHORT: + return 1; + case T_INT: + return 2; + case T_LONG: + return 3; + case T_FLOAT: + return 4; + case T_DOUBLE: + return 5; + default: + ShouldNotReachHere(); + } +} diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index 5c05832afbeb..f96d3ffb8635 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -177,6 +177,9 @@ FloatRegister fsrc, FloatRegister vsrc, unsigned vector_length_in_bytes, FloatRegister vtmp); + void neon_reduce_add_fp16(FloatRegister dst, FloatRegister fsrc, FloatRegister vsrc, + unsigned vector_length_in_bytes, FloatRegister vtmp); + void neon_reduce_logical(int opc, Register dst, BasicType bt, Register isrc, FloatRegister vsrc, unsigned vector_length_in_bytes); @@ -249,4 +252,5 @@ void sve_cpy(FloatRegister dst, SIMD_RegVariant T, PRegister pg, int imm8, bool isMerge); + int vector_iota_entry_index(BasicType bt); #endif // CPU_AARCH64_C2_MACROASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 7fa2e8086ad4..7bec0a3c0caf 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -55,6 +55,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/integerCast.hpp" #include "utilities/powerOfTwo.hpp" #ifdef COMPILER1 #include "c1/c1_LIRAssembler.hpp" @@ -2916,7 +2917,11 @@ void MacroAssembler::increment(Address dst, int value) // Push lots of registers in the bit set supplied. Don't push sp. // Return the number of words pushed -int MacroAssembler::push(unsigned int bitset, Register stack) { +int MacroAssembler::push(RegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int words_pushed = 0; // Scan bitset to accumulate register pairs @@ -2946,7 +2951,11 @@ int MacroAssembler::push(unsigned int bitset, Register stack) { return count; } -int MacroAssembler::pop(unsigned int bitset, Register stack) { +int MacroAssembler::pop(RegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int words_pushed = 0; // Scan bitset to accumulate register pairs @@ -2978,7 +2987,11 @@ int MacroAssembler::pop(unsigned int bitset, Register stack) { // Push lots of registers in the bit set supplied. Don't push sp. // Return the number of dwords pushed -int MacroAssembler::push_fp(unsigned int bitset, Register stack, FpPushPopMode mode) { +int MacroAssembler::push_fp(FloatRegSet regset, Register stack, FpPushPopMode mode) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int words_pushed = 0; bool use_sve = false; int sve_vector_size_in_bytes = 0; @@ -3091,7 +3104,11 @@ int MacroAssembler::push_fp(unsigned int bitset, Register stack, FpPushPopMode m } // Return the number of dwords popped -int MacroAssembler::pop_fp(unsigned int bitset, Register stack, FpPushPopMode mode) { +int MacroAssembler::pop_fp(FloatRegSet regset, Register stack, FpPushPopMode mode) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int words_pushed = 0; bool use_sve = false; int sve_vector_size_in_bytes = 0; @@ -3201,7 +3218,11 @@ int MacroAssembler::pop_fp(unsigned int bitset, Register stack, FpPushPopMode mo } // Return the number of dwords pushed -int MacroAssembler::push_p(unsigned int bitset, Register stack) { +int MacroAssembler::push_p(PRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); bool use_sve = false; int sve_predicate_size_in_slots = 0; @@ -3238,7 +3259,11 @@ int MacroAssembler::push_p(unsigned int bitset, Register stack) { } // Return the number of dwords popped -int MacroAssembler::pop_p(unsigned int bitset, Register stack) { +int MacroAssembler::pop_p(PRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); bool use_sve = false; int sve_predicate_size_in_slots = 0; diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 994fbe3c80fe..a6cc862d05ca 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -499,29 +499,20 @@ class MacroAssembler: public Assembler { void mov_immediate64(Register dst, uint64_t imm64); void mov_immediate32(Register dst, uint32_t imm32); - int push(unsigned int bitset, Register stack); - int pop(unsigned int bitset, Register stack); - - int push_fp(unsigned int bitset, Register stack, FpPushPopMode mode); - int pop_fp(unsigned int bitset, Register stack, FpPushPopMode mode); - - int push_p(unsigned int bitset, Register stack); - int pop_p(unsigned int bitset, Register stack); - void mov(Register dst, Address a); public: - void push(RegSet regs, Register stack) { if (regs.bits()) push(regs.bits(), stack); } - void pop(RegSet regs, Register stack) { if (regs.bits()) pop(regs.bits(), stack); } + int push(RegSet regset, Register stack); + int pop(RegSet regset, Register stack); - void push_fp(FloatRegSet regs, Register stack, FpPushPopMode mode = PushPopFull) { if (regs.bits()) push_fp(regs.bits(), stack, mode); } - void pop_fp(FloatRegSet regs, Register stack, FpPushPopMode mode = PushPopFull) { if (regs.bits()) pop_fp(regs.bits(), stack, mode); } + int push_fp(FloatRegSet regset, Register stack, FpPushPopMode mode = PushPopFull); + int pop_fp(FloatRegSet regset, Register stack, FpPushPopMode mode = PushPopFull); static RegSet call_clobbered_gp_registers(); - void push_p(PRegSet regs, Register stack) { if (regs.bits()) push_p(regs.bits(), stack); } - void pop_p(PRegSet regs, Register stack) { if (regs.bits()) pop_p(regs.bits(), stack); } + int push_p(PRegSet regset, Register stack); + int pop_p(PRegSet regset, Register stack); // Push and pop everything that might be clobbered by a native // runtime call except rscratch1 and rscratch2. (They are always @@ -899,10 +890,6 @@ class MacroAssembler: public Assembler { // thread in the default location (rthread) void reset_last_Java_frame(bool clear_fp); - // Stores - void store_check(Register obj); // store check for obj - register is destroyed afterwards - void store_check(Register obj, Address dst); // same as above, dst is exact store location (reg. is destroyed) - void resolve_jobject(Register value, Register tmp1, Register tmp2); void resolve_global_jobject(Register value, Register tmp1, Register tmp2); diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp index fc7274714ad7..ab9896fa4266 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp @@ -97,7 +97,7 @@ class NativeInstruction { #define MACOS_WX_WRITE MACOS_AARCH64_ONLY(os::thread_wx_enable_write()) void set_char_at(int offset, char c) { MACOS_WX_WRITE; *addr_at(offset) = (u_char)c; } void set_int_at(int offset, jint i) { MACOS_WX_WRITE; *(jint*)addr_at(offset) = i; } - void set_uint_at(int offset, jint i) { MACOS_WX_WRITE; *(juint*)addr_at(offset) = i; } + void set_uint_at(int offset, juint i) { MACOS_WX_WRITE; *(juint*)addr_at(offset) = i; } void set_ptr_at(int offset, address ptr) { MACOS_WX_WRITE; *(address*)addr_at(offset) = ptr; } void set_oop_at(int offset, oop o) { MACOS_WX_WRITE; *(oop*)addr_at(offset) = o; } #undef MACOS_WX_WRITE @@ -178,13 +178,11 @@ class NativeCall: public NativeInstruction { address destination() const; void set_destination(address dest) { - int offset = dest - instruction_address(); - unsigned int insn = 0b100101 << 26; + int64_t offset = dest - instruction_address(); + juint insn = 0b100101u << 26u; assert((offset & 3) == 0, "should be"); - offset >>= 2; - offset &= (1 << 26) - 1; // mask off insn part - insn |= offset; - set_int_at(displacement_offset, insn); + Instruction_aarch64::spatch(reinterpret_cast
(&insn), 25, 0, offset >> 2); + set_uint_at(displacement_offset, insn); } void verify_alignment() { ; } diff --git a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp index 9dac6a39b82b..d1f59e479dbd 100644 --- a/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubDeclarations_aarch64.hpp @@ -29,32 +29,39 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 10000) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ +// count needed for declaration of vector_iota_indices stub +#define VECTOR_IOTA_COUNT 6 #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 70000) \ do_stub(compiler, vector_iota_indices) \ - do_arch_entry(aarch64, compiler, vector_iota_indices, \ - vector_iota_indices, vector_iota_indices) \ + do_arch_entry_array(aarch64, compiler, vector_iota_indices, \ + vector_iota_indices, vector_iota_indices, \ + VECTOR_IOTA_COUNT) \ do_stub(compiler, large_array_equals) \ do_arch_entry(aarch64, compiler, large_array_equals, \ large_array_equals, large_array_equals) \ @@ -115,7 +122,8 @@ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 20000 ZGC_ONLY(+85000)) \ do_stub(final, copy_byte_f) \ do_arch_entry(aarch64, final, copy_byte_f, copy_byte_f, \ diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 32fd8afb2685..fddb37b7b8d8 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -819,12 +819,19 @@ class StubGenerator: public StubCodeGenerator { } // Generate indices for iota vector. - address generate_iota_indices(StubId stub_id) { + void generate_iota_indices(StubId stub_id) { + GrowableArray
entries; int entry_count = StubInfo::entry_count(stub_id); - assert(entry_count == 1, "sanity check"); - address start = load_archive_data(stub_id); + assert(entry_count == VECTOR_IOTA_COUNT, "sanity check"); + address start = load_archive_data(stub_id, &entries); if (start != nullptr) { - return start; + assert(entries.length() == entry_count - 1, + "unexpected entries count %d", entries.length()); + StubRoutines::aarch64::_vector_iota_indices[0] = start; + for (int i = 1; i < VECTOR_IOTA_COUNT; i++) { + StubRoutines::aarch64::_vector_iota_indices[i] = entries.at(i - 1); + } + return; } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); @@ -832,26 +839,37 @@ class StubGenerator: public StubCodeGenerator { // B __ emit_data64(0x0706050403020100, relocInfo::none); __ emit_data64(0x0F0E0D0C0B0A0908, relocInfo::none); + entries.append(__ pc()); // H __ emit_data64(0x0003000200010000, relocInfo::none); __ emit_data64(0x0007000600050004, relocInfo::none); + entries.append(__ pc()); // S __ emit_data64(0x0000000100000000, relocInfo::none); __ emit_data64(0x0000000300000002, relocInfo::none); + entries.append(__ pc()); // D __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0x0000000000000001, relocInfo::none); + entries.append(__ pc()); // S - FP __ emit_data64(0x3F80000000000000, relocInfo::none); // 0.0f, 1.0f __ emit_data64(0x4040000040000000, relocInfo::none); // 2.0f, 3.0f + entries.append(__ pc()); // D - FP __ emit_data64(0x0000000000000000, relocInfo::none); // 0.0d __ emit_data64(0x3FF0000000000000, relocInfo::none); // 1.0d // record the stub entry and end - store_archive_data(stub_id, start, __ pc()); + store_archive_data(stub_id, start, __ pc(), &entries); - return start; + // install the entry addresses in the entry array + assert(entries.length() == entry_count - 1, + "unexpected entries count %d", entries.length()); + StubRoutines::aarch64::_vector_iota_indices[0] = start; + for (int i = 1; i < VECTOR_IOTA_COUNT; i++) { + StubRoutines::aarch64::_vector_iota_indices[i] = entries.at(i - 1); + } } // The inner part of zero_words(). This is the bulk operation, @@ -12621,7 +12639,7 @@ class StubGenerator: public StubCodeGenerator { #if COMPILER2_OR_JVMCI if (UseSVE == 0) { - StubRoutines::aarch64::_vector_iota_indices = generate_iota_indices(StubId::stubgen_vector_iota_indices_id); + generate_iota_indices(StubId::stubgen_vector_iota_indices_id); } // array equals stub for large arrays. @@ -12807,7 +12825,7 @@ class StubGenerator: public StubCodeGenerator { #if INCLUDE_CDS static void init_AOTAddressTable(GrowableArray
& external_addresses) { // external data defined in this file -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); ADD(_sha256_round_consts); ADD(_sha512_round_consts); ADD(_sha3_round_consts); diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp index 35ec22b08973..f02b681ca109 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp @@ -41,8 +41,12 @@ static void empty_spin_wait() { } #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +#define DEFINE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) [count]; +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) + +#undef DEFINE_ARCH_ENTRY_ARARAY #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY @@ -431,10 +435,8 @@ void StubRoutines::init_AOTAddressTable() { AOTCodeCache::publish_external_addresses(external_addresses); } - -#define ADD(addr) external_addresses.append((address)addr); - void StubRoutines::aarch64::init_AOTAddressTable(GrowableArray
& external_addresses) { +#define ADD(addr) external_addresses.append((address)(addr)); ADD(_kyberConsts); ADD(_dilithiumConsts); // this is added in generic code @@ -445,7 +447,6 @@ void StubRoutines::aarch64::init_AOTAddressTable(GrowableArray
& externa ADD(_dcos_coef); ADD(_two_over_pi); ADD(_pio2); -} - #undef ADD +} #endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp index f77192a37418..6067408ef138 100644 --- a/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp @@ -60,9 +60,13 @@ class aarch64 { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count]; + private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -78,8 +82,15 @@ class aarch64 { #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { \ + assert(0 <= idx && idx < count, "entry array index out of range"); \ + return STUB_FIELD_NAME(field_name) [idx]; \ + } + + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_ENTRY_GETTER diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 15af2d5c4e21..441bd4859fe8 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -437,10 +437,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA3Intrinsics || UseSHA512Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - if (supports_pmull()) { if (FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { FLAG_SET_DEFAULT(UseGHASHIntrinsics, true); diff --git a/src/hotspot/cpu/arm/stubDeclarations_arm.hpp b/src/hotspot/cpu/arm/stubDeclarations_arm.hpp index 5f768a205a54..5fb0d4e901fe 100644 --- a/src/hotspot/cpu/arm/stubDeclarations_arm.hpp +++ b/src/hotspot/cpu/arm/stubDeclarations_arm.hpp @@ -29,7 +29,8 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 500) \ do_stub(preuniverse, atomic_load_long) \ do_arch_entry(Arm, preuniverse, atomic_load_long, \ @@ -42,7 +43,8 @@ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 9000) \ do_stub(initial, idiv_irem) \ do_arch_entry(Arm, initial, idiv_irem, \ @@ -51,14 +53,16 @@ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 22000) \ do_stub(compiler, partial_subtype_check) \ do_arch_entry(Arm, compiler, partial_subtype_check, \ @@ -68,7 +72,8 @@ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 22000) \ diff --git a/src/hotspot/cpu/arm/stubRoutines_arm.cpp b/src/hotspot/cpu/arm/stubRoutines_arm.cpp index 3ed747ea11a2..38a9b298562f 100644 --- a/src/hotspot/cpu/arm/stubRoutines_arm.cpp +++ b/src/hotspot/cpu/arm/stubRoutines_arm.cpp @@ -32,7 +32,7 @@ #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY diff --git a/src/hotspot/cpu/arm/stubRoutines_arm.hpp b/src/hotspot/cpu/arm/stubRoutines_arm.hpp index 45ab10d14f99..29d96d0e6535 100644 --- a/src/hotspot/cpu/arm/stubRoutines_arm.hpp +++ b/src/hotspot/cpu/arm/stubRoutines_arm.hpp @@ -55,9 +55,13 @@ class Arm { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count] ; + private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -71,8 +75,12 @@ class Arm { #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { return STUB_FIELD_NAME(field_name) [idx] ; } + + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_ENTRY_GETTER diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 5b5728560d74..0b48653ae64c 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -176,11 +176,6 @@ void LIR_Assembler::osr_entry() { // Restore. __ sub_const_optimized(OSR_buf, OSR_buf, locals_space); } - - if (use_OSR_bias) { - // Restore. - __ sub_const_optimized(OSR_buf, OSR_buf, locals_space); - } } } diff --git a/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp b/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp index be51afe42a44..41b8b71486df 100644 --- a/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp +++ b/src/hotspot/cpu/ppc/stubDeclarations_ppc.hpp @@ -29,35 +29,40 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 20000) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 24000) \ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 24000) \ diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index 0b69ef7d25a3..3e3b1103c864 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -311,11 +311,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - - #ifdef COMPILER2 if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) { UseSquareToLenIntrinsic = true; diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index a2b7970f9f69..0e32c602d953 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -49,6 +49,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/integerCast.hpp" #include "utilities/powerOfTwo.hpp" #ifdef COMPILER2 #include "opto/compile.hpp" @@ -1947,14 +1948,12 @@ void MacroAssembler::restore_cpu_control_state_after_jni(Register tmp) { } } -void MacroAssembler::push_reg(Register Rs) -{ +void MacroAssembler::push_reg(Register Rs) { subi(esp, esp, wordSize); sd(Rs, Address(esp, 0)); } -void MacroAssembler::pop_reg(Register Rd) -{ +void MacroAssembler::pop_reg(Register Rd) { ld(Rd, Address(esp, 0)); addi(esp, esp, wordSize); } @@ -1973,7 +1972,11 @@ int MacroAssembler::bitset_to_regs(unsigned int bitset, unsigned char* regs) { // Push integer registers in the bitset supplied. Don't push sp. // Return the number of words pushed -int MacroAssembler::push_reg(unsigned int bitset, Register stack) { +int MacroAssembler::push_reg(RegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); DEBUG_ONLY(int words_pushed = 0;) unsigned char regs[32]; int count = bitset_to_regs(bitset, regs); @@ -1993,7 +1996,11 @@ int MacroAssembler::push_reg(unsigned int bitset, Register stack) { return count; } -int MacroAssembler::pop_reg(unsigned int bitset, Register stack) { +int MacroAssembler::pop_reg(RegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); DEBUG_ONLY(int words_popped = 0;) unsigned char regs[32]; int count = bitset_to_regs(bitset, regs); @@ -2015,7 +2022,11 @@ int MacroAssembler::pop_reg(unsigned int bitset, Register stack) { // Push floating-point registers in the bitset supplied. // Return the number of words pushed -int MacroAssembler::push_fp(unsigned int bitset, Register stack) { +int MacroAssembler::push_fp(FloatRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); DEBUG_ONLY(int words_pushed = 0;) unsigned char regs[32]; int count = bitset_to_regs(bitset, regs); @@ -2035,7 +2046,11 @@ int MacroAssembler::push_fp(unsigned int bitset, Register stack) { return count; } -int MacroAssembler::pop_fp(unsigned int bitset, Register stack) { +int MacroAssembler::pop_fp(FloatRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); DEBUG_ONLY(int words_popped = 0;) unsigned char regs[32]; int count = bitset_to_regs(bitset, regs); @@ -2721,7 +2736,11 @@ void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len, #ifdef COMPILER2 // Push vector registers in the bitset supplied. // Return the number of words pushed -int MacroAssembler::push_v(unsigned int bitset, Register stack) { +int MacroAssembler::push_v(VectorRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int vector_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE); // Scan bitset to accumulate register pairs @@ -2736,7 +2755,11 @@ int MacroAssembler::push_v(unsigned int bitset, Register stack) { return count * vector_size_in_bytes / wordSize; } -int MacroAssembler::pop_v(unsigned int bitset, Register stack) { +int MacroAssembler::pop_v(VectorRegSet regset, Register stack) { + if (regset.bits() == 0) { + return 0; + } + auto bitset = integer_cast(regset.bits()); int vector_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE); // Scan bitset to accumulate register pairs diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index a51a6aea468e..4cc55e7ae23b 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -818,15 +818,6 @@ class MacroAssembler: public Assembler { void double_bgt(FloatRegister Rs1, FloatRegister Rs2, Label &l, bool is_far = false, bool is_unordered = false); private: - int push_reg(unsigned int bitset, Register stack); - int pop_reg(unsigned int bitset, Register stack); - int push_fp(unsigned int bitset, Register stack); - int pop_fp(unsigned int bitset, Register stack); -#ifdef COMPILER2 - int push_v(unsigned int bitset, Register stack); - int pop_v(unsigned int bitset, Register stack); -#endif // COMPILER2 - // The signed 20-bit upper imm can materialize at most negative 0xF...F80000000, two G. // The following signed 12-bit imm can at max subtract 0x800, two K, from that previously loaded two G. bool is_valid_32bit_offset(int64_t x) { @@ -844,15 +835,19 @@ class MacroAssembler: public Assembler { } public: + // Stack push and pop individual 64 bit registers void push_reg(Register Rs); void pop_reg(Register Rd); - void push_reg(RegSet regs, Register stack) { if (regs.bits()) push_reg(regs.bits(), stack); } - void pop_reg(RegSet regs, Register stack) { if (regs.bits()) pop_reg(regs.bits(), stack); } - void push_fp(FloatRegSet regs, Register stack) { if (regs.bits()) push_fp(regs.bits(), stack); } - void pop_fp(FloatRegSet regs, Register stack) { if (regs.bits()) pop_fp(regs.bits(), stack); } + + int push_reg(RegSet regset, Register stack); + int pop_reg(RegSet regset, Register stack); + + int push_fp(FloatRegSet regset, Register stack); + int pop_fp(FloatRegSet regset, Register stack); + #ifdef COMPILER2 - void push_v(VectorRegSet regs, Register stack) { if (regs.bits()) push_v(regs.bits(), stack); } - void pop_v(VectorRegSet regs, Register stack) { if (regs.bits()) pop_v(regs.bits(), stack); } + int push_v(VectorRegSet regset, Register stack); + int pop_v(VectorRegSet regset, Register stack); #endif // COMPILER2 // Push and pop everything that might be clobbered by a native diff --git a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp index f977d759d204..890e354fd278 100644 --- a/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp +++ b/src/hotspot/cpu/riscv/stubDeclarations_riscv.hpp @@ -29,28 +29,32 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 10000) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 45000) \ do_stub(compiler, compare_long_string_LL) \ do_arch_entry(riscv, compiler, compare_long_string_LL, \ @@ -81,7 +85,8 @@ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 20000 ZGC_ONLY(+10000)) \ do_stub(final, copy_byte_f) \ do_arch_entry(riscv, final, copy_byte_f, copy_byte_f, \ diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp index 51e31aa3672d..b7f69eff9fa3 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp @@ -42,8 +42,12 @@ #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +#define DEFINE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) [count] ; +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) + +#undef DEFINE_ARCH_ENTRY_ARRAY #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp index 2c4e7210413f..ec67a3380521 100644 --- a/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp +++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.hpp @@ -61,9 +61,13 @@ class riscv { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count] ; + private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -79,8 +83,12 @@ class riscv { #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { return STUB_FIELD_NAME(field_name) [idx] ; } + + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_ENTRY_GETTER diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index 36f0864da0bb..3a6415d52bd3 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -420,11 +420,6 @@ void VM_Version::c2_initialize() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - // UseSHA - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA3Intrinsics || UseSHA512Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - // AES if (UseZvkn) { UseAES = UseAES || FLAG_IS_DEFAULT(UseAES); diff --git a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp index c3ad3cefeb97..d0e26beedab9 100644 --- a/src/hotspot/cpu/s390/stubDeclarations_s390.hpp +++ b/src/hotspot/cpu/s390/stubDeclarations_s390.hpp @@ -29,28 +29,32 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 20000) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 2000) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 20000 ) \ do_stub(compiler, partial_subtype_check) \ do_arch_entry(zarch, compiler, partial_subtype_check, \ @@ -60,7 +64,8 @@ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 20000) \ diff --git a/src/hotspot/cpu/s390/stubRoutines_s390.cpp b/src/hotspot/cpu/s390/stubRoutines_s390.cpp index 3db4995338de..eda0ebfdecc9 100644 --- a/src/hotspot/cpu/s390/stubRoutines_s390.cpp +++ b/src/hotspot/cpu/s390/stubRoutines_s390.cpp @@ -40,8 +40,12 @@ #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +#define DEFINE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) [idx] ; +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) + +#undef DEFINE_ARCH_ENTRY_ARRAY #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY diff --git a/src/hotspot/cpu/s390/stubRoutines_s390.hpp b/src/hotspot/cpu/s390/stubRoutines_s390.hpp index 0a07efae46c0..e575115b7319 100644 --- a/src/hotspot/cpu/s390/stubRoutines_s390.hpp +++ b/src/hotspot/cpu/s390/stubRoutines_s390.hpp @@ -81,9 +81,13 @@ class zarch { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count] ; + private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -108,8 +112,12 @@ class zarch { #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { return STUB_FIELD_NAME(field_name) [idx] ; } + + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_ENTRY_GETTER diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index 7f5b4870aab4..7e9000991cae 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -289,10 +289,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - if (UseSecondarySupersTable && VM_Version::get_model_index() < 5 /* z196/z11 */) { if (!FLAG_IS_DEFAULT(UseSecondarySupersTable)) { warning("UseSecondarySupersTable requires z196 or later."); diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 29925e71aafc..5c05b3702bb3 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -74,7 +74,7 @@ static jlong *double_signflip_pool = double_quadword(&fp_signmask_pool[4*2], (jl #if INCLUDE_CDS // publish external addresses defined in this file void LIR_Assembler::init_AOTAddressTable(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); ADD(float_signmask_pool); ADD(double_signmask_pool); ADD(float_signflip_pool); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index f36c816dd5e4..b4d8aa10de28 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -1706,12 +1706,8 @@ void C2_MacroAssembler::load_constant_vector(BasicType bt, XMMRegister dst, Inte } void C2_MacroAssembler::load_iota_indices(XMMRegister dst, int vlen_in_bytes, BasicType bt) { - // The iota indices are ordered by type B/S/I/L/F/D, and the offset between two types is 64. - int offset = exact_log2(type2aelembytes(bt)) << 6; - if (is_floating_point_type(bt)) { - offset += 128; - } - ExternalAddress addr(StubRoutines::x86::vector_iota_indices() + offset); + int entry_idx = vector_iota_entry_index(bt); + ExternalAddress addr(StubRoutines::x86::vector_iota_indices(entry_idx)); load_vector(T_BYTE, dst, addr, vlen_in_bytes); } @@ -7164,3 +7160,24 @@ void C2_MacroAssembler::vminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMReg evminmaxph(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MIN_COMPARE_SIGN, vlen_enc); } } + +int C2_MacroAssembler::vector_iota_entry_index(BasicType bt) { + // The vector iota entries array is ordered by type B/S/I/L/F/D, and + // the offset between two types is 16. + switch(bt) { + case T_BYTE: + return 0; + case T_SHORT: + return 1; + case T_INT: + return 2; + case T_LONG: + return 3; + case T_FLOAT: + return 4; + case T_DOUBLE: + return 5; + default: + ShouldNotReachHere(); + } +} diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index 4e77f8a5f6f6..9b229ad72219 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -596,4 +596,5 @@ void reconstruct_frame_pointer(Register rtmp); + int vector_iota_entry_index(BasicType bt); #endif // CPU_X86_C2_MACROASSEMBLER_X86_HPP diff --git a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp index 07a1ab622edb..24886deb3c5f 100644 --- a/src/hotspot/cpu/x86/stubDeclarations_x86.hpp +++ b/src/hotspot/cpu/x86/stubDeclarations_x86.hpp @@ -29,14 +29,16 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 500) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, PRODUCT_ONLY(20000) NOT_PRODUCT(21000) WINDOWS_ONLY(+1000)) \ do_stub(initial, verify_mxcsr) \ do_arch_entry(x86, initial, verify_mxcsr, verify_mxcsr_entry, \ @@ -65,14 +67,18 @@ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 3000) \ +// count needed for declaration of vector_iota_indices stub +#define VECTOR_IOTA_COUNT 6 #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 120000 WINDOWS_ONLY(+2000)) \ do_stub(compiler, vector_float_sign_mask) \ do_arch_entry(x86, compiler, vector_float_sign_mask, \ @@ -126,8 +132,9 @@ do_arch_entry(x86, compiler, vector_long_sign_mask, \ vector_long_sign_mask, vector_long_sign_mask) \ do_stub(compiler, vector_iota_indices) \ - do_arch_entry(x86, compiler, vector_iota_indices, \ - vector_iota_indices, vector_iota_indices) \ + do_arch_entry_array(x86, compiler, vector_iota_indices, \ + vector_iota_indices, vector_iota_indices, \ + VECTOR_IOTA_COUNT) \ do_stub(compiler, vector_count_leading_zeros_lut) \ do_arch_entry(x86, compiler, vector_count_leading_zeros_lut, \ vector_count_leading_zeros_lut, \ @@ -250,7 +257,8 @@ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 33000 \ WINDOWS_ONLY(+22000) ZGC_ONLY(+20000)) \ diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 40be816fbf0f..993d19640340 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -893,13 +893,20 @@ address StubGenerator::generate_popcount_avx_lut() { return start; } -address StubGenerator::generate_iota_indices() { +void StubGenerator::generate_iota_indices() { StubId stub_id = StubId::stubgen_vector_iota_indices_id; + GrowableArray
entries; int entry_count = StubInfo::entry_count(stub_id); - assert(entry_count == 1, "sanity check"); - address start = load_archive_data(stub_id); + assert(entry_count == VECTOR_IOTA_COUNT, "sanity check"); + address start = load_archive_data(stub_id, &entries); if (start != nullptr) { - return start; + assert(entries.length() == VECTOR_IOTA_COUNT - 1, + "unexpected extra entry count %d", entries.length()); + StubRoutines::x86::_vector_iota_indices[0] = start; + for (int i = 1; i < VECTOR_IOTA_COUNT; i++) { + StubRoutines::x86::_vector_iota_indices[i] = entries.at(i - 1); + } + return; } __ align(CodeEntryAlignment); StubCodeMark mark(this, stub_id); @@ -913,6 +920,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x2F2E2D2C2B2A2928, relocInfo::none); __ emit_data64(0x3736353433323130, relocInfo::none); __ emit_data64(0x3F3E3D3C3B3A3938, relocInfo::none); + entries.append(__ pc()); // W __ emit_data64(0x0003000200010000, relocInfo::none); __ emit_data64(0x0007000600050004, relocInfo::none); @@ -922,6 +930,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x0017001600150014, relocInfo::none); __ emit_data64(0x001B001A00190018, relocInfo::none); __ emit_data64(0x001F001E001D001C, relocInfo::none); + entries.append(__ pc()); // D __ emit_data64(0x0000000100000000, relocInfo::none); __ emit_data64(0x0000000300000002, relocInfo::none); @@ -931,6 +940,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x0000000B0000000A, relocInfo::none); __ emit_data64(0x0000000D0000000C, relocInfo::none); __ emit_data64(0x0000000F0000000E, relocInfo::none); + entries.append(__ pc()); // Q __ emit_data64(0x0000000000000000, relocInfo::none); __ emit_data64(0x0000000000000001, relocInfo::none); @@ -940,6 +950,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x0000000000000005, relocInfo::none); __ emit_data64(0x0000000000000006, relocInfo::none); __ emit_data64(0x0000000000000007, relocInfo::none); + entries.append(__ pc()); // D - FP __ emit_data64(0x3F80000000000000, relocInfo::none); // 0.0f, 1.0f __ emit_data64(0x4040000040000000, relocInfo::none); // 2.0f, 3.0f @@ -949,6 +960,7 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x4130000041200000, relocInfo::none); // 10.0f, 11.0f __ emit_data64(0x4150000041400000, relocInfo::none); // 12.0f, 13.0f __ emit_data64(0x4170000041600000, relocInfo::none); // 14.0f, 15.0f + entries.append(__ pc()); // Q - FP __ emit_data64(0x0000000000000000, relocInfo::none); // 0.0d __ emit_data64(0x3FF0000000000000, relocInfo::none); // 1.0d @@ -960,9 +972,15 @@ address StubGenerator::generate_iota_indices() { __ emit_data64(0x401c000000000000, relocInfo::none); // 7.0d // record the stub entry and end - store_archive_data(stub_id, start, __ pc()); + store_archive_data(stub_id, start, __ pc(), &entries); - return start; + // install the entry addresses in the entry array + assert(entries.length() == entry_count - 1, + "unexpected entries count %d", entries.length()); + StubRoutines::x86::_vector_iota_indices[0] = start; + for (int i = 1; i < VECTOR_IOTA_COUNT; i++) { + StubRoutines::x86::_vector_iota_indices[i] = entries.at(i - 1); + } } address StubGenerator::generate_vector_reverse_bit_lut() { @@ -4837,7 +4855,7 @@ void StubGenerator::generate_compiler_stubs() { StubRoutines::x86::_vector_short_shuffle_mask = generate_vector_mask(StubId::stubgen_vector_short_shuffle_mask_id, 0x0100010001000100); StubRoutines::x86::_vector_long_shuffle_mask = generate_vector_mask(StubId::stubgen_vector_long_shuffle_mask_id, 0x0000000100000000); StubRoutines::x86::_vector_long_sign_mask = generate_vector_mask(StubId::stubgen_vector_long_sign_mask_id, 0x8000000000000000); - StubRoutines::x86::_vector_iota_indices = generate_iota_indices(); + generate_iota_indices(); StubRoutines::x86::_vector_count_leading_zeros_lut = generate_count_leading_zeros_lut(); StubRoutines::x86::_vector_reverse_bit_lut = generate_vector_reverse_bit_lut(); StubRoutines::x86::_vector_reverse_byte_perm_mask_long = generate_vector_reverse_byte_perm_mask_long(); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 05e8384d6364..d3823cb559fa 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -84,7 +84,7 @@ class StubGenerator: public StubCodeGenerator { address generate_count_leading_zeros_lut(); address generate_popcount_avx_lut(); - address generate_iota_indices(); + void generate_iota_indices(); address generate_vector_reverse_bit_lut(); address generate_vector_reverse_byte_perm_mask_long(); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp index 45c13b7b3972..19e1ca680b37 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_constants.cpp @@ -247,8 +247,9 @@ void StubGenerator::init_AOTAddressTable_constants(GrowableArray
& exter ADD(_SC_2); ADD(_SC_3); ADD(_SC_4); - ADD(_PI_4); - ADD(((address)_PI_4+8)); + // Use value which was already cast to (address): StubGenerator::PI_4; + ADD(PI_4); + ADD(PI_4 + 8); ADD(_PI32INV); ADD(_NEG_ZERO); ADD(_P_1); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp index 2ed9858bf0c7..3c8babcbecfc 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp @@ -397,13 +397,14 @@ address StubGenerator::generate_libmExp() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_exp(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); - ADD(_cv); - ADD(((address)_cv+16)); - ADD(((address)_cv+32)); - ADD(((address)_cv+48)); - ADD(((address)_cv+64)); - ADD(((address)_cv+80)); +#define ADD(addr) external_addresses.append((address)(addr)); + address cv = (address)_cv; + ADD(cv); + ADD(cv + 16); + ADD(cv + 32); + ADD(cv + 48); + ADD(cv + 64); + ADD(cv + 80); ADD(_mmask); ADD(_bias); ADD(_Tbl_addr); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp index f73c8ed459e1..f53985a13b7b 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_fmod.cpp @@ -537,7 +537,7 @@ address StubGenerator::generate_libmFmod() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_fmod(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); ADD(CONST_NaN); ADD(CONST_1p260); ADD(CONST_MAX); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp index 557fe6233510..9ebab07589eb 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_ghash.cpp @@ -558,7 +558,7 @@ void StubGenerator::generateHtbl_eight_blocks(Register htbl) { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_ghash(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); ADD(GHASH_SHUFFLE_MASK); ADD(GHASH_LONG_SWAP_MASK); ADD(GHASH_BYTE_SWAP_MASK); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp index 8849597c94b1..07683a51e3d1 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp @@ -729,22 +729,28 @@ address StubGenerator::generate_libmLog10() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_log(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); + address log2 = (address)_log2; + address coeff = (address)_coeff; + address LOG10_E = (address)_LOG10_E; + address log2_log10 = (address)_log2_log10; + address coeff_log10 = (address)_coeff_log10; + ADD(_L_tbl); - ADD(_log2); - ADD(((address)_log2+8)); - ADD(_coeff); - ADD(((address)_coeff+16)); - ADD(((address)_coeff+32)); + ADD(log2); + ADD(log2 + 8); + ADD(coeff); + ADD(coeff + 16); + ADD(coeff + 32); ADD(_HIGHSIGMASK_log10); - ADD(_LOG10_E); - ADD(((address)_LOG10_E+8)); + ADD(LOG10_E); + ADD(LOG10_E + 8); ADD(_L_tbl_log10); - ADD(_log2_log10); - ADD(((address)_log2_log10+8)); - ADD(_coeff_log10); - ADD(((address)_coeff_log10+16)); - ADD(((address)_coeff_log10+32)); + ADD(log2_log10); + ADD(log2_log10 + 8); + ADD(coeff_log10); + ADD(coeff_log10 + 16); + ADD(coeff_log10 + 32); #undef ADD } #endif // INCLUDE_CDS diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp index 1d0e961c82d9..ea7e6d642540 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly1305.cpp @@ -1709,7 +1709,7 @@ void StubGenerator::poly1305_msg_mul_reduce_vec4_avx2( #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_poly1305(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); ADD(POLY1305_PAD_MSG); ADD(POLY1305_MASK42); ADD(POLY1305_MASK44); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp index 4648fe03aa04..308a80429938 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_poly_mont.cpp @@ -788,7 +788,7 @@ address StubGenerator::generate_intpoly_assign() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_poly_mont(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); // use accessors to retrieve all correct addresses ADD(shift_1L()); ADD(shift_1R()); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp index 5ff09e2b3775..a9a6dc10da47 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp @@ -1875,25 +1875,30 @@ address StubGenerator::generate_libmPow() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_pow(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); + address HIGHMASK_Y = (address)_HIGHMASK_Y; + address e_coeff = (address)_e_coeff; + address coeff_h = (address)_coeff_h; + address coeff_pow = (address)_coeff_pow; + ADD(_HIGHSIGMASK); ADD(_LOG2_E); - ADD(_HIGHMASK_Y); - ADD((address)_HIGHMASK_Y+8); + ADD(HIGHMASK_Y); + ADD(HIGHMASK_Y + 8); ADD(_T_exp); - ADD(_e_coeff); - ADD((address)_e_coeff+16); - ADD((address)_e_coeff+32); - ADD(_coeff_h); - ADD((address)_coeff_h+8); + ADD(e_coeff); + ADD(e_coeff + 16); + ADD(e_coeff + 32); + ADD(coeff_h); + ADD(coeff_h + 8); ADD(_HIGHMASK_LOG_X); ADD(_HALFMASK); - ADD(_coeff_pow); - ADD((address)_coeff_pow+16); - ADD((address)_coeff_pow+32); - ADD((address)_coeff_pow+48); - ADD((address)_coeff_pow+64); - ADD((address)_coeff_pow+80); + ADD(coeff_pow); + ADD(coeff_pow + 16); + ADD(coeff_pow + 32); + ADD(coeff_pow + 48); + ADD(coeff_pow + 64); + ADD(coeff_pow + 80); ADD(_L_tbl_pow); ADD(_log2_pow); ADD(_DOUBLE2); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp index 075d25dcac89..58f81652a0c9 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp @@ -530,7 +530,7 @@ void StubGenerator::generate_sha3_stubs() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_sha3(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); ADD(round_constsAddr()); ADD(permsAndRotsAddr()); #undef ADD diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp index eaeaea2c5662..00c759a369ba 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sin.cpp @@ -661,7 +661,7 @@ address StubGenerator::generate_libmSin() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_sin(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); ADD(_ALL_ONES); #undef ADD } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp index f6e1d241948f..9969866cfc70 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sinh.cpp @@ -535,21 +535,25 @@ address StubGenerator::generate_libmSinh() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_sinh(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); - ADD(_L2E); - ADD(_L2E + 8); +#define ADD(addr) external_addresses.append((address)(addr)); + address L2E = (address)_L2E; + address cv = (address)_cv; + address pv = (address)_pv; + + ADD(L2E); + ADD(L2E + 8); ADD(_HALFMASK); ADD(_Shifter); - ADD(_cv); - ADD(_cv + 16); - ADD(_cv + 32); - ADD(_cv + 48); - ADD(_cv + 64); + ADD(cv); + ADD(cv + 16); + ADD(cv + 32); + ADD(cv + 48); + ADD(cv + 64); ADD(_T2f); ADD(_T2_neg_f); - ADD(_pv); - ADD(_pv + 16); - ADD(_pv + 32); + ADD(pv); + ADD(pv + 16); + ADD(pv + 32); ADD(_MASK3); #undef ADD } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp index 3bfa5a7277fc..9f91b9e8f841 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_tan.cpp @@ -1041,7 +1041,9 @@ address StubGenerator::generate_libmTan() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_tan(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); + address PI_4_tan = (address)_PI_4_tan; + ADD(_MUL16); ADD(_sign_mask_tan); ADD(_PI32INV_tan); @@ -1055,8 +1057,8 @@ void StubGenerator::init_AOTAddressTable_tan(GrowableArray
& external_ad ADD(_Q_7_tan); ADD(_Q_5_tan); ADD(_Q_3_tan); - ADD(_PI_4_tan); - ADD(((address)_PI_4_tan+8)); + ADD(PI_4_tan); + ADD(PI_4_tan + 8); ADD(_QQ_2_tan); #undef ADD } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp index dcf5f3eb824d..4f2fe8a460bb 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp @@ -511,20 +511,24 @@ address StubGenerator::generate_libmTanh() { #if INCLUDE_CDS void StubGenerator::init_AOTAddressTable_tanh(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); - ADD(_L2E); - ADD(_L2E + 8); +#define ADD(addr) external_addresses.append((address)(addr)); + address L2E = (address)_L2E; + address cv = (address)_cv; + address pv = (address)_pv; + + ADD(L2E); + ADD(L2E + 8); ADD(_HALFMASK); ADD(_ONEMASK); ADD(_TWOMASK); ADD(_Shifter); - ADD(_cv); - ADD(_cv + 16); - ADD(_cv + 32); + ADD(cv); + ADD(cv + 16); + ADD(cv + 32); ADD(_T2_neg_f); - ADD(_pv); - ADD(_pv + 16); - ADD(_pv + 32); + ADD(pv); + ADD(pv + 16); + ADD(pv + 32); ADD(_MASK3); ADD(_RMASK); #undef ADD diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.cpp b/src/hotspot/cpu/x86/stubRoutines_x86.cpp index 8696180c512a..ce11925dde2b 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.cpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.cpp @@ -44,8 +44,12 @@ #define DEFINE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) = CAST_FROM_FN_PTR(address, init_function); -STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT) +#define DEFINE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + address StubRoutines:: arch :: STUB_FIELD_NAME(field_name) [count]; +STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT, DEFINE_ARCH_ENTRY_ARRAY) + +#undef DEFINE_ARCH_ENTRY_ARRAY #undef DEFINE_ARCH_ENTRY_INIT #undef DEFINE_ARCH_ENTRY @@ -435,7 +439,7 @@ void StubRoutines::init_AOTAddressTable() { // publish addresses of external data defined in this file which may // be referenced from stub or code void StubRoutines::x86::init_AOTAddressTable(GrowableArray
& external_addresses) { -#define ADD(addr) external_addresses.append((address)addr); +#define ADD(addr) external_addresses.append((address)(addr)); ADD(&_mxcsr_std); ADD(&_mxcsr_rz); ADD(crc_by128_masks_addr()); diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index 3c6d75c1d4ed..7283798888be 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -55,9 +55,13 @@ class x86 { #define DECLARE_ARCH_ENTRY_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DECLARE_ARCH_ENTRY(arch, blob_name, stub_name, field_name, getter_name) +#define DECLARE_ARCH_ENTRY_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address STUB_FIELD_NAME(field_name) [count] ; + private: - STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT) + STUBGEN_ARCH_ENTRIES_DO(DECLARE_ARCH_ENTRY, DECLARE_ARCH_ENTRY_INIT, DECLARE_ARCH_ENTRY_ARRAY) +#undef DECLARE_ARCH_ENTRY_ARRAY #undef DECLARE_ARCH_ENTRY_INIT #undef DECLARE_ARCH_ENTRY @@ -70,9 +74,13 @@ class x86 { #define DEFINE_ARCH_ENTRY_GETTER_INIT(arch, blob_name, stub_name, field_name, getter_name, init_function) \ DEFINE_ARCH_ENTRY_GETTER(arch, blob_name, stub_name, field_name, getter_name) +#define DEFINE_ARCH_ENTRY_GETTER_ARRAY(arch, blob_name, stub_name, field_name, getter_name, count) \ + static address getter_name(int idx) { return STUB_FIELD_NAME(field_name) [idx]; } + public: - STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT) + STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY_GETTER, DEFINE_ARCH_ENTRY_GETTER_INIT, DEFINE_ARCH_ENTRY_GETTER_ARRAY) +#undef DEFINE_ARCH_ENTRY_GETTER_ARRAY #undef DEFINE_ARCH_ENTRY_GETTER_INIT #undef DEFINE_ARCH_GETTER_ENTRY diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index d8f998520d12..cf9de40a2370 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1187,7 +1187,7 @@ void VM_Version::get_processor_features() { } if (!UseAESIntrinsics) { if (UseAESCTRIntrinsics) { - if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + if (!FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { warning("AES-CTR intrinsics require UseAESIntrinsics flag to be enabled. Intrinsics will be disabled."); } FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); @@ -1207,22 +1207,22 @@ void VM_Version::get_processor_features() { } } } else { - if (!UseAES) { + if (!cpu_supports_aes()) { if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { - warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); + warning("AES intrinsics are not available on this CPU"); } FLAG_SET_DEFAULT(UseAESIntrinsics, false); if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - warning("AES_CTR intrinsics require UseAES flag to be enabled. AES_CTR intrinsics will be disabled."); + warning("AES-CTR intrinsics are not available on this CPU"); } FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); - } else if (!cpu_supports_aes()) { + } else if (!UseAES) { if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { - warning("AES intrinsics are not available on this CPU"); + warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); } FLAG_SET_DEFAULT(UseAESIntrinsics, false); if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { - warning("AES-CTR intrinsics are not available on this CPU"); + warning("AES_CTR intrinsics require UseAES flag to be enabled. AES_CTR intrinsics will be disabled."); } FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); } @@ -1373,10 +1373,6 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics || UseSHA3Intrinsics)) { - FLAG_SET_DEFAULT(UseSHA, false); - } - #if COMPILER2_OR_JVMCI int max_vector_size = 0; if (UseAVX == 0 || !os_supports_avx_vectors()) { diff --git a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp index 2357bbb51699..9abe313b3a72 100644 --- a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp +++ b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp @@ -29,35 +29,40 @@ #define STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(preuniverse, 0) \ #define STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(initial, 0) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(continuation, 0) \ #define STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(compiler, 0) \ #define STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_arch_blob(final, 0) \ diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 13a005591fb8..4a2d75ecdf3b 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -40,6 +40,8 @@ // Inlined from for portability. #ifndef CGROUP2_SUPER_MAGIC # define CGROUP2_SUPER_MAGIC 0x63677270 +#else + STATIC_ASSERT(CGROUP2_SUPER_MAGIC == 0x63677270); #endif // controller names have to match the *_IDX indices diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 1cc19f4a66ae..0cc82c38edf8 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -2163,8 +2163,6 @@ void os::print_os_info(outputStream* st) { os::Posix::print_rlimit_info(st); - os::print_open_file_descriptors(st); - os::Posix::print_load_average(st); st->cr(); diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 4dd2bff7c897..5802217c1c1c 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -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 @@ -4233,11 +4233,13 @@ int MatchRule::is_expensive() const { strcmp(opType,"PopulateIndex")==0 || strcmp(opType,"AddReductionVI")==0 || strcmp(opType,"AddReductionVL")==0 || + strcmp(opType,"AddReductionVHF")==0 || strcmp(opType,"AddReductionVF")==0 || strcmp(opType,"AddReductionVD")==0 || strcmp(opType,"MulReductionVI")==0 || strcmp(opType,"MulReductionVL")==0 || strcmp(opType,"MulReductionVF")==0 || + strcmp(opType,"MulReductionVHF")==0 || strcmp(opType,"MulReductionVD")==0 || strcmp(opType,"MinReductionV")==0 || strcmp(opType,"MaxReductionV")==0 || @@ -4348,9 +4350,9 @@ bool MatchRule::is_vector() const { "MaxV", "MinV", "MinVHF", "MaxVHF", "UMinV", "UMaxV", "CompressV", "ExpandV", "CompressM", "CompressBitsV", "ExpandBitsV", "AddReductionVI", "AddReductionVL", - "AddReductionVF", "AddReductionVD", + "AddReductionVHF", "AddReductionVF", "AddReductionVD", "MulReductionVI", "MulReductionVL", - "MulReductionVF", "MulReductionVD", + "MulReductionVHF", "MulReductionVF", "MulReductionVD", "MaxReductionV", "MinReductionV", "AndReductionV", "OrReductionV", "XorReductionV", "MulAddVS2VI", "MacroLogicV", diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index 6a288e0dad06..854cf73049bf 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -858,6 +858,13 @@ csize_t CodeBuffer::figure_expanded_capacities(CodeSection* which_cs, } void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { +#ifdef ASSERT + // The code below copies contents across temp buffers. The following + // sizes relate to buffer contents, and should not be changed by buffer + // expansion. + int old_total_skipped = total_skipped_instructions_size(); +#endif + #ifndef PRODUCT if (PrintNMethods && (WizardMode || Verbose)) { tty->print("expanding CodeBuffer:"); @@ -916,6 +923,7 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { assert(cb_sect->capacity() >= new_capacity[n], "big enough"); address cb_start = cb_sect->start(); cb_sect->set_end(cb_start + this_sect->size()); + cb_sect->register_skipped(this_sect->_skipped_instructions_size); if (this_sect->mark() == nullptr) { cb_sect->clear_mark(); } else { @@ -952,6 +960,9 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { this->print_on(tty); } #endif //PRODUCT + + assert(old_total_skipped == total_skipped_instructions_size(), + "Should match: %d == %d", old_total_skipped, total_skipped_instructions_size()); } void CodeBuffer::adjust_internal_address(address from, address to) { diff --git a/src/hotspot/share/cds/aotGrowableArray.hpp b/src/hotspot/share/cds/aotGrowableArray.hpp deleted file mode 100644 index 0a0c137ed071..000000000000 --- a/src/hotspot/share/cds/aotGrowableArray.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_AOT_AOTGROWABLEARRAY_HPP -#define SHARE_AOT_AOTGROWABLEARRAY_HPP - -#include -#include - -class AOTGrowableArrayHelper { -public: - static void deallocate(void* mem); -}; - -// An AOTGrowableArray provides the same functionality as a GrowableArray that -// uses the C heap allocator. In addition, AOTGrowableArray can be iterated with -// MetaspaceClosure. This type should be used for growable arrays that need to be -// stored in the AOT cache. See ModuleEntry::_reads for an example. -template -class AOTGrowableArray : public GrowableArrayWithAllocator> { - friend class VMStructs; - friend class GrowableArrayWithAllocator; - - static E* allocate(int max, MemTag mem_tag) { - return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), mem_tag); - } - - E* allocate() { - return allocate(this->_capacity, mtClass); - } - - void deallocate(E* mem) { -#if INCLUDE_CDS - AOTGrowableArrayHelper::deallocate(mem); -#else - GrowableArrayCHeapAllocator::deallocate(mem); -#endif - } - -public: - AOTGrowableArray(int initial_capacity, MemTag mem_tag) : - GrowableArrayWithAllocator( - allocate(initial_capacity, mem_tag), - initial_capacity) {} - - AOTGrowableArray() : AOTGrowableArray(0, mtClassShared) {} - - // methods required by MetaspaceClosure - void metaspace_pointers_do(MetaspaceClosure* it); - int size_in_heapwords() const { return (int)heap_word_size(sizeof(*this)); } - MetaspaceClosureType type() const { return MetaspaceClosureType::GrowableArrayType; } - static bool is_read_only_by_default() { return false; } -}; - -#endif // SHARE_AOT_AOTGROWABLEARRAY_HPP diff --git a/src/hotspot/share/cds/cppVtables.cpp b/src/hotspot/share/cds/cppVtables.cpp index dc5a777d7b1a..57da12dee489 100644 --- a/src/hotspot/share/cds/cppVtables.cpp +++ b/src/hotspot/share/cds/cppVtables.cpp @@ -22,7 +22,6 @@ * */ -#include "cds/aotGrowableArray.hpp" #include "cds/aotMetaspace.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" @@ -41,6 +40,7 @@ #include "oops/typeArrayKlass.hpp" #include "runtime/arguments.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" // Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables. // (In GCC this is the field ::_vptr, i.e., first word in the object.) @@ -58,10 +58,10 @@ #ifndef PRODUCT -// AOTGrowableArray has a vtable only when in non-product builds (due to +// GrowableArray has a vtable only when in non-product builds (due to // the virtual printing functions in AnyObj). -using GrowableArray_ModuleEntry_ptr = AOTGrowableArray; +using GrowableArray_ModuleEntry_ptr = GrowableArray; #define DEBUG_CPP_VTABLE_TYPES_DO(f) \ f(GrowableArray_ModuleEntry_ptr) \ diff --git a/src/hotspot/share/classfile/compactHashtable.hpp b/src/hotspot/share/classfile/compactHashtable.hpp index 81f2951289de..1711c5f8cd33 100644 --- a/src/hotspot/share/classfile/compactHashtable.hpp +++ b/src/hotspot/share/classfile/compactHashtable.hpp @@ -307,14 +307,9 @@ class CompactHashtable : public SimpleCompactHashtable { template inline void iterate(ITER* iter) const { iterate([&](V v) { iter->do_value(v); }); } - template - inline void iterate(const Function& function) const { // lambda enabled API - iterate(const_cast(function)); - } - // Iterate through the values in the table, stopping when the lambda returns false. template - inline void iterate(Function& function) const { // lambda enabled API + inline void iterate(Function function) const { // lambda enabled API for (u4 i = 0; i < _bucket_count; i++) { u4 bucket_info = _buckets[i]; u4 bucket_offset = BUCKET_OFFSET(bucket_info); diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index b5b8aa4ef554..c7fadeaea9ba 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -23,7 +23,6 @@ */ #include "cds/aotClassLocation.hpp" -#include "cds/aotGrowableArray.inline.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -168,7 +167,7 @@ void ModuleEntry::add_read(ModuleEntry* m) { } else { if (reads() == nullptr) { // Lazily create a module's reads list - AOTGrowableArray* new_reads = new (mtModule) AOTGrowableArray(MODULE_READS_SIZE, mtModule); + GrowableArray* new_reads = new (mtModule) GrowableArray(MODULE_READS_SIZE, mtModule); set_reads(new_reads); } diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp index 1a0251a2c2ac..10dec73e9fa8 100644 --- a/src/hotspot/share/classfile/moduleEntry.hpp +++ b/src/hotspot/share/classfile/moduleEntry.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_CLASSFILE_MODULEENTRY_HPP #define SHARE_CLASSFILE_MODULEENTRY_HPP -#include "cds/aotGrowableArray.hpp" #include "jni.h" #include "memory/metaspaceClosureType.hpp" #include "oops/oopHandle.hpp" @@ -70,7 +69,7 @@ class ModuleEntry : public CHeapObj { // for shared classes from this module Symbol* _name; // name of this module ClassLoaderData* _loader_data; - AOTGrowableArray* _reads; // list of modules that are readable by this module + GrowableArray* _reads; // list of modules that are readable by this module Symbol* _version; // module version number Symbol* _location; // module location @@ -118,10 +117,10 @@ class ModuleEntry : public CHeapObj { bool can_read(ModuleEntry* m) const; bool has_reads_list() const; - AOTGrowableArray* reads() const { + GrowableArray* reads() const { return _reads; } - void set_reads(AOTGrowableArray* r) { + void set_reads(GrowableArray* r) { _reads = r; } void pack_reads() { diff --git a/src/hotspot/share/classfile/packageEntry.cpp b/src/hotspot/share/classfile/packageEntry.cpp index 3e61f2e3a3e2..3eb50fcb5a7b 100644 --- a/src/hotspot/share/classfile/packageEntry.cpp +++ b/src/hotspot/share/classfile/packageEntry.cpp @@ -22,7 +22,6 @@ * */ -#include "cds/aotGrowableArray.inline.hpp" #include "cds/aotMetaspace.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" @@ -83,7 +82,7 @@ void PackageEntry::add_qexport(ModuleEntry* m) { if (!has_qual_exports_list()) { // Lazily create a package's qualified exports list. // Initial size is small, do not anticipate export lists to be large. - _qualified_exports = new (mtModule) AOTGrowableArray(QUAL_EXP_SIZE, mtModule); + _qualified_exports = new (mtModule) GrowableArray(QUAL_EXP_SIZE, mtModule); } // Determine, based on this newly established export to module m, diff --git a/src/hotspot/share/classfile/packageEntry.hpp b/src/hotspot/share/classfile/packageEntry.hpp index 7b174a922878..e064e53b2633 100644 --- a/src/hotspot/share/classfile/packageEntry.hpp +++ b/src/hotspot/share/classfile/packageEntry.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_CLASSFILE_PACKAGEENTRY_HPP #define SHARE_CLASSFILE_PACKAGEENTRY_HPP -#include "cds/aotGrowableArray.hpp" #include "classfile/moduleEntry.hpp" #include "memory/metaspaceClosureType.hpp" #include "oops/symbol.hpp" @@ -116,7 +115,7 @@ class PackageEntry : public CHeapObj { bool _must_walk_exports; // Contains list of modules this package is qualifiedly exported to. Access // to this list is protected by the Module_lock. - AOTGrowableArray* _qualified_exports; + GrowableArray* _qualified_exports; JFR_ONLY(DEFINE_TRACE_ID_FIELD;) // Initial size of a package entry's list of qualified exports. diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index e84acd622843..3f85fd16b614 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -469,6 +469,9 @@ class methodHandle; do_intrinsic(_Reference_clear0, java_lang_ref_Reference, clear0_name, void_method_signature, F_RN) \ do_intrinsic(_PhantomReference_clear0, java_lang_ref_PhantomReference, clear0_name, void_method_signature, F_RN) \ \ + do_intrinsic(_Reference_reachabilityFence, java_lang_ref_Reference, reachabilityFence_name, object_void_signature, F_S) \ + do_name(reachabilityFence_name, "reachabilityFence") \ + \ /* support for com.sun.crypto.provider.AES_Crypt and some of its callers */ \ do_class(com_sun_crypto_provider_aescrypt, "com/sun/crypto/provider/AES_Crypt") \ do_intrinsic(_aescrypt_encryptBlock, com_sun_crypto_provider_aescrypt, encryptBlock_name, byteArray_int_byteArray_int_signature, F_R) \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 2ae42bebcfdb..33d00b93365f 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -702,6 +702,7 @@ class SerializeClosure; template(appendToClassPathForInstrumentation_name, "appendToClassPathForInstrumentation") \ do_alias(appendToClassPathForInstrumentation_signature, string_void_signature) \ template(serializePropertiesToByteArray_name, "serializePropertiesToByteArray") \ + template(serializeSecurityPropertiesToByteArray_name, "serializeSecurityPropertiesToByteArray") \ template(serializeAgentPropertiesToByteArray_name, "serializeAgentPropertiesToByteArray") \ template(encodeThrowable_name, "encodeThrowable") \ template(encodeThrowable_signature, "(Ljava/lang/Throwable;JI)I") \ diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index d3888d1b7eb2..c2838917516d 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -1093,11 +1093,13 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind // now we have added all the other data we can write details of any // extra the AOT relocations - bool write_ok; + bool write_ok = true; if (AOTCodeEntry::is_multi_stub_blob(entry_kind)) { - CodeSection* cs = code_buffer->code_section(CodeBuffer::SECT_INSTS); - RelocIterator iter(cs); - write_ok = cache->write_relocations(blob, iter); + if (reloc_count > 0) { + CodeSection* cs = code_buffer->code_section(CodeBuffer::SECT_INSTS); + RelocIterator iter(cs); + write_ok = cache->write_relocations(blob, iter); + } } else { RelocIterator iter(&blob); write_ok = cache->write_relocations(blob, iter); @@ -1403,13 +1405,15 @@ void AOTCodeReader::restore(CodeBlob* code_blob) { // reinstate the AOT-load time relocs we saved from the code // buffer that generated this blob in a new code buffer and use // the latter to iterate over them - CodeBuffer code_buffer(code_blob); - relocInfo* locs = (relocInfo*)_reloc_data; - code_buffer.insts()->initialize_shared_locs(locs, _reloc_count); - code_buffer.insts()->set_locs_end(locs + _reloc_count); - CodeSection *cs = code_buffer.code_section(CodeBuffer::SECT_INSTS); - RelocIterator reloc_iter(cs); - fix_relocations(code_blob, reloc_iter); + if (_reloc_count > 0) { + CodeBuffer code_buffer(code_blob); + relocInfo* locs = (relocInfo*)_reloc_data; + code_buffer.insts()->initialize_shared_locs(locs, _reloc_count); + code_buffer.insts()->set_locs_end(locs + _reloc_count); + CodeSection *cs = code_buffer.code_section(CodeBuffer::SECT_INSTS); + RelocIterator reloc_iter(cs); + fix_relocations(code_blob, reloc_iter); + } } else { // the AOT-load time relocs will be in the blob's restored relocs RelocIterator reloc_iter(code_blob); @@ -1858,11 +1862,8 @@ void AOTCodeReader::read_dbg_strings(DbgStrings& dbg_strings) { // addresses, respectively, keyed by the relevant address void AOTCodeAddressTable::hash_address(address addr, int idx) { - // only do this if we are caching stubs and we have a non-null - // address to record - if (!AOTStubCaching) { - return; - } + // only do this if we have a non-null address to record and the + // cache is open for dumping if (addr == nullptr) { return; } @@ -1905,9 +1906,6 @@ void AOTCodeAddressTable::init_extrs() { ADD_EXTERNAL_ADDRESS(SharedRuntime::handle_wrong_method_ic_miss); #if defined(AARCH64) && !defined(ZERO) ADD_EXTERNAL_ADDRESS(JavaThread::aarch64_get_thread_helper); -#endif - -#if defined(AARCH64) ADD_EXTERNAL_ADDRESS(BarrierSetAssembler::patching_epoch_addr()); #endif @@ -2203,6 +2201,10 @@ void AOTCodeCache::load_strings() { if (strings_count == 0) { return; } + if (strings_count > MAX_STR_COUNT) { + fatal("Invalid strings_count loaded from AOT Code Cache: %d > MAX_STR_COUNT [%d]", strings_count, MAX_STR_COUNT); + return; + } uint strings_offset = _load_header->strings_offset(); uint* string_lengths = (uint*)addr(strings_offset); strings_offset += (strings_count * sizeof(uint)); @@ -2213,7 +2215,6 @@ void AOTCodeCache::load_strings() { char* p = NEW_C_HEAP_ARRAY(char, strings_size+1, mtCode); memcpy(p, addr(strings_offset), strings_size); _C_strings_buf = p; - assert(strings_count <= MAX_STR_COUNT, "sanity"); for (uint i = 0; i < strings_count; i++) { _C_strings[i] = p; uint len = string_lengths[i]; @@ -2514,10 +2515,11 @@ AOTStubData::AOTStubData(BlobId blob_id) : // cannot be accessed before initialising the universe if (blob_id == BlobId::stubgen_preuniverse_id) { // invalidate any attempt to use this - _flags |= INVALID; + _flags = INVALID; return; } if (AOTCodeCache::is_on()) { + _flags = OPEN; // allow update of stub entry addresses if (AOTCodeCache::is_using_stub()) { // allow stub loading diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index c4ebe2717676..5b773a986f19 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -236,9 +236,10 @@ class AOTStubData : public StackObj { // whether we are loading or storing stubs or have encountered any // invalid stubs. enum Flags { - USING = 1 << 0, // open and loading stubs - DUMPING = 1 << 1, // open and storing stubs - INVALID = 1 << 2, // found invalid stub when loading + OPEN = 1 << 0, // cache is open + USING = 1 << 1, // open and loading stubs + DUMPING = 1 << 2, // open and storing stubs + INVALID = 1 << 3, // found invalid stub when loading }; uint32_t _flags; @@ -253,6 +254,7 @@ class AOTStubData : public StackObj { ~AOTStubData() CDS_ONLY({FREE_C_HEAP_ARRAY(StubAddrRange, _ranges);}) NOT_CDS({}) + bool is_open() CDS_ONLY({ return (_flags & OPEN) != 0; }) NOT_CDS_RETURN_(false); bool is_using() CDS_ONLY({ return (_flags & USING) != 0; }) NOT_CDS_RETURN_(false); bool is_dumping() CDS_ONLY({ return (_flags & DUMPING) != 0; }) NOT_CDS_RETURN_(false); bool is_invalid() CDS_ONLY({ return (_flags & INVALID) != 0; }) NOT_CDS_RETURN_(false); diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index 6a1686b80e23..d372e72fc23d 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -45,9 +45,10 @@ class OopMapSet; enum class CodeBlobType { MethodNonProfiled = 0, // Execution level 1 and 4 (non-profiled) nmethods (including native nmethods) MethodProfiled = 1, // Execution level 2 and 3 (profiled) nmethods - NonNMethod = 2, // Non-nmethods like Buffers, Adapters and Runtime Stubs - All = 3, // All types (No code cache segmentation) - NumTypes = 4 // Number of CodeBlobTypes + MethodHot = 2, // Nmethods predicted to be always hot + NonNMethod = 3, // Non-nmethods like Buffers, Adapters and Runtime Stubs + All = 4, // All types (No code cache segmentation) + NumTypes = 5 // Number of CodeBlobTypes }; // CodeBlob - superclass for all entries in the CodeCache. @@ -603,7 +604,7 @@ class DeoptimizationBlob: public SingletonBlob { ); public: - static const int ENTRY_COUNT = 4 JVMTI_ONLY(+ 2); + static const int ENTRY_COUNT = 4 JVMCI_ONLY(+ 2); // Creation static DeoptimizationBlob* create( CodeBuffer* cb, diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 2a0256cc3163..c0b4918102ed 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -201,6 +201,7 @@ void CodeCache::initialize_heaps() { CodeHeapInfo non_nmethod = {NonNMethodCodeHeapSize, FLAG_IS_CMDLINE(NonNMethodCodeHeapSize), true}; CodeHeapInfo profiled = {ProfiledCodeHeapSize, FLAG_IS_CMDLINE(ProfiledCodeHeapSize), true}; CodeHeapInfo non_profiled = {NonProfiledCodeHeapSize, FLAG_IS_CMDLINE(NonProfiledCodeHeapSize), true}; + CodeHeapInfo hot = {HotCodeHeapSize, FLAG_IS_CMDLINE(HotCodeHeapSize), true}; const bool cache_size_set = FLAG_IS_CMDLINE(ReservedCodeCacheSize); const size_t ps = page_size(false, 8); @@ -219,6 +220,12 @@ void CodeCache::initialize_heaps() { profiled.enabled = false; } + if (!heap_available(CodeBlobType::MethodHot)) { + hot.size = 0; + hot.set = true; + hot.enabled = false; + } + assert(heap_available(CodeBlobType::MethodNonProfiled), "MethodNonProfiled heap is always available for segmented code heap"); size_t compiler_buffer_size = 0; @@ -238,14 +245,36 @@ void CodeCache::initialize_heaps() { set_size_of_unset_code_heap(&non_profiled, cache_size, non_nmethod.size + profiled.size, min_size); } - if (!profiled.set && non_profiled.set) { - set_size_of_unset_code_heap(&profiled, cache_size, non_nmethod.size + non_profiled.size, min_size); + if (!profiled.set && non_profiled.set && hot.set) { + set_size_of_unset_code_heap(&profiled, cache_size, non_nmethod.size + non_profiled.size + hot.size, min_size); + } + + if (hot.enabled) { + if (!hot.set) { + assert(hot.size == 0, "must be calculated during heaps initialization"); + // An application usually has ~20% hot code which is mostly non-profiled code. + // We set the hot code heap size to 20% of the non-profiled code heap. + hot.size = MAX2(non_profiled.size / 5, min_size); + + if (non_profiled.set) { + err_msg msg("Must manually set HotCodeHeapSize when NonProfiledCodeHeapSize is set"); + vm_exit_during_initialization("Invalid code heap sizes", msg); + } + + non_profiled.size -= hot.size; + } + + if (hot.size > non_profiled.size) { + err_msg msg("Hot (%zuK) exceeds NonProfiled (%zuK).", + hot.size / K, non_profiled.size / K); + vm_exit_during_initialization("Invalid code heap sizes", msg); + } } // Compatibility. size_t non_nmethod_min_size = min_cache_size + compiler_buffer_size; - if (!non_nmethod.set && profiled.set && non_profiled.set) { - set_size_of_unset_code_heap(&non_nmethod, cache_size, profiled.size + non_profiled.size, non_nmethod_min_size); + if (!non_nmethod.set && profiled.set && non_profiled.set && hot.set) { + set_size_of_unset_code_heap(&non_nmethod, cache_size, profiled.size + non_profiled.size + hot.size, non_nmethod_min_size); } // Note: if large page support is enabled, min_size is at least the large @@ -253,8 +282,9 @@ void CodeCache::initialize_heaps() { non_nmethod.size = align_up(non_nmethod.size, min_size); profiled.size = align_up(profiled.size, min_size); non_profiled.size = align_up(non_profiled.size, min_size); + hot.size = align_up(hot.size, min_size); - size_t aligned_total = non_nmethod.size + profiled.size + non_profiled.size; + size_t aligned_total = non_nmethod.size + profiled.size + non_profiled.size + hot.size; if (!cache_size_set) { // If ReservedCodeCacheSize is explicitly set and exceeds CODE_CACHE_SIZE_LIMIT, // it is rejected by flag validation elsewhere. Here we only handle the case @@ -262,15 +292,15 @@ void CodeCache::initialize_heaps() { // sizes (after alignment) exceed the platform limit. if (aligned_total > CODE_CACHE_SIZE_LIMIT) { err_msg message("ReservedCodeCacheSize (%zuK), Max (%zuK)." - "Segments: NonNMethod (%zuK), NonProfiled (%zuK), Profiled (%zuK).", + "Segments: NonNMethod (%zuK), NonProfiled (%zuK), Profiled (%zuK), Hot (%zuK).", aligned_total/K, CODE_CACHE_SIZE_LIMIT/K, - non_nmethod.size/K, non_profiled.size/K, profiled.size/K); + non_nmethod.size/K, non_profiled.size/K, profiled.size/K, hot.size/K); vm_exit_during_initialization("Code cache size exceeds platform limit", message); } if (aligned_total != cache_size) { log_info(codecache)("ReservedCodeCache size %zuK changed to total segments size NonNMethod " - "%zuK NonProfiled %zuK Profiled %zuK = %zuK", - cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, aligned_total/K); + "%zuK NonProfiled %zuK Profiled %zuK Hot %zuK = %zuK", + cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, hot.size/K, aligned_total/K); // Adjust ReservedCodeCacheSize as necessary because it was not set explicitly cache_size = aligned_total; } @@ -295,19 +325,23 @@ void CodeCache::initialize_heaps() { } if (profiled.enabled && !profiled.set && profiled.size > min_size) { profiled.size -= min_size; + if (--delta == 0) break; + } + if (hot.enabled && !hot.set && hot.size > min_size) { + hot.size -= min_size; delta--; } if (delta == start_delta) { break; } } - aligned_total = non_nmethod.size + profiled.size + non_profiled.size; + aligned_total = non_nmethod.size + profiled.size + non_profiled.size + hot.size; } } log_debug(codecache)("Initializing code heaps ReservedCodeCache %zuK NonNMethod %zuK" - " NonProfiled %zuK Profiled %zuK", - cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K); + " NonProfiled %zuK Profiled %zuK Hot %zuK", + cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, hot.size/K); // Validation // Check minimal required sizes @@ -318,6 +352,9 @@ void CodeCache::initialize_heaps() { if (non_profiled.enabled) { // non_profiled.enabled is always ON for segmented code heap, leave it checked for clarity check_min_size("non-profiled code heap", non_profiled.size, min_size); } + if (hot.enabled) { + check_min_size("hot code heap", hot.size, min_size); + } // ReservedCodeCacheSize was set explicitly, so report an error and abort if it doesn't match the segment sizes if (aligned_total != cache_size && cache_size_set) { @@ -328,6 +365,9 @@ void CodeCache::initialize_heaps() { if (non_profiled.enabled) { message.append(" + NonProfiledCodeHeapSize (%zuK)", non_profiled.size/K); } + if (hot.enabled) { + message.append(" + HotCodeHeapSize (%zuK)", hot.size/K); + } message.append(" = %zuK", aligned_total/K); message.append((aligned_total > cache_size) ? " is greater than " : " is less than "); message.append("ReservedCodeCacheSize (%zuK).", cache_size/K); @@ -348,6 +388,7 @@ void CodeCache::initialize_heaps() { FLAG_SET_ERGO(NonNMethodCodeHeapSize, non_nmethod.size); FLAG_SET_ERGO(ProfiledCodeHeapSize, profiled.size); FLAG_SET_ERGO(NonProfiledCodeHeapSize, non_profiled.size); + FLAG_SET_ERGO(HotCodeHeapSize, hot.size); FLAG_SET_ERGO(ReservedCodeCacheSize, cache_size); ReservedSpace rs = reserve_heap_memory(cache_size, ps); @@ -368,6 +409,13 @@ void CodeCache::initialize_heaps() { // Non-nmethods (stubs, adapters, ...) add_heap(non_method_space, "CodeHeap 'non-nmethods'", CodeBlobType::NonNMethod); + if (hot.enabled) { + ReservedSpace hot_space = rs.partition(offset, hot.size); + offset += hot.size; + // Nmethods known to be always hot. + add_heap(hot_space, "CodeHeap 'hot nmethods'", CodeBlobType::MethodHot); + } + if (non_profiled.enabled) { ReservedSpace non_profiled_space = rs.partition(offset, non_profiled.size); // Tier 1 and tier 4 (non-profiled) methods and native methods @@ -406,16 +454,25 @@ bool CodeCache::heap_available(CodeBlobType code_blob_type) { // Interpreter only: we don't need any method code heaps return (code_blob_type == CodeBlobType::NonNMethod); } else if (CompilerConfig::is_c1_profiling()) { - // Tiered compilation: use all code heaps + // Tiered compilation: use all code heaps including + // the hot code heap when it is present. + + if (COMPILER2_PRESENT(!HotCodeHeap &&) (code_blob_type == CodeBlobType::MethodHot)) { + return false; + } + return (code_blob_type < CodeBlobType::All); } else { // No TieredCompilation: we only need the non-nmethod and non-profiled code heap + // and the hot code heap if it is requested. return (code_blob_type == CodeBlobType::NonNMethod) || - (code_blob_type == CodeBlobType::MethodNonProfiled); + (code_blob_type == CodeBlobType::MethodNonProfiled) + COMPILER2_PRESENT(|| ((code_blob_type == CodeBlobType::MethodHot) && HotCodeHeap)); } } -const char* CodeCache::get_code_heap_flag_name(CodeBlobType code_blob_type) { +// Returns the name of the VM option to set the size of the corresponding CodeHeap +static const char* get_code_heap_flag_name(CodeBlobType code_blob_type) { switch(code_blob_type) { case CodeBlobType::NonNMethod: return "NonNMethodCodeHeapSize"; @@ -426,6 +483,9 @@ const char* CodeCache::get_code_heap_flag_name(CodeBlobType code_blob_type) { case CodeBlobType::MethodProfiled: return "ProfiledCodeHeapSize"; break; + case CodeBlobType::MethodHot: + return "HotCodeHeapSize"; + break; default: ShouldNotReachHere(); return nullptr; @@ -542,7 +602,7 @@ CodeBlob* CodeCache::allocate(uint size, CodeBlobType code_blob_type, bool handl // Get CodeHeap for the given CodeBlobType CodeHeap* heap = get_code_heap(code_blob_type); - assert(heap != nullptr, "heap is null"); + assert(heap != nullptr, "No heap for given code_blob_type (%d), heap is null", (int)code_blob_type); while (true) { cb = (CodeBlob*)heap->allocate(size); @@ -570,6 +630,9 @@ CodeBlob* CodeCache::allocate(uint size, CodeBlobType code_blob_type, bool handl type = CodeBlobType::MethodNonProfiled; } break; + case CodeBlobType::MethodHot: + type = CodeBlobType::MethodNonProfiled; + break; default: break; } diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 349cc652bf41..6384cb397b8f 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -118,10 +118,6 @@ class CodeCache : AllStatic { // Creates a new heap with the given name and size, containing CodeBlobs of the given type static void add_heap(ReservedSpace rs, const char* name, CodeBlobType code_blob_type); static CodeHeap* get_code_heap_containing(void* p); // Returns the CodeHeap containing the given pointer, or nullptr - static CodeHeap* get_code_heap(const void* cb); // Returns the CodeHeap for the given CodeBlob - static CodeHeap* get_code_heap(CodeBlobType code_blob_type); // Returns the CodeHeap for the given CodeBlobType - // Returns the name of the VM option to set the size of the corresponding CodeHeap - static const char* get_code_heap_flag_name(CodeBlobType code_blob_type); static ReservedSpace reserve_heap_memory(size_t size, size_t rs_ps); // Reserves one continuous chunk of memory for the CodeHeaps // Iteration @@ -145,6 +141,8 @@ class CodeCache : AllStatic { static int code_heap_compare(CodeHeap* const &lhs, CodeHeap* const &rhs); static void add_heap(CodeHeap* heap); + static CodeHeap* get_code_heap(const void* cb); // Returns the CodeHeap for the given CodeBlob + static CodeHeap* get_code_heap(CodeBlobType code_blob_type); // Returns the CodeHeap for the given CodeBlobType static const GrowableArray* heaps() { return _heaps; } static const GrowableArray* nmethod_heaps() { return _nmethod_heaps; } @@ -264,7 +262,7 @@ class CodeCache : AllStatic { } static bool code_blob_type_accepts_nmethod(CodeBlobType type) { - return type == CodeBlobType::All || type <= CodeBlobType::MethodProfiled; + return type == CodeBlobType::All || type <= CodeBlobType::MethodHot; } static bool code_blob_type_accepts_allocable(CodeBlobType type) { diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index a302df418d76..815c0c7b4b0a 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -66,6 +66,9 @@ #include "runtime/flags/flagSetting.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" +#ifdef COMPILER2 +#include "runtime/hotCodeCollector.hpp" +#endif // COMPILER2 #include "runtime/icache.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/orderAccess.hpp" @@ -1258,6 +1261,11 @@ void nmethod::post_init() { ICache::invalidate_range(code_begin(), code_size()); Universe::heap()->register_nmethod(this); + +#ifdef COMPILER2 + HotCodeCollector::register_nmethod(this); +#endif // COMPILER2 + DEBUG_ONLY(Universe::heap()->verify_nmethod(this)); CodeCache::commit(this); @@ -2476,6 +2484,11 @@ void nmethod::purge(bool unregister_nmethod) { if (unregister_nmethod) { Universe::heap()->unregister_nmethod(this); } + +#ifdef COMPILER2 + HotCodeCollector::unregister_nmethod(this); +#endif // COMPILER2 + CodeCache::unregister_old_nmethod(this); JVMCI_ONLY( _metadata_size = 0; ) diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 0e4e211453b4..cf7744cfe03c 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -286,8 +286,38 @@ void CompilerConfig::set_compilation_policy_flags() { } } +#ifdef COMPILER2 + if (HotCodeHeap) { + if (FLAG_IS_DEFAULT(SegmentedCodeCache)) { + FLAG_SET_ERGO(SegmentedCodeCache, true); + } else if (!SegmentedCodeCache) { + vm_exit_during_initialization("HotCodeHeap requires SegmentedCodeCache enabled"); + } + + if (FLAG_IS_DEFAULT(NMethodRelocation)) { + FLAG_SET_ERGO(NMethodRelocation, true); + } else if (!NMethodRelocation) { + vm_exit_during_initialization("HotCodeHeap requires NMethodRelocation enabled"); + } + + if (!is_c2_enabled()) { + vm_exit_during_initialization("HotCodeHeap requires C2 enabled"); + } + + if (HotCodeMinSamplingMs > HotCodeMaxSamplingMs) { + vm_exit_during_initialization("HotCodeMinSamplingMs cannot be larger than HotCodeMaxSamplingMs"); + } + } else if (HotCodeHeapSize > 0) { + vm_exit_during_initialization("HotCodeHeapSize requires HotCodeHeap enabled"); + } +#else + if (HotCodeHeapSize > 0) { + vm_exit_during_initialization("HotCodeHeapSize requires C2 present"); + } +#endif // COMPILER2 + if (CompileThresholdScaling < 0) { - vm_exit_during_initialization("Negative value specified for CompileThresholdScaling", nullptr); + vm_exit_during_initialization("Negative value specified for CompileThresholdScaling"); } if (CompilationModeFlag::disable_intermediate()) { diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp index 60602ef942bb..0da2f90da3f3 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp @@ -90,7 +90,7 @@ G1CardSetMemoryManager::~G1CardSetMemoryManager() { for (uint i = 0; i < num_mem_object_types(); i++) { _allocators[i].~G1CardSetAllocator(); } - FREE_C_HEAP_ARRAY(G1CardSetAllocator, _allocators); + FREE_C_HEAP_ARRAY(G1CardSetAllocator, _allocators); } void G1CardSetMemoryManager::free(uint type, void* value) { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index fe286793ae74..2709e6b30087 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -3219,7 +3219,7 @@ void G1CollectedHeap::retire_gc_alloc_region(G1HeapRegion* alloc_region, G1HeapRegionPrinter::retire(alloc_region); } -void G1CollectedHeap::mark_evac_failure_object(uint worker_id, const oop obj, size_t obj_size) const { +void G1CollectedHeap::mark_evac_failure_object(const oop obj) const { assert(!_cm->is_marked_in_bitmap(obj), "must be"); _cm->raw_mark_in_bitmap(obj); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index b5cb9167d928..3a47453819e6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -1275,7 +1275,7 @@ class G1CollectedHeap : public CollectedHeap { inline bool is_obj_dead_full(const oop obj) const; // Mark the live object that failed evacuation in the bitmap. - void mark_evac_failure_object(uint worker_id, oop obj, size_t obj_size) const; + void mark_evac_failure_object(oop obj) const; G1ConcurrentMark* concurrent_mark() const { return _cm; } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index a72d5fc5cf9d..dbb5ba509a2c 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -2173,8 +2173,7 @@ void G1CMTask::reset_for_restart() { void G1CMTask::register_partial_array_splitter() { ::new (&_partial_array_splitter) PartialArraySplitter(_cm->partial_array_state_manager(), - _cm->max_num_tasks(), - ObjArrayMarkingStride); + _cm->max_num_tasks()); } void G1CMTask::unregister_partial_array_splitter() { @@ -2359,7 +2358,7 @@ size_t G1CMTask::start_partial_array_processing(objArrayOop obj) { process_klass(obj->klass()); size_t array_length = obj->length(); - size_t initial_chunk_size = _partial_array_splitter.start(_task_queue, obj, nullptr, array_length); + size_t initial_chunk_size = _partial_array_splitter.start(_task_queue, obj, nullptr, array_length, ObjArrayMarkingStride); process_array_chunk(obj, 0, initial_chunk_size); @@ -2917,7 +2916,7 @@ G1CMTask::G1CMTask(uint worker_id, _cm(cm), _mark_bitmap(nullptr), _task_queue(task_queue), - _partial_array_splitter(_cm->partial_array_state_manager(), _cm->max_num_tasks(), ObjArrayMarkingStride), + _partial_array_splitter(_cm->partial_array_state_manager(), _cm->max_num_tasks()), _mark_stats_cache(mark_stats, G1RegionMarkStatsCache::RegionMarkStatsCacheSize), _calls(0), _time_target_ms(0.0), diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index 8546e6e2d640..e12a8c284de0 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, 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 @@ -326,11 +326,14 @@ bool G1ConcurrentRefineSweepState::complete_work(bool concurrent, bool print_log if (print_log) { G1ConcurrentRefineStats* s = &_stats; - log_debug(gc, refine)("Refinement took %.2fms (pre-sweep %.2fms card refine %.2f) " + State state_bounded_by_sweeprt = (_state == State::SweepRT || _state == State::CompleteRefineWork) + ? State::SweepRT : _state; + + log_debug(gc, refine)("Refinement took %.2fms (pre-sweep %.2fms card refine %.2fms) " "(scanned %zu clean %zu (%.2f%%) not_clean %zu (%.2f%%) not_parsable %zu " "refers_to_cset %zu (%.2f%%) still_refers_to_cset %zu (%.2f%%) no_cross_region %zu pending %zu)", get_duration(State::Idle, _state).seconds() * 1000.0, - get_duration(State::Idle, State::SweepRT).seconds() * 1000.0, + get_duration(State::Idle, state_bounded_by_sweeprt).seconds() * 1000.0, TimeHelper::counter_to_millis(s->refine_duration()), s->cards_scanned(), s->cards_clean(), diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp index 2b0b78ac1ce6..3be4ab8d839e 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp @@ -39,7 +39,7 @@ G1FullGCMarker::G1FullGCMarker(G1FullCollector* collector, _worker_id(worker_id), _bitmap(collector->mark_bitmap()), _task_queue(), - _partial_array_splitter(collector->partial_array_state_manager(), collector->workers(), ObjArrayMarkingStride), + _partial_array_splitter(collector->partial_array_state_manager(), collector->workers()), _mark_closure(worker_id, this, ClassLoaderData::_claim_stw_fullgc_mark, G1CollectedHeap::heap()->ref_processor_stw()), _stack_closure(this), _cld_closure(mark_closure(), ClassLoaderData::_claim_stw_fullgc_mark), @@ -60,14 +60,26 @@ void G1FullGCMarker::process_partial_array(PartialArrayState* state, bool stolen process_array_chunk(obj_array, claim._start, claim._end); } +static uintx calc_array_stride(uint array_len, uint num_threads) { + precond(num_threads > 0); + + const size_t stride = (array_len + num_threads - 1) / num_threads; + return clamp(stride, ArrayMarkingMinStride, ObjArrayMarkingStride); +} + void G1FullGCMarker::start_partial_array_processing(objArrayOop obj) { mark_closure()->do_klass(obj->klass()); // Don't push empty arrays to avoid unnecessary work. - size_t array_length = obj->length(); - if (array_length > 0) { - size_t initial_chunk_size = _partial_array_splitter.start(task_queue(), obj, nullptr, array_length); - process_array_chunk(obj, 0, initial_chunk_size); + const int array_length = obj->length(); + + if (array_length == 0) { + return; } + + const uintx stride = calc_array_stride(array_length, _collector->workers()); + const size_t initial_chunk_size = _partial_array_splitter.start(task_queue(), obj, nullptr, array_length, stride); + + process_array_chunk(obj, 0, initial_chunk_size); } void G1FullGCMarker::complete_marking(G1ScannerTasksQueueSet* task_queues, diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index cb857dc6eabd..52c8d4d43895 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -78,7 +78,7 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, _surviving_young_words(nullptr), _surviving_words_length(collection_set->young_region_length() + 1), _old_gen_is_full(false), - _partial_array_splitter(g1h->partial_array_state_manager(), num_workers, ParGCArrayScanChunk), + _partial_array_splitter(g1h->partial_array_state_manager(), num_workers), _string_dedup_requests(), _max_num_optional_regions(collection_set->num_optional_regions()), _numa(g1h->numa()), @@ -253,7 +253,7 @@ void G1ParScanThreadState::start_partial_objarray(oop from_obj, size_t array_length = to_array->length(); size_t initial_chunk_size = // The source array is unused when processing states. - _partial_array_splitter.start(_task_queue, nullptr, to_array, array_length); + _partial_array_splitter.start(_task_queue, nullptr, to_array, array_length, ParGCArrayScanChunk); assert(_scanner.skip_card_mark_set(), "must be"); // Process the initial chunk. No need to process the type in the @@ -650,7 +650,7 @@ oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markWord m, Kla // Mark the failing object in the marking bitmap and later use the bitmap to handle // evacuation failure recovery. - _g1h->mark_evac_failure_object(_worker_id, old, word_sz); + _g1h->mark_evac_failure_object(old); _evacuation_failed_info.register_copy_failure(word_sz); diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 5744bbc2f032..78a533d62c02 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -962,12 +962,12 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar _free_regions_at_end_of_collection = _g1h->num_free_regions(); + _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); // Do not update dynamic IHOP due to G1 periodic collection as it is highly likely // that in this case we are not running in a "normal" operating mode. if (_g1h->gc_cause() != GCCause::_g1_periodic_collection) { update_young_length_bounds(); - _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); if (update_ihop_prediction(app_time_ms / 1000.0, is_young_only_pause)) { _ihop_control->report_statistics(_g1h->gc_tracer_stw(), _g1h->non_young_occupancy_after_allocation(allocation_word_size)); } diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.cpp b/src/hotspot/share/gc/parallel/psCompactionManager.cpp index 0108f1a97629..048355bfad3f 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.cpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.cpp @@ -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 @@ -58,7 +58,7 @@ PreservedMarksSet* ParCompactionManager::_preserved_marks_set = nullptr; ParCompactionManager::ParCompactionManager(PreservedMarks* preserved_marks, ReferenceProcessor* ref_processor, uint parallel_gc_threads) - :_partial_array_splitter(_partial_array_state_manager, parallel_gc_threads, ObjArrayMarkingStride), + :_partial_array_splitter(_partial_array_state_manager, parallel_gc_threads), _mark_and_push_closure(this, ref_processor) { ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); @@ -126,7 +126,7 @@ void ParCompactionManager::push_objArray(oop obj) { objArrayOop obj_array = objArrayOop(obj); size_t array_length = obj_array->length(); size_t initial_chunk_size = - _partial_array_splitter.start(&_marking_stack, obj_array, nullptr, array_length); + _partial_array_splitter.start(&_marking_stack, obj_array, nullptr, array_length, ObjArrayMarkingStride); follow_array(obj_array, 0, initial_chunk_size); } diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.cpp b/src/hotspot/share/gc/parallel/psPromotionManager.cpp index 39fcc5556c62..ac22430aa4c0 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.cpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -158,7 +158,7 @@ PartialArrayTaskStats* PSPromotionManager::partial_array_task_stats() { // Most members are initialized either by initialize() or reset(). PSPromotionManager::PSPromotionManager() - : _partial_array_splitter(_partial_array_state_manager, ParallelGCThreads, ParGCArrayScanChunk) + : _partial_array_splitter(_partial_array_state_manager, ParallelGCThreads) { // We set the old lab's start array. _old_lab.set_start_array(old_gen()->start_array()); @@ -273,7 +273,7 @@ void PSPromotionManager::push_objArray(oop old_obj, oop new_obj) { size_t array_length = to_array->length(); size_t initial_chunk_size = // The source array is unused when processing states. - _partial_array_splitter.start(&_claimed_stack_depth, nullptr, to_array, array_length); + _partial_array_splitter.start(&_claimed_stack_depth, nullptr, to_array, array_length, ParGCArrayScanChunk); process_array_chunk(to_array, 0, initial_chunk_size); } diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 66ca10f1fb6b..c91029441974 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.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 @@ -256,6 +256,12 @@ "before pushing a continuation entry") \ range(1, INT_MAX/2) \ \ + product(uintx, ArrayMarkingMinStride, 64, DIAGNOSTIC, \ + "Minimum chunk size for split array processing during marking; " \ + "the effective stride is clamped between this value " \ + "and ObjArrayMarkingStride.") \ + constraint(ArrayMarkingMinStrideConstraintFunc,AfterErgo) \ + \ product(bool, AggressiveHeap, false, \ "(Deprecated) Optimize heap options for long-running memory " \ "intensive apps") \ diff --git a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp index ea3d644d105a..4d7ffce3a5dd 100644 --- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp +++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp @@ -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 @@ -414,3 +414,15 @@ JVMFlag::Error GCCardSizeInBytesConstraintFunc(uint value, bool verbose) { return JVMFlag::SUCCESS; } } + +JVMFlag::Error ArrayMarkingMinStrideConstraintFunc(uintx value, bool verbose) { + if (value > ObjArrayMarkingStride) { + JVMFlag::printError(verbose, + "ArrayMarkingMinStride (%zu) must be " + "less than or equal to ObjArrayMarkingStride (%zu)\n", + value, ObjArrayMarkingStride); + return JVMFlag::VIOLATES_CONSTRAINT; + } else { + return JVMFlag::SUCCESS; + } +} diff --git a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp index a89f42959e1b..1d2f45397aa7 100644 --- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp +++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, 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 @@ -66,7 +66,8 @@ f(uintx, SurvivorRatioConstraintFunc) \ f(size_t, MetaspaceSizeConstraintFunc) \ f(size_t, MaxMetaspaceSizeConstraintFunc) \ - f(uint, GCCardSizeInBytesConstraintFunc) + f(uint, GCCardSizeInBytesConstraintFunc) \ + f(uintx, ArrayMarkingMinStrideConstraintFunc) SHARED_GC_CONSTRAINTS(DECLARE_CONSTRAINT) diff --git a/src/hotspot/share/gc/shared/partialArraySplitter.cpp b/src/hotspot/share/gc/shared/partialArraySplitter.cpp index d18338726837..04884d5e666c 100644 --- a/src/hotspot/share/gc/shared/partialArraySplitter.cpp +++ b/src/hotspot/share/gc/shared/partialArraySplitter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -28,10 +28,9 @@ #include "utilities/macros.hpp" PartialArraySplitter::PartialArraySplitter(PartialArrayStateManager* manager, - uint num_workers, - size_t chunk_size) + uint num_workers) : _allocator(manager), - _stepper(num_workers, chunk_size) + _stepper(num_workers) TASKQUEUE_STATS_ONLY(COMMA _stats()) {} diff --git a/src/hotspot/share/gc/shared/partialArraySplitter.hpp b/src/hotspot/share/gc/shared/partialArraySplitter.hpp index 87cc137e7977..340f370d1d5d 100644 --- a/src/hotspot/share/gc/shared/partialArraySplitter.hpp +++ b/src/hotspot/share/gc/shared/partialArraySplitter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -44,8 +44,7 @@ class PartialArraySplitter { public: PartialArraySplitter(PartialArrayStateManager* manager, - uint num_workers, - size_t chunk_size); + uint num_workers); ~PartialArraySplitter() = default; NONCOPYABLE(PartialArraySplitter); @@ -60,6 +59,8 @@ class PartialArraySplitter { // // length is their length in elements. // + // chunk_size the size of a single chunk. + // // If t is a ScannerTask, queue->push(t) must be a valid expression. The // result of that expression is ignored. // @@ -76,7 +77,8 @@ class PartialArraySplitter { size_t start(Queue* queue, objArrayOop from_array, objArrayOop to_array, - size_t length); + size_t length, + size_t chunk_size); // Result type for claim(), carrying multiple values. Provides the claimed // chunk's start and end array indices. diff --git a/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp b/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp index abb0cf131016..7679358e2188 100644 --- a/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArraySplitter.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -39,14 +39,16 @@ template size_t PartialArraySplitter::start(Queue* queue, objArrayOop source, objArrayOop destination, - size_t length) { - PartialArrayTaskStepper::Step step = _stepper.start(length); + size_t length, + size_t chunk_size) { + precond(chunk_size > 0); + PartialArrayTaskStepper::Step step = _stepper.start(length, chunk_size); // Push initial partial scan tasks. if (step._ncreate > 0) { TASKQUEUE_STATS_ONLY(_stats.inc_split();); TASKQUEUE_STATS_ONLY(_stats.inc_pushed(step._ncreate);) PartialArrayState* state = - _allocator.allocate(source, destination, step._index, length, step._ncreate); + _allocator.allocate(source, destination, step._index, length, chunk_size, step._ncreate); for (uint i = 0; i < step._ncreate; ++i) { queue->push(ScannerTask(state)); } @@ -75,9 +77,10 @@ PartialArraySplitter::claim(PartialArrayState* state, Queue* queue, bool stolen) queue->push(ScannerTask(state)); } } + size_t chunk_size = state->chunk_size(); // Release state, decrementing refcount, now that we're done with it. _allocator.release(state); - return Claim{step._index, step._index + _stepper.chunk_size()}; + return Claim{step._index, step._index + chunk_size}; } #endif // SHARE_GC_SHARED_PARTIALARRAYSPLITTER_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index aadbc46b7c1f..d3b21c2fdaa0 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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,10 +35,12 @@ PartialArrayState::PartialArrayState(oop src, oop dst, size_t index, size_t length, + size_t chunk_size, size_t initial_refcount) : _source(src), _destination(dst), _length(length), + _chunk_size(chunk_size), _index(index), _refcount(initial_refcount) { @@ -77,6 +79,7 @@ PartialArrayStateAllocator::~PartialArrayStateAllocator() { PartialArrayState* PartialArrayStateAllocator::allocate(oop src, oop dst, size_t index, size_t length, + size_t chunk_size, size_t initial_refcount) { void* p; FreeListEntry* head = _free_list; @@ -87,7 +90,7 @@ PartialArrayState* PartialArrayStateAllocator::allocate(oop src, oop dst, head->~FreeListEntry(); p = head; } - return ::new (p) PartialArrayState(src, dst, index, length, initial_refcount); + return ::new (p) PartialArrayState(src, dst, index, length, chunk_size, initial_refcount); } void PartialArrayStateAllocator::release(PartialArrayState* state) { diff --git a/src/hotspot/share/gc/shared/partialArrayState.hpp b/src/hotspot/share/gc/shared/partialArrayState.hpp index 3dafeb0f14ce..75e297526aef 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.hpp +++ b/src/hotspot/share/gc/shared/partialArrayState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -61,6 +61,7 @@ class PartialArrayState { oop _source; oop _destination; size_t _length; + size_t _chunk_size; Atomic _index; Atomic _refcount; @@ -68,7 +69,7 @@ class PartialArrayState { PartialArrayState(oop src, oop dst, size_t index, size_t length, - size_t initial_refcount); + size_t chunk_size, size_t initial_refcount); public: // Deleted to require management by allocator object. @@ -89,6 +90,8 @@ class PartialArrayState { // The length of the array oop. size_t length() const { return _length; } + size_t chunk_size() const { return _chunk_size; } + // A pointer to the start index for the next segment to process, for atomic // update. Atomic* index_addr() { return &_index; } @@ -130,6 +133,7 @@ class PartialArrayStateAllocator : public CHeapObj { // from the associated manager. PartialArrayState* allocate(oop src, oop dst, size_t index, size_t length, + size_t chunk_size, size_t initial_refcount); // Decrement the state's refcount. If the new refcount is zero, add the diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp index d91ba347d6c8..f7d53c9348ab 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, 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,8 +48,7 @@ static uint compute_task_fanout(uint task_limit) { return result; } -PartialArrayTaskStepper::PartialArrayTaskStepper(uint n_workers, size_t chunk_size) : - _chunk_size(chunk_size), +PartialArrayTaskStepper::PartialArrayTaskStepper(uint n_workers) : _task_limit(compute_task_limit(n_workers)), _task_fanout(compute_task_fanout(_task_limit)) {} diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp index 11499ca2ffeb..594cc7b245a9 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, 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 @@ -40,19 +40,19 @@ class PartialArrayState; // substantially expand the task queues. class PartialArrayTaskStepper { public: - PartialArrayTaskStepper(uint n_workers, size_t chunk_size); + PartialArrayTaskStepper(uint n_workers); struct Step { size_t _index; // Array index for the step. uint _ncreate; // Number of new tasks to create. }; - // Called with the length of the array to be processed. Returns a Step with - // _index being the end of the initial chunk, which the caller should - // process. This is also the starting index for the next chunk to process. + // Called with the length of the array to be processed and chunk size. + // Returns a Step with _index being the end of the initial chunk, which the + // caller should process. This is also the starting index for the next chunk to process. // The _ncreate is the number of tasks to enqueue to continue processing the // array. If _ncreate is zero then _index will be length. - inline Step start(size_t length) const; + inline Step start(size_t length, size_t chunk_size) const; // Atomically increment state's index by chunk_size() to claim the next // chunk. Returns a Step with _index being the starting index of the @@ -60,21 +60,16 @@ class PartialArrayTaskStepper { // to enqueue. inline Step next(PartialArrayState* state) const; - // The size of chunks to claim for each task. - inline size_t chunk_size() const; - class TestSupport; // For unit tests private: - // Size (number of elements) of a chunk to process. - size_t _chunk_size; // Limit on the number of partial array tasks to create for a given array. uint _task_limit; // Maximum number of new tasks to create when processing an existing task. uint _task_fanout; // For unit tests. - inline Step next_impl(size_t length, Atomic* index_addr) const; + inline Step next_impl(size_t length, size_t chunk_size, Atomic* index_addr) const; }; #endif // SHARE_GC_SHARED_PARTIALARRAYTASKSTEPPER_HPP diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp index 6946f7c69ff3..538815698f29 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, 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 @@ -31,13 +31,9 @@ #include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" -size_t PartialArrayTaskStepper::chunk_size() const { - return _chunk_size; -} - PartialArrayTaskStepper::Step -PartialArrayTaskStepper::start(size_t length) const { - size_t end = length % _chunk_size; // End of initial chunk. +PartialArrayTaskStepper::start(size_t length, size_t chunk_size) const { + size_t end = length % chunk_size; // End of initial chunk. // If the initial chunk is the complete array, then don't need any partial // tasks. Otherwise, start with just one partial task; see new task // calculation in next(). @@ -45,24 +41,24 @@ PartialArrayTaskStepper::start(size_t length) const { } PartialArrayTaskStepper::Step -PartialArrayTaskStepper::next_impl(size_t length, Atomic* index_addr) const { +PartialArrayTaskStepper::next_impl(size_t length, size_t chunk_size, Atomic* index_addr) const { // The start of the next task is in the state's index. // Atomically increment by the chunk size to claim the associated chunk. // Because we limit the number of enqueued tasks to being no more than the // number of remaining chunks to process, we can use an atomic add for the // claim, rather than a CAS loop. - size_t start = index_addr->fetch_then_add(_chunk_size, memory_order_relaxed); + size_t start = index_addr->fetch_then_add(chunk_size, memory_order_relaxed); assert(start < length, "invariant: start %zu, length %zu", start, length); - assert(((length - start) % _chunk_size) == 0, + assert(((length - start) % chunk_size) == 0, "invariant: start %zu, length %zu, chunk size %zu", - start, length, _chunk_size); + start, length, chunk_size); // Determine the number of new tasks to create. // Zero-based index for this partial task. The initial task isn't counted. - uint task_num = checked_cast(start / _chunk_size); + uint task_num = checked_cast(start / chunk_size); // Number of tasks left to process, including this one. - uint remaining_tasks = checked_cast((length - start) / _chunk_size); + uint remaining_tasks = checked_cast((length - start) / chunk_size); assert(remaining_tasks > 0, "invariant"); // Compute number of pending tasks, including this one. The maximum number // of tasks is a function of task_num (N) and _task_fanout (F). @@ -89,7 +85,7 @@ PartialArrayTaskStepper::next_impl(size_t length, Atomic* index_addr) co PartialArrayTaskStepper::Step PartialArrayTaskStepper::next(PartialArrayState* state) const { - return next_impl(state->length(), state->index_addr()); + return next_impl(state->length(), state->chunk_size(), state->index_addr()); } #endif // SHARE_GC_SHARED_PARTIALARRAYTASKSTEPPER_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index 45ba2740ea5b..594367e29729 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -38,11 +38,6 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; -typedef struct { - ShenandoahHeapRegion* _region; - size_t _live_data; -} AgedRegionData; - static int compare_by_aged_live(AgedRegionData a, AgedRegionData b) { if (a._live_data < b._live_data) return -1; @@ -74,15 +69,27 @@ ShenandoahGenerationalHeuristics::ShenandoahGenerationalHeuristics(ShenandoahGen : ShenandoahAdaptiveHeuristics(generation), _generation(generation), _add_regions_to_old(0) { } -void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { +void ShenandoahGenerationalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* collection_set, + RegionData* data, size_t data_size, + size_t free) { ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap(); - assert(collection_set->is_empty(), "Collection set must be empty here"); - _add_regions_to_old = 0; - // Choose the collection set - filter_regions(collection_set); + // Find the amount that will be promoted, regions that will be promoted in + // place, and preselected older regions that will be promoted by evacuation. + ShenandoahInPlacePromotionPlanner in_place_promotions(heap); + compute_evacuation_budgets(in_place_promotions, heap); + + // Call the subclasses to add regions into the collection set. + select_collection_set_regions(collection_set, data, data_size, free); + + // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. + adjust_evacuation_budgets(heap, collection_set); + + if (collection_set->has_old_regions()) { + heap->shenandoah_policy()->record_mixed_cycle(); + } if (_generation->is_global()) { // We have just chosen a collection set for a global cycle. The mark bitmap covering old regions is complete, so @@ -96,6 +103,14 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio // after a global cycle for old regions that were not included in this collection set. heap->old_generation()->transition_old_generation_after_global_gc(); } + + ShenandoahTracer::report_promotion_info(collection_set, + in_place_promotions.humongous_region_stats().count, + in_place_promotions.humongous_region_stats().garbage, + in_place_promotions.humongous_region_stats().free, + in_place_promotions.regular_region_stats().count, + in_place_promotions.regular_region_stats().garbage, + in_place_promotions.regular_region_stats().free); } void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahInPlacePromotionPlanner& in_place_promotions, @@ -221,112 +236,48 @@ void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahInPl // case of a GLOBAL gc. During choose_collection_set() of GLOBAL, old will be expanded on demand. } -void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* collection_set) { - auto heap = ShenandoahGenerationalHeap::heap(); - const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - - // Check all pinned regions have updated status before choosing the collection set. - heap->assert_pinned_region_status(_generation); - - // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away. - - const size_t num_regions = heap->num_regions(); - - RegionData* candidates = _region_data; - - size_t cand_idx = 0; - - size_t total_garbage = 0; - - size_t immediate_garbage = 0; - size_t immediate_regions = 0; - - size_t free = 0; - size_t free_regions = 0; +void ShenandoahGenerationalHeuristics::add_tenured_regions_to_collection_set(const size_t old_promotion_reserve, + ShenandoahGenerationalHeap *const heap, + size_t candidates, AgedRegionData* sorted_regions) { + size_t old_consumed = 0; + if (candidates > 0) { + // Sort in increasing order according to live data bytes. Note that + // candidates represents the number of regions that qualify to be promoted + // by evacuation. + QuickSort::sort(sorted_regions, candidates, + compare_by_aged_live); - for (size_t i = 0; i < num_regions; i++) { - ShenandoahHeapRegion* region = heap->get_region(i); - if (!_generation->contains(region)) { - continue; - } - const size_t garbage = region->garbage(); - total_garbage += garbage; - if (region->is_empty()) { - free_regions++; - free += region_size_bytes; - } else if (region->is_regular()) { - if (!region->has_live()) { - // We can recycle it right away and put it in the free set. - immediate_regions++; - immediate_garbage += garbage; - region->make_trash_immediate(); - } else { - // This is our candidate for later consideration. Note that this region - // could still be promoted in place and may not necessarily end up in the - // collection set. - assert(region->get_top_before_promote() == nullptr, "Cannot add region %zu scheduled for in-place-promotion to the collection set", i); - candidates[cand_idx].set_region_and_garbage(region, garbage); - cand_idx++; - } - } else if (region->is_humongous_start()) { - // Reclaim humongous regions here, and count them as the immediate garbage - DEBUG_ONLY(assert_humongous_mark_consistency(region)); - if (!region->has_live()) { - heap->trash_humongous_region_at(region); - - // Count only the start. Continuations would be counted on "trash" path - immediate_regions++; - immediate_garbage += garbage; + size_t selected_regions = 0; + size_t selected_live = 0; + for (size_t i = 0; i < candidates; i++) { + ShenandoahHeapRegion *const region = sorted_regions[i]._region; + const size_t region_live_data = sorted_regions[i]._live_data; + const size_t promotion_need = (size_t)(region_live_data * ShenandoahPromoEvacWaste); + if (old_consumed + promotion_need > old_promotion_reserve) { + // We rejected the remaining promotable regions from the collection set + // because we have no room to hold their evacuees. We do not need to + // iterate the remaining regions to estimate the amount we expect to + // promote because we know it directly form the census we computed + // during the preceding mark phase. + break; } - } else if (region->is_trash()) { - // Count in just trashed humongous continuation regions - immediate_regions++; - immediate_garbage += garbage; - } - } - - // Step 2. Look back at garbage statistics, and decide if we want to collect anything, - // given the amount of immediately reclaimable garbage. If we do, figure out the collection set. - assert(immediate_garbage <= total_garbage, - "Cannot have more immediate garbage than total garbage: " PROPERFMT " vs " PROPERFMT, - PROPERFMTARGS(immediate_garbage), PROPERFMTARGS(total_garbage)); - - const size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); - ShenandoahInPlacePromotionPlanner in_place_promotions(heap); - if (immediate_percent <= ShenandoahImmediateThreshold) { - - // Find the amount that will be promoted, regions that will be promoted in - // place, and preselected older regions that will be promoted by evacuation. - compute_evacuation_budgets(in_place_promotions, heap); - - // Call the subclasses to add young-gen regions into the collection set. - choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); - - // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. - adjust_evacuation_budgets(heap, collection_set); - if (collection_set->has_old_regions()) { - heap->shenandoah_policy()->record_mixed_cycle(); + old_consumed += promotion_need; + heap->collection_set()->add_region(region); + selected_regions++; + selected_live += region_live_data; } + log_debug(gc, ergo)( "Preselected %zu regions containing " PROPERFMT " live data," + " consuming: " PROPERFMT " of budgeted: " PROPERFMT, + selected_regions, PROPERFMTARGS(selected_live), + PROPERFMTARGS(old_consumed), PROPERFMTARGS(old_promotion_reserve)); } - - collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); - ShenandoahTracer::report_evacuation_info(collection_set, - free_regions, - in_place_promotions.humongous_region_stats().count, - in_place_promotions.regular_region_stats().count, - in_place_promotions.regular_region_stats().garbage, - in_place_promotions.regular_region_stats().free, - immediate_regions, - immediate_garbage); } -// Select for inclusion into the collection set all regions whose age is at or above tenure age and for which the -// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). We -// identify these regions by setting the appropriate entry of the collection set's preselected regions array to true. -// All entries are initialized to false before calling this function. +// Select for inclusion into the collection set all regions whose age is at or +// above tenure age and for which the +// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). // -// During the subsequent selection of the collection set, we give priority to these promotion set candidates. // Without this prioritization, we found that the aged regions tend to be ignored because they typically have // much less garbage and much more live data than the recently allocated "eden" regions. When aged regions are // repeatedly excluded from the collection set, the amount of live memory within the young generation tends to @@ -334,8 +285,8 @@ void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* c // CPU and wall-clock time. // // A second benefit of treating aged regions differently than other regions during collection set selection is -// that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation -// of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be +// that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation +// of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be // reserved in the young generation. size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, const size_t old_promotion_reserve) { @@ -345,7 +296,6 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr auto const heap = ShenandoahGenerationalHeap::heap(); - size_t promo_potential = 0; size_t candidates = 0; // Sort the promotion-eligible regions in order of increasing live-data-bytes so that we can first reclaim regions that require @@ -386,66 +336,28 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr sorted_regions[candidates]._live_data = r->get_live_data_bytes(); candidates++; } - } else { - // We only evacuate & promote objects from regular regions whose garbage() is above old-garbage-threshold. - // Objects in tenure-worthy regions with less garbage are promoted in place. These take a different path to - // old-gen. Regions excluded from promotion because their garbage content is too low (causing us to anticipate that - // the region would be promoted in place) may be eligible for evacuation promotion by the time promotion takes - // place during a subsequent GC pass because more garbage is found within the region between now and then. This - // should not happen if we are properly adapting the tenure age. The theory behind adaptive tenuring threshold - // is to choose the youngest age that demonstrates no "significant" further loss of population since the previous - // age. If not this, we expect the tenure age to demonstrate linear population decay for at least two population - // samples, whereas we expect to observe exponential population decay for ages younger than the tenure age. - // - // In the case that certain regions which were anticipated to be promoted in place need to be promoted by - // evacuation, it may be the case that there is not sufficient reserve within old-gen to hold evacuation of - // these regions. The likely outcome is that these regions will not be selected for evacuation or promotion - // in the current cycle and we will anticipate that they will be promoted in the next cycle. This will cause - // us to reserve more old-gen memory so that these objects can be promoted in the subsequent cycle. - if (heap->is_aging_cycle() && heap->age_census()->is_tenurable(r->age() + 1)) { - if (r->garbage() >= in_place_promotions.old_garbage_threshold()) { - promo_potential += r->get_live_data_bytes(); - } - } } - // Note that we keep going even if one region is excluded from selection. - // Subsequent regions may be selected if they have smaller live data. } in_place_promotions.complete_planning(); - // Sort in increasing order according to live data bytes. Note that candidates represents the number of regions - // that qualify to be promoted by evacuation. - size_t old_consumed = 0; - if (candidates > 0) { - size_t selected_regions = 0; - size_t selected_live = 0; - QuickSort::sort(sorted_regions, candidates, compare_by_aged_live); - for (size_t i = 0; i < candidates; i++) { - ShenandoahHeapRegion* const region = sorted_regions[i]._region; - const size_t region_live_data = sorted_regions[i]._live_data; - const size_t promotion_need = (size_t) (region_live_data * ShenandoahPromoEvacWaste); - if (old_consumed + promotion_need <= old_promotion_reserve) { - old_consumed += promotion_need; - heap->collection_set()->add_region(region); - selected_regions++; - selected_live += region_live_data; - } else { - // We rejected this promotable region from the collection set because we had no room to hold its copy. - // Add this region to promo potential for next GC. - promo_potential += region_live_data; - assert(!heap->collection_set()->is_in(region), "Region %zu shouldn't be in the collection set", region->index()); - } - // We keep going even if one region is excluded from selection because we need to accumulate all eligible - // regions that are not preselected into promo_potential - } - log_debug(gc, ergo)("Preselected %zu regions containing " PROPERFMT " live data," - " consuming: " PROPERFMT " of budgeted: " PROPERFMT, - selected_regions, PROPERFMTARGS(selected_live), PROPERFMTARGS(old_consumed), PROPERFMTARGS(old_promotion_reserve)); - } + add_tenured_regions_to_collection_set(old_promotion_reserve, heap, candidates, sorted_regions); - log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential)); + const uint tenuring_threshold = heap->age_census()->tenuring_threshold(); + const size_t tenurable_this_cycle = heap->age_census()->get_tenurable_bytes(tenuring_threshold); + const size_t tenurable_next_cycle = heap->age_census()->get_tenurable_bytes(tenuring_threshold - 1); + assert(tenurable_next_cycle >= tenurable_this_cycle, + "Tenurable next cycle (" PROPERFMT ") should include tenurable this cycle (" PROPERFMT ")", + PROPERFMTARGS(tenurable_next_cycle), PROPERFMTARGS(tenurable_this_cycle)); + + const size_t max_promotions = tenurable_this_cycle * ShenandoahPromoEvacWaste; + const size_t old_consumed = MIN2(max_promotions, old_promotion_reserve); + + // Don't include the bytes we expect to promote in this cycle in the next cycle + const size_t promo_potential = (tenurable_next_cycle - tenurable_this_cycle) * ShenandoahPromoEvacWaste; heap->old_generation()->set_promotion_potential(promo_potential); + log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential)); + return old_consumed; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index d6551cffb73a..8ea5cdb36c8b 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -34,6 +34,11 @@ class ShenandoahHeap; class ShenandoahCollectionSet; class RegionData; +typedef struct { + ShenandoahHeapRegion* _region; + size_t _live_data; +} AgedRegionData; + /* * This class serves as the base class for heuristics used to trigger and * choose the collection sets for young and global collections. It leans @@ -48,9 +53,12 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { public: explicit ShenandoahGenerationalHeuristics(ShenandoahGeneration* generation); - void choose_collection_set(ShenandoahCollectionSet* collection_set) override; + void post_initialize() override; - virtual void post_initialize() override; + // Wraps budget computation, subclass region selection, budget adjustment, and tracing. + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + RegionData* data, size_t data_size, + size_t free) override; private: // Compute evacuation budgets prior to choosing collection set. @@ -73,8 +81,11 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { // to false. size_t select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, const size_t old_promotion_reserve); - // Filter and sort remaining regions before adding to collection set. - void filter_regions(ShenandoahCollectionSet* collection_set); + // Select regions for inclusion in the collection set that are tenured, but do + // not hold enough live data to warrant promotion in place. + void add_tenured_regions_to_collection_set(size_t old_promotion_reserve, + ShenandoahGenerationalHeap *const heap, + size_t candidates, AgedRegionData* sorted_regions); // Adjust evacuation budgets after choosing collection set. On entry, the instance variable _regions_to_xfer // represents regions to be transferred to old based on decisions made in top_off_collection_set() @@ -82,6 +93,11 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { ShenandoahCollectionSet* const collection_set); protected: + // Subclasses override this to perform generation-specific region selection. + virtual void select_collection_set_regions(ShenandoahCollectionSet* set, + RegionData* data, size_t data_size, + size_t free) = 0; + ShenandoahGeneration* _generation; size_t _add_regions_to_old; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp index ed25cd2e1a98..9452e8b28cbb 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp @@ -112,9 +112,9 @@ ShenandoahGlobalHeuristics::ShenandoahGlobalHeuristics(ShenandoahGlobalGeneratio : ShenandoahGenerationalHeuristics(generation) { } -void ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +void ShenandoahGlobalHeuristics::select_collection_set_regions(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { QuickSort::sort(data, size, compare_by_garbage); choose_global_collection_set(cset, data, size, actual_free, 0); } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp index 8102fa24d145..1e96a665704b 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp @@ -161,9 +161,9 @@ class ShenandoahGlobalHeuristics : public ShenandoahGenerationalHeuristics { public: ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation); - void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) override; + void select_collection_set_regions(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; private: void choose_global_collection_set(ShenandoahCollectionSet* cset, diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 895088381ee6..3091b19b6003 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -29,6 +29,7 @@ #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahTrace.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" #include "runtime/globals_extension.hpp" @@ -79,10 +80,6 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec ShenandoahHeap* heap = ShenandoahHeap::heap(); assert(collection_set->is_empty(), "Must be empty"); - assert(!heap->mode()->is_generational(), "Wrong heuristic for heap mode"); - - // Check all pinned regions have updated status before choosing the collection set. - heap->assert_pinned_region_status(); // Step 1. Build up the region candidates we care about, rejecting losers and accepting winners right away. @@ -103,6 +100,10 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec for (size_t i = 0; i < num_regions; i++) { ShenandoahHeapRegion* region = heap->get_region(i); + if (!_space_info->contains(region)) { + continue; + } + size_t garbage = region->garbage(); total_garbage += garbage; @@ -117,6 +118,8 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec region->make_trash_immediate(); } else { // This is our candidate for later consideration. + assert(region->get_top_before_promote() == nullptr, + "Cannot add region %zu scheduled for in-place-promotion to the collection set", i); candidates[cand_idx].set_region_and_garbage(region, garbage); cand_idx++; } @@ -149,6 +152,7 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); } collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); + ShenandoahTracer::report_evacuation_info(collection_set, free_regions, immediate_regions, immediate_garbage); } void ShenandoahHeuristics::start_idle_span() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp index 6ed05abf0b16..765061a43ed7 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp @@ -27,6 +27,8 @@ #include "utilities/globalDefinitions.hpp" +class ShenandoahHeapRegion; + /* * The purpose of this interface is to decouple the heuristics from a * direct dependency on the ShenandoahHeap singleton instance. This is @@ -46,6 +48,9 @@ class ShenandoahSpaceInfo { // in time within each GC cycle. For certain GC cycles, the value returned may include some bytes allocated before // the start of the current GC cycle. virtual size_t bytes_allocated_since_gc_start() const = 0; + + // Return true if this region belongs to this space. + virtual bool contains(ShenandoahHeapRegion* region) const = 0; }; #endif //SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHSPACEINFO_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp index 68ffb6592db1..27aa9a475101 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -37,9 +37,9 @@ ShenandoahYoungHeuristics::ShenandoahYoungHeuristics(ShenandoahYoungGeneration* } -void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +void ShenandoahYoungHeuristics::select_collection_set_regions(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { // See comments in ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(): // we do the same here, but with the following adjustments for generational mode: // diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp index 806cef673d5b..8fabc40693ca 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp @@ -38,9 +38,9 @@ class ShenandoahYoungHeuristics : public ShenandoahGenerationalHeuristics { explicit ShenandoahYoungHeuristics(ShenandoahYoungGeneration* generation); - void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) override; + void select_collection_set_regions(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; bool should_start_gc() override; diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp index 5ef21719ed47..1c2c15c40dc4 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahMode.cpp @@ -26,7 +26,6 @@ #include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" #include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp" #include "gc/shenandoah/mode/shenandoahMode.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp index 71fd6e376148..a81efa99d709 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp @@ -171,6 +171,15 @@ void ShenandoahAgeCensus::update_census(size_t age0_pop) { NOT_PRODUCT(update_total();) } +size_t ShenandoahAgeCensus::get_tenurable_bytes(const uint tenuring_threshold) const { + assert(_epoch < MAX_SNAPSHOTS, "Out of bounds"); + size_t total = 0; + const AgeTable* pv = _global_age_tables[_epoch]; + for (uint i = tenuring_threshold; i < MAX_COHORTS; i++) { + total += pv->sizes[i]; + } + return total * HeapWordSize; +} // Reset the epoch for the global age tables, // clearing all history. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp index 9c5baaedcd60..c140f445e210 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp @@ -216,6 +216,12 @@ class ShenandoahAgeCensus: public CHeapObj { // allocated when the concurrent marking was in progress. void update_census(size_t age0_pop); + // Return the total size of the population at or above the given threshold for the current epoch + size_t get_tenurable_bytes(uint tenuring_threshold) const; + + // As above, but use the current tenuring threshold + size_t get_tenurable_bytes() const { return get_tenurable_bytes(tenuring_threshold()); } + // Reset the epoch, clearing accumulated census history // Note: this isn't currently used, but reserved for planned // future usage. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp index c1fa4b964b77..e9d6a6866943 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp @@ -200,6 +200,7 @@ void ShenandoahArguments::initialize() { && strcmp(ShenandoahGCHeuristics, "adaptive") != 0) { log_warning(gc)("Ignoring -XX:ShenandoahGCHeuristics input: %s, because generational shenandoah only" " supports adaptive heuristics", ShenandoahGCHeuristics); + FLAG_SET_ERGO(ShenandoahGCHeuristics, "adaptive"); } FullGCForwarding::initialize_flags(MaxHeapSize); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index f0125c38caea..ba0aa1a13045 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -112,6 +112,24 @@ void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap heap->concurrent_prepare_for_update_refs(); } +void ShenandoahConcurrentGC::entry_update_card_table() { + ShenandoahHeap* const heap = ShenandoahHeap::heap(); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + + static const char* msg = "Concurrent update cards"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_card_table); + EventMark em("%s", msg); + + ShenandoahWorkerScope scope(heap->workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), + "concurrent update cards"); + + // Heap needs to be parsable here. + // Also, parallel heap region iterate must have a phase set. + assert(ShenandoahTimingsTracker::is_current_phase_valid(), "Current phase must be set"); + ShenandoahGenerationalHeap::heap()->old_generation()->update_card_table(); +} + bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { ShenandoahHeap* const heap = ShenandoahHeap::heap(); _generation->ref_processor()->set_soft_reference_policy( @@ -206,6 +224,11 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { // Perform update-refs phase. entry_concurrent_update_refs_prepare(heap); + + if (ShenandoahHeap::heap()->mode()->is_generational()) { + entry_update_card_table(); + } + if (ShenandoahVerify) { vmop_entry_init_update_refs(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp index 54d43416fdb3..ba228901d722 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp @@ -59,8 +59,6 @@ class ShenandoahConcurrentGC : public ShenandoahGC { bool collect(GCCause::Cause cause) override; ShenandoahDegenPoint degen_point() const; - void entry_concurrent_update_refs_prepare(ShenandoahHeap* heap); - // Return true if this cycle found enough immediate garbage to skip evacuation bool abbreviated() const { return _abbreviated; } @@ -95,6 +93,8 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void entry_cleanup_early(); void entry_evacuate(); void entry_update_thread_roots(); + void entry_update_card_table(); + void entry_concurrent_update_refs_prepare(ShenandoahHeap* heap); void entry_update_refs(); void entry_cleanup_complete(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.cpp b/src/hotspot/share/gc/shenandoah/shenandoahController.cpp index 50aabad7d42e..0096aad2570d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahController.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.cpp @@ -23,13 +23,13 @@ * */ +#include "gc/shared/allocTracer.hpp" #include "gc/shared/gc_globals.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahController.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" - void ShenandoahController::update_gc_id() { _gc_id.add_then_fetch((size_t)1); } @@ -45,10 +45,12 @@ void ShenandoahController::handle_alloc_failure(const ShenandoahAllocRequest& re const GCCause::Cause cause = is_humongous ? GCCause::_shenandoah_humongous_allocation_failure : GCCause::_allocation_failure; ShenandoahHeap* const heap = ShenandoahHeap::heap(); + size_t req_byte = req.size() * HeapWordSize; if (heap->cancel_gc(cause)) { - log_info(gc)("Failed to allocate %s, " PROPERFMT, req.type_string(), PROPERFMTARGS(req.size() * HeapWordSize)); + log_info(gc)("Failed to allocate %s, " PROPERFMT, req.type_string(), PROPERFMTARGS(req_byte)); request_gc(cause); } + AllocTracer::send_allocation_requiring_gc_event(req_byte, checked_cast(get_gc_id())); if (block) { MonitorLocker ml(&_alloc_failure_waiters_lock); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 1873d8180939..84b22f13d470 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -277,6 +277,11 @@ void ShenandoahDegenGC::op_degenerated() { _abbreviated = true; } + // labs are retired, walk the old regions and update remembered set + if (ShenandoahHeap::heap()->mode()->is_generational()) { + ShenandoahGenerationalHeap::heap()->old_generation()->update_card_table(); + } + case _degenerated_update_refs: if (heap->has_forwarded_objects()) { op_update_refs(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index a579d6d36942..592c5bffa5a7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -1569,7 +1569,7 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah // We must call try_recycle_under_lock() even if !r->is_trash(). The reason is that if r is being recycled at this // moment by a GC worker thread, it may appear to be not trash even though it has not yet been fully recycled. If // we proceed without waiting for the worker to finish recycling the region, the worker thread may overwrite the - // region's affiliation with FREE after we set the region's affiliation to req.afiliation() below + // region's affiliation with FREE after we set the region's affiliation to req.affiliation() below r->try_recycle_under_lock(); in_new_region = r->is_empty(); if (in_new_region) { @@ -1585,7 +1585,6 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah // concurrent preparations for mixed evacuations are completed), we mark this region as not requiring any // coalesce-and-fill processing. r->end_preemptible_coalesce_and_fill(); - _heap->old_generation()->clear_cards_for(r); } #ifdef ASSERT ShenandoahMarkingContext* const ctx = _heap->marking_context(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index 3d592e9f9be3..7d082e4a8b00 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -301,6 +301,7 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { collection_set->clear(); ShenandoahHeapLocker locker(heap->lock()); + heap->assert_pinned_region_status(this); _heuristics->choose_collection_set(collection_set); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index 6f32d1011527..9f8944127c00 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -143,7 +143,7 @@ class ShenandoahGeneration : public CHeapObj, public ShenandoahSpaceInfo { virtual bool contains(ShenandoahAffiliation affiliation) const = 0; // Return true if this region is affiliated with this generation. - virtual bool contains(ShenandoahHeapRegion* region) const = 0; + virtual bool contains(ShenandoahHeapRegion* region) const override = 0; // Return true if this object is affiliated with this generation. virtual bool contains(oop obj) const = 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp index 6912750378e5..ca15c6db443e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -133,3 +133,4 @@ void ShenandoahGenerationalEvacuationTask::evacuate_and_promote_regions() { } } } + diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index d5cfa4b7fb92..a51449e91f49 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -363,10 +363,6 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint // Record that the evacuation succeeded evac_tracker()->end_evacuation(thread, size * HeapWordSize, FROM_GENERATION, TO_GENERATION); } - - if (TO_GENERATION == OLD_GENERATION) { - old_generation()->handle_evacuation(copy, size); - } } else { // Failed to evacuate. We need to deal with the object that is left behind. Since this // new allocation is certainly after TAMS, it will be considered live in the next cycle. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 5bf765055065..75d3ade4e491 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1955,6 +1955,26 @@ void ShenandoahHeap::heap_region_iterate(ShenandoahHeapRegionClosure* blk) const } } +class ShenandoahHeapRegionIteratorTask : public WorkerTask { +private: + ShenandoahRegionIterator _regions; + ShenandoahHeapRegionClosure* _closure; + +public: + ShenandoahHeapRegionIteratorTask(ShenandoahHeapRegionClosure* closure) + : WorkerTask("Shenandoah Heap Region Iterator") + , _closure(closure) {} + + void work(uint worker_id) override { + ShenandoahParallelWorkerSession worker_session(worker_id); + ShenandoahHeapRegion* region = _regions.next(); + while (region != nullptr) { + _closure->heap_region_do(region); + region = _regions.next(); + } + } +}; + class ShenandoahParallelHeapRegionTask : public WorkerTask { private: ShenandoahHeap* const _heap; @@ -2011,6 +2031,11 @@ void ShenandoahHeap::parallel_heap_region_iterate(ShenandoahHeapRegionClosure* b } } +void ShenandoahHeap::heap_region_iterator(ShenandoahHeapRegionClosure* closure) const { + ShenandoahHeapRegionIteratorTask task(closure); + workers()->run_task(&task); +} + class ShenandoahRendezvousHandshakeClosure : public HandshakeClosure { public: inline ShenandoahRendezvousHandshakeClosure(const char* name) : HandshakeClosure(name) {} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index d4604be0aece..ab7dd00b7743 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -298,6 +298,7 @@ class ShenandoahHeap : public CollectedHeap { void heap_region_iterate(ShenandoahHeapRegionClosure* blk) const; void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* blk) const; + void heap_region_iterator(ShenandoahHeapRegionClosure* blk) const; inline ShenandoahMmuTracker* mmu_tracker() { return &_mmu_tracker; }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index afc6b24e168c..3db11000af52 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -67,6 +67,7 @@ ShenandoahHeapRegion::ShenandoahHeapRegion(HeapWord* start, size_t index, bool c _new_top(nullptr), _empty_time(os::elapsedTime()), _top_before_promoted(nullptr), + _top_at_evac_start(start), _state(committed ? _empty_committed : _empty_uncommitted), _top(start), _tlab_allocs(0), @@ -565,12 +566,17 @@ void ShenandoahHeapRegion::recycle_internal() { assert(_recycling.is_set() && is_trash(), "Wrong state"); ShenandoahHeap* heap = ShenandoahHeap::heap(); + _top_at_evac_start = _bottom; _mixed_candidate_garbage_words = 0; set_top(bottom()); clear_live_data(); reset_alloc_metadata(); heap->marking_context()->reset_top_at_mark_start(this); set_update_watermark(bottom()); + if (is_old()) { + heap->old_generation()->clear_cards_for(this); + } + if (ZapUnusedHeapArea) { SpaceMangler::mangle_region(MemRegion(bottom(), end())); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp index 3a0ac042f574..569b64f756b9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp @@ -246,6 +246,7 @@ class ShenandoahHeapRegion { double _empty_time; HeapWord* _top_before_promoted; + HeapWord* _top_at_evac_start; // Seldom updated fields Atomic _state; @@ -365,12 +366,15 @@ class ShenandoahHeapRegion { } // Returns true iff this region was promoted in place subsequent to the most recent start of concurrent old marking. - inline bool was_promoted_in_place() { + bool was_promoted_in_place() const { return _promoted_in_place; } inline void restore_top_before_promote(); inline size_t garbage_before_padded_for_promote() const; + HeapWord* get_top_at_evac_start() const { return _top_at_evac_start; } + void record_top_at_evac_start() { _top_at_evac_start = _top; } + // If next available memory is not aligned on address that is multiple of alignment, fill the empty space // so that returned object is aligned on an address that is a multiple of alignment_in_bytes. Requested // size is in words. It is assumed that this->is_old(). A pad object is allocated, filled, and registered diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp index 3c6fe1a3df12..7554a9c9a2ce 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.cpp @@ -80,6 +80,11 @@ void ShenandoahFinalMarkUpdateRegionStateClosure::heap_region_do(ShenandoahHeapR // Remember limit for updating refs. It's guaranteed that we get no // from-space-refs written from here on. r->set_update_watermark_at_safepoint(r->top()); + + if (r->is_old()) { + // Record where we need to start updating the remembered set + r->record_top_at_evac_start(); + } } else { assert(!r->has_live(), "Region %zu should have no live data", r->index()); assert(_ctx == nullptr || _ctx->top_at_mark_start(r) == r->top(), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp index 10d61221e87e..153193fa3a33 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp @@ -238,6 +238,9 @@ void ShenandoahInPlacePromoter::promote(ShenandoahHeapRegion* region) const { // is_collector_free range. We'll add it to that range below. region->restore_top_before_promote(); + // We also need to record where those allocations begin so that we can later update the remembered set. + region->record_top_at_evac_start(); + assert(region->used() + pip_pad_bytes + pip_unpadded == region_size_bytes, "invariant"); // The update_watermark was likely established while we had the artificially high value of top. Make it sane now. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 4ad7d2a1ae55..37de5966554e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -318,6 +318,11 @@ void ShenandoahOldGeneration::heap_region_iterate(ShenandoahHeapRegionClosure* c ShenandoahHeap::heap()->heap_region_iterate(&old_regions_cl); } +void ShenandoahOldGeneration::heap_region_iterator(ShenandoahHeapRegionClosure* cl) { + ShenandoahIncludeRegionClosure old_regions_cl(cl); + ShenandoahHeap::heap()->heap_region_iterator(&old_regions_cl); +} + void ShenandoahOldGeneration::set_concurrent_mark_in_progress(bool in_progress) { ShenandoahHeap::heap()->set_concurrent_old_mark_in_progress(in_progress); } @@ -326,6 +331,12 @@ bool ShenandoahOldGeneration::is_concurrent_mark_in_progress() { return ShenandoahHeap::heap()->is_concurrent_old_mark_in_progress(); } +void ShenandoahOldGeneration::record_tops_at_evac_start() { + for_each_region([](ShenandoahHeapRegion* region) { + region->record_top_at_evac_start(); + }); +} + void ShenandoahOldGeneration::cancel_marking() { if (is_concurrent_mark_in_progress()) { log_debug(gc)("Abandon SATB buffers"); @@ -662,15 +673,19 @@ void ShenandoahOldGeneration::log_failed_promotion(LogStream& ls, Thread* thread } } -void ShenandoahOldGeneration::handle_evacuation(HeapWord* obj, size_t words) const { - // Only register the copy of the object that won the evacuation race. - _card_scan->register_object_without_lock(obj); - - // Mark the entire range of the evacuated object as dirty. At next remembered set scan, - // we will clear dirty bits that do not hold interesting pointers. It's more efficient to - // do this in batch, in a background GC thread than to try to carefully dirty only cards - // that hold interesting pointers right now. - _card_scan->mark_range_as_dirty(obj, words); +void ShenandoahOldGeneration::update_card_table() { + for_each_region([this](ShenandoahHeapRegion* region) { + if (region->is_regular()) { + // Humongous regions are promoted in place, remembered set maintenance is handled there + // Regular regions that are promoted in place have their rset maintenance handled for + // the objects in the region when it was promoted. We record TEAS for such a region + // when the in-place-promotion is completed. Such a region may be used for additional + // promotions in the same cycle it was itself promoted. + if (region->top() > region->get_top_at_evac_start()) { + _card_scan->update_card_table(region->get_top_at_evac_start(), region->top()); + } + } + }); } bool ShenandoahOldGeneration::has_unprocessed_collection_candidates() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 630736190f0c..942f93c5c68d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -172,8 +172,8 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { void handle_failed_promotion(Thread* thread, size_t size) const; void log_failed_promotion(LogStream& ls, Thread* thread, size_t size) const; - // A successful evacuation re-dirties the cards and registers the object with the remembered set - void handle_evacuation(HeapWord* obj, size_t words) const; + // Iterate over recently promoted objects to update card table and object registrations + void update_card_table(); // Clear the flag after it is consumed by the control thread bool clear_failed_evacuation() { @@ -199,11 +199,36 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { // Mark card for this location as dirty void mark_card_as_dirty(void* location); - void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + template + class ShenandoahHeapRegionLambda : public ShenandoahHeapRegionClosure { + T _region_lambda; + public: + explicit ShenandoahHeapRegionLambda(T region_lambda) : _region_lambda(region_lambda) {} - void parallel_heap_region_iterate_free(ShenandoahHeapRegionClosure* cl) override; + void heap_region_do(ShenandoahHeapRegion* r) override { + _region_lambda(r); + } + + bool is_thread_safe() override { + return true; + } + + size_t parallel_region_stride() override { + // Temporarily override to force parallelism when updating card table + return 8; + } + }; + + template + void for_each_region(LambdaT lambda) { + ShenandoahHeapRegionLambda l(lambda); + heap_region_iterator(&l); + } + void parallel_heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + void parallel_heap_region_iterate_free(ShenandoahHeapRegionClosure* cl) override; void heap_region_iterate(ShenandoahHeapRegionClosure* cl) override; + void heap_region_iterator(ShenandoahHeapRegionClosure* cl); bool contains(ShenandoahAffiliation affiliation) const override; bool contains(ShenandoahHeapRegion* region) const override; @@ -212,8 +237,17 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { void set_concurrent_mark_in_progress(bool in_progress) override; bool is_concurrent_mark_in_progress() override; + // For old regions, objects between top at evac start and top represent promoted objects. + // These objects will need to have their cards dirtied and their offsets within the cards registered. + void record_tops_at_evac_start(); + bool entry_coalesce_and_fill(); + + // Global collections touch old regions, so the old generation needs to be informed of this. + // The old generation may decide to schedule additional mixed collections, or may decide to + // immediately coalesce-and-fill old objects in regions that were not collected. void transition_old_generation_after_global_gc(); + void prepare_gc() override; void prepare_regions_and_collection_set(bool concurrent) override; void record_success_concurrent(bool abbreviated) override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp index 412cfa9447e7..5049113b6658 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp @@ -210,12 +210,4 @@ void ShenandoahPLAB::retire() { // plab->retire() overwrites unused memory between plab->top() and plab->hard_end() with a dummy object to make memory parsable. // It adds the size of this unused memory, in words, to plab->waste(). _plab->retire(); - if (top != nullptr && _plab->waste() > original_waste && _heap->is_in_old(top)) { - // If retiring the plab created a filler object, then we need to register it with our card scanner so it can - // safely walk the region backing the plab. - log_debug(gc, plab)("retire_plab() is registering remnant of size %zu at " PTR_FORMAT, - (_plab->waste() - original_waste) * HeapWordSize, p2i(top)); - // No lock is necessary because the PLAB memory is aligned on card boundaries. - _heap->old_generation()->card_scan()->register_object_without_lock(top); - } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index e890008b9160..a454de68f006 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -109,6 +109,7 @@ class outputStream; f(conc_strong_roots, "Concurrent Strong Roots") \ SHENANDOAH_PAR_PHASE_DO(conc_strong_roots_, " CSR: ", f) \ f(conc_evac, "Concurrent Evacuation") \ + f(conc_update_card_table, "Concurrent Update Cards") \ f(conc_final_roots, "Concurrent Final Roots") \ f(promote_in_place, " Promote Regions") \ f(final_roots_gross, "Pause Verify Final Roots (G)") \ @@ -254,7 +255,7 @@ class ShenandoahPhaseTimings : public CHeapObj { void flush_cycle_to_global(); static const char* phase_name(Phase phase) { - assert(phase >= 0 && phase < _num_phases, "Out of bound"); + assert(phase >= 0 && phase < _num_phases, "Out of bounds: %d", phase); return _phase_names[phase]; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp index 9e160d5b2945..8d7ba2dc46ff 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp @@ -31,6 +31,33 @@ #include "logging/log.hpp" #include "runtime/threads.hpp" +// A closure that takes an oop in the old generation and, if it's pointing +// into the young generation, dirties the corresponding remembered set entry. +class ShenandoahDirtyRememberedSetClosure : public BasicOopIterateClosure { +protected: + ShenandoahGenerationalHeap* const _heap; + ShenandoahScanRemembered* const _scanner; + +public: + ShenandoahDirtyRememberedSetClosure() : + _heap(ShenandoahGenerationalHeap::heap()), + _scanner(_heap->old_generation()->card_scan()) {} + + template + void work(T* p) { + assert(_heap->is_in_old(p), "Expecting to get an old gen address"); + if (T o = RawAccess<>::oop_load(p); !CompressedOops::is_null(o)) { + if (const oop obj = CompressedOops::decode_not_null(o); _heap->is_in_young(obj)) { + // Dirty the card containing the cross-generational pointer. + _scanner->mark_card_as_dirty((HeapWord*) p); + } + } + } + + void do_oop(narrowOop* p) override { work(p); } + void do_oop(oop* p) override { work(p); } +}; + size_t ShenandoahDirectCardMarkRememberedSet::last_valid_index() const { return _card_table->last_valid_index(); } @@ -161,7 +188,6 @@ void ShenandoahCardCluster::register_object_without_lock(HeapWord* address) { uint8_t offset_in_card = checked_cast(pointer_delta(address, card_start_address)); if (!starts_object(card_at_start)) { - set_starts_object_bit(card_at_start); set_first_start(card_at_start, offset_in_card); set_last_start(card_at_start, offset_in_card); } else { @@ -172,6 +198,49 @@ void ShenandoahCardCluster::register_object_without_lock(HeapWord* address) { } } +void ShenandoahCardCluster::update_card_table(HeapWord* start, HeapWord* end) { + HeapWord* address = start; + HeapWord* previous_address = nullptr; + uint8_t previous_offset = 0; + size_t previous_card_index = -1; + ShenandoahDirtyRememberedSetClosure make_cards_dirty; + + log_debug(gc, remset)("Update remembered set from " PTR_FORMAT ", to " PTR_FORMAT, p2i(start), p2i(end)); + _rs->mark_range_as_dirty(start, pointer_delta(end, start)); + + while (address < end) { + + // Compute card and offset in card for this object + const size_t object_card_index = _rs->card_index_for_addr(address); + const HeapWord* card_start_address = _rs->addr_for_card_index(object_card_index); + const uint8_t offset_in_card = checked_cast(pointer_delta(address, card_start_address)); + + if (object_card_index != previous_card_index) { + if (previous_address != nullptr) { + // Register the previous object on the previous card, we are starting a new card here + set_last_start(previous_card_index, previous_offset); + } + + previous_card_index = object_card_index; + if (!starts_object(object_card_index)) { + // The previous cycle may have recorded an earlier start in this card. Do not overwrite it. + set_first_start(object_card_index, offset_in_card); + } + } + + previous_offset = offset_in_card; + previous_address = address; + + const oop obj = cast_to_oop(address); + address += obj->size(); + } + + // Register the last object seen in this range. + if (previous_address != nullptr) { + set_last_start(previous_card_index, previous_offset); + } +} + void ShenandoahCardCluster::coalesce_objects(HeapWord* address, size_t length_in_words) { size_t card_at_start = _rs->card_index_for_addr(address); @@ -641,36 +710,6 @@ void ShenandoahScanRemembered::merge_worker_card_stats_cumulative( } #endif -// A closure that takes an oop in the old generation and, if it's pointing -// into the young generation, dirties the corresponding remembered set entry. -// This is only used to rebuild the remembered set after a full GC. -class ShenandoahDirtyRememberedSetClosure : public BasicOopIterateClosure { -protected: - ShenandoahGenerationalHeap* const _heap; - ShenandoahScanRemembered* const _scanner; - -public: - ShenandoahDirtyRememberedSetClosure() : - _heap(ShenandoahGenerationalHeap::heap()), - _scanner(_heap->old_generation()->card_scan()) {} - - template - inline void work(T* p) { - assert(_heap->is_in_old(p), "Expecting to get an old gen address"); - T o = RawAccess<>::oop_load(p); - if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); - if (_heap->is_in_young(obj)) { - // Dirty the card containing the cross-generational pointer. - _scanner->mark_card_as_dirty((HeapWord*) p); - } - } - } - - virtual void do_oop(narrowOop* p) { work(p); } - virtual void do_oop(oop* p) { work(p); } -}; - ShenandoahDirectCardMarkRememberedSet::ShenandoahDirectCardMarkRememberedSet(ShenandoahCardTable* card_table, size_t total_card_count) : LogCardValsPerIntPtr(log2i_exact(sizeof(intptr_t)) - log2i_exact(sizeof(CardValue))), LogCardSizeInWords(log2i_exact(CardTable::card_size_in_words())) { @@ -1039,38 +1078,44 @@ void ShenandoahReconstructRememberedSetTask::work(uint worker_id) { ShenandoahDirtyRememberedSetClosure dirty_cards_for_cross_generational_pointers; while (r != nullptr) { - if (r->is_old() && r->is_active()) { - HeapWord* obj_addr = r->bottom(); - if (r->is_humongous_start()) { - // First, clear the remembered set - oop obj = cast_to_oop(obj_addr); - size_t size = obj->size(); - - size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); - size_t region_index = r->index(); - ShenandoahHeapRegion* humongous_region = heap->get_region(region_index); - while (num_regions-- != 0) { - scanner->reset_object_range(humongous_region->bottom(), humongous_region->end()); - region_index++; - humongous_region = heap->get_region(region_index); - } - - // Then register the humongous object and DIRTY relevant remembered set cards - scanner->register_object_without_lock(obj_addr); - obj->oop_iterate(&dirty_cards_for_cross_generational_pointers); - } else if (!r->is_humongous()) { - scanner->reset_object_range(r->bottom(), r->end()); - - // Then iterate over all objects, registering object and DIRTYing relevant remembered set cards - HeapWord* t = r->top(); - while (obj_addr < t) { + if (r->is_active()) { + if (r->is_old()) { + HeapWord* obj_addr = r->bottom(); + if (r->is_humongous_start()) { + // First, clear the remembered set oop obj = cast_to_oop(obj_addr); + size_t size = obj->size(); + + size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); + size_t region_index = r->index(); + ShenandoahHeapRegion* humongous_region = heap->get_region(region_index); + while (num_regions-- != 0) { + scanner->reset_object_range(humongous_region->bottom(), humongous_region->end()); + region_index++; + humongous_region = heap->get_region(region_index); + } + + // Then register the humongous object and DIRTY relevant remembered set cards scanner->register_object_without_lock(obj_addr); - obj_addr += obj->oop_iterate_size(&dirty_cards_for_cross_generational_pointers); - } - } // else, ignore humongous continuation region + obj->oop_iterate(&dirty_cards_for_cross_generational_pointers); + } else if (!r->is_humongous()) { + scanner->reset_object_range(r->bottom(), r->end()); + + // Then iterate over all objects, registering object and DIRTYing relevant remembered set cards + HeapWord* t = r->top(); + while (obj_addr < t) { + oop obj = cast_to_oop(obj_addr); + scanner->register_object_without_lock(obj_addr); + obj_addr += obj->oop_iterate_size(&dirty_cards_for_cross_generational_pointers); + } + } // else, ignore humongous continuation region + } else { + // The region is young, but it may become old again and we don't want stale remembered set data. + assert(r->is_young(), "Region: %zu, is active but free", r->index()); + heap->old_generation()->clear_cards_for(r); + } } - // else, this region is FREE or YOUNG or inactive and we can ignore it. + // else, this region is FREE or inactive and we can ignore it. r = _regions->next(); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp index c2c117e86e6a..53f00e64a037 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp @@ -603,6 +603,9 @@ class ShenandoahCardCluster: public CHeapObj { // as address. void register_object_without_lock(HeapWord* address); + // Dirty cards and register objects for the given range in memory. + void update_card_table(HeapWord* start, HeapWord* end); + // During the reference updates phase of GC, we walk through each old-gen memory region that was // not part of the collection set and we invalidate all unmarked objects. As part of this effort, // we coalesce neighboring dead objects in order to make future remembered set scanning more @@ -814,6 +817,10 @@ class ShenandoahScanRemembered: public CHeapObj { } } + void update_card_table(HeapWord* start, HeapWord* end) const { + _scc->update_card_table(start, end); + } + // Return true iff this object is "properly" registered. bool verify_registration(HeapWord* address, ShenandoahMarkingContext* ctx); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp index bbb44348355b..c28e572dd6bd 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp @@ -27,9 +27,7 @@ #include "jfr/jfrEvents.hpp" void ShenandoahTracer::report_evacuation_info(const ShenandoahCollectionSet* cset, - size_t free_regions, size_t regions_promoted_humongous, size_t regions_promoted_regular, - size_t regular_promoted_garbage, size_t regular_promoted_free, size_t regions_immediate, - size_t immediate_size) { + size_t free_regions, size_t regions_immediate, size_t immediate_size) { EventShenandoahEvacuationInformation e; if (e.should_commit()) { @@ -37,16 +35,30 @@ void ShenandoahTracer::report_evacuation_info(const ShenandoahCollectionSet* cse e.set_cSetRegions(cset->count()); e.set_cSetUsedBefore(cset->used()); e.set_cSetUsedAfter(cset->live()); + e.set_freeRegions(free_regions); + e.set_regionsImmediate(regions_immediate); + e.set_immediateBytes(immediate_size); + + e.commit(); + } +} + +void ShenandoahTracer::report_promotion_info(const ShenandoahCollectionSet* cset, + size_t regions_promoted_humongous, size_t humongous_promoted_garbage, size_t humongous_promoted_free, + size_t regions_promoted_regular, size_t regular_promoted_garbage, size_t regular_promoted_free) { + + EventShenandoahPromotionInformation e; + if (e.should_commit()) { + e.set_gcId(GCId::current()); e.set_collectedOld(cset->get_live_bytes_in_old_regions()); e.set_collectedPromoted(cset->get_live_bytes_in_tenurable_regions()); e.set_collectedYoung(cset->get_live_bytes_in_untenurable_regions()); e.set_regionsPromotedHumongous(regions_promoted_humongous); + e.set_humongousPromotedGarbage(humongous_promoted_garbage); + e.set_humongousPromotedFree(humongous_promoted_free); e.set_regionsPromotedRegular(regions_promoted_regular); e.set_regularPromotedGarbage(regular_promoted_garbage); e.set_regularPromotedFree(regular_promoted_free); - e.set_freeRegions(free_regions); - e.set_regionsImmediate(regions_immediate); - e.set_immediateBytes(immediate_size); e.commit(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp index 116968103dea..e5c80e0705fe 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp @@ -34,11 +34,14 @@ class ShenandoahTracer : public GCTracer, public CHeapObj { public: ShenandoahTracer() : GCTracer(Shenandoah) {} - // Sends a JFR event (if enabled) summarizing the composition of the collection set + // Sends a JFR event summarizing the composition of the collection set static void report_evacuation_info(const ShenandoahCollectionSet* cset, - size_t free_regions, size_t regions_promoted_humongous, size_t regions_promoted_regular, - size_t regular_promoted_garbage, size_t regular_promoted_free, size_t regions_immediate, - size_t immediate_size); + size_t free_regions, size_t regions_immediate, size_t immediate_size); + + // Sends a JFR event summarizing in-place promotion activity (generational mode only) + static void report_promotion_info(const ShenandoahCollectionSet* cset, + size_t regions_promoted_humongous, size_t humongous_promoted_garbage, size_t humongous_promoted_free, + size_t regions_promoted_regular, size_t regular_promoted_garbage, size_t regular_promoted_free); }; #endif diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 2b0821650057..09d9e0ccabfc 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -1273,16 +1273,22 @@ + + + + + + + + + - - - diff --git a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp index 426ba4e76509..969c9ca60c1d 100644 --- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp +++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -281,7 +281,9 @@ TRACE_REQUEST_FUNC(SystemProcess) { #if INCLUDE_JVMTI template -static void send_agent_event(AgentEvent& event, const JvmtiAgent* agent) { +static void send_agent_event(AgentEvent& event, const JvmtiAgent* agent, Ticks& timestamp) { + event.set_starttime(timestamp); + event.set_endtime(timestamp); event.set_name(agent->name()); event.set_options(agent->options()); event.set_dynamic(agent->is_dynamic()); @@ -292,29 +294,31 @@ static void send_agent_event(AgentEvent& event, const JvmtiAgent* agent) { TRACE_REQUEST_FUNC(JavaAgent) { JvmtiAgentList::Iterator it = JvmtiAgentList::java_agents(); + Ticks ticks = timestamp(); while (it.has_next()) { const JvmtiAgent* agent = it.next(); assert(agent->is_jplis(), "invariant"); EventJavaAgent event; - send_agent_event(event, agent); + send_agent_event(event, agent, ticks); } } -static void send_native_agent_events(JvmtiAgentList::Iterator& it) { +static void send_native_agent_events(JvmtiAgentList::Iterator& it, Ticks& timestamp) { while (it.has_next()) { const JvmtiAgent* agent = it.next(); assert(!agent->is_jplis(), "invariant"); EventNativeAgent event; event.set_path(agent->os_lib_path()); - send_agent_event(event, agent); + send_agent_event(event, agent, timestamp); } } TRACE_REQUEST_FUNC(NativeAgent) { + Ticks ticks = timestamp(); JvmtiAgentList::Iterator native_agents_it = JvmtiAgentList::native_agents(); - send_native_agent_events(native_agents_it); + send_native_agent_events(native_agents_it, ticks); JvmtiAgentList::Iterator xrun_agents_it = JvmtiAgentList::xrun_agents(); - send_native_agent_events(xrun_agents_it); + send_native_agent_events(xrun_agents_it, ticks); } #else // INCLUDE_JVMTI TRACE_REQUEST_FUNC(JavaAgent) {} diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 2f1ebd90d1dc..a6280558db27 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -96,6 +96,7 @@ class outputStream; LOG_TAG(heap) \ LOG_TAG(heapdump) \ NOT_PRODUCT(LOG_TAG(heapsampling)) \ + COMPILER2_PRESENT(LOG_TAG(hotcode)) \ LOG_TAG(humongous) \ LOG_TAG(ihop) \ LOG_TAG(iklass) \ diff --git a/src/hotspot/share/memory/metaspaceClosure.cpp b/src/hotspot/share/memory/metaspaceClosure.cpp index 0239eadf6929..0926b55b9a30 100644 --- a/src/hotspot/share/memory/metaspaceClosure.cpp +++ b/src/hotspot/share/memory/metaspaceClosure.cpp @@ -22,11 +22,11 @@ * */ -#include "cds/aotGrowableArray.hpp" #include "classfile/packageEntry.hpp" #include "memory/metaspaceClosure.hpp" #include "oops/array.hpp" #include "oops/instanceKlass.hpp" +#include "utilities/growableArray.hpp" // Sanity checks static_assert(!HAS_METASPACE_POINTERS_DO(int)); @@ -35,8 +35,6 @@ static_assert(HAS_METASPACE_POINTERS_DO(Array)); static_assert(HAS_METASPACE_POINTERS_DO(Array)); static_assert(HAS_METASPACE_POINTERS_DO(InstanceKlass)); static_assert(HAS_METASPACE_POINTERS_DO(PackageEntry)); -static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray)); -static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray)); void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) { if (_enclosing_ref != nullptr) { diff --git a/src/hotspot/share/memory/metaspaceClosure.hpp b/src/hotspot/share/memory/metaspaceClosure.hpp index b6ba69d6f632..ac42dd13c6c9 100644 --- a/src/hotspot/share/memory/metaspaceClosure.hpp +++ b/src/hotspot/share/memory/metaspaceClosure.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_MEMORY_METASPACECLOSURE_HPP #define SHARE_MEMORY_METASPACECLOSURE_HPP -#include "cds/aotGrowableArray.hpp" #include "cppstdlib/type_traits.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" @@ -90,7 +89,9 @@ class MetaspaceClosure { // int size_in_heapwords() const; // // Currently, the iterable types include all subtypes of MetsapceObj, as well - // as GrowableArray, ModuleEntry and PackageEntry. + // as GrowableArray (C-heap allocated only), ModuleEntry, and PackageEntry. + // + // (Note that GrowableArray is supported specially and does not require the above functions.) // // Calling these functions would be trivial if these were virtual functions. // However, to save space, MetaspaceObj has NO vtable. The vtable is introduced @@ -303,11 +304,38 @@ class MetaspaceClosure { }; //-------------------------------- - // Support for AOTGrowableArray + // Support for GrowableArray //-------------------------------- + // GrowableArrayRef -- iterate an instance of GrowableArray. + template class GrowableArrayRef : public Ref { + GrowableArray** _mpp; + GrowableArray* dereference() const { + return *_mpp; + } + + public: + GrowableArrayRef(GrowableArray** mpp, Writability w) : Ref(w), _mpp(mpp) {} + + virtual void** mpp() const { + return (void**)_mpp; + } + + virtual void metaspace_pointers_do(MetaspaceClosure *it) const { + GrowableArray* array = dereference(); + log_trace(aot)("Iter(GrowableArray): %p [%d]", array, array->length()); + array->assert_on_C_heap(); + it->push_c_array(array->data_addr(), array->capacity()); + } + + virtual bool is_read_only_by_default() const { return false; } + virtual bool not_null() const { return dereference() != nullptr; } + virtual int size() const { return (int)heap_word_size(sizeof(*dereference())); } + virtual MetaspaceClosureType type() const { return MetaspaceClosureType::GrowableArrayType; } + }; + // Abstract base class for MSOCArrayRef, MSOPointerCArrayRef and OtherCArrayRef. - // These are used for iterating the buffer held by AOTGrowableArray. + // These are used for iterating the buffer held by GrowableArray. template class CArrayRef : public Ref { T** _mpp; int _num_elems; // Number of elements @@ -354,7 +382,7 @@ class MetaspaceClosure { // MSOCArrayRef -- iterate a C array of type T, where T has metaspace_pointer_do(). // We recursively call T::metaspace_pointers_do() for each element in this array. - // This is for supporting AOTGrowableArray. + // This is for supporting GrowableArray. // // E.g., PackageEntry* _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry objects // ... @@ -377,7 +405,7 @@ class MetaspaceClosure { // MSOPointerCArrayRef -- iterate a C array of type T*, where T has metaspace_pointer_do(). // We recursively call MetaspaceClosure::push() for each pointer in this array. - // This is for supporting AOTGrowableArray. + // This is for supporting GrowableArray. // // E.g., PackageEntry** _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry pointers // ... @@ -440,11 +468,11 @@ class MetaspaceClosure { // Array*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef // Array* a5 = ...; it->push(&a5); => MSOPointerArrayRef // - // AOTGrowableArrays have a separate "C array" buffer, so they are scanned in two steps: + // GrowableArrays have a separate "C array" buffer, so they are scanned in two steps: // - // AOTGrowableArray* ga1 = ...; it->push(&ga1); => MSORef => OtherCArrayRef - // AOTGrowableArray* ga2 = ...; it->push(&ga2); => MSORef => MSOCArrayRef - // AOTGrowableArray* ga3 = ...; it->push(&ga3); => MSORef => MSOPointerCArrayRef + // GrowableArray* ga1 = ...; it->push(&ga1); => GrowableArrayRef => OtherCArrayRef + // GrowableArray* ga2 = ...; it->push(&ga2); => GrowableArrayRef => MSOCArrayRef + // GrowableArray* ga3 = ...; it->push(&ga3); => GrowableArrayRef => MSOPointerCArrayRef // // Note that the following will fail to compile: // @@ -476,7 +504,12 @@ class MetaspaceClosure { push_with_ref>(mpp, w); } - // --- The buffer of AOTGrowableArray + template + void push(GrowableArray** mpp, Writability w = _default) { + push_with_ref>(mpp, w); + } + + // --- The buffer of GrowableArray template void push_c_array(T** mpp, int num_elems, Writability w = _default) { push_impl(new OtherCArrayRef(mpp, num_elems, w)); diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp index bd696f52a8b4..a6decdce7f0d 100644 --- a/src/hotspot/share/oops/trainingData.hpp +++ b/src/hotspot/share/oops/trainingData.hpp @@ -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 @@ -217,11 +217,7 @@ class TrainingData : public Metadata { return *prior; } template - void iterate(const Function& fn) const { // lambda enabled API - iterate(const_cast(fn)); - } - template - void iterate(Function& fn) const { // lambda enabled API + void iterate(Function fn) const { // lambda enabled API return _table.iterate_all([&](const TrainingData::Key* k, TrainingData* td) { fn(td); }); } int size() const { return _table.number_of_entries(); } @@ -304,10 +300,7 @@ class TrainingData : public Metadata { } template - static void iterate(const Function& fn) { iterate(const_cast(fn)); } - - template - static void iterate(Function& fn) { // lambda enabled API + static void iterate(Function fn) { // lambda enabled API TrainingDataLocker l; if (have_data()) { archived_training_data_dictionary()->iterate_all(fn); diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index dab0ca989118..2f64482f55b5 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -258,6 +258,19 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c return mem; } +// We may have narrowed the type of base because this runs with PhaseIterGVN::_delay_transform true, explicitly +// update the type of the AddP so it's consistent with its base and load() picks the right memory slice. +Node* ArrayCopyNode::make_and_transform_addp(PhaseGVN* phase, Node* base, Node* offset) { + return make_and_transform_addp(phase, base, base, offset); +} + +Node* ArrayCopyNode::make_and_transform_addp(PhaseGVN* phase, Node* base, Node* ptr, Node* offset) { + assert(phase->is_IterGVN() == nullptr || phase->is_IterGVN()->delay_transform(), "helper method when delay transform is set"); + Node* addp = phase->transform(AddPNode::make_with_base(base, ptr, offset)); + phase->set_type(addp, addp->Value(phase)); + return addp; +} + bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, Node*& adr_src, Node*& base_src, @@ -332,12 +345,11 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, Node* dest_scale = phase->transform(new LShiftXNode(dest_offset, phase->intcon(shift))); - adr_src = phase->transform(AddPNode::make_with_base(base_src, src_scale)); - adr_dest = phase->transform(AddPNode::make_with_base(base_dest, dest_scale)); - - adr_src = phase->transform(AddPNode::make_with_base(base_src, adr_src, phase->MakeConX(header))); - adr_dest = phase->transform(AddPNode::make_with_base(base_dest, adr_dest, phase->MakeConX(header))); + adr_src = make_and_transform_addp(phase, base_src, src_scale); + adr_dest = make_and_transform_addp(phase, base_dest, dest_scale); + adr_src = make_and_transform_addp(phase, base_src, adr_src, phase->MakeConX(header)); + adr_dest = make_and_transform_addp(phase, base_dest, adr_dest, phase->MakeConX(header)); copy_type = dest_elem; } else { assert(ary_src != nullptr, "should be a clone"); @@ -355,8 +367,8 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, return false; } - adr_src = phase->transform(AddPNode::make_with_base(base_src, src_offset)); - adr_dest = phase->transform(AddPNode::make_with_base(base_dest, dest_offset)); + adr_src = make_and_transform_addp(phase, base_src, src_offset); + adr_dest = make_and_transform_addp(phase, base_dest, dest_offset); // The address is offsetted to an aligned address where a raw copy would start. // If the clone copy is decomposed into load-stores - the address is adjusted to @@ -366,8 +378,8 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, int diff = arrayOopDesc::base_offset_in_bytes(elem) - offset; assert(diff >= 0, "clone should not start after 1st array element"); if (diff > 0) { - adr_src = phase->transform(AddPNode::make_with_base(base_src, adr_src, phase->MakeConX(diff))); - adr_dest = phase->transform(AddPNode::make_with_base(base_dest, adr_dest, phase->MakeConX(diff))); + adr_src = make_and_transform_addp(phase, base_src, adr_src, phase->MakeConX(diff)); + adr_dest = make_and_transform_addp(phase, base_dest, adr_dest, phase->MakeConX(diff)); } copy_type = elem; value_type = ary_src->elem(); @@ -429,12 +441,8 @@ Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase, store(bs, phase, forward_ctl, mm, adr_dest, atp_dest, v, value_type, copy_type); for (int i = 1; i < count; i++) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); - Node* next_src = phase->transform(AddPNode::make_with_base(base_src,adr_src,off)); - // We may have narrowed the type of next_src right before calling this method but because this runs with - // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its - // base and load() picks the right memory slice. - phase->set_type(next_src, next_src->Value(phase)); - Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest,adr_dest,off)); + Node* next_src = make_and_transform_addp(phase, base_src,adr_src,off); + Node* next_dest = make_and_transform_addp(phase, base_dest,adr_dest,off); // Same as above phase->set_type(next_dest, next_dest->Value(phase)); v = load(bs, phase, forward_ctl, mm, next_src, atp_src, value_type, copy_type); @@ -473,13 +481,8 @@ Node* ArrayCopyNode::array_copy_backward(PhaseGVN *phase, if (count > 0) { for (int i = count-1; i >= 1; i--) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); - Node* next_src = phase->transform(AddPNode::make_with_base(base_src,adr_src,off)); - // We may have narrowed the type of next_src right before calling this method but because this runs with - // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its - // base and store() picks the right memory slice. - phase->set_type(next_src, next_src->Value(phase)); - Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest,adr_dest,off)); - phase->set_type(next_dest, next_dest->Value(phase)); + Node* next_src = make_and_transform_addp(phase, base_src,adr_src,off); + Node* next_dest = make_and_transform_addp(phase, base_dest,adr_dest,off); Node* v = load(bs, phase, backward_ctl, mm, next_src, atp_src, value_type, copy_type); store(bs, phase, backward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type); } @@ -618,21 +621,31 @@ Node *ArrayCopyNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->set_type(src, phase->type(src)->join_speculative(atp_src)); phase->set_type(dest, phase->type(dest)->join_speculative(atp_dest)); + // Control flow is going to be created, it's easier to do with _delay_transform set to true. + + // prepare_array_copy() doesn't build control flow, but it creates AddP nodes. The src/dest type possibly gets + // narrowed above. If a newly created AddP node is commoned with a pre-existing one, then the type narrowing is lost. + // Setting _delay_transform before prepare_array_copy() guarantees this doesn't happen. + if (can_reshape) { + assert(!phase->is_IterGVN()->delay_transform(), "cannot delay transforms"); + phase->is_IterGVN()->set_delay_transform(true); + } + if (!prepare_array_copy(phase, can_reshape, adr_src, base_src, adr_dest, base_dest, copy_type, value_type, disjoint_bases)) { assert(adr_src == nullptr, "no node can be left behind"); assert(adr_dest == nullptr, "no node can be left behind"); + if (can_reshape) { + assert(phase->is_IterGVN()->delay_transform(), "cannot delay transforms"); + phase->is_IterGVN()->set_delay_transform(false); + } + return nullptr; } Node* in_mem = in(TypeFunc::Memory); - if (can_reshape) { - assert(!phase->is_IterGVN()->delay_transform(), "cannot delay transforms"); - phase->is_IterGVN()->set_delay_transform(true); - } - Node* backward_ctl = phase->C->top(); Node* forward_ctl = phase->C->top(); array_copy_test_overlap(phase, can_reshape, disjoint_bases, count, forward_ctl, backward_ctl); diff --git a/src/hotspot/share/opto/arraycopynode.hpp b/src/hotspot/share/opto/arraycopynode.hpp index 483a3a862672..aa62ee05cd0c 100644 --- a/src/hotspot/share/opto/arraycopynode.hpp +++ b/src/hotspot/share/opto/arraycopynode.hpp @@ -104,6 +104,10 @@ class ArrayCopyNode : public CallNode { static const TypePtr* get_address_type(PhaseGVN* phase, const TypePtr* atp, Node* n); Node* try_clone_instance(PhaseGVN *phase, bool can_reshape, int count); + + Node* make_and_transform_addp(PhaseGVN* phase, Node* base, Node* offset); + Node* make_and_transform_addp(PhaseGVN* phase, Node* base, Node* ptr, Node* offset); + bool prepare_array_copy(PhaseGVN *phase, bool can_reshape, Node*& adr_src, Node*& base_src, Node*& adr_dest, Node*& base_dest, BasicType& copy_type, const Type*& value_type, bool& disjoint_bases); diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 7d3d4ec16f4f..a93e2e43a29a 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -179,9 +179,11 @@ int Block::is_Empty() const { // Ideal nodes (except BoxLock) are allowable in empty blocks: skip them. Only // Mach and BoxLock nodes turn directly into code via emit(). + // Keep ReachabilityFence for diagnostic purposes. while ((end_idx > 0) && !get_node(end_idx)->is_Mach() && - !get_node(end_idx)->is_BoxLock()) { + !get_node(end_idx)->is_BoxLock() && + !get_node(end_idx)->is_ReachabilityFence()) { end_idx--; } diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 983ac8a32c66..dacc8ce9c261 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -76,6 +76,17 @@ develop(bool, StressBailout, false, \ "Perform bailouts randomly at C2 failing() checks") \ \ + product(bool, OptimizeReachabilityFences, true, DIAGNOSTIC, \ + "Optimize reachability fences " \ + "(leave reachability fence nodes intact when turned off)") \ + \ + product(bool, PreserveReachabilityFencesOnConstants, false, DIAGNOSTIC, \ + "Keep reachability fences on compile-time constants") \ + \ + product(bool, StressReachabilityFences, false, DIAGNOSTIC, \ + "Aggressively insert reachability fences " \ + "for all oop method arguments") \ + \ develop(uint, StressBailoutMean, 100000, \ "The expected number of failing() checks made until " \ "a random bailout.") \ @@ -914,6 +925,44 @@ \ develop(bool, StressCountedLoop, false, \ "Randomly delay conversion to counted loops") \ + \ + product(bool, HotCodeHeap, false, EXPERIMENTAL, \ + "Enable the code heap for hot C2 nmethods") \ + \ + product(double, HotCodeSamplePercent, 80, EXPERIMENTAL, \ + "Minimum percentage of profiling samples that must be in " \ + "the MethodHot heap before stopping hot code collection") \ + range(0, 100) \ + \ + product(double, HotCodeStablePercent, 5, EXPERIMENTAL, \ + "Maximum percentage of newly compiled to total C2 nmethods " \ + "to treat nmethod count as stable. " \ + "Values less than zero disable the stable check") \ + range(-1, DBL_MAX) \ + \ + product(uint, HotCodeIntervalSeconds, 300, EXPERIMENTAL, \ + "Seconds between hot code grouping attempts") \ + range(0, max_juint) \ + \ + product(uint, HotCodeSampleSeconds, 120, EXPERIMENTAL, \ + "Seconds to sample application threads per grouping attempt") \ + range(0, max_juint) \ + \ + product(uint, HotCodeStartupDelaySeconds, 120, EXPERIMENTAL, \ + "Seconds to delay before starting hot code grouping thread") \ + range(0, max_juint) \ + \ + product(uint, HotCodeMinSamplingMs, 5, EXPERIMENTAL, \ + "Minimum sampling interval in milliseconds") \ + range(0, max_juint) \ + \ + product(uint, HotCodeMaxSamplingMs, 15, EXPERIMENTAL, \ + "Maximum sampling interval in milliseconds") \ + range(0, max_juint) \ + \ + product(uint, HotCodeCallLevel, 1, EXPERIMENTAL, \ + "Number of levels of callees to relocate per candidate") \ + range(0, max_juint) \ // end of C2_FLAGS diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index ead1b78cdea9..5d170f919c8c 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -775,6 +775,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_longBitsToDouble: case vmIntrinsics::_Reference_get0: case vmIntrinsics::_Reference_refersTo0: + case vmIntrinsics::_Reference_reachabilityFence: case vmIntrinsics::_PhantomReference_refersTo0: case vmIntrinsics::_Reference_clear0: case vmIntrinsics::_PhantomReference_clear0: diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 1465da02ac80..49897ca3c176 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -611,6 +611,20 @@ void CallGenerator::do_late_inline_helper() { } Compile* C = Compile::current(); + + uint endoff = call->jvms()->endoff(); + if (C->inlining_incrementally()) { + // No reachability edges should be present when incremental inlining takes place. + // Inlining logic doesn't expect any extra edges past debug info and fails with + // an assert in SafePointNode::grow_stack. + assert(endoff == call->req(), "reachability edges not supported"); + } else { + if (call->req() > endoff) { // reachability edges present + assert(OptimizeReachabilityFences, "required"); + return; // keep the original call node as the holder of reachability info + } + } + // Remove inlined methods from Compiler's lists. if (call->is_macro()) { C->remove_macro_node(call); diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index b2b01079379e..eb4f506d14f1 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -898,7 +898,7 @@ bool CallNode::may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const { } // Does this call have a direct reference to n other than debug information? -bool CallNode::has_non_debug_use(Node *n) { +bool CallNode::has_non_debug_use(const Node *n) { const TypeTuple * d = tf()->domain(); for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { Node *arg = in(i); @@ -940,7 +940,7 @@ Node *CallNode::result_cast() { } -void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts) const { +void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts, bool allow_handlers) const { projs->fallthrough_proj = nullptr; projs->fallthrough_catchproj = nullptr; projs->fallthrough_ioproj = nullptr; @@ -961,14 +961,13 @@ void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj projs->fallthrough_proj = pn; const Node* cn = pn->unique_ctrl_out_or_null(); if (cn != nullptr && cn->is_Catch()) { - ProjNode *cpn = nullptr; for (DUIterator_Fast kmax, k = cn->fast_outs(kmax); k < kmax; k++) { - cpn = cn->fast_out(k)->as_Proj(); - assert(cpn->is_CatchProj(), "must be a CatchProjNode"); - if (cpn->_con == CatchProjNode::fall_through_index) + CatchProjNode* cpn = cn->fast_out(k)->as_CatchProj(); + assert(allow_handlers || !cpn->is_handler_proj(), "not allowed"); + if (cpn->_con == CatchProjNode::fall_through_index) { + assert(cpn->handler_bci() == CatchProjNode::no_handler_bci, ""); projs->fallthrough_catchproj = cpn; - else { - assert(cpn->_con == CatchProjNode::catch_all_index, "must be correct index."); + } else if (!cpn->is_handler_proj()) { projs->catchall_catchproj = cpn; } } @@ -976,15 +975,20 @@ void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj break; } case TypeFunc::I_O: - if (pn->_is_io_use) + if (pn->_is_io_use) { projs->catchall_ioproj = pn; - else + } else { projs->fallthrough_ioproj = pn; + } for (DUIterator j = pn->outs(); pn->has_out(j); j++) { Node* e = pn->out(j); - if (e->Opcode() == Op_CreateEx && e->in(0)->is_CatchProj() && e->outcnt() > 0) { - assert(projs->exobj == nullptr, "only one"); - projs->exobj = e; + if (e->Opcode() == Op_CreateEx && e->outcnt() > 0) { + CatchProjNode* ecpn = e->in(0)->isa_CatchProj(); + assert(allow_handlers || ecpn == nullptr || !ecpn->is_handler_proj(), "not allowed"); + if (ecpn != nullptr && ecpn->_con != CatchProjNode::fall_through_index && !ecpn->is_handler_proj()) { + assert(projs->exobj == nullptr, "only one"); + projs->exobj = e; + } } } break; @@ -1609,6 +1613,33 @@ void SafePointNode::disconnect_from_root(PhaseIterGVN *igvn) { } } +void SafePointNode::remove_non_debug_edges(NodeEdgeTempStorage& non_debug_edges) { + assert(non_debug_edges._state == NodeEdgeTempStorage::state_initial, "not processed"); + assert(non_debug_edges.is_empty(), "edges not processed"); + + while (req() > jvms()->endoff()) { + uint last = req() - 1; + non_debug_edges.push(in(last)); + del_req(last); + } + + assert(jvms()->endoff() == req(), "no extra edges past debug info allowed"); + DEBUG_ONLY(non_debug_edges._state = NodeEdgeTempStorage::state_populated); +} + +void SafePointNode::restore_non_debug_edges(NodeEdgeTempStorage& non_debug_edges) { + assert(non_debug_edges._state == NodeEdgeTempStorage::state_populated, "not populated"); + assert(jvms()->endoff() == req(), "no extra edges past debug info allowed"); + + while (!non_debug_edges.is_empty()) { + Node* non_debug_edge = non_debug_edges.pop(); + add_req(non_debug_edge); + } + + assert(non_debug_edges.is_empty(), "edges not processed"); + DEBUG_ONLY(non_debug_edges._state = NodeEdgeTempStorage::state_processed); +} + //============== SafePointScalarObjectNode ============== SafePointScalarObjectNode::SafePointScalarObjectNode(const TypeOopPtr* tp, Node* alloc, uint first_index, uint depth, uint n_fields) : diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 5241ae6cd2b3..e4c548fc744e 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -503,6 +503,66 @@ class SafePointNode : public MultiNode { return _has_ea_local_in_scope; } + // A temporary storge for node edges. + // Intended for a single use. + class NodeEdgeTempStorage : public StackObj { + friend class SafePointNode; + + PhaseIterGVN& _igvn; + Node* _node_hook; + +#ifdef ASSERT + enum State { state_initial, state_populated, state_processed }; + + State _state; // monotonically transitions from initial to processed state. +#endif // ASSERT + + bool is_empty() const { + return _node_hook == nullptr || _node_hook->req() == 1; + } + void push(Node* n) { + assert(n != nullptr, ""); + if (_node_hook == nullptr) { + _node_hook = new Node(nullptr); + } + _node_hook->add_req(n); + } + Node* pop() { + assert(!is_empty(), ""); + int idx = _node_hook->req()-1; + Node* r = _node_hook->in(idx); + _node_hook->del_req(idx); + assert(r != nullptr, ""); + return r; + } + + public: + NodeEdgeTempStorage(PhaseIterGVN &igvn) : _igvn(igvn), _node_hook(nullptr) + DEBUG_ONLY(COMMA _state(state_initial)) { + assert(is_empty(), ""); + } + + ~NodeEdgeTempStorage() { + assert(_state == state_processed, "not processed"); + assert(is_empty(), ""); + if (_node_hook != nullptr) { + _node_hook->destruct(&_igvn); + } + } + + void remove_edge_if_present(Node* n) { + if (!is_empty()) { + int idx = _node_hook->find_edge(n); + if (idx > 0) { + _node_hook->del_req(idx); + } + } + } + }; + + void remove_non_debug_edges(NodeEdgeTempStorage& non_debug_edges); + void restore_non_debug_edges(NodeEdgeTempStorage& non_debug_edges); + void disconnect_from_root(PhaseIterGVN *igvn); // Standard Node stuff @@ -736,7 +796,7 @@ class CallNode : public SafePointNode { // Returns true if the call may modify n virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase) const; // Does this node have a use of n other than in debug information? - bool has_non_debug_use(Node* n); + bool has_non_debug_use(const Node* n); // Returns the unique CheckCastPP of a call // or result projection is there are several CheckCastPP // or returns null if there is no one. @@ -751,7 +811,10 @@ class CallNode : public SafePointNode { // Collect all the interesting edges from a call for use in // replacing the call by something else. Used by macro expansion // and the late inlining support. - void extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts = true) const; + void extract_projections(CallProjections* projs, + bool separate_io_proj, + bool do_asserts = true, + bool allow_handlers = false) const; virtual uint match_edge(uint idx) const; diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 54269a56c19c..7bb6b1dcb77d 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -413,6 +413,43 @@ Node* CastLLNode::Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } +// CastPPNodes are removed before matching, while alias classes are needed in global code motion. +// As a result, it is not valid for a CastPPNode to change the oop such that the derived pointers +// lie in different alias classes with and without the node. For example, a CastPPNode c may not +// cast an Object to a Bottom[], because later removal of c would affect the alias class of c's +// array length field (c + arrayOopDesc::length_offset_in_bytes()). +// +// This function verifies that a CastPPNode on an oop does not violate the aforementioned property. +// +// TODO 8382147: Currently, this verification only applies during the construction of a CastPPNode, +// we may want to apply the same verification during IGVN transformations, as well as final graph +// reshaping. +void CastPPNode::verify_type(const Type* in_type, const Type* out_type) { +#ifdef ASSERT + out_type = out_type->join(in_type); + if (in_type->empty() || out_type->empty()) { + return; + } + if (in_type == TypePtr::NULL_PTR || out_type == TypePtr::NULL_PTR) { + return; + } + if (!in_type->isa_oopptr() && !out_type->isa_oopptr()) { + return; + } + + assert(in_type->isa_oopptr() && out_type->isa_oopptr(), "must be both oops or both non-oops"); + if (in_type->isa_aryptr() && out_type->isa_aryptr()) { + const Type* e1 = in_type->is_aryptr()->elem(); + const Type* e2 = out_type->is_aryptr()->elem(); + assert(e1->basic_type() == e2->basic_type(), "must both be arrays of the same primitive type or both be oops arrays"); + return; + } + + assert(in_type->isa_instptr() && out_type->isa_instptr(), "must be both array oops or both non-array oops"); + assert(in_type->is_instptr()->instance_klass() == out_type->is_instptr()->instance_klass(), "must not cast to a different type"); +#endif // ASSERT +} + //------------------------------Value------------------------------------------ // Take 'join' of input and cast-up type, unless working with an Interface const Type* CheckCastPPNode::Value(PhaseGVN* phase) const { @@ -440,6 +477,11 @@ const Type* CheckCastPPNode::Value(PhaseGVN* phase) const { return result; } +Node* CheckCastPPNode::pin_node_under_control_impl() const { + assert(_dependency.is_floating(), "already pinned"); + return new CheckCastPPNode(in(0), in(1), bottom_type(), _dependency.with_pinned_dependency(), _extra_types); +} + //============================================================================= //------------------------------Value------------------------------------------ const Type* CastX2PNode::Value(PhaseGVN* phase) const { diff --git a/src/hotspot/share/opto/castnode.hpp b/src/hotspot/share/opto/castnode.hpp index 38545fd6f41d..dce54eb73c0c 100644 --- a/src/hotspot/share/opto/castnode.hpp +++ b/src/hotspot/share/opto/castnode.hpp @@ -303,14 +303,18 @@ class CastVVNode: public ConstraintCastNode { //------------------------------CastPPNode------------------------------------- // cast pointer to pointer (different type) -class CastPPNode: public ConstraintCastNode { - public: - CastPPNode (Node* ctrl, Node* n, const Type* t, const DependencyType& dependency = DependencyType::FloatingNarrowing, const TypeTuple* types = nullptr) +class CastPPNode : public ConstraintCastNode { +public: + CastPPNode(Node* ctrl, Node* n, const Type* t, const DependencyType& dependency = DependencyType::FloatingNarrowing, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { init_class_id(Class_CastPP); + verify_type(n->bottom_type(), t); } virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } + +private: + static void verify_type(const Type* in_type, const Type* out_type); }; //------------------------------CheckCastPPNode-------------------------------- @@ -329,6 +333,7 @@ class CheckCastPPNode: public ConstraintCastNode { private: virtual bool depends_only_on_test_impl() const { return !type()->isa_rawptr() && ConstraintCastNode::depends_only_on_test_impl(); } + virtual Node* pin_node_under_control_impl() const; }; diff --git a/src/hotspot/share/opto/classes.cpp b/src/hotspot/share/opto/classes.cpp index b760a179b57b..1cd6c52393b0 100644 --- a/src/hotspot/share/opto/classes.cpp +++ b/src/hotspot/share/opto/classes.cpp @@ -43,6 +43,7 @@ #include "opto/narrowptrnode.hpp" #include "opto/node.hpp" #include "opto/opaquenode.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/subnode.hpp" #include "opto/subtypenode.hpp" diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index d92904923374..0f67cf90183b 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -396,6 +396,7 @@ macro(AddVL) macro(AddReductionVL) macro(AddVF) macro(AddVHF) +macro(AddReductionVHF) macro(AddReductionVF) macro(AddVD) macro(AddReductionVD) @@ -413,6 +414,7 @@ macro(MulReductionVI) macro(MulVL) macro(MulReductionVL) macro(MulVF) +macro(MulReductionVHF) macro(MulReductionVF) macro(MulVD) macro(MulReductionVD) @@ -547,3 +549,4 @@ macro(MaskAll) macro(AndVMask) macro(OrVMask) macro(XorVMask) +macro(ReachabilityFence) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 7b5f78a8ad33..e05df8ea716b 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -74,6 +74,7 @@ #include "opto/output.hpp" #include "opto/parse.hpp" #include "opto/phaseX.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/stringopts.hpp" @@ -396,6 +397,9 @@ void Compile::remove_useless_node(Node* dead) { if (dead->is_expensive()) { remove_expensive_node(dead); } + if (dead->is_ReachabilityFence()) { + remove_reachability_fence(dead->as_ReachabilityFence()); + } if (dead->is_OpaqueTemplateAssertionPredicate()) { remove_template_assertion_predicate_opaque(dead->as_OpaqueTemplateAssertionPredicate()); } @@ -459,6 +463,7 @@ void Compile::disconnect_useless_nodes(Unique_Node_List& useful, Unique_Node_Lis // Remove useless Template Assertion Predicate opaque nodes remove_useless_nodes(_template_assertion_predicate_opaques, useful); remove_useless_nodes(_expensive_nodes, useful); // remove useless expensive nodes + remove_useless_nodes(_reachability_fences, useful); // remove useless node recorded for post loop opts IGVN pass remove_useless_nodes(_for_post_loop_igvn, useful); // remove useless node recorded for post loop opts IGVN pass remove_useless_nodes(_for_merge_stores_igvn, useful); // remove useless node recorded for merge stores IGVN pass remove_useless_unstable_if_traps(useful); // remove useless unstable_if traps @@ -665,6 +670,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, _parse_predicates(comp_arena(), 8, 0, nullptr), _template_assertion_predicate_opaques(comp_arena(), 8, 0, nullptr), _expensive_nodes(comp_arena(), 8, 0, nullptr), + _reachability_fences(comp_arena(), 8, 0, nullptr), _for_post_loop_igvn(comp_arena(), 8, 0, nullptr), _for_merge_stores_igvn(comp_arena(), 8, 0, nullptr), _unstable_if_traps(comp_arena(), 8, 0, nullptr), @@ -934,6 +940,7 @@ Compile::Compile(ciEnv* ci_env, _directive(directive), _log(ci_env->log()), _first_failure_details(nullptr), + _reachability_fences(comp_arena(), 8, 0, nullptr), _for_post_loop_igvn(comp_arena(), 8, 0, nullptr), _for_merge_stores_igvn(comp_arena(), 8, 0, nullptr), _congraph(nullptr), @@ -2510,12 +2517,23 @@ void Compile::Optimize() { return; } - if (failing()) return; - C->clear_major_progress(); // ensure that major progress is now clear process_for_post_loop_opts_igvn(igvn); + if (failing()) return; + + // Once loop optimizations are over, it is safe to get rid of all reachability fence nodes and + // migrate reachability edges to safepoints. + if (OptimizeReachabilityFences && _reachability_fences.length() > 0) { + TracePhase tp1(_t_idealLoop); + TracePhase tp2(_t_reachability); + PhaseIdealLoop::optimize(igvn, PostLoopOptsExpandReachabilityFences); + print_method(PHASE_EXPAND_REACHABILITY_FENCES, 2); + if (failing()) return; + assert(_reachability_fences.length() == 0 || PreserveReachabilityFencesOnConstants, "no RF nodes allowed"); + } + process_for_merge_stores_igvn(igvn); if (failing()) return; @@ -3182,10 +3200,10 @@ void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Uni !n->in(2)->is_Con() ) { // right use is not a constant // Check for commutative opcode switch( nop ) { - case Op_AddI: case Op_AddF: case Op_AddD: case Op_AddL: + case Op_AddI: case Op_AddF: case Op_AddD: case Op_AddHF: case Op_AddL: case Op_MaxI: case Op_MaxL: case Op_MaxF: case Op_MaxD: case Op_MinI: case Op_MinL: case Op_MinF: case Op_MinD: - case Op_MulI: case Op_MulF: case Op_MulD: case Op_MulL: + case Op_MulI: case Op_MulF: case Op_MulD: case Op_MulHF: case Op_MulL: case Op_AndL: case Op_XorL: case Op_OrL: case Op_AndI: case Op_XorI: case Op_OrI: { // Move "last use" input to left by swapping inputs @@ -3264,6 +3282,8 @@ void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) { void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) { switch( nop ) { // Count all float operations that may use FPU + case Op_AddHF: + case Op_MulHF: case Op_AddF: case Op_SubF: case Op_MulF: @@ -3770,10 +3790,12 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f case Op_AddReductionVI: case Op_AddReductionVL: + case Op_AddReductionVHF: case Op_AddReductionVF: case Op_AddReductionVD: case Op_MulReductionVI: case Op_MulReductionVL: + case Op_MulReductionVHF: case Op_MulReductionVF: case Op_MulReductionVD: case Op_MinReductionV: @@ -3973,10 +3995,28 @@ void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_R } } + expand_reachability_edges(sfpt); + // Skip next transformation if compressed oops are not used. if (UseCompressedOops && !Matcher::gen_narrow_oop_implicit_null_checks()) return; + // Go over ReachabilityFence nodes to skip DecodeN nodes for referents. + // The sole purpose of RF node is to keep the referent oop alive and + // decoding the oop for that is not needed. + for (int i = 0; i < C->reachability_fences_count(); i++) { + ReachabilityFenceNode* rf = C->reachability_fence(i); + DecodeNNode* dn = rf->in(1)->isa_DecodeN(); + if (dn != nullptr) { + if (!dn->has_non_debug_uses() || Matcher::narrow_oop_use_complex_address()) { + rf->set_req(1, dn->in(1)); + if (dn->outcnt() == 0) { + dn->disconnect_inputs(this); + } + } + } + } + // Go over safepoints nodes to skip DecodeN/DecodeNKlass nodes for debug edges. // It could be done for an uncommon traps or any safepoints/calls // if the DecodeN/DecodeNKlass node is referenced only in a debug info. @@ -3990,21 +4030,8 @@ void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_R n->as_CallStaticJava()->uncommon_trap_request() != 0); for (int j = start; j < end; j++) { Node* in = n->in(j); - if (in->is_DecodeNarrowPtr()) { - bool safe_to_skip = true; - if (!is_uncommon ) { - // Is it safe to skip? - for (uint i = 0; i < in->outcnt(); i++) { - Node* u = in->raw_out(i); - if (!u->is_SafePoint() || - (u->is_Call() && u->as_Call()->has_non_debug_use(n))) { - safe_to_skip = false; - } - } - } - if (safe_to_skip) { - n->set_req(j, in->in(1)); - } + if (in->is_DecodeNarrowPtr() && (is_uncommon || !in->has_non_debug_uses())) { + n->set_req(j, in->in(1)); if (in->outcnt() == 0) { in->disconnect_inputs(this); } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index eb6be669f241..ff0085d79dea 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -80,6 +80,7 @@ class PhaseIterGVN; class PhaseRegAlloc; class PhaseCCP; class PhaseOutput; +class ReachabilityFenceNode; class RootNode; class relocInfo; class StartNode; @@ -107,7 +108,8 @@ enum LoopOptsMode { LoopOptsMaxUnroll, LoopOptsShenandoahExpand, LoopOptsSkipSplitIf, - LoopOptsVerify + LoopOptsVerify, + PostLoopOptsExpandReachabilityFences }; // The type of all node counts and indexes. @@ -385,6 +387,7 @@ class Compile : public Phase { // of Template Assertion Predicates themselves. GrowableArray _template_assertion_predicate_opaques; GrowableArray _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common + GrowableArray _reachability_fences; // List of reachability fences GrowableArray _for_post_loop_igvn; // List of nodes for IGVN after loop opts are over GrowableArray _for_merge_stores_igvn; // List of nodes for IGVN merge stores GrowableArray _unstable_if_traps; // List of ifnodes after IGVN @@ -714,11 +717,13 @@ class Compile : public Phase { int template_assertion_predicate_count() const { return _template_assertion_predicate_opaques.length(); } int expensive_count() const { return _expensive_nodes.length(); } int coarsened_count() const { return _coarsened_locks.length(); } - Node* macro_node(int idx) const { return _macro_nodes.at(idx); } Node* expensive_node(int idx) const { return _expensive_nodes.at(idx); } + ReachabilityFenceNode* reachability_fence(int idx) const { return _reachability_fences.at(idx); } + int reachability_fences_count() const { return _reachability_fences.length(); } + ConnectionGraph* congraph() { return _congraph;} void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;} void add_macro_node(Node * n) { @@ -740,6 +745,14 @@ class Compile : public Phase { _expensive_nodes.remove_if_existing(n); } + void add_reachability_fence(ReachabilityFenceNode* rf) { + _reachability_fences.append(rf); + } + + void remove_reachability_fence(ReachabilityFenceNode* n) { + _reachability_fences.remove_if_existing(n); + } + void add_parse_predicate(ParsePredicateNode* n) { assert(!_parse_predicates.contains(n), "duplicate entry in Parse Predicate list"); _parse_predicates.append(n); @@ -1300,6 +1313,9 @@ class Compile : public Phase { // Definitions of pd methods static void pd_compiler2_init(); + // Materialize reachability fences from reachability edges on safepoints. + void expand_reachability_edges(Unique_Node_List& safepoints); + // Static parse-time type checking logic for gen_subtype_check: enum SubTypeCheckResult { SSC_always_false, SSC_always_true, SSC_easy_test, SSC_full_test }; SubTypeCheckResult static_subtype_check(const TypeKlassPtr* superk, const TypeKlassPtr* subk, bool skip = StressReflectiveCode); diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index 9a1da726f00c..d6e75f17f501 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -1081,13 +1081,13 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { #ifndef PRODUCT void Parse::count_compiled_calls(bool at_method_entry, bool is_inline) { - if( CountCompiledCalls ) { - if( at_method_entry ) { + if (CountCompiledCalls) { + if (at_method_entry) { // bump invocation counter if top method (for statistics) if (CountCompiledCalls && depth() == 1) { const TypePtr* addr_type = TypeMetadataPtr::make(method()); Node* adr1 = makecon(addr_type); - Node* adr2 = basic_plus_adr(adr1, adr1, in_bytes(Method::compiled_invocation_counter_offset())); + Node* adr2 = off_heap_plus_addr(adr1, in_bytes(Method::compiled_invocation_counter_offset())); increment_counter(adr2); } } else if (is_inline) { diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 64ee60037d65..a05ad0ef99a3 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -1273,21 +1273,33 @@ bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, No for (uint spi = 0; spi < safepoints.size(); spi++) { SafePointNode* sfpt = safepoints.at(spi)->as_SafePoint(); - JVMState *jvms = sfpt->jvms(); - uint merge_idx = (sfpt->req() - jvms->scloff()); - int debug_start = jvms->debug_start(); + + SafePointNode::NodeEdgeTempStorage non_debug_edges_worklist(*_igvn); + + // All sfpt inputs are implicitly included into debug info during the scalarization process below. + // Keep non-debug inputs separately, so they stay non-debug. + sfpt->remove_non_debug_edges(non_debug_edges_worklist); + + JVMState* jvms = sfpt->jvms(); + uint merge_idx = (sfpt->req() - jvms->scloff()); + int debug_start = jvms->debug_start(); SafePointScalarMergeNode* smerge = new SafePointScalarMergeNode(merge_t, merge_idx); smerge->init_req(0, _compile->root()); _igvn->register_new_node_with_optimizer(smerge); + assert(sfpt->jvms()->endoff() == sfpt->req(), "no extra edges past debug info allowed"); + // The next two inputs are: // (1) A copy of the original pointer to NSR objects. // (2) A selector, used to decide if we need to rematerialize an object // or use the pointer to a NSR object. - // See more details of these fields in the declaration of SafePointScalarMergeNode + // See more details of these fields in the declaration of SafePointScalarMergeNode. + // It is safe to include them into debug info straight away since create_scalarized_object_description() + // will include all newly added inputs into debug info anyway. sfpt->add_req(nsr_merge_pointer); sfpt->add_req(selector); + sfpt->jvms()->set_endoff(sfpt->req()); for (uint i = 1; i < ophi->req(); i++) { Node* base = ophi->in(i); @@ -1302,13 +1314,15 @@ bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, No AllocateNode* alloc = ptn->ideal_node()->as_Allocate(); SafePointScalarObjectNode* sobj = mexp.create_scalarized_object_description(alloc, sfpt); if (sobj == nullptr) { - return false; + sfpt->restore_non_debug_edges(non_debug_edges_worklist); + return false; // non-recoverable failure; recompile } // Now make a pass over the debug information replacing any references // to the allocated object with "sobj" Node* ccpp = alloc->result_cast(); sfpt->replace_edges_in_range(ccpp, sobj, debug_start, jvms->debug_end(), _igvn); + non_debug_edges_worklist.remove_edge_if_present(ccpp); // drop scalarized input from non-debug info // Register the scalarized object as a candidate for reallocation smerge->add_req(sobj); @@ -1316,11 +1330,15 @@ bool ConnectionGraph::reduce_phi_on_safepoints_helper(Node* ophi, Node* cast, No // Replaces debug information references to "original_sfpt_parent" in "sfpt" with references to "smerge" sfpt->replace_edges_in_range(original_sfpt_parent, smerge, debug_start, jvms->debug_end(), _igvn); + non_debug_edges_worklist.remove_edge_if_present(original_sfpt_parent); // drop scalarized input from non-debug info // The call to 'replace_edges_in_range' above might have removed the // reference to ophi that we need at _merge_pointer_idx. The line below make // sure the reference is maintained. sfpt->set_req(smerge->merge_pointer_idx(jvms), nsr_merge_pointer); + + sfpt->restore_non_debug_edges(non_debug_edges_worklist); + _igvn->_worklist.push(sfpt); } @@ -4712,6 +4730,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, op == Op_StrIndexOf || op == Op_StrIndexOfChar || op == Op_SubTypeCheck || op == Op_ReinterpretS2HF || + op == Op_ReachabilityFence || BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use))) { n->dump(); use->dump(); diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index 4a1553b1e009..e3d3108a22e7 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -152,9 +152,12 @@ bool PhaseCFG::is_CFG(Node* n) { } bool PhaseCFG::is_control_proj_or_safepoint(Node* n) const { - bool result = (n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_SafePoint) || (n->is_Proj() && n->as_Proj()->bottom_type() == Type::CONTROL); - assert(!result || (n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_SafePoint) - || (n->is_Proj() && n->as_Proj()->_con == 0), "If control projection, it must be projection 0"); + bool result = n->is_ReachabilityFence() || + (n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_SafePoint) || + (n->is_Proj() && n->as_Proj()->bottom_type() == Type::CONTROL); + assert(!n->is_Proj() || + n->as_Proj()->bottom_type() != Type::CONTROL || + n->as_Proj()->_con == 0, "If control projection, it must be projection 0"); return result; } diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 8d32694e9a51..bbd00d111f73 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -41,6 +41,7 @@ #include "opto/machnode.hpp" #include "opto/opaquenode.hpp" #include "opto/parse.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/subtypenode.hpp" @@ -3541,6 +3542,15 @@ Node* GraphKit::insert_mem_bar_volatile(int opcode, int alias_idx, Node* precede return membar; } +//------------------------------insert_reachability_fence---------------------- +Node* GraphKit::insert_reachability_fence(Node* referent) { + assert(!referent->is_top(), ""); + Node* rf = _gvn.transform(new ReachabilityFenceNode(C, control(), referent)); + set_control(rf); + C->record_for_igvn(rf); + return rf; +} + //------------------------------shared_lock------------------------------------ // Emit locking code. FastLockNode* GraphKit::shared_lock(Node* obj) { diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 273009ca7ce7..f53f73d09784 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -805,6 +805,7 @@ class GraphKit : public Phase { int next_monitor(); Node* insert_mem_bar(int opcode, Node* precedent = nullptr); Node* insert_mem_bar_volatile(int opcode, int alias_idx, Node* precedent = nullptr); + Node* insert_reachability_fence(Node* referent); // Optional 'precedent' is appended as an extra edge, to force ordering. FastLockNode* shared_lock(Node* obj); void shared_unlock(Node* box, Node* obj); diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index e0f95377cde6..ff7bc2c10d3b 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -568,6 +568,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_Reference_get0: return inline_reference_get0(); case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false); + case vmIntrinsics::_Reference_reachabilityFence: return inline_reference_reachabilityFence(); case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true); case vmIntrinsics::_Reference_clear0: return inline_reference_clear0(false); case vmIntrinsics::_PhantomReference_clear0: return inline_reference_clear0(true); @@ -4310,7 +4311,7 @@ Node* LibraryCallKit::generate_array_guard_common(Node* kls, RegionNode* region, if (obj != nullptr && is_array_ctrl != nullptr && is_array_ctrl != top()) { // Keep track of the fact that 'obj' is an array to prevent // array specific accesses from floating above the guard. - *obj = _gvn.transform(new CastPPNode(is_array_ctrl, *obj, TypeAryPtr::BOTTOM)); + *obj = _gvn.transform(new CheckCastPPNode(is_array_ctrl, *obj, TypeAryPtr::BOTTOM)); } return ctrl; } @@ -7025,6 +7026,14 @@ bool LibraryCallKit::inline_reference_clear0(bool is_phantom) { return true; } +//-----------------------inline_reference_reachabilityFence----------------- +// bool java.lang.ref.Reference.reachabilityFence(); +bool LibraryCallKit::inline_reference_reachabilityFence() { + Node* referent = argument(0); + insert_reachability_fence(referent); + return true; +} + Node* LibraryCallKit::load_field_from_object(Node* fromObj, const char* fieldName, const char* fieldTypeString, DecoratorSet decorators, bool is_static, ciInstanceKlass* fromKls) { diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 9b87df645e19..9aae48302cf0 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -312,6 +312,7 @@ class LibraryCallKit : public GraphKit { bool inline_divmod_methods(vmIntrinsics::ID id); bool inline_reference_get0(); bool inline_reference_refersTo0(bool is_phantom); + bool inline_reference_reachabilityFence(); bool inline_reference_clear0(bool is_phantom); bool inline_Class_cast(); bool inline_aescrypt_Block(vmIntrinsics::ID id); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 80b17efb998e..b65f90093ab7 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -35,7 +35,9 @@ #include "opto/loopnode.hpp" #include "opto/movenode.hpp" #include "opto/mulnode.hpp" +#include "opto/node.hpp" #include "opto/opaquenode.hpp" +#include "opto/opcodes.hpp" #include "opto/phase.hpp" #include "opto/predicates.hpp" #include "opto/rootnode.hpp" @@ -50,17 +52,70 @@ // Given an IfNode, return the loop-exiting projection or null if both // arms remain in the loop. Node *IdealLoopTree::is_loop_exit(Node *iff) const { - if (iff->outcnt() != 2) return nullptr; // Ignore partially dead tests - PhaseIdealLoop *phase = _phase; + assert(iff->is_If(), "not an If: %s", iff->Name()); + assert(is_member(_phase->get_loop(iff)), "not related"); + + if (iff->outcnt() != 2) { + return nullptr; // Ignore partially dead tests + } // Test is an IfNode, has 2 projections. If BOTH are in the loop // we need loop unswitching instead of peeling. - if (!is_member(phase->get_loop(iff->raw_out(0)))) + if (!is_member(_phase->get_loop(iff->raw_out(0)))) { return iff->raw_out(0); - if (!is_member(phase->get_loop(iff->raw_out(1)))) + } + if (!is_member(_phase->get_loop(iff->raw_out(1)))) { return iff->raw_out(1); + } return nullptr; } +//------------------------------unique_loop_exit_or_null---------------------- +// Return the loop-exit projection if loop exit is unique. +IfFalseNode* IdealLoopTree::unique_loop_exit_proj_or_null() { + if (is_loop() && head()->is_BaseCountedLoop()) { + IfNode* loop_end = head()->as_BaseCountedLoop()->loopexit_or_null(); + if (loop_end == nullptr) { + return nullptr; // malformed loop shape + } + // Look for other loop exits. + assert(_phase->is_dominator(head(), tail()), "sanity"); + for (Node* ctrl = tail(); ctrl != head(); ctrl = ctrl->in(0)) { + assert(is_member(_phase->get_loop(ctrl)), "sanity"); + if (ctrl->is_If()) { + if (!is_loop_exit(ctrl->as_If())) { + continue; // local branch + } else if (ctrl != loop_end) { + return nullptr; // multiple loop exits + } + } else if (ctrl->is_Region()) { + return nullptr; // give up on control flow merges + } else if (ctrl->is_ReachabilityFence() || + ctrl->is_SafePoint() || + ctrl->is_MemBar() || + ctrl->Opcode() == Op_Blackhole) { + continue; // skip + } else if (ctrl->is_Proj()) { + if (ctrl->is_IfProj() || + ctrl->Opcode() == Op_SCMemProj || + ctrl->Opcode() == Op_Proj) { + continue; // skip simple control projections + } else if (ctrl->is_CatchProj() || + ctrl->is_JumpProj()) { + return nullptr; // give up on control flow splits + } else { + assert(false, "unknown control projection: %s", ctrl->Name()); + return nullptr; // stop on unknown control node + } + } else { + assert(false, "unknown CFG node: %s", ctrl->Name()); + return nullptr; // stop on unknown control node + } + } + assert(is_loop_exit(loop_end), "not a loop exit?"); + return loop_end->false_proj_or_null(); + } + return nullptr; // not found or multiple loop exits +} //============================================================================= @@ -1774,13 +1829,39 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, for (DUIterator i = main_head->outs(); main_head->has_out(i); i++) { Node* main_phi = main_head->out(i); if (main_phi->is_Phi() && main_phi->in(0) == main_head && main_phi->outcnt() > 0) { - Node* cur_phi = old_new[main_phi->_idx]; + Node* post_phi = old_new[main_phi->_idx]; + Node* loopback_input = main_phi->in(LoopNode::LoopBackControl); Node* fallnew = clone_up_backedge_goo(main_head->back_control(), post_head->init_control(), - main_phi->in(LoopNode::LoopBackControl), + loopback_input, visited, clones); - _igvn.hash_delete(cur_phi); - cur_phi->set_req(LoopNode::EntryControl, fallnew); + // Technically, the entry value of post_phi must be the loop back input of the corresponding + // Phi of the outer loop, not the Phi of the inner loop (i.e. main_phi). However, we have not + // constructed the Phis for the OuterStripMinedLoop yet, so the input must be inferred from + // the loop back input of main_phi. + // - If post_phi is a data Phi, then we can use the loop back input of main_phi. + // - If post_phi is a memory Phi, since Stores can be sunk below the inner loop, but still + // inside the outer loop, we have 2 cases: + // + If the loop back input of main_phi is on the backedge, then the entry input of + // post_phi is the clone of the node on the entry of post_head, similar to when post_phi + // is a data Phi. + // + If the loop back input of main_phi is not on the backedge, we need to find whether + // there is a sunk Store corresponding to post_phi, if there is any, the latest such + // store will be the entry input of post_phi. Fortunately, the safepoint at the exit of + // the outer loop captures all memory states, so we can use it as the entry input of + // post_phi. + // Another way to see it is that, the memory phi should capture the latest state at the + // post-loop entry. If loopback_input is cloned by clone_up_backedge_goo, it is pinned at + // the post-loop entry, and is surely the latest state. Otherwise, the latest memory state + // corresponding to post_phi is the memory state at the exit of the outer main-loop, which + // is captured by the safepoint there. + if (main_head->is_strip_mined() && fallnew == loopback_input && post_phi->is_memory_phi()) { + SafePointNode* main_safepoint = main_head->outer_safepoint(); + assert(main_safepoint != nullptr, "outer loop must have a safepoint"); + fallnew = main_safepoint->memory(); + } + _igvn.hash_delete(post_phi); + post_phi->set_req(LoopNode::EntryControl, fallnew); } } // Store nodes that were moved to the outer loop by PhaseIdealLoop::try_move_store_after_loop @@ -3107,9 +3188,13 @@ static CountedLoopNode* locate_pre_from_main(CountedLoopNode* main_loop) { Node* ctrl = main_loop->skip_assertion_predicates_with_halt(); assert(ctrl->Opcode() == Op_IfTrue || ctrl->Opcode() == Op_IfFalse, ""); Node* iffm = ctrl->in(0); - assert(iffm->Opcode() == Op_If, ""); + assert(iffm->Opcode() == Op_If, "%s", iffm->Name()); Node* p_f = iffm->in(0); - assert(p_f->Opcode() == Op_IfFalse, ""); + // Skip ReachabilityFences hoisted out of pre-loop. + while (p_f->is_ReachabilityFence()) { + p_f = p_f->in(0); + } + assert(p_f->Opcode() == Op_IfFalse, "%s", p_f->Name()); CountedLoopNode* pre_loop = p_f->in(0)->as_CountedLoopEnd()->loopnode(); assert(pre_loop->is_pre_loop(), "No pre loop found"); return pre_loop; diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 6963a67118fd..35a9108892cc 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -44,6 +44,7 @@ #include "opto/opaquenode.hpp" #include "opto/opcodes.hpp" #include "opto/predicates.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/vectorization.hpp" @@ -3934,6 +3935,7 @@ IdealLoopTree::IdealLoopTree(PhaseIdealLoop* phase, Node* head, Node* tail): _pa _has_range_checks(0), _has_range_checks_computed(0), _safepts(nullptr), _required_safept(nullptr), + _reachability_fences(nullptr), _allow_optimizations(true) { precond(_head != nullptr); precond(_tail != nullptr); @@ -4855,6 +4857,15 @@ uint IdealLoopTree::est_loop_flow_merge_sz() const { return 0; } +void IdealLoopTree::register_reachability_fence(ReachabilityFenceNode* rf) { + if (_reachability_fences == nullptr) { + _reachability_fences = new Node_List(); + } + if (!_reachability_fences->contains(rf)) { + _reachability_fences->push(rf); + } +} + #ifndef PRODUCT //------------------------------dump_head-------------------------------------- // Dump 1 liner for loop header info @@ -4914,6 +4925,9 @@ void IdealLoopTree::dump_head() { if (_has_call) tty->print(" has_call"); if (_has_sfpt) tty->print(" has_sfpt"); if (_rce_candidate) tty->print(" rce"); + if (_reachability_fences != nullptr && _reachability_fences->size() > 0) { + tty->print(" has_rf"); + } if (_safepts != nullptr && _safepts->size() > 0) { tty->print(" sfpts={"); _safepts->dump_simple(); tty->print(" }"); } @@ -4921,6 +4935,9 @@ void IdealLoopTree::dump_head() { tty->print(" req={"); _required_safept->dump_simple(); tty->print(" }"); } if (Verbose) { + if (_reachability_fences != nullptr && _reachability_fences->size() > 0) { + tty->print(" rfs={"); _reachability_fences->dump_simple(); tty->print(" }"); + } tty->print(" body={"); _body.dump_simple(); tty->print(" }"); } if (_head->is_Loop() && _head->as_Loop()->is_strip_mined()) { @@ -5179,12 +5196,14 @@ bool PhaseIdealLoop::process_expensive_nodes() { // Create a PhaseLoop. Build the ideal Loop tree. Map each Ideal Node to // its corresponding LoopNode. If 'optimize' is true, do some loop cleanups. void PhaseIdealLoop::build_and_optimize() { - assert(!C->post_loop_opts_phase(), "no loop opts allowed"); - bool do_split_ifs = (_mode == LoopOptsDefault); bool skip_loop_opts = (_mode == LoopOptsNone); bool do_max_unroll = (_mode == LoopOptsMaxUnroll); + bool do_verify = (_mode == LoopOptsVerify); + bool do_expand_reachability_fences = (_mode == PostLoopOptsExpandReachabilityFences); + assert(!C->post_loop_opts_phase() || do_expand_reachability_fences || do_verify, + "no loop opts allowed"); bool old_progress = C->major_progress(); uint orig_worklist_size = _igvn._worklist.size(); @@ -5251,11 +5270,13 @@ void PhaseIdealLoop::build_and_optimize() { BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); // Nothing to do, so get out - bool stop_early = !C->has_loops() && !skip_loop_opts && !do_split_ifs && !do_max_unroll && !_verify_me && - !_verify_only && !bs->is_gc_specific_loop_opts_pass(_mode); + bool stop_early = !C->has_loops() && !skip_loop_opts && !do_split_ifs && !do_max_unroll && + !do_expand_reachability_fences && !_verify_me && !_verify_only && + !bs->is_gc_specific_loop_opts_pass(_mode) ; bool do_expensive_nodes = C->should_optimize_expensive_nodes(_igvn); + bool do_optimize_reachability_fences = OptimizeReachabilityFences && (C->reachability_fences_count() > 0); bool strip_mined_loops_expanded = bs->strip_mined_loops_expanded(_mode); - if (stop_early && !do_expensive_nodes) { + if (stop_early && !do_expensive_nodes && !do_optimize_reachability_fences) { return; } @@ -5331,7 +5352,7 @@ void PhaseIdealLoop::build_and_optimize() { // Given early legal placement, try finding counted loops. This placement // is good enough to discover most loop invariants. - if (!_verify_me && !_verify_only && !strip_mined_loops_expanded) { + if (!_verify_me && !_verify_only && !strip_mined_loops_expanded && !do_expand_reachability_fences) { _ltree_root->counted_loop( this ); } @@ -5361,8 +5382,14 @@ void PhaseIdealLoop::build_and_optimize() { eliminate_useless_multiversion_if(); if (stop_early) { - assert(do_expensive_nodes, "why are we here?"); - if (process_expensive_nodes()) { + assert(do_expensive_nodes || do_optimize_reachability_fences, "why are we here?"); + // Use the opportunity to optimize reachability fence nodes irrespective of + // whether loop optimizations are performed or not. + if (do_optimize_reachability_fences && optimize_reachability_fences()) { + recompute_dom_depth(); + DEBUG_ONLY( if (VerifyLoopOptimizations) { verify(); } ); + } + if (do_expensive_nodes && process_expensive_nodes()) { // If we made some progress when processing expensive nodes then // the IGVN may modify the graph in a way that will allow us to // make some more progress: we need to try processing expensive @@ -5390,6 +5417,22 @@ void PhaseIdealLoop::build_and_optimize() { } #endif + if (do_optimize_reachability_fences && optimize_reachability_fences()) { + recompute_dom_depth(); + DEBUG_ONLY( if (VerifyLoopOptimizations) { verify(); } ); + } + + if (do_expand_reachability_fences) { + assert(C->post_loop_opts_phase(), "required"); + if (expand_reachability_fences()) { + recompute_dom_depth(); + DEBUG_ONLY( if (VerifyLoopOptimizations) { verify(); } ); + } + return; + } + + assert(!C->post_loop_opts_phase(), "required"); + if (skip_loop_opts) { C->restore_major_progress(old_progress); return; @@ -6286,6 +6329,8 @@ int PhaseIdealLoop::build_loop_tree_impl(Node* n, int pre_order) { // Record all safepoints in this loop. if (innermost->_safepts == nullptr) innermost->_safepts = new Node_List(); innermost->_safepts->push(n); + } else if (n->is_ReachabilityFence()) { + innermost->register_reachability_fence(n->as_ReachabilityFence()); } } } diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 6667c71511c6..26b822593279 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -44,6 +44,7 @@ class PredicateBlock; class PathFrequency; class PhaseIdealLoop; class LoopSelector; +class ReachabilityFenceNode; class UnswitchedLoopSelector; class VectorSet; class VSharedData; @@ -662,6 +663,7 @@ class IdealLoopTree : public ResourceObj { Node_List* _safepts; // List of safepoints in this loop Node_List* _required_safept; // A inner loop cannot delete these safepts; + Node_List* _reachability_fences; // List of reachability fences in this loop bool _allow_optimizations; // Allow loop optimizations IdealLoopTree(PhaseIdealLoop* phase, Node* head, Node* tail); @@ -720,6 +722,9 @@ class IdealLoopTree : public ResourceObj { // Check for Node being a loop-breaking test Node *is_loop_exit(Node *iff) const; + // Return unique loop-exit projection or null if the loop has multiple exits. + IfFalseNode* unique_loop_exit_proj_or_null(); + // Remove simplistic dead code from loop body void DCE_loop_body(); @@ -825,6 +830,9 @@ class IdealLoopTree : public ResourceObj { return _head->as_Loop()->is_strip_mined() ? _parent : this; } + // Registers a reachability fence node in the loop. + void register_reachability_fence(ReachabilityFenceNode* rf); + #ifndef PRODUCT void dump_head(); // Dump loop head only void dump(); // Dump this loop recursively @@ -1167,6 +1175,16 @@ class PhaseIdealLoop : public PhaseTransform { forward_ctrl(old_node, new_node); } + void remove_dead_data_node(Node* dead) { + assert(dead->outcnt() == 0 && !dead->is_top(), "must be dead"); + assert(!dead->is_CFG(), "not a data node"); + Node* c = get_ctrl(dead); + IdealLoopTree* lpt = get_loop(c); + _loop_or_ctrl.map(dead->_idx, nullptr); // This node is useless + lpt->_body.yank(dead); + igvn().remove_dead_node(dead, PhaseIterGVN::NodeOrigin::Graph); + } + private: // Place 'n' in some loop nest, where 'n' is a CFG node @@ -1599,6 +1617,15 @@ class PhaseIdealLoop : public PhaseTransform { // Implementation of the loop predication to promote checks outside the loop bool loop_predication_impl(IdealLoopTree *loop); + // Reachability Fence (RF) support. + private: + void insert_rf(Node* ctrl, Node* referent); + void replace_rf(Node* old_node, Node* new_node); + void remove_rf(ReachabilityFenceNode* rf); + public: + bool optimize_reachability_fences(); + bool expand_reachability_fences(); + private: bool loop_predication_impl_helper(IdealLoopTree* loop, IfProjNode* if_success_proj, ParsePredicateSuccessProj* parse_predicate_proj, CountedLoopNode* cl, ConNode* zero, diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 01c671878834..8c5dbe7fb481 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -44,6 +44,7 @@ #include "opto/node.hpp" #include "opto/opaquenode.hpp" #include "opto/phaseX.hpp" +#include "opto/reachability.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/subnode.hpp" @@ -683,6 +684,8 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode use->as_ArrayCopy()->is_copyofrange_validated()) && use->in(ArrayCopyNode::Dest) == res) { // ok to eliminate + } else if (use->is_ReachabilityFence() && OptimizeReachabilityFences) { + // ok to eliminate } else if (use->is_SafePoint()) { SafePointNode* sfpt = use->as_SafePoint(); if (sfpt->is_Call() && sfpt->as_Call()->has_non_debug_use(res)) { @@ -778,7 +781,13 @@ void PhaseMacroExpand::undo_previous_scalarizations(GrowableArray 0) { SafePointNode* sfpt_done = safepoints_done.pop(); + + SafePointNode::NodeEdgeTempStorage non_debug_edges_worklist(igvn()); + + sfpt_done->remove_non_debug_edges(non_debug_edges_worklist); + // remove any extra entries we added to the safepoint + assert(sfpt_done->jvms()->endoff() == sfpt_done->req(), "no extra edges past debug info allowed"); uint last = sfpt_done->req() - 1; for (int k = 0; k < nfields; k++) { sfpt_done->del_req(last--); @@ -799,6 +808,9 @@ void PhaseMacroExpand::undo_previous_scalarizations(GrowableArray restore_non_debug_edges(non_debug_edges_worklist); + _igvn._worklist.push(sfpt_done); } } @@ -839,6 +851,8 @@ void PhaseMacroExpand::undo_previous_scalarizations(GrowableArray jvms()->endoff() == sfpt->req(), "no extra edges past debug info allowed"); + // Fields of scalar objs are referenced only at the end // of regular debuginfo at the last (youngest) JVMS. // Record relative start index. @@ -964,17 +978,25 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio } // Do scalar replacement. -bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray & safepoints) { - GrowableArray safepoints_done; +bool PhaseMacroExpand::scalar_replacement(AllocateNode* alloc, GrowableArray& safepoints) { + GrowableArray safepoints_done; Node* res = alloc->result_cast(); assert(res == nullptr || res->is_CheckCastPP(), "unexpected AllocateNode result"); // Process the safepoint uses while (safepoints.length() > 0) { SafePointNode* sfpt = safepoints.pop(); + + SafePointNode::NodeEdgeTempStorage non_debug_edges_worklist(igvn()); + + // All sfpt inputs are implicitly included into debug info during the scalarization process below. + // Keep non-debug inputs separately, so they stay non-debug. + sfpt->remove_non_debug_edges(non_debug_edges_worklist); + SafePointScalarObjectNode* sobj = create_scalarized_object_description(alloc, sfpt); if (sobj == nullptr) { + sfpt->restore_non_debug_edges(non_debug_edges_worklist); undo_previous_scalarizations(safepoints_done, alloc); return false; } @@ -983,6 +1005,8 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray jvms(); sfpt->replace_edges_in_range(res, sobj, jvms->debug_start(), jvms->debug_end(), &_igvn); + non_debug_edges_worklist.remove_edge_if_present(res); // drop scalarized input from non-debug info + sfpt->restore_non_debug_edges(non_debug_edges_worklist); _igvn._worklist.push(sfpt); // keep it for rollback @@ -1073,6 +1097,8 @@ void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc) { } } _igvn._worklist.push(ac); + } else if (use->is_ReachabilityFence() && OptimizeReachabilityFences) { + use->as_ReachabilityFence()->clear_referent(_igvn); // redundant fence; will be removed during IGVN } else { eliminate_gc_barrier(use); } diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 9ec3402c7016..c7fe45084730 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -4081,7 +4081,7 @@ uint LoadStoreNode::ideal_reg() const { bool LoadStoreNode::result_not_used() const { for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { Node *x = fast_out(i); - if (x->Opcode() == Op_SCMemProj) { + if (x->Opcode() == Op_SCMemProj || x->is_ReachabilityFence()) { continue; } if (x->bottom_type() == TypeTuple::MEMBAR && diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index cb5795a12509..3eafd97d7c18 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -38,6 +38,7 @@ #include "opto/matcher.hpp" #include "opto/node.hpp" #include "opto/opcodes.hpp" +#include "opto/reachability.hpp" #include "opto/regmask.hpp" #include "opto/rootnode.hpp" #include "opto/type.hpp" @@ -503,6 +504,9 @@ Node *Node::clone() const { if (is_expensive()) { C->add_expensive_node(n); } + if (is_ReachabilityFence()) { + C->add_reachability_fence(n->as_ReachabilityFence()); + } if (for_post_loop_opts_igvn()) { // Don't add cloned node to Compile::_for_post_loop_opts_igvn list automatically. // If it is applicable, it will happen anyway when the cloned node is registered with IGVN. @@ -622,6 +626,9 @@ void Node::destruct(PhaseValues* phase) { if (is_expensive()) { compile->remove_expensive_node(this); } + if (is_ReachabilityFence()) { + compile->remove_reachability_fence(as_ReachabilityFence()); + } if (is_OpaqueTemplateAssertionPredicate()) { compile->remove_template_assertion_predicate_opaque(as_OpaqueTemplateAssertionPredicate()); } @@ -2994,6 +3001,25 @@ bool Node::is_data_proj_of_pure_function(const Node* maybe_pure_function) const return Opcode() == Op_Proj && as_Proj()->_con == TypeFunc::Parms && maybe_pure_function->is_CallLeafPure(); } +//--------------------------has_non_debug_uses------------------------------ +// Checks whether the node has any non-debug uses or not. +bool Node::has_non_debug_uses() const { + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + Node* u = fast_out(i); + if (u->is_SafePoint()) { + if (u->is_Call() && u->as_Call()->has_non_debug_use(this)) { + return true; + } + // Non-call safepoints have only debug uses. + } else if (u->is_ReachabilityFence()) { + // Reachability fence is treated as debug use. + } else { + return true; // everything else is conservatively treated as non-debug use + } + } + return false; // no non-debug uses found +} + //============================================================================= //------------------------------yank------------------------------------------- // Find and remove diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 46b89aa2c5fd..8c6622e643ec 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -168,6 +168,7 @@ class Pipeline; class PopulateIndexNode; class ProjNode; class RangeCheckNode; +class ReachabilityFenceNode; class ReductionNode; class RegMask; class RegionNode; @@ -452,6 +453,9 @@ class Node { // Check whether node has become unreachable bool is_unreachable(PhaseIterGVN &igvn) const; + // Does the node have any immediate non-debug uses? + bool has_non_debug_uses() const; + // Set a required input edge, also updates corresponding output edge void add_req( Node *n ); // Append a NEW required input void add_req( Node *n0, Node *n1 ) { @@ -824,6 +828,7 @@ class Node { DEFINE_CLASS_ID(Move, Node, 20) DEFINE_CLASS_ID(LShift, Node, 21) DEFINE_CLASS_ID(Neg, Node, 22) + DEFINE_CLASS_ID(ReachabilityFence, Node, 23) _max_classes = ClassMask_Neg }; @@ -1013,6 +1018,7 @@ class Node { DEFINE_CLASS_QUERY(PCTable) DEFINE_CLASS_QUERY(Phi) DEFINE_CLASS_QUERY(Proj) + DEFINE_CLASS_QUERY(ReachabilityFence) DEFINE_CLASS_QUERY(Reduction) DEFINE_CLASS_QUERY(Region) DEFINE_CLASS_QUERY(Root) @@ -1180,6 +1186,7 @@ class Node { return nullptr; } assert(!res->depends_only_on_test(), "the result must not depends_only_on_test"); + assert(Opcode() == res->Opcode(), "pinning must result in the same kind of node %s - %s", Name(), res->Name()); return res; } diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index 42f44f0f7ea8..5118019fc313 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -356,6 +356,7 @@ class Parse : public GraphKit { bool _wrote_stable; // Did we write a @Stable field? bool _wrote_fields; // Did we write any field? Node* _alloc_with_final_or_stable; // An allocation node with final or @Stable field + Node* _stress_rf_hook; // StressReachabilityFences support // Variables which track Java semantics during bytecode parsing: diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 683633f6355b..6a400631bffb 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -369,6 +369,15 @@ void Parse::load_interpreter_state(Node* osr_buf) { continue; } set_local(index, check_interpreter_type(l, type, bad_type_exit)); + if (StressReachabilityFences && type->isa_oopptr() != nullptr) { + // Keep all oop locals alive until the method returns as if there are + // reachability fences for them at the end of the method. + Node* loc = local(index); + if (loc->bottom_type() != TypePtr::NULL_PTR) { + assert(loc->bottom_type()->isa_oopptr() != nullptr, "%s", Type::str(loc->bottom_type())); + _stress_rf_hook->add_req(loc); + } + } } for (index = 0; index < sp(); index++) { @@ -377,6 +386,15 @@ void Parse::load_interpreter_state(Node* osr_buf) { if (l->is_top()) continue; // nothing here const Type *type = osr_block->stack_type_at(index); set_stack(index, check_interpreter_type(l, type, bad_type_exit)); + if (StressReachabilityFences && type->isa_oopptr() != nullptr) { + // Keep all oops on stack alive until the method returns as if there are + // reachability fences for them at the end of the method. + Node* stk = stack(index); + if (stk->bottom_type() != TypePtr::NULL_PTR) { + assert(stk->bottom_type()->isa_oopptr() != nullptr, "%s", Type::str(stk->bottom_type())); + _stress_rf_hook->add_req(stk); + } + } } if (bad_type_exit->control()->req() > 1) { @@ -411,6 +429,7 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) _wrote_stable = false; _wrote_fields = false; _alloc_with_final_or_stable = nullptr; + _stress_rf_hook = (StressReachabilityFences ? new Node(1) : nullptr); _block = nullptr; _first_return = true; _replaced_nodes_for_exceptions = false; @@ -642,6 +661,11 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) if (log) log->done("parse nodes='%d' live='%d' memory='%zu'", C->unique(), C->live_nodes(), C->node_arena()->used()); + + if (StressReachabilityFences) { + _stress_rf_hook->destruct(&_gvn); + _stress_rf_hook = nullptr; + } } //---------------------------do_all_blocks------------------------------------- @@ -1194,6 +1218,14 @@ SafePointNode* Parse::create_entry_map() { return entry_map; } +//-----------------------is_auto_boxed_primitive------------------------------ +// Helper method to detect auto-boxed primitives (result of valueOf() call). +static bool is_auto_boxed_primitive(Node* n) { + return (n->is_Proj() && n->as_Proj()->_con == TypeFunc::Parms && + n->in(0)->is_CallJava() && + n->in(0)->as_CallJava()->method()->is_boxing_method()); +} + //-----------------------------do_method_entry-------------------------------- // Emit any code needed in the pseudo-block before BCI zero. // The main thing to do is lock the receiver of a synchronized method. @@ -1207,6 +1239,19 @@ void Parse::do_method_entry() { make_dtrace_method_entry(method()); } + if (StressReachabilityFences) { + // Keep all oop arguments alive until the method returns as if there are + // reachability fences for them at the end of the method. + int max_locals = jvms()->loc_size(); + for (int idx = 0; idx < max_locals; idx++) { + Node* loc = local(idx); + if (loc->bottom_type()->isa_oopptr() != nullptr && + !is_auto_boxed_primitive(loc)) { // ignore auto-boxed primitives + _stress_rf_hook->add_req(loc); + } + } + } + #ifdef ASSERT // Narrow receiver type when it is too broad for the method being parsed. if (!method()->is_static()) { @@ -2197,6 +2242,15 @@ void Parse::return_current(Node* value) { call_register_finalizer(); } + if (StressReachabilityFences) { + // Insert reachability fences for all oop arguments at the end of the method. + for (uint i = 1; i < _stress_rf_hook->req(); i++) { + Node* referent = _stress_rf_hook->in(i); + assert(referent->bottom_type()->isa_oopptr(), "%s", Type::str(referent->bottom_type())); + insert_reachability_fence(referent); + } + } + // Do not set_parse_bci, so that return goo is credited to the return insn. set_bci(InvocationEntryBci); if (method()->is_synchronized()) { diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index a7c5398171b6..d732e6f04e12 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -1803,8 +1803,8 @@ static bool match_type_check(PhaseGVN& gvn, assert(idx == 1 || idx == 2, ""); Node* vcon = val->in(idx); - assert(val->find_edge(con) > 0, ""); if ((btest == BoolTest::eq && vcon == con) || (btest == BoolTest::ne && vcon != con)) { + assert(val->find_edge(con) > 0, "mismatch"); SubTypeCheckNode* sub = b1->in(1)->as_SubTypeCheck(); Node* obj_or_subklass = sub->in(SubTypeCheckNode::ObjOrSubKlass); Node* superklass = sub->in(SubTypeCheckNode::SuperKlass); @@ -1833,17 +1833,21 @@ void Parse::sharpen_type_after_if(BoolTest::mask btest, &obj, &cast_type)) { assert(obj != nullptr && cast_type != nullptr, "missing type check info"); const Type* obj_type = _gvn.type(obj); - const TypeOopPtr* tboth = obj_type->join_speculative(cast_type)->isa_oopptr(); - if (tboth != nullptr && tboth != obj_type && tboth->higher_equal(obj_type)) { + const Type* tboth = obj_type->filter_speculative(cast_type); + assert(tboth->higher_equal(obj_type) && tboth->higher_equal(cast_type), "sanity"); + if (tboth == Type::TOP && KillPathsReachableByDeadTypeNode) { + // Let dead type node cleaning logic prune effectively dead path for us. + // CheckCastPP::Value() == TOP and it will trigger the cleanup during GVN. + // Don't materialize the cast when cleanup is disabled, because + // it kills data and control leaving IR in broken state. + tboth = cast_type; + } + if (tboth != Type::TOP && tboth != obj_type) { int obj_in_map = map()->find_edge(obj); - JVMState* jvms = this->jvms(); if (obj_in_map >= 0 && - (jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) { + (jvms()->is_loc(obj_in_map) || jvms()->is_stk(obj_in_map))) { TypeNode* ccast = new CheckCastPPNode(control(), obj, tboth); - const Type* tcc = ccast->as_Type()->type(); - assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve"); - // Delay transform() call to allow recovery of pre-cast value - // at the control merge. + // Delay transform() call to allow recovery of pre-cast value at the control merge. _gvn.set_type_bottom(ccast); record_for_igvn(ccast); // Here's the payoff. diff --git a/src/hotspot/share/opto/phase.cpp b/src/hotspot/share/opto/phase.cpp index 5603033ce69d..3f1866990e2c 100644 --- a/src/hotspot/share/opto/phase.cpp +++ b/src/hotspot/share/opto/phase.cpp @@ -90,6 +90,9 @@ void Phase::print_timers() { tty->print_cr (" Prune Useless: %7.3f s", timers[_t_vector_pru].seconds()); tty->print_cr (" Renumber Live: %7.3f s", timers[_t_renumberLive].seconds()); tty->print_cr (" IdealLoop: %7.3f s", timers[_t_idealLoop].seconds()); + tty->print_cr (" ReachabilityFence: %7.3f s", timers[_t_reachability].seconds()); + tty->print_cr (" Optimize: %7.3f s", timers[_t_reachability_optimize].seconds()); + tty->print_cr (" Expand: %7.3f s", timers[_t_reachability_expand].seconds()); tty->print_cr (" AutoVectorize: %7.3f s", timers[_t_autoVectorize].seconds()); tty->print_cr (" IdealLoop Verify: %7.3f s", timers[_t_idealLoopVerify].seconds()); tty->print_cr (" Cond Const Prop: %7.3f s", timers[_t_ccp].seconds()); diff --git a/src/hotspot/share/opto/phase.hpp b/src/hotspot/share/opto/phase.hpp index 6700df6ec177..5bd3c34f15f1 100644 --- a/src/hotspot/share/opto/phase.hpp +++ b/src/hotspot/share/opto/phase.hpp @@ -85,6 +85,9 @@ class Phase : public StackObj { f( _t_vector_pru, "vector_pru") \ f( _t_renumberLive, "") \ f( _t_idealLoop, "idealLoop") \ + f( _t_reachability, "reachabilityFence") \ + f( _t_reachability_optimize, "reachabilityFence_optimize") \ + f( _t_reachability_expand, "reachabilityFence_expand") \ f( _t_autoVectorize, "autoVectorize") \ f( _t_idealLoopVerify, "idealLoopVerify") \ f( _t_ccp, "ccp") \ diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 0fecc14f31a7..a4d6a6c33d08 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2123,7 +2123,12 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { if (n->is_Vector()) { // Found with tier1-3. Not investigated yet. - // The observed issue was with AndVNode::Identity + // The observed issue was with AndVNode::Identity and + // VectorStoreMaskNode::Identity (see JDK-8370863). + // + // Found with: + // compiler/vectorapi/VectorStoreMaskIdentityTest.java + // -XX:CompileThreshold=100 -XX:-TieredCompilation -XX:VerifyIterativeGVN=1110 return; } diff --git a/src/hotspot/share/opto/phasetype.hpp b/src/hotspot/share/opto/phasetype.hpp index ce432fbfc01d..2b599ace03ae 100644 --- a/src/hotspot/share/opto/phasetype.hpp +++ b/src/hotspot/share/opto/phasetype.hpp @@ -106,6 +106,7 @@ flags(PHASEIDEALLOOP1, "PhaseIdealLoop 1") \ flags(PHASEIDEALLOOP2, "PhaseIdealLoop 2") \ flags(PHASEIDEALLOOP3, "PhaseIdealLoop 3") \ + flags(EXPAND_REACHABILITY_FENCES, "Expand Reachability Fences") \ flags(AUTO_VECTORIZATION1_BEFORE_APPLY, "AutoVectorization 1, before Apply") \ flags(AUTO_VECTORIZATION3_AFTER_ADJUST_LIMIT, "AutoVectorization 2, after Adjusting Pre-loop Limit") \ flags(AUTO_VECTORIZATION4_AFTER_SPECULATIVE_RUNTIME_CHECKS, "AutoVectorization 3, after Adding Speculative Runtime Checks") \ diff --git a/src/hotspot/share/opto/reachability.cpp b/src/hotspot/share/opto/reachability.cpp new file mode 100644 index 000000000000..b45b340ab85a --- /dev/null +++ b/src/hotspot/share/opto/reachability.cpp @@ -0,0 +1,512 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "opto/c2_MacroAssembler.hpp" +#include "opto/callnode.hpp" +#include "opto/compile.hpp" +#include "opto/loopnode.hpp" +#include "opto/phaseX.hpp" +#include "opto/reachability.hpp" +#include "opto/regalloc.hpp" +#include "opto/runtime.hpp" +#include "utilities/pair.hpp" + +/* + * java.lang.ref.Reference::reachabilityFence support. + * + * Reachability Fence (RF) ensures that the given object (referent) remains strongly reachable + * regardless of any optimizing transformations the virtual machine may perform that might otherwise + * allow the object to become unreachable. + * + * RFs are intended to be used in performance-critical code, so the primary goal for C2 support is + * to reduce their runtime overhead as much as possible. + * + * Reference::reachabilityFence() calls are intrinsified into ReachabilityFence CFG nodes. RF node keeps + * its referent alive, so the referent's location is recorded at every safepoint (in its oop map) which + * interferes with referent's live range. + * + * It is tempting to directly attach referents to interfering safepoints right from the beginning, but it + * doesn't play well with some optimizations C2 does (e.g., during loop-invariant code motion a safepoint + * can become interfering once a load is hoisted). + * + * Instead, reachability representation transitions through multiple phases: + * (0) initial set of RFs is materialized during parsing (as a result of + * Reference.reachabilityFence intrinsification); + * (1) optimization pass during loop opts eliminates redundant RF nodes and + * moves the ones with loop-invariant referents outside (after) loops; + * (2) after loop opts are over, RF nodes are eliminated and their referents are transferred to + * safepoint nodes (appended as edges after debug info); + * (3) during final graph reshaping, referent edges are removed from safepoints and materialized as RF nodes + * attached to their safepoint node (closely following it in CFG graph). + * + * Some implementation considerations. + * + * (a) It looks attractive to get rid of RF nodes early and transfer to safepoint-attached representation, + * but it is not correct until loop opts are done. + * + * Live ranges of values are routinely extended during loop opts. And it can break the invariant that + * all interfering safepoints contain the referent in their oop map. (If an interfering safepoint doesn't + * keep the referent alive, then it becomes possible for the referent to be prematurely GCed.) + * + * compiler/c2/TestReachabilityFence.java demonstrates a situation where a load is hoisted out of a loop thus + * extending the live range of the value it produces beyond the safepoint on loop-back edge. + * + * After loop opts are over, it becomes possible to reliably enumerate all interfering safepoints and + * to ensure that the referent is present in their oop maps. Current assumption is that after loop opts the IR graph + * is stable enough, so relative order of memory operations and safepoints is preserved and only safepoints between + * a referent and it's uses are taken into account. A more conservative analysis can be employed -- any safepoint dominated + * by a referent is treated as interfering with it -- if it turns out that the assumption doesn't hold. + * + * (b) RF nodes may interfere with Register Allocator (RA). If a safepoint is pruned during macro expansion, + * it can make some RF nodes redundant, but we don't have information about their relations anymore to detect that. + * Redundant RF node unnecessarily extends referent's live range and increases register pressure. + * + * Hence, we eliminate RF nodes and transfer their referents to corresponding safepoints (phase #2). + * When safepoints are pruned, corresponding reachability edges also go away. + * + * (c) Unfortunately, it's not straightforward to stay with safepoint-attached representation till the very end, + * because information about derived oops is attached to safepoints in a similar way. So, for now RFs are + * rematerialized at safepoints before RA (phase #3). + */ + +bool ReachabilityFenceNode::is_redundant(PhaseGVN& gvn) { + const Type* referent_t = gvn.type(referent()); + if (referent_t == TypePtr::NULL_PTR) { + return true; // no-op fence: null referent + } + if (!OptimizeReachabilityFences) { + return false; // keep reachability fence nodes intact + } + if (!PreserveReachabilityFencesOnConstants && referent_t->singleton()) { + return true; // no-op fence: constants are strongly reachable + } + return false; +} + +Node* ReachabilityFenceNode::Ideal(PhaseGVN* phase, bool can_reshape) { + return (remove_dead_region(phase, can_reshape) ? this : nullptr); +} + +Node* ReachabilityFenceNode::Identity(PhaseGVN* phase) { + if (is_redundant(*phase)) { + return in(0); + } + return this; +} + +// Turn the RF node into a no-op by setting its referent to null. +// Subsequent IGVN pass removes cleared nodes. +bool ReachabilityFenceNode::clear_referent(PhaseIterGVN& phase) { + if (phase.type(referent()) == TypePtr::NULL_PTR) { + return false; + } else { + phase.replace_input_of(this, 1, phase.makecon(TypePtr::NULL_PTR)); + return true; + } +} + +#ifndef PRODUCT +static void rf_desc(outputStream* st, const ReachabilityFenceNode* rf, PhaseRegAlloc* ra) { + char buf[50]; + ra->dump_register(rf->referent(), buf, sizeof(buf)); + st->print("reachability fence [%s]", buf); +} + +void ReachabilityFenceNode::format(PhaseRegAlloc* ra, outputStream* st) const { + rf_desc(st, this, ra); +} + +void ReachabilityFenceNode::emit(C2_MacroAssembler* masm, PhaseRegAlloc* ra) const { + ResourceMark rm; + stringStream ss; + rf_desc(&ss, this, ra); + const char* desc = masm->code_string(ss.freeze()); + masm->block_comment(desc); +} +#endif + +// Detect safepoint nodes which are important for reachability tracking purposes. +// Some SafePoint nodes can't affect referent's reachability in any noticeable way and +// can be safely ignored during the analysis. +static bool is_interfering_sfpt_candidate(SafePointNode* sfpt) { + if (sfpt->jvms() == nullptr) { + return false; // not a real safepoint + } else if (sfpt->is_CallStaticJava() && sfpt->as_CallStaticJava()->is_uncommon_trap()) { + return false; // uncommon traps are exit points + } + return true; // a full-blown safepoint can interfere with a reachability fence and its referent +} + +void PhaseIdealLoop::insert_rf(Node* ctrl, Node* referent) { + IdealLoopTree* lpt = get_loop(ctrl); + Node* ctrl_end = ctrl->unique_ctrl_out(); + + auto new_rf = new ReachabilityFenceNode(C, ctrl, referent); + + register_control(new_rf, lpt, ctrl); + set_idom(new_rf, ctrl, dom_depth(ctrl) + 1); + lpt->register_reachability_fence(new_rf); + + igvn().rehash_node_delayed(ctrl_end); + ctrl_end->replace_edge(ctrl, new_rf); + + if (idom(ctrl_end) == ctrl) { + set_idom(ctrl_end, new_rf, dom_depth(new_rf) + 1); + } else { + assert(ctrl_end->is_Region(), ""); + } +} + +void PhaseIdealLoop::replace_rf(Node* old_node, Node* new_node) { + assert(old_node->is_ReachabilityFence() || + (old_node->is_Proj() && old_node->in(0)->is_ReachabilityFence()), + "%s", NodeClassNames[old_node->Opcode()]); + + IdealLoopTree* lpt = get_loop(old_node); + lpt->_body.yank(old_node); + assert(lpt->_reachability_fences != nullptr, "missing"); + assert(lpt->_reachability_fences->contains(old_node), "missing"); + lpt->_reachability_fences->yank(old_node); + replace_node_and_forward_ctrl(old_node, new_node); +} + +void PhaseIdealLoop::remove_rf(ReachabilityFenceNode* rf) { + Node* rf_ctrl_in = rf->in(0); + Node* referent = rf->referent(); + if (rf->clear_referent(igvn()) && referent->outcnt() == 0) { + remove_dead_data_node(referent); + } + replace_rf(rf, rf_ctrl_in); +} + +//====================================================================== +//---------------------------- Phase 1 --------------------------------- +// Optimization pass over reachability fences during loop opts. +// Moves RFs with loop-invariant referents out of the loop. +bool PhaseIdealLoop::optimize_reachability_fences() { + Compile::TracePhase tp(_t_reachability_optimize); + + assert(OptimizeReachabilityFences, "required"); + + // ResourceMark rm; // NB! not safe because insert_rf may trigger _idom reallocation + Unique_Node_List redundant_rfs; + typedef Pair LoopExit; + GrowableArray worklist; + + for (int i = 0; i < C->reachability_fences_count(); i++) { + ReachabilityFenceNode* rf = C->reachability_fence(i); + assert(!rf->is_redundant(igvn()), "required"); + // Move RFs out of counted loops when possible. + IdealLoopTree* lpt = get_loop(rf); + Node* referent = rf->referent(); + if (lpt->is_invariant(referent)) { + IfFalseNode* unique_loop_exit = lpt->unique_loop_exit_proj_or_null(); + if (unique_loop_exit != nullptr) { + // Skip over an outer strip-mined loop. + if (!lpt->is_root()) { + IdealLoopTree* outer_lpt = lpt->_parent; + if (outer_lpt->head()->is_OuterStripMinedLoop()) { + if (outer_lpt->is_invariant(referent)) { + IfNode* outer_loop_end = outer_lpt->head()->as_OuterStripMinedLoop()->outer_loop_end(); + if (outer_loop_end != nullptr && outer_loop_end->false_proj_or_null() != nullptr) { + unique_loop_exit = outer_loop_end->false_proj_or_null(); + } + } else { + // An attempt to insert an RF node inside outer strip-mined loop breaks + // its IR invariants and manifests as assertion failures. + assert(false, "not loop invariant in outer strip-mined loop"); + continue; // skip + } + } + } + + LoopExit p(referent, unique_loop_exit); + worklist.push(p); + redundant_rfs.push(rf); + +#ifndef PRODUCT + if (TraceLoopOpts) { + IdealLoopTree* loop = get_loop(unique_loop_exit->in(0)); + tty->print_cr("ReachabilityFence: N%d: %s N%d/N%d -> loop exit N%d (%s N%d/N%d)", + rf->_idx, lpt->head()->Name(), lpt->head()->_idx, lpt->tail()->_idx, + unique_loop_exit->_idx, + loop->head()->Name(), loop->head()->_idx, loop->tail()->_idx); + } +#endif // !PRODUCT + } + } + } + + // Populate RFs outside loops. + while (worklist.is_nonempty()) { + LoopExit p = worklist.pop(); + Node* referent = p.first; + Node* ctrl_out = p.second; + insert_rf(ctrl_out, referent); + } + + // Eliminate redundant RFs. + bool progress = (redundant_rfs.size() > 0); + while (redundant_rfs.size() > 0) { + remove_rf(redundant_rfs.pop()->as_ReachabilityFence()); + } + + return progress; +} + +//====================================================================== +//---------------------------- Phase 2 --------------------------------- + +// Linearly traverse CFG upwards starting at ctrl_start until first merge point. +// All encountered safepoints are recorded in safepoints list, except +// the ones filtered out by is_interfering_sfpt_candidate(). +static void enumerate_interfering_sfpts_linear_traversal(Node* ctrl_start, Node_Stack& stack, VectorSet& visited, + Node_List& interfering_sfpts) { + for (Node* ctrl = ctrl_start; ctrl != nullptr; ctrl = ctrl->in(0)) { + assert(ctrl->is_CFG(), ""); + if (visited.test_set(ctrl->_idx)) { + return; + } else { + if (ctrl->is_Region()) { + stack.push(ctrl, 1); + return; // stop at merge points + } else if (ctrl->is_SafePoint() && is_interfering_sfpt_candidate(ctrl->as_SafePoint())) { + assert(!ctrl->is_CallStaticJava() || !ctrl->as_CallStaticJava()->is_uncommon_trap(), + "uncommon traps should not be enumerated"); + interfering_sfpts.push(ctrl); + } + } + } +} + +// Enumerate all safepoints which are reachable from the RF to its referent through CFG. +// Start at RF node and traverse CFG upwards until referent's control node is reached. +static void enumerate_interfering_sfpts(ReachabilityFenceNode* rf, PhaseIdealLoop* phase, + Node_Stack& stack, VectorSet& visited, + Node_List& interfering_sfpts) { + assert(stack.is_empty(), "required"); + assert(visited.is_empty(), "required"); + + Node* referent = rf->referent(); + Node* referent_ctrl = phase->get_ctrl(referent); + assert(phase->is_dominator(referent_ctrl, rf), "sanity"); + + visited.set(referent_ctrl->_idx); // end point + enumerate_interfering_sfpts_linear_traversal(rf, stack, visited, interfering_sfpts); // starting point in CFG + while (stack.is_nonempty()) { + Node* cur = stack.node(); + uint idx = stack.index(); + + assert(cur != nullptr, ""); + assert(cur->is_Region(), "%s", NodeClassNames[cur->Opcode()]); + assert(phase->is_dominator(referent_ctrl, cur), ""); + assert(idx > 0 && idx <= cur->req(), "%d %d", idx, cur->req()); + + if (idx < cur->req()) { + stack.set_index(idx + 1); + enumerate_interfering_sfpts_linear_traversal(cur->in(idx), stack, visited, interfering_sfpts); + } else { + stack.pop(); + } + } + // Reset temporary structures to their initial state. + assert(stack.is_empty(), "required"); + visited.clear(); +} + +// Start offset for reachability info on a safepoint node. +static uint rf_base_offset(SafePointNode* sfpt) { + return sfpt->jvms()->debug_end(); +} + +static bool dominates_another_rf(ReachabilityFenceNode* rf, PhaseIdealLoop* phase) { + assert(!rf->is_redundant(phase->igvn()), ""); + + for (int i = 0; i < phase->C->reachability_fences_count(); i++) { + ReachabilityFenceNode* other_rf = phase->C->reachability_fence(i); + assert(other_rf->outcnt() > 0, "dead node"); + if (rf != other_rf && rf->referent()->eqv_uncast(other_rf->referent()) && + phase->is_dominator(rf, other_rf)) { + return true; // dominates another reachability fence with the same referent + } + } + return false; +} + +// Phase 2: migrate reachability info to safepoints. +// All RFs are replaced with edges from corresponding referents to interfering safepoints. +// Interfering safepoints are safepoint nodes which are reachable from the RF to its referent through CFG. +bool PhaseIdealLoop::expand_reachability_fences() { + Compile::TracePhase tp(_t_reachability_expand); + + assert(OptimizeReachabilityFences, "required"); + assert(C->post_loop_opts_phase(), "required"); + DEBUG_ONLY( int no_of_constant_rfs = 0; ) + + ResourceMark rm; + Unique_Node_List for_removal; + typedef Pair ReachabilityEdge; + GrowableArray reachability_edges; + { + // Reuse temporary structures to avoid allocating them for every single RF node. + Node_List sfpt_worklist; + Node_Stack stack(0); + VectorSet visited; + + for (int i = 0; i < C->reachability_fences_count(); i++) { + ReachabilityFenceNode* rf = C->reachability_fence(i); + assert(!rf->is_redundant(igvn()), "missed"); + if (PreserveReachabilityFencesOnConstants) { + const Type* referent_t = igvn().type(rf->referent()); + assert(referent_t != TypePtr::NULL_PTR, "redundant rf"); + bool is_constant_rf = referent_t->singleton(); + if (is_constant_rf) { + DEBUG_ONLY( no_of_constant_rfs += 1; ) + continue; // leave RFs on constants intact + } + } + if (dominates_another_rf(rf, this)) { + // Redundant fence: dominates another RF with the same referent. + // RF is redundant for some referent oop when the referent has another RF which + // keeps it alive across the RF. In terms of dominance relation it can be formulated + // as "a referent has another RF which is dominated by the redundant RF". + // + // NB! It is safe to assume that dominating RF is redundant only during expansion. + // Otherwise, there's a chance for the superseding RF node to go away before expansion. + // Non-RF users are ignored for a similar reason: they can go away before or after expansion + // takes place, so no guarantees reachability information is preserved. + } else { + assert(sfpt_worklist.size() == 0, "not empty"); + enumerate_interfering_sfpts(rf, this, stack, visited, sfpt_worklist); + + Node* referent = rf->referent(); + while (sfpt_worklist.size() > 0) { + SafePointNode* sfpt = sfpt_worklist.pop()->as_SafePoint(); + assert(is_dominator(get_ctrl(referent), sfpt), ""); + assert(sfpt->req() == rf_base_offset(sfpt), "no extra edges allowed"); + if (sfpt->find_edge(referent) == -1) { + ReachabilityEdge p(sfpt, referent); + reachability_edges.push(p); + } + } + } + for_removal.push(rf); + } + } + // Materialize reachability edges. + while (reachability_edges.length() > 0) { + ReachabilityEdge p = reachability_edges.pop(); + SafePointNode* sfpt = p.first; + Node* referent = p.second; + if (sfpt->find_edge(referent) == -1) { + sfpt->add_req(referent); + igvn()._worklist.push(sfpt); + } + } + // Eliminate processed RFs. They become redundant once reachability edges are added. + bool progress = (for_removal.size() > 0); + while (for_removal.size() > 0) { + remove_rf(for_removal.pop()->as_ReachabilityFence()); + } + + assert(C->reachability_fences_count() == no_of_constant_rfs, ""); + return progress; +} + +//====================================================================== +//---------------------------- Phase 3 --------------------------------- + +// Find a point in CFG right after safepoint node to insert reachability fence. +static Node* sfpt_ctrl_out(SafePointNode* sfpt) { + if (sfpt->is_Call()) { + CallProjections callprojs; + sfpt->as_Call()->extract_projections(&callprojs, + false /*separate_io_proj*/, + false /*do_asserts*/, + true /*allow_handlers*/); + // Calls can have multiple control projections. However, reachability edge expansion + // happens during final graph reshaping which is performed very late in compilation pipeline. + // The assumption here is that the control path chosen for insertion can't go away. + // So, materializing a reachability fence on a single control path produced by a call + // is enough to keep the referent oop alive across the call. + if (callprojs.fallthrough_catchproj != nullptr) { + return callprojs.fallthrough_catchproj; + } else if (callprojs.catchall_catchproj != nullptr) { + return callprojs.catchall_catchproj; // rethrow stub + } else if (callprojs.fallthrough_proj != nullptr) { + return callprojs.fallthrough_proj; // no exceptions thrown + } else { + ShouldNotReachHere(); + } + } else { + return sfpt; + } +} + +// Phase 3: materialize reachability fences from reachability edges on safepoints. +// Turn extra safepoint edges into reachability fences immediately following the safepoint. +// +// NB! As of now, a special care is needed to properly enumerate reachability edges because +// there are other use cases for non-debug safepoint edges. expand_reachability_edges() runs +// after macro expansion where runtime calls during array allocation are annotated with +// valid_length_test_input as an extra edges. Until there's a mechanism to distinguish between +// different types of non-debug edges, unrelated cases are filtered out explicitly and in ad-hoc manner. +void Compile::expand_reachability_edges(Unique_Node_List& safepoints) { + for (uint i = 0; i < safepoints.size(); i++) { + SafePointNode* sfpt = safepoints.at(i)->as_SafePoint(); + + uint rf_offset = rf_base_offset(sfpt); + if (sfpt->jvms() != nullptr && sfpt->req() > rf_offset) { + assert(is_interfering_sfpt_candidate(sfpt), ""); + Node* ctrl_out = sfpt_ctrl_out(sfpt); + Node* ctrl_end = ctrl_out->unique_ctrl_out(); + + Node* extra_edge = nullptr; + if (sfpt->is_Call()) { + address entry = sfpt->as_Call()->entry_point(); + if (entry == OptoRuntime::new_array_Java() || + entry == OptoRuntime::new_array_nozero_Java()) { + // valid_length_test_input is appended during macro expansion at the very end + int last_idx = sfpt->req() - 1; + extra_edge = sfpt->in(last_idx); + sfpt->del_req(last_idx); + } + } + + while (sfpt->req() > rf_offset) { + int idx = sfpt->req() - 1; + Node* referent = sfpt->in(idx); + sfpt->del_req(idx); + + Node* new_rf = new ReachabilityFenceNode(C, ctrl_out, referent); + ctrl_end->replace_edge(ctrl_out, new_rf); + ctrl_end = new_rf; + } + + if (extra_edge != nullptr) { + sfpt->add_req(extra_edge); // Add valid_length_test_input edge back + } + } + } +} diff --git a/src/hotspot/share/opto/reachability.hpp b/src/hotspot/share/opto/reachability.hpp new file mode 100644 index 000000000000..ba435c8484f2 --- /dev/null +++ b/src/hotspot/share/opto/reachability.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#ifndef SHARE_OPTO_REACHABILITY_HPP +#define SHARE_OPTO_REACHABILITY_HPP + +#include "opto/multnode.hpp" +#include "opto/node.hpp" +#include "opto/opcodes.hpp" +#include "opto/type.hpp" + +//------------------------ReachabilityFenceNode-------------------------- +// Represents a Reachability Fence (RF) in the code. +// +// RF ensures that the given object (referent) remains strongly reachable regardless of +// any optimizing transformations the virtual machine may perform that might otherwise +// allow the object to become unreachable. + +// java.lang.ref.Reference::reachabilityFence calls are intrinsified into ReachabilityFence nodes. +// +// More details in reachability.cpp. +class ReachabilityFenceNode : public Node { +public: + ReachabilityFenceNode(Compile* C, Node* ctrl, Node* referent) + : Node(1) { + assert(referent->bottom_type()->isa_oopptr() || + referent->bottom_type()->isa_narrowoop() != nullptr || + referent->bottom_type() == TypePtr::NULL_PTR, + "%s", Type::str(referent->bottom_type())); + init_class_id(Class_ReachabilityFence); + init_req(TypeFunc::Control, ctrl); + add_req(referent); + C->add_reachability_fence(this); + } + 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 uint ideal_reg() const { return 0; } // not matched in the AD file + virtual const Type* bottom_type() const { return Type::CONTROL; } + virtual const RegMask& in_RegMask(uint idx) const { + // Fake input register mask for the referent: accepts all registers and all stack slots. + // This avoids redundant register moves around reachability fences. + return RegMask::ALL; + } + virtual const RegMask& out_RegMask() const { + return RegMask::EMPTY; + } + + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); + + Node* referent() const { return in(1); } + bool is_redundant(PhaseGVN& gvn); + bool clear_referent(PhaseIterGVN& phase); + +#ifndef PRODUCT + virtual void format(PhaseRegAlloc* ra, outputStream* st) const; + virtual void emit(C2_MacroAssembler* masm, PhaseRegAlloc* ra) const; +#endif +}; + +#endif // SHARE_OPTO_REACHABILITY_HPP diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index f44df7e6da22..d35717c59222 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, 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 @@ -455,14 +455,12 @@ void PhaseVector::expand_vunbox_node(VectorUnboxNode* vec_unbox) { gvn.record_for_igvn(local_mem); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); C2OptAccess access(gvn, ctrl, local_mem, decorators, T_OBJECT, obj, addr); - const Type* type = TypeOopPtr::make_from_klass(field->type()->as_klass()); - vec_field_ld = bs->load_at(access, type); - } - // For proper aliasing, attach concrete payload type. - ciKlass* payload_klass = ciTypeArrayKlass::make(bt); - const Type* payload_type = TypeAryPtr::make_from_klass(payload_klass)->cast_to_ptr_type(TypePtr::NotNull); - vec_field_ld = gvn.transform(new CastPPNode(nullptr, vec_field_ld, payload_type)); + // For proper aliasing, attach concrete payload type. + ciKlass* payload_klass = ciTypeArrayKlass::make(bt); + const Type* payload_type = TypeAryPtr::make_from_klass(payload_klass)->cast_to_ptr_type(TypePtr::NotNull); + vec_field_ld = bs->load_at(access, payload_type); + } Node* adr = kit.array_element_address(vec_field_ld, gvn.intcon(0), bt); const TypePtr* adr_type = adr->bottom_type()->is_ptr(); diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 6012bdef86ee..d19aa476196a 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -1047,6 +1047,20 @@ Node* VectorNode::Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } +// Traverses a chain of VectorMaskCast and returns the first non VectorMaskCast node. +// +// Due to the unique nature of vector masks, for specific IR patterns, +// VectorMaskCast does not affect the output results. For example: +// (VectorStoreMask (VectorMaskCast* (VectorLoadMask x))) => (x) +// x remains to be a bool vector with no changes. +// This function can be used to eliminate the VectorMaskCast in such patterns. +Node* VectorNode::uncast_mask(Node* n) { + while (n->Opcode() == Op_VectorMaskCast) { + n = n->in(1); + } + return n; +} + // Return initial Pack node. Additional operands added with add_opd() calls. PackNode* PackNode::make(Node* s, uint vlen, BasicType bt) { const TypeVect* vt = TypeVect::make(bt, vlen); @@ -1246,6 +1260,10 @@ int ReductionNode::opcode(int opc, BasicType bt) { assert(bt == T_LONG, "must be"); vopc = Op_AddReductionVL; break; + case Op_AddHF: + assert(bt == T_SHORT, "must be"); + vopc = Op_AddReductionVHF; + break; case Op_AddF: assert(bt == T_FLOAT, "must be"); vopc = Op_AddReductionVF; @@ -1270,6 +1288,10 @@ int ReductionNode::opcode(int opc, BasicType bt) { assert(bt == T_LONG, "must be"); vopc = Op_MulReductionVL; break; + case Op_MulHF: + assert(bt == T_SHORT, "must be"); + vopc = Op_MulReductionVHF; + break; case Op_MulF: assert(bt == T_FLOAT, "must be"); vopc = Op_MulReductionVF; @@ -1418,10 +1440,12 @@ ReductionNode* ReductionNode::make(int opc, Node* ctrl, Node* n1, Node* n2, Basi switch (vopc) { case Op_AddReductionVI: return new AddReductionVINode(ctrl, n1, n2); case Op_AddReductionVL: return new AddReductionVLNode(ctrl, n1, n2); + case Op_AddReductionVHF: return new AddReductionVHFNode(ctrl, n1, n2, requires_strict_order); case Op_AddReductionVF: return new AddReductionVFNode(ctrl, n1, n2, requires_strict_order); case Op_AddReductionVD: return new AddReductionVDNode(ctrl, n1, n2, requires_strict_order); case Op_MulReductionVI: return new MulReductionVINode(ctrl, n1, n2); case Op_MulReductionVL: return new MulReductionVLNode(ctrl, n1, n2); + case Op_MulReductionVHF: return new MulReductionVHFNode(ctrl, n1, n2, requires_strict_order); case Op_MulReductionVF: return new MulReductionVFNode(ctrl, n1, n2, requires_strict_order); case Op_MulReductionVD: return new MulReductionVDNode(ctrl, n1, n2, requires_strict_order); case Op_MinReductionV: return new MinReductionVNode (ctrl, n1, n2); @@ -1495,10 +1519,12 @@ Node* VectorLoadMaskNode::Identity(PhaseGVN* phase) { Node* VectorStoreMaskNode::Identity(PhaseGVN* phase) { // Identity transformation on boolean vectors. - // VectorStoreMask (VectorLoadMask bv) elem_size ==> bv + // VectorStoreMask (VectorMaskCast* VectorLoadMask bv) elem_size ==> bv // vector[n]{bool} => vector[n]{t} => vector[n]{bool} - if (in(1)->Opcode() == Op_VectorLoadMask) { - return in(1)->in(1); + Node* in1 = VectorNode::uncast_mask(in(1)); + if (in1->Opcode() == Op_VectorLoadMask) { + assert(length() == in1->as_Vector()->length(), "vector length must match"); + return in1->in(1); } return this; } @@ -1597,6 +1623,8 @@ Node* ReductionNode::make_identity_con_scalar(PhaseGVN& gvn, int sopc, BasicType return nullptr; } break; + case Op_AddReductionVHF: + return gvn.makecon(TypeH::ZERO); case Op_AddReductionVI: // fallthrough case Op_AddReductionVL: // fallthrough case Op_AddReductionVF: // fallthrough @@ -1608,6 +1636,8 @@ Node* ReductionNode::make_identity_con_scalar(PhaseGVN& gvn, int sopc, BasicType return gvn.makecon(TypeInt::ONE); case Op_MulReductionVL: return gvn.makecon(TypeLong::ONE); + case Op_MulReductionVHF: + return gvn.makecon(TypeH::ONE); case Op_MulReductionVF: return gvn.makecon(TypeF::ONE); case Op_MulReductionVD: @@ -1700,12 +1730,14 @@ bool ReductionNode::auto_vectorization_requires_strict_order(int vopc) { // These are cases that all have associative operations, which can // thus be reordered, allowing non-strict order reductions. return false; + case Op_AddReductionVHF: + case Op_MulReductionVHF: case Op_AddReductionVF: case Op_MulReductionVF: case Op_AddReductionVD: case Op_MulReductionVD: // Floating-point addition and multiplication are non-associative, - // so AddReductionVF/D and MulReductionVF/D require strict ordering + // so AddReductionVHF/VF/VD and MulReductionVHF/VF/VD require strict ordering // in auto-vectorization. return true; default: @@ -1959,11 +1991,12 @@ Node* VectorMaskOpNode::Ideal(PhaseGVN* phase, bool can_reshape) { } Node* VectorMaskCastNode::Identity(PhaseGVN* phase) { - Node* in1 = in(1); - // VectorMaskCast (VectorMaskCast x) => x - if (in1->Opcode() == Op_VectorMaskCast && - vect_type()->eq(in1->in(1)->bottom_type())) { - return in1->in(1); + // (VectorMaskCast+ x) => (x) + // If the types of the input and output nodes in a VectorMaskCast chain are + // exactly the same, the intermediate VectorMaskCast nodes can be eliminated. + Node* n = VectorNode::uncast_mask(this); + if (vect_type()->eq(n->bottom_type())) { + return n; } return this; } @@ -2432,67 +2465,68 @@ bool MulVLNode::has_uint_inputs() const { has_vector_elements_fit_uint(in(2)); } -static Node* UMinMaxV_Ideal(Node* n, PhaseGVN* phase, bool can_reshape) { +static Node* MinMaxV_Common_Ideal(MinMaxVNode* n, PhaseGVN* phase, bool can_reshape) { int vopc = n->Opcode(); - assert(vopc == Op_UMinV || vopc == Op_UMaxV, "Unexpected opcode"); + int min_opcode = n->min_opcode(); + int max_opcode = n->max_opcode(); - Node* umin = nullptr; - Node* umax = nullptr; + Node* min_op = nullptr; + Node* max_op = nullptr; int lopc = n->in(1)->Opcode(); int ropc = n->in(2)->Opcode(); - if (lopc == Op_UMinV && ropc == Op_UMaxV) { - umin = n->in(1); - umax = n->in(2); - } else if (lopc == Op_UMaxV && ropc == Op_UMinV) { - umin = n->in(2); - umax = n->in(1); + if (lopc == min_opcode && ropc == max_opcode) { + min_op = n->in(1); + max_op = n->in(2); + } else if (lopc == max_opcode && ropc == min_opcode) { + min_op = n->in(2); + max_op = n->in(1); } else { return nullptr; } - // UMin (UMin(a, b), UMax(a, b)) => UMin(a, b) - // UMin (UMax(a, b), UMin(b, a)) => UMin(a, b) - // UMax (UMin(a, b), UMax(a, b)) => UMax(a, b) - // UMax (UMax(a, b), UMin(b, a)) => UMax(a, b) - if (umin != nullptr && umax != nullptr) { - if ((umin->in(1) == umax->in(1) && umin->in(2) == umax->in(2)) || - (umin->in(2) == umax->in(1) && umin->in(1) == umax->in(2))) { - if (vopc == Op_UMinV) { - return new UMinVNode(umax->in(1), umax->in(2), n->bottom_type()->is_vect()); - } else { - return new UMaxVNode(umax->in(1), umax->in(2), n->bottom_type()->is_vect()); + // Min (Min(a, b), Max(a, b)) => Min(a, b) + // Min (Max(a, b), Min(b, a)) => Min(a, b) + // Max (Min(a, b), Max(a, b)) => Max(a, b) + // Max (Max(a, b), Min(b, a)) => Max(a, b) + + if (min_op != nullptr && max_op != nullptr) { + // Skip if predication status is inconsistent across n, min_op, and max_op, + // or if predicated operands carry different masks. + if (n->is_predicated_vector() != min_op->is_predicated_vector() || + min_op->is_predicated_vector() != max_op->is_predicated_vector()) { + return nullptr; + } + if (min_op->is_predicated_vector() && + !(n->in(3) == min_op->in(3) && min_op->in(3) == max_op->in(3))) { + return nullptr; + } + + if ((min_op->in(1) == max_op->in(1) && min_op->in(2) == max_op->in(2)) || + (min_op->in(2) == max_op->in(1) && min_op->in(1) == max_op->in(2))) { + // Use n->in(1) inputs for the result to preserve correct merge-masking + // passthrough: inactive lanes use in(1), so result->in(1) must equal + // n->in(1)->in(1) to maintain the original passthrough semantics. + VectorNode* result = VectorNode::make(vopc, n->in(1)->in(1), n->in(1)->in(2), n->bottom_type()->is_vect()); + if (n->is_predicated_vector()) { + result->add_req(n->in(3)); + result->add_flag(Node::Flag_is_predicated_vector); } + return result; } } return nullptr; } -Node* UMinVNode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* progress = UMinMaxV_Ideal(this, phase, can_reshape); - if (progress != nullptr) return progress; - - return VectorNode::Ideal(phase, can_reshape); -} - -Node* UMinVNode::Identity(PhaseGVN* phase) { - // UMin (a, a) => a - if (in(1) == in(2)) { - return in(1); - } - return this; -} - -Node* UMaxVNode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* progress = UMinMaxV_Ideal(this, phase, can_reshape); +Node* MinMaxVNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* progress = MinMaxV_Common_Ideal(this, phase, can_reshape); if (progress != nullptr) return progress; return VectorNode::Ideal(phase, can_reshape); } -Node* UMaxVNode::Identity(PhaseGVN* phase) { - // UMax (a, a) => a +Node* MinMaxVNode::Identity(PhaseGVN* phase) { if (in(1) == in(2)) { return in(1); } @@ -2502,4 +2536,5 @@ Node* UMaxVNode::Identity(PhaseGVN* phase) { void VectorBoxAllocateNode::dump_spec(outputStream *st) const { CallStaticJavaNode::dump_spec(st); } + #endif // !PRODUCT diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index dce43f929056..91cff9fcae89 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright 2026 Arm Limited and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -195,6 +196,7 @@ class VectorNode : public TypeNode { static bool is_scalar_op_that_returns_int_but_vector_op_returns_long(int opc); static bool is_reinterpret_opcode(int opc); + static Node* uncast_mask(Node* n); static void trace_new_vector(Node* n, const char* context) { #ifdef ASSERT @@ -321,7 +323,7 @@ class ReductionNode : public Node { virtual uint size_of() const { return sizeof(*this); } // Floating-point addition and multiplication are non-associative, so - // AddReductionVF/D and MulReductionVF/D require strict ordering + // AddReductionVHF/F/D and MulReductionVHF/F/D require strict ordering // in auto-vectorization. Vector API can generate AddReductionVF/D // and MulReductionVF/VD without strict ordering, which can benefit // some platforms. @@ -358,6 +360,35 @@ class AddReductionVLNode : public ReductionNode { virtual int Opcode() const; }; +// Vector add half float as a reduction +class AddReductionVHFNode : public ReductionNode { +private: + // True if add reduction operation for half floats requires strict ordering. + // As an example - The value is true when add reduction for half floats is auto-vectorized + // as auto-vectorization mandates strict ordering but the value is false when this node + // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. + const bool _requires_strict_order; + +public: + // _requires_strict_order is set to true by default as mandated by auto-vectorization + AddReductionVHFNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : + ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} + + int Opcode() const override; + bool requires_strict_order() const override { return _requires_strict_order; } + + uint hash() const override { return Node::hash() + _requires_strict_order; } + + bool cmp(const Node& n) const override { + return Node::cmp(n) && _requires_strict_order == ((ReductionNode&)n).requires_strict_order(); + } + + uint size_of() const override { return sizeof(*this); } + + const Type* bottom_type() const override { return Type::HALF_FLOAT; } + uint ideal_reg() const override { return Op_RegF; } +}; + // Vector add float as a reduction class AddReductionVFNode : public ReductionNode { private: @@ -367,7 +398,7 @@ class AddReductionVFNode : public ReductionNode { // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. const bool _requires_strict_order; public: - //_requires_strict_order is set to true by default as mandated by auto-vectorization + // _requires_strict_order is set to true by default as mandated by auto-vectorization AddReductionVFNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} @@ -393,7 +424,7 @@ class AddReductionVDNode : public ReductionNode { // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. const bool _requires_strict_order; public: - //_requires_strict_order is set to true by default as mandated by auto-vectorization + // _requires_strict_order is set to true by default as mandated by auto-vectorization AddReductionVDNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} @@ -577,6 +608,35 @@ class MulReductionVLNode : public ReductionNode { virtual int Opcode() const; }; +// Vector multiply half float as a reduction +class MulReductionVHFNode : public ReductionNode { +private: + // True if mul reduction operation for half floats requires strict ordering. + // As an example - The value is true when mul reduction for half floats is auto-vectorized + // as auto-vectorization mandates strict ordering but the value is false when this node + // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. + const bool _requires_strict_order; + +public: + // _requires_strict_order is set to true by default as mandated by auto-vectorization + MulReductionVHFNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : + ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} + + int Opcode() const override; + bool requires_strict_order() const override { return _requires_strict_order; } + + uint hash() const override { return Node::hash() + _requires_strict_order; } + + bool cmp(const Node& n) const override { + return Node::cmp(n) && _requires_strict_order == ((ReductionNode&)n).requires_strict_order(); + } + + uint size_of() const override { return sizeof(*this); } + + const Type* bottom_type() const override { return Type::HALF_FLOAT; } + uint ideal_reg() const override { return Op_RegF; } +}; + // Vector multiply float as a reduction class MulReductionVFNode : public ReductionNode { // True if mul reduction operation for floats requires strict ordering. @@ -585,7 +645,7 @@ class MulReductionVFNode : public ReductionNode { // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. const bool _requires_strict_order; public: - //_requires_strict_order is set to true by default as mandated by auto-vectorization + // _requires_strict_order is set to true by default as mandated by auto-vectorization MulReductionVFNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} @@ -610,7 +670,7 @@ class MulReductionVDNode : public ReductionNode { // is generated through VectorAPI as VectorAPI does not impose any such rules on ordering. const bool _requires_strict_order; public: - //_requires_strict_order is set to true by default as mandated by auto-vectorization + // _requires_strict_order is set to true by default as mandated by auto-vectorization MulReductionVDNode(Node* ctrl, Node* in1, Node* in2, bool requires_strict_order = true) : ReductionNode(ctrl, in1, in2), _requires_strict_order(requires_strict_order) {} @@ -662,10 +722,22 @@ class AbsVSNode : public VectorNode { virtual int Opcode() const; }; +// Common superclass for Min/Max vector nodes +class MinMaxVNode : public VectorNode { +public: + MinMaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} + virtual int min_opcode() const = 0; + virtual int max_opcode() const = 0; + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); +}; + // Vector Min -class MinVNode : public VectorNode { +class MinVNode : public MinMaxVNode { public: - MinVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} + MinVNode(Node* in1, Node* in2, const TypeVect* vt) : MinMaxVNode(in1, in2, vt) {} + virtual int min_opcode() const { return Op_MinV; } + virtual int max_opcode() const { return Op_MaxV; } virtual int Opcode() const; }; @@ -684,31 +756,33 @@ class MaxVHFNode : public VectorNode { }; // Vector Unsigned Min -class UMinVNode : public VectorNode { +class UMinVNode : public MinMaxVNode { public: - UMinVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2 ,vt) { + UMinVNode(Node* in1, Node* in2, const TypeVect* vt) : MinMaxVNode(in1, in2, vt) { assert(is_integral_type(vt->element_basic_type()), ""); } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual Node* Identity(PhaseGVN* phase); + virtual int min_opcode() const { return Op_UMinV; } + virtual int max_opcode() const { return Op_UMaxV; } virtual int Opcode() const; }; // Vector Max -class MaxVNode : public VectorNode { +class MaxVNode : public MinMaxVNode { public: - MaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} + MaxVNode(Node* in1, Node* in2, const TypeVect* vt) : MinMaxVNode(in1, in2, vt) {} + virtual int min_opcode() const { return Op_MinV; } + virtual int max_opcode() const { return Op_MaxV; } virtual int Opcode() const; }; // Vector Unsigned Max -class UMaxVNode : public VectorNode { +class UMaxVNode : public MinMaxVNode { public: - UMaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) { + UMaxVNode(Node* in1, Node* in2, const TypeVect* vt) : MinMaxVNode(in1, in2, vt) { assert(is_integral_type(vt->element_basic_type()), ""); } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); - virtual Node* Identity(PhaseGVN* phase); + virtual int min_opcode() const { return Op_UMinV; } + virtual int max_opcode() const { return Op_UMaxV; } virtual int Opcode() const; }; diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index cb44b833c489..832c8a33c888 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -544,6 +544,11 @@ JvmtiEventControllerPrivate::recompute_env_thread_enabled(JvmtiEnvThreadState* e } switch (JvmtiEnv::get_phase()) { + case JVMTI_PHASE_ONLOAD: + case JVMTI_PHASE_PRIMORDIAL: + case JVMTI_PHASE_START: + now_enabled &= EARLY_EVENT_BITS; + break; case JVMTI_PHASE_DEAD: // no events allowed when dead now_enabled = 0; diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp index 7d80ed327fd6..5c6010acdf19 100644 --- a/src/hotspot/share/prims/vectorSupport.cpp +++ b/src/hotspot/share/prims/vectorSupport.cpp @@ -226,7 +226,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_AddL; case LT_FLOAT: return Op_AddF; case LT_DOUBLE: return Op_AddD; - default: fatal("ADD: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -238,7 +238,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_SubL; case LT_FLOAT: return Op_SubF; case LT_DOUBLE: return Op_SubD; - default: fatal("SUB: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -250,7 +250,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_MulL; case LT_FLOAT: return Op_MulF; case LT_DOUBLE: return Op_MulD; - default: fatal("MUL: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -262,7 +262,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_DivL; case LT_FLOAT: return Op_DivF; case LT_DOUBLE: return Op_DivD; - default: fatal("DIV: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -274,7 +274,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_MinL; case LT_FLOAT: return Op_MinF; case LT_DOUBLE: return Op_MinD; - default: fatal("MIN: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -286,7 +286,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_MaxL; case LT_FLOAT: return Op_MaxF; case LT_DOUBLE: return Op_MaxD; - default: fatal("MAX: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -296,7 +296,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: case LT_LONG: return Op_UMinV; - default: fatal("MIN: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -306,7 +306,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: case LT_LONG: return Op_UMaxV; - default: fatal("MAX: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -318,7 +318,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_AbsL; case LT_FLOAT: return Op_AbsF; case LT_DOUBLE: return Op_AbsD; - default: fatal("ABS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -330,7 +330,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: return Op_NegL; case LT_FLOAT: return Op_NegF; case LT_DOUBLE: return Op_NegD; - default: fatal("NEG: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -340,7 +340,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_AndI; case LT_LONG: return Op_AndL; - default: fatal("AND: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -350,7 +350,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_OrI; case LT_LONG: return Op_OrL; - default: fatal("OR: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -360,7 +360,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_XorI; case LT_LONG: return Op_XorL; - default: fatal("XOR: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -368,7 +368,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { switch (lt) { case LT_FLOAT: return Op_SqrtF; case LT_DOUBLE: return Op_SqrtD; - default: fatal("SQRT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -376,7 +376,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { switch (lt) { case LT_FLOAT: return Op_FmaF; case LT_DOUBLE: return Op_FmaD; - default: fatal("FMA: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -386,7 +386,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_LShiftI; case LT_LONG: return Op_LShiftL; - default: fatal("LSHIFT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -396,7 +396,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: return Op_RShiftI; case LT_LONG: return Op_RShiftL; - default: fatal("RSHIFT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -406,7 +406,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: return Op_URShiftS; case LT_INT: return Op_URShiftI; case LT_LONG: return Op_URShiftL; - default: fatal("URSHIFT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -416,7 +416,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: return Op_RotateLeft; - default: fatal("LROTATE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -426,7 +426,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: return Op_RotateRight; - default: fatal("RROTATE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -438,7 +438,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskLastTrue; - default: fatal("MASK_LASTTRUE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -450,7 +450,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskFirstTrue; - default: fatal("MASK_FIRSTTRUE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -462,7 +462,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskTrueCount; - default: fatal("MASK_TRUECOUNT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -474,7 +474,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_VectorMaskToLong; - default: fatal("MASK_TOLONG: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -486,7 +486,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_ExpandV; - default: fatal("EXPAND: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -498,7 +498,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_CompressV; - default: fatal("COMPRESS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -510,7 +510,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_LONG: // fall-through case LT_FLOAT: // fall-through case LT_DOUBLE: return Op_CompressM; - default: fatal("MASK_COMPRESS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -520,7 +520,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // for byte and short types temporarily case LT_INT: return Op_PopCountI; case LT_LONG: return Op_PopCountL; - default: fatal("BILT_COUNT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -530,7 +530,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: return Op_CountTrailingZerosI; case LT_LONG: return Op_CountTrailingZerosL; - default: fatal("TZ_COUNT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -540,7 +540,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: case LT_INT: return Op_CountLeadingZerosI; case LT_LONG: return Op_CountLeadingZerosL; - default: fatal("LZ_COUNT: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -550,7 +550,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // Op_ReverseI for byte and short case LT_INT: return Op_ReverseI; case LT_LONG: return Op_ReverseL; - default: fatal("REVERSE: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -565,7 +565,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_BYTE: // Intentionally fall-through case LT_INT: return Op_ReverseBytesI; case LT_LONG: return Op_ReverseBytesL; - default: fatal("REVERSE_BYTES: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -576,7 +576,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: return Op_SaturatingAddV; - default: fatal("S[U]ADD: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -587,7 +587,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case LT_SHORT: // fall-through case LT_INT: // fall-through case LT_LONG: return Op_SaturatingSubV; - default: fatal("S[U}SUB: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -595,7 +595,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { switch (lt) { case LT_INT: case LT_LONG: return Op_CompressBits; - default: fatal("COMPRESS_BITS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -603,7 +603,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { switch (lt) { case LT_INT: case LT_LONG: return Op_ExpandBits; - default: fatal("EXPAND_BITS: %s", lanetype2name(lt)); + default: return 0; } break; } @@ -627,7 +627,7 @@ int VectorSupport::vop2ideal(jint id, LaneType lt) { case VECTOR_OP_EXPM1: // fall-through case VECTOR_OP_HYPOT: return 0; // not supported; should be handled in Java code - default: fatal("unknown op: %d", vop); + default: return 0; } return 0; // Unimplemented } diff --git a/src/hotspot/share/runtime/abstract_vm_version.hpp b/src/hotspot/share/runtime/abstract_vm_version.hpp index 61a8aa840801..794fa4dabcf0 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.hpp +++ b/src/hotspot/share/runtime/abstract_vm_version.hpp @@ -55,7 +55,7 @@ enum class vmIntrinsicID; } \ } else if (Use##feature) { \ if (!FLAG_IS_DEFAULT(Use##feature)) { \ - warning(#feature " instructions not available on this CPU"); \ + warning(#feature " instructions are not available on this CPU"); \ } \ FLAG_SET_DEFAULT(Use##feature, false); \ } diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index caa847bf6168..1f2f1221e5b0 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -533,9 +533,6 @@ static SpecialFlag const special_jvm_flags[] = { { "DynamicDumpSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "RequireSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "UseSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, -#ifdef _LP64 - { "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() }, -#endif { "AggressiveHeap", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() }, @@ -546,6 +543,9 @@ static SpecialFlag const special_jvm_flags[] = { #if defined(AARCH64) { "NearCpool", JDK_Version::undefined(), JDK_Version::jdk(25), JDK_Version::undefined() }, #endif +#ifdef _LP64 + { "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() }, +#endif { "PSChunkLargeArrays", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "ParallelRefProcEnabled", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, @@ -1515,7 +1515,7 @@ void Arguments::set_heap_size() { !FLAG_IS_DEFAULT(MinRAMPercentage) || !FLAG_IS_DEFAULT(InitialRAMPercentage); - const size_t avail_mem = os::physical_memory(); + const physical_memory_size_type avail_mem = os::physical_memory(); // If the maximum heap size has not been set with -Xmx, then set it as // fraction of the size of physical memory, respecting the maximum and diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index 8f969600ba8c..d691a3c80285 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -1286,7 +1286,7 @@ class FrameValuesOopClosure: public OopClosure, public DerivedOopClosure { } bool is_good(oop* p) { - return *p == nullptr || (dbg_is_safe(*p, -1) && dbg_is_safe((*p)->klass(), -1) && oopDesc::is_oop_or_null(*p)); + return *p == nullptr || (dbg_is_safe(*p, -1) && dbg_is_safe((*p)->klass_without_asserts(), -1) && oopDesc::is_oop_or_null(*p)); } void describe(FrameValues& values, int frame_no) { for (int i = 0; i < _oops->length(); i++) { diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 39aa661b04bd..150b3fd5eb05 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1670,6 +1670,10 @@ const int ObjectAlignmentInBytes = 8; "Size of code heap with non-nmethods (in bytes)") \ constraint(VMPageSizeConstraintFunc, AtParse) \ \ + product(size_t, HotCodeHeapSize, 0, EXPERIMENTAL, \ + "Size of code heap with predicted hot methods (in bytes)") \ + range(0, SIZE_MAX) \ + \ product_pd(size_t, CodeCacheExpansionSize, \ "Code cache expansion size (in bytes)") \ range(32*K, SIZE_MAX) \ diff --git a/src/hotspot/share/runtime/hotCodeCollector.cpp b/src/hotspot/share/runtime/hotCodeCollector.cpp new file mode 100644 index 000000000000..643cf3a8bbb2 --- /dev/null +++ b/src/hotspot/share/runtime/hotCodeCollector.cpp @@ -0,0 +1,258 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifdef COMPILER2 + +#include "code/codeCache.hpp" +#include "code/compiledIC.hpp" +#include "compiler/compilerDefinitions.inline.hpp" +#include "logging/log.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/hotCodeCollector.hpp" +#include "runtime/hotCodeSampler.hpp" +#include "runtime/java.hpp" +#include "runtime/javaThread.inline.hpp" + +// Initialize static variables +bool HotCodeCollector::_is_initialized = false; +int HotCodeCollector::_new_c2_nmethods_count = 0; +int HotCodeCollector::_total_c2_nmethods_count = 0; + +HotCodeCollector::HotCodeCollector() : JavaThread(thread_entry) {} + +void HotCodeCollector::initialize() { + EXCEPTION_MARK; + + assert(HotCodeHeap, "HotCodeCollector requires HotCodeHeap enabled"); + assert(CompilerConfig::is_c2_enabled(), "HotCodeCollector requires C2 enabled"); + assert(NMethodRelocation, "HotCodeCollector requires NMethodRelocation enabled"); + assert(HotCodeHeapSize > 0, "HotCodeHeapSize must be non-zero to use HotCodeCollector"); + assert(CodeCache::get_code_heap(CodeBlobType::MethodHot) != nullptr, "MethodHot code heap not found"); + + Handle thread_oop = JavaThread::create_system_thread_object("HotCodeCollectorThread", CHECK); + HotCodeCollector* thread = new HotCodeCollector(); + JavaThread::vm_exit_on_osthread_failure(thread); + JavaThread::start_internal_daemon(THREAD, thread, thread_oop, NormPriority); + + _is_initialized = true; +} + +bool HotCodeCollector::is_nmethod_count_stable() { + if (HotCodeStablePercent < 0) { + log_info(hotcode)("HotCodeStablePercent is less than zero, stable check disabled"); + return true; + } + + MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + if (_total_c2_nmethods_count <= 0) { + log_info(hotcode)("No registered C2 nmethods"); + return false; + } + + const double percent_new = 100.0 * _new_c2_nmethods_count / _total_c2_nmethods_count; + bool is_stable_nmethod_count = percent_new <= HotCodeStablePercent; + + log_info(hotcode)("C2 nmethod count %s", is_stable_nmethod_count ? "stable" : "not stable"); + log_debug(hotcode)("C2 nmethod stats: New: %d, Total: %d, Percent new: %f", _new_c2_nmethods_count, _total_c2_nmethods_count, percent_new); + + _new_c2_nmethods_count = 0; + + return is_stable_nmethod_count; +} + +void HotCodeCollector::thread_entry(JavaThread* thread, TRAPS) { + // Initial sleep to allow JVM to warm up + thread->sleep(HotCodeStartupDelaySeconds * 1000); + + while (true) { + ResourceMark rm; + + // Sample application and group hot nmethods if nmethod count is stable + if (is_nmethod_count_stable()) { + log_info(hotcode)("Sampling..."); + + ThreadSampler sampler; + uint64_t start_time = os::javaTimeMillis(); + while (os::javaTimeMillis() - start_time <= HotCodeSampleSeconds * 1000) { + sampler.sample_all_java_threads(); + thread->sleep(rand_sampling_period_ms()); + } + + Candidates candidates(sampler); + do_grouping(candidates); + } + + thread->sleep(HotCodeIntervalSeconds * 1000); + } +} + +void HotCodeCollector::do_grouping(Candidates& candidates) { + int num_relocated = 0; + + // Sort nmethods by increasing sample count so pop() returns the hottest + candidates.sort(); + + while (candidates.has_candidates()) { + + double percent_from_hot = candidates.get_hot_sample_percent(); + log_debug(hotcode)("Percentage of samples from hot code heap: %f", percent_from_hot); + if (percent_from_hot >= HotCodeSamplePercent) { + log_info(hotcode)("Percentage of samples from hot nmethods over threshold. Done collecting hot code"); + break; + } + + nmethod* candidate = candidates.get_candidate(); + + MutexLocker ml_Compile_lock(Compile_lock); + MutexLocker ml_CompiledIC_lock(CompiledIC_lock, Mutex::_no_safepoint_check_flag); + MutexLocker ml_CodeCache_lock(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + num_relocated += do_relocation(candidate, 0); + } + + log_info(hotcode)("Collection done. Relocated %d nmethods to the MethodHot heap", num_relocated); +} + +int HotCodeCollector::do_relocation(void* candidate, uint call_level) { + if (candidate == nullptr) { + return 0; + } + + // Verify that address still points to CodeBlob + CodeBlob* blob = CodeCache::find_blob(candidate); + if (blob == nullptr) { + return 0; + } + + // Verify that blob is nmethod + nmethod* nm = blob->as_nmethod_or_null(); + if (nm == nullptr || nm->method() == nullptr) { + return 0; + } + + // The candidate may have been recompiled or already relocated. + // Retrieve the latest nmethod from the Method + nm = nm->method()->code(); + + // Verify the nmethod is still valid for relocation + if (nm == nullptr || !nm->is_in_use() || !nm->is_compiled_by_c2()) { + return 0; + } + + // Verify code heap has space + if (CodeCache::get_code_heap(CodeBlobType::MethodHot)->unallocated_capacity() < (size_t)nm->size()) { + log_info(hotcode)("Not enough free space in MethodHot heap (%zd bytes) to relocate nm (%d bytes). Bailing out", + CodeCache::get_code_heap(CodeBlobType::MethodHot)->unallocated_capacity(), nm->size()); + return 0; + } + + // Number of nmethods relocated (candidate + callees) + int num_relocated = 0; + + // Pointer to nmethod in hot heap + nmethod* hot_nm = nullptr; + + if (CodeCache::get_code_blob_type(nm) != CodeBlobType::MethodHot) { + CompiledICLocker ic_locker(nm); + hot_nm = nm->relocate(CodeBlobType::MethodHot); + + if (hot_nm != nullptr) { + // Successfully relocated nmethod. Update counts and proceed to callee relocation. + log_debug(hotcode)("Successful relocation: nmethod (%p), method (%s), call level (%d)", nm, hot_nm->method()->name_and_sig_as_C_string(), call_level); + num_relocated++; + } else { + // Relocation failed so return and do not attempt to relocate callees + log_debug(hotcode)("Failed relocation: nmethod (%p), call level (%d)", nm, call_level); + return 0; + } + } else { + // Skip relocation since already in hot heap, but still relocate callees + // since they may not have been compiled when this method was first relocated + log_debug(hotcode)("Already relocated: nmethod (%p), method (%s), call level (%d)", nm, nm->method()->name_and_sig_as_C_string(), call_level); + hot_nm = nm; + } + + assert(hot_nm != nullptr, "unable to relocate callees"); + + if (call_level < HotCodeCallLevel) { + // Loop over relocations to relocate callees + RelocIterator relocIter(hot_nm); + while (relocIter.next()) { + // Check if this is a call + Relocation* reloc = relocIter.reloc(); + if (!reloc->is_call()) { + continue; + } + + // Find the call destination address + address dest = ((CallRelocation*) reloc)->destination(); + + // Recursively relocate callees + num_relocated += do_relocation(dest, call_level + 1); + } + } + + return num_relocated; +} + +void HotCodeCollector::unregister_nmethod(nmethod* nm) { + assert_lock_strong(CodeCache_lock); + if (!_is_initialized) { + return; + } + + if (!nm->is_compiled_by_c2()) { + return; + } + + if (CodeCache::get_code_blob_type(nm) == CodeBlobType::MethodHot) { + // Nmethods in the hot code heap do not count towards total C2 nmethods. + return; + } + + // CodeCache_lock is held, so we can safely decrement the count. + _total_c2_nmethods_count--; +} + +void HotCodeCollector::register_nmethod(nmethod* nm) { + assert_lock_strong(CodeCache_lock); + if (!_is_initialized) { + return; + } + + if (!nm->is_compiled_by_c2()) { + return; // Only C2 nmethods are relocated to HotCodeHeap. + } + + if (CodeCache::get_code_blob_type(nm) == CodeBlobType::MethodHot) { + // Nmethods in the hot code heap do not count towards total C2 nmethods. + return; + } + + // CodeCache_lock is held, so we can safely increment the count. + _new_c2_nmethods_count++; + _total_c2_nmethods_count++; +} +#endif // COMPILER2 diff --git a/src/hotspot/share/runtime/hotCodeCollector.hpp b/src/hotspot/share/runtime/hotCodeCollector.hpp new file mode 100644 index 000000000000..dbefa3dc788c --- /dev/null +++ b/src/hotspot/share/runtime/hotCodeCollector.hpp @@ -0,0 +1,56 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifdef COMPILER2 +#ifndef SHARE_RUNTIME_HOTCODECOLLECTOR_HPP +#define SHARE_RUNTIME_HOTCODECOLLECTOR_HPP + +#include "runtime/javaThread.hpp" + +class Candidates; + +class HotCodeCollector : public JavaThread { + private: + static bool _is_initialized; + + static int _new_c2_nmethods_count; + static int _total_c2_nmethods_count; + + HotCodeCollector(); + + static void do_grouping(Candidates& candidates); + + static int do_relocation(void* candidate, uint call_level); + + public: + static void initialize(); + static void thread_entry(JavaThread* thread, TRAPS); + static void unregister_nmethod(nmethod* nm); + static void register_nmethod(nmethod* nm); + + static bool is_nmethod_count_stable(); +}; + +#endif // SHARE_RUNTIME_HOTCODECOLLECTOR_HPP +#endif // COMPILER2 diff --git a/src/hotspot/share/runtime/hotCodeSampler.cpp b/src/hotspot/share/runtime/hotCodeSampler.cpp new file mode 100644 index 000000000000..730a47d238aa --- /dev/null +++ b/src/hotspot/share/runtime/hotCodeSampler.cpp @@ -0,0 +1,121 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifdef COMPILER2 + +#include "code/codeCache.hpp" +#include "logging/log.hpp" +#include "runtime/hotCodeSampler.hpp" +#include "runtime/javaThread.inline.hpp" + +void ThreadSampler::sample_all_java_threads() { + // Collect samples for each JavaThread + for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { + if (jt->is_hidden_from_external_view() || + jt->in_deopt_handler() || + (jt->thread_state() != _thread_in_native && jt->thread_state() != _thread_in_Java)) { + continue; + } + + GetPCTask task(jt); + task.run(); + address pc = task.pc(); + if (pc == nullptr) { + continue; + } + + if (CodeCache::contains(pc)) { + nmethod* nm = CodeCache::find_blob(pc)->as_nmethod_or_null(); + if (nm != nullptr) { + bool created = false; + int *count = _samples.put_if_absent(nm, 0, &created); + (*count)++; + if (created) { + _samples.maybe_grow(); + } + } + } + } +} + +Candidates::Candidates(ThreadSampler& sampler) + : _hot_sample_count(0), _non_profiled_sample_count(0) { + auto func = [&](nmethod* nm, int count) { + if (CodeCache::get_code_blob_type(nm) == CodeBlobType::MethodNonProfiled) { + _candidates.append(Pair(nm, count)); + add_non_profiled_sample_count(count); + } else if (CodeCache::get_code_blob_type(nm) == CodeBlobType::MethodHot) { + add_hot_sample_count(count); + } + }; + sampler.iterate_samples(func); + + log_info(hotcode)("Generated candidate list from %d samples corresponding to %d nmethods", _non_profiled_sample_count + _hot_sample_count, _candidates.length()); +} + +void Candidates::add_candidate(nmethod* nm, int count) { + _candidates.append(Pair(nm, count)); +} + +void Candidates::add_hot_sample_count(int count) { + _hot_sample_count += count; +} + +void Candidates::add_non_profiled_sample_count(int count) { + _non_profiled_sample_count += count; +} + +void Candidates::sort() { + _candidates.sort( + [](Pair* a, Pair* b) { + if (a->second > b->second) return 1; + if (a->second < b->second) return -1; + return 0; + } + ); +} + +bool Candidates::has_candidates() { + return !_candidates.is_empty(); +} + +nmethod* Candidates::get_candidate() { + assert(has_candidates(), "must not be empty"); + Pair candidate = _candidates.pop(); + + _hot_sample_count += candidate.second; + _non_profiled_sample_count -= candidate.second; + + return candidate.first; +} + +double Candidates::get_hot_sample_percent() { + if (_hot_sample_count + _non_profiled_sample_count == 0) { + return 0; + } + + return 100.0 * _hot_sample_count / (_hot_sample_count + _non_profiled_sample_count); +} + +#endif // COMPILER2 diff --git a/src/hotspot/share/runtime/hotCodeSampler.hpp b/src/hotspot/share/runtime/hotCodeSampler.hpp new file mode 100644 index 000000000000..d61cac791e1e --- /dev/null +++ b/src/hotspot/share/runtime/hotCodeSampler.hpp @@ -0,0 +1,104 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifdef COMPILER2 +#ifndef SHARE_RUNTIME_HOTCODESAMPLER_HPP +#define SHARE_RUNTIME_HOTCODESAMPLER_HPP + +#include "runtime/javaThread.hpp" +#include "runtime/suspendedThreadTask.hpp" +#include "runtime/threadSMR.hpp" +#include "utilities/pair.hpp" +#include "utilities/resizableHashTable.hpp" + +// Generate a random sampling period between min and max +static inline uint rand_sampling_period_ms() { + assert(HotCodeMaxSamplingMs >= HotCodeMinSamplingMs, "max cannot be smaller than min"); + julong range = (julong)HotCodeMaxSamplingMs - (julong)HotCodeMinSamplingMs + 1; + return (uint)(os::random() % range) + HotCodeMinSamplingMs; +} + +class ThreadSampler; + +class Candidates : public StackObj { + private: + GrowableArray> _candidates; + int _hot_sample_count; + int _non_profiled_sample_count; + + public: + Candidates(ThreadSampler& sampler); + + void add_candidate(nmethod* nm, int count); + void add_hot_sample_count(int count); + void add_non_profiled_sample_count(int count); + void sort(); + + bool has_candidates(); + nmethod* get_candidate(); + double get_hot_sample_percent(); +}; + +class GetPCTask : public SuspendedThreadTask { + private: + address _pc; + + void do_task(const SuspendedThreadTaskContext& context) override { + JavaThread* jt = JavaThread::cast(context.thread()); + if (jt->thread_state() != _thread_in_native && jt->thread_state() != _thread_in_Java) { + return; + } + _pc = os::fetch_frame_from_context(context.ucontext(), nullptr, nullptr); + } + + public: + GetPCTask(JavaThread* thread) : SuspendedThreadTask(thread), _pc(nullptr) {} + + address pc() const { + return _pc; + } +}; + +class ThreadSampler : public StackObj { + private: + static const int INITIAL_TABLE_SIZE = 109; + + // Table of nmethods found during profiling with sample count + ResizeableHashTable _samples; + + public: + ThreadSampler() : _samples(INITIAL_TABLE_SIZE, HotCodeSampleSeconds * 1000 / HotCodeMaxSamplingMs) {} + + // Iterate over and sample all Java threads + void sample_all_java_threads(); + + // Iterate over all samples with a callback function + template + void iterate_samples(Function func) { + _samples.iterate_all(func); + } +}; + +#endif // SHARE_RUNTIME_HOTCODESAMPLER_HPP +#endif // COMPILER2 diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp index d1ce378ee20e..ed1b3ea2e78f 100644 --- a/src/hotspot/share/runtime/stubDeclarations.hpp +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -539,18 +539,19 @@ // generated. // // Architecture-specific entries need to be declared using the -// do_arch_entry template +// do_arch_entry templates // // do_arch_entry(arch, blob_name, stub_name, field_name, getter_name) // // do_arch_entry_init(arch, blob_name, stub_name, field_name, // getter_name, init_function) // +// do_arch_entry_array(arch, blob_name, stub_name, field_name, +// getter_name, count) +// // The only difference between these templates and the generic ones is // that they receive an extra argument which identifies the current // architecture e.g. x86, aarch64 etc. -// -// Currently there is no support for a do_arch_array_entry template. // Include arch-specific stub and entry declarations and make sure the // relevant template macros have been defined @@ -598,7 +599,8 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(preuniverse) \ do_stub(preuniverse, fence) \ do_entry(preuniverse, fence, fence_entry, fence_entry) \ @@ -615,7 +617,8 @@ atomic_cmpxchg_long_entry) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_PREUNIVERSE_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(preuniverse) \ #define STUBGEN_INITIAL_BLOBS_DO(do_blob, end_blob, \ @@ -623,7 +626,8 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(initial) \ do_stub(initial, call_stub) \ do_entry(initial, call_stub, call_stub_entry, call_stub_entry) \ @@ -669,7 +673,8 @@ do_entry(initial, fmod, fmod, fmod) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(initial) \ @@ -679,7 +684,8 @@ do_entry_array, \ do_arch_blob, \ do_arch_entry, \ - do_arch_entry_init) \ + do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(continuation) \ do_stub(continuation, cont_thaw) \ do_entry(continuation, cont_thaw, cont_thaw, cont_thaw) \ @@ -694,7 +700,8 @@ cont_returnBarrierExc) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(continuation) \ @@ -703,7 +710,8 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(compiler) \ do_stub(compiler, array_sort) \ do_entry(compiler, array_sort, array_sort, select_arraysort_function) \ @@ -848,7 +856,8 @@ bigIntegerLeftShiftWorker, bigIntegerLeftShift) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_COMPILER_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(compiler) \ @@ -857,7 +866,8 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ do_blob(final) \ do_stub(final, verify_oop) \ do_entry(final, verify_oop, verify_oop_subroutine_entry, \ @@ -1069,7 +1079,8 @@ lookup_secondary_supers_table_slow_path_stub) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_FINAL_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ end_blob(final) \ @@ -1082,37 +1093,43 @@ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_PREUNIVERSE_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_INITIAL_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_CONTINUATION_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_COMPILER_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_FINAL_BLOBS_DO(do_blob, end_blob, \ do_stub, \ do_entry, do_entry_init, \ do_entry_array, \ do_arch_blob, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ // Convenience macros for use by template implementations @@ -1162,6 +1179,9 @@ #define STUBGEN_COUNT5(_1, _2, _3, _4, count) \ + count +#define STUBGEN_COUNT6(_1, _2, _3, _4, _5, count) \ + + count + // Convenience templates that emit nothing // ignore do_blob(blob_name, type) declarations @@ -1200,7 +1220,8 @@ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ DO_ARCH_BLOB_EMPTY2, \ - DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macro to operate only on StubGenerator stubs @@ -1210,7 +1231,8 @@ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ DO_ARCH_BLOB_EMPTY2, \ - DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macros to operate only on StubGenerator blobs and stubs @@ -1220,18 +1242,21 @@ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ DO_ARCH_BLOB_EMPTY2, \ - DO_ARCH_ENTRY_EMPTY5,DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5,DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macro to operate only on StubGenerator generci and arch entries #define STUBGEN_ALL_ENTRIES_DO(do_entry, do_entry_init, do_entry_array, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_ALL_DO(DO_BLOB_EMPTY1, DO_BLOB_EMPTY1, \ DO_STUB_EMPTY2, \ do_entry, do_entry_init, \ do_entry_array, \ DO_ARCH_BLOB_EMPTY2, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ // client macro to operate only on StubGenerator entries @@ -1241,7 +1266,8 @@ do_entry, do_entry_init, \ do_entry_array, \ DO_ARCH_BLOB_EMPTY2, \ - DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macro to operate only on StubGenerator arch blobs @@ -1251,16 +1277,19 @@ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ do_arch_blob, \ - DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6) \ + DO_ARCH_ENTRY_EMPTY5, DO_ARCH_ENTRY_EMPTY6, \ + DO_ARCH_ENTRY_EMPTY6) \ // client macro to operate only on StubGenerator arch entries -#define STUBGEN_ARCH_ENTRIES_DO(do_arch_entry, do_arch_entry_init) \ +#define STUBGEN_ARCH_ENTRIES_DO(do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ STUBGEN_ALL_DO(DO_BLOB_EMPTY1, DO_BLOB_EMPTY1, \ DO_STUB_EMPTY2, \ DO_ENTRY_EMPTY4, DO_ENTRY_EMPTY5, \ DO_ENTRY_EMPTY5, \ DO_ARCH_BLOB_EMPTY2, \ - do_arch_entry, do_arch_entry_init) \ + do_arch_entry, do_arch_entry_init, \ + do_arch_entry_array) \ #endif // SHARE_RUNTIME_STUBDECLARATIONS_HPP diff --git a/src/hotspot/share/runtime/stubInfo.cpp b/src/hotspot/share/runtime/stubInfo.cpp index c07c0298f2bb..4d4d865cf95b 100644 --- a/src/hotspot/share/runtime/stubInfo.cpp +++ b/src/hotspot/share/runtime/stubInfo.cpp @@ -574,6 +574,18 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, field_name, id), \ 0); \ +#define PROCESS_STUBGEN_ENTRY_ARCH_ARRAY(arch_name, blob, stub, \ + field_name, getter_name, \ + count) \ + process_stubgen_entry(_group_cursor, _blob_cursor, \ + _stub_cursor, _entry_cursor, \ + #arch_name "_" # field_name "_entry (stub gen)", \ + BlobId:: JOIN3(stubgen, blob, id), \ + StubId:: JOIN3(stubgen, stub, id), \ + EntryId:: JOIN4(stubgen, arch_name, \ + field_name, id), \ + count); \ + void StubInfo::populate_stub_tables() { StubGroup _group_cursor; BlobId _blob_cursor = BlobId::NO_BLOBID; @@ -615,7 +627,8 @@ void StubInfo::populate_stub_tables() { PROCESS_STUBGEN_ENTRY, PROCESS_STUBGEN_ENTRY_INIT, PROCESS_STUBGEN_ENTRY_ARRAY, DO_ARCH_BLOB_EMPTY2, - PROCESS_STUBGEN_ENTRY_ARCH, PROCESS_STUBGEN_ENTRY_ARCH_INIT); + PROCESS_STUBGEN_ENTRY_ARCH, PROCESS_STUBGEN_ENTRY_ARCH_INIT, + PROCESS_STUBGEN_ENTRY_ARCH_ARRAY); assert(next(_blob_cursor) == BlobId::NUM_BLOBIDS, "should have exhausted all blob ids!"); assert(next(_stub_cursor) == StubId::NUM_STUBIDS, "should have exhausted all stub ids!"); assert(next(_entry_cursor) == EntryId::NUM_ENTRYIDS, "should have exhausted all entry ids!"); @@ -636,6 +649,7 @@ void StubInfo::populate_stub_tables() { #undef PROCESS_STUBGEN_ENTRY_ARRAY #undef PROCESS_STUBGEN_ENTRY_ARCH #undef PROCESS_STUBGEN_ENTRY_ARCH_INIT +#undef PROCESS_STUBGEN_ENTRY_ARCH_ARRAY #ifdef ASSERT diff --git a/src/hotspot/share/runtime/stubInfo.hpp b/src/hotspot/share/runtime/stubInfo.hpp index 447f7d3a5825..2fe503a8d0eb 100644 --- a/src/hotspot/share/runtime/stubInfo.hpp +++ b/src/hotspot/share/runtime/stubInfo.hpp @@ -349,6 +349,14 @@ enum class StubId : int { init_function) \ JOIN4(stubgen, arch_name, field_name, id), \ +#define STUBGEN_DECLARE_ARCH_ARRAY_TAG(arch_name, blob_name, stub_name, \ + field_name, getter_name, \ + count) \ + JOIN4(stubgen, arch_name, field_name, id), \ + JOIN4(stubgen, arch_name, field_name, max) = \ + JOIN4(stubgen, arch_name, field_name, id) + \ + count - 1, \ + // the above macros are enough to declare the enum enum class EntryId : int { @@ -366,7 +374,8 @@ enum class EntryId : int { STUBGEN_DECLARE_INIT_TAG, STUBGEN_DECLARE_ARRAY_TAG, STUBGEN_DECLARE_ARCH_TAG, - STUBGEN_DECLARE_ARCH_INIT_TAG) + STUBGEN_DECLARE_ARCH_INIT_TAG, + STUBGEN_DECLARE_ARCH_ARRAY_TAG) NUM_ENTRYIDS }; @@ -379,6 +388,7 @@ enum class EntryId : int { #undef STUBGEN_DECLARE_ARRAY_TAG #undef STUBGEN_DECLARE_ARCH_TAG #undef STUBGEN_DECLARE_ARCH_INIT_TAG +#undef STUBGEN_DECLARE_ARCH_ARRAY_TAG // we need static init expressions for blob, stub and entry counts in // each stubgroup @@ -404,7 +414,8 @@ enum class EntryId : int { #define STUBGEN_ENTRY_COUNT_INITIALIZER \ 0 STUBGEN_ALL_ENTRIES_DO(COUNT4, COUNT5, \ STUBGEN_COUNT5, \ - COUNT5, COUNT6) + COUNT5, COUNT6, \ + STUBGEN_COUNT6) // Declare management class StubInfo diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 0bc71c654718..f5509b9d9964 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -178,18 +178,23 @@ static BufferBlob* initialize_stubs(BlobId blob_id, AOTStubData* stub_data_p = nullptr; LogTarget(Info, stubs) lt; + // we need to track and publish details of stubs in a stubgen blob + // when we are 1) using stubs from the cache 2) dumping stubs to the + // cache 3) generating stubs that may be needed by other cache + // elements. + + if (stub_data.is_open()) { + stub_data_p = &stub_data; + } if (code_size > 0 && stub_data.is_using()) { - // AOTCodeEntry tracks and logs status of any cached blob - bool loaded = stub_data.load_code_blob(); - if (loaded) { + // try to load the blob and details of its stubs from cache. if + // that fails we will still generate all necessary stubs + if (stub_data.load_code_blob()) { if (lt.is_enabled()) { LogStream ls(lt); ls.print_cr("Found blob %s in AOT cache", StubInfo::name(blob_id)); } - stub_data_p = &stub_data; } - } else if (stub_data.is_dumping()) { - stub_data_p = &stub_data; } // Even if we managed to load a blob from the AOT cache we still @@ -236,17 +241,8 @@ static BufferBlob* initialize_stubs(BlobId blob_id, "increase %s, code_size: %d, used: %d, free: %d", assert_msg, code_size, buffer.total_content_size(), buffer.insts_remaining()); - if (stub_data.is_using()) { - // we generated some new entries so republish all entries TODO - - // ensure we publish collect and publish the preuniverse stubs but - // don't try to save them - AOTCodeCache::publish_stub_addresses(*stubs_code, blob_id, &stub_data); - if (lt.is_enabled()) { - LogStream ls(lt); - ls.print_cr("Republished entries for blob '%s'", buffer_name); - } - } else if (stub_data.is_dumping()) { - // save the blob and publihs the entry addresses + if (stub_data.is_dumping()) { + // save the blob and publish the entry addresses if (stub_data.store_code_blob(*stubs_code, &buffer)) { if (lt.is_enabled()) { LogStream ls(lt); @@ -258,6 +254,17 @@ static BufferBlob* initialize_stubs(BlobId blob_id, ls.print_cr("Failed to store blob '%s' to Startup Code Cache", buffer_name); } } + } else if (stub_data.is_open()) { + // we either loaded some entries or generated new entries so + // publish all entries + // + // TODO - ensure we publish collect and publish the preuniverse + // stubs but don't try to save them + AOTCodeCache::publish_stub_addresses(*stubs_code, blob_id, &stub_data); + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("Republished entries for blob '%s'", buffer_name); + } } // close off recording of any further stubgen generation diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index c2fb224ac20d..71b8930255c3 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -113,6 +113,7 @@ #endif #ifdef COMPILER2 #include "opto/idealGraphPrinter.hpp" +#include "runtime/hotCodeCollector.hpp" #endif #if INCLUDE_JFR #include "jfr/jfr.hpp" @@ -818,6 +819,12 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { StringDedup::start(); } +#ifdef COMPILER2 + if (HotCodeHeap) { + HotCodeCollector::initialize(); + } +#endif // COMPILER2 + // Pre-initialize some JSR292 core classes to avoid deadlock during class loading. // It is done after compilers are initialized, because otherwise compilations of // signature polymorphic MH intrinsics can be missed diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index 837033ed1aab..a9792078b508 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -106,6 +106,7 @@ void DCmd::register_dcmds() { DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export)); @@ -383,8 +384,8 @@ void JVMTIJmcAgentLoadDCmd::execute(DCmdSource source, TRAPS) { #endif // INCLUDE_JVMTI #endif // INCLUDE_SERVICES -void PrintSystemPropertiesDCmd::execute(DCmdSource source, TRAPS) { - // load VMSupport +// helper method for printing system and security properties +static void print_properties(Symbol* method_name, outputStream* out, TRAPS) { Symbol* klass = vmSymbols::jdk_internal_vm_VMSupport(); Klass* k = SystemDictionary::resolve_or_fail(klass, true, CHECK); InstanceKlass* ik = InstanceKlass::cast(k); @@ -392,39 +393,36 @@ void PrintSystemPropertiesDCmd::execute(DCmdSource source, TRAPS) { ik->initialize(THREAD); } if (HAS_PENDING_EXCEPTION) { - java_lang_Throwable::print(PENDING_EXCEPTION, output()); - output()->cr(); + java_lang_Throwable::print(PENDING_EXCEPTION, out); + out->cr(); CLEAR_PENDING_EXCEPTION; return; } - - // invoke the serializePropertiesToByteArray method JavaValue result(T_OBJECT); JavaCallArguments args; - Symbol* signature = vmSymbols::void_byte_array_signature(); - JavaCalls::call_static(&result, - ik, - vmSymbols::serializePropertiesToByteArray_name(), - signature, - &args, - THREAD); + JavaCalls::call_static(&result, ik, method_name, signature, &args, THREAD); + if (HAS_PENDING_EXCEPTION) { - java_lang_Throwable::print(PENDING_EXCEPTION, output()); - output()->cr(); + java_lang_Throwable::print(PENDING_EXCEPTION, out); + out->cr(); CLEAR_PENDING_EXCEPTION; return; } - - // The result should be a [B oop res = result.get_oop(); - assert(res->is_typeArray(), "just checking"); - assert(TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "just checking"); - - // copy the bytes to the output stream + assert(res->is_typeArray(), "should be a byte array"); + assert(TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "should be a byte array"); typeArrayOop ba = typeArrayOop(res); - jbyte* addr = typeArrayOop(res)->byte_at_addr(0); - output()->print_raw((const char*)addr, ba->length()); + jbyte* addr = ba->byte_at_addr(0); + out->print_raw((const char*)addr, ba->length()); +} + +void PrintSystemPropertiesDCmd::execute(DCmdSource source, TRAPS) { + print_properties(vmSymbols::serializePropertiesToByteArray_name(), output(), THREAD); +} + +void PrintSecurityPropertiesDCmd::execute(DCmdSource source, TRAPS) { + print_properties(vmSymbols::serializeSecurityPropertiesToByteArray_name(), output(), THREAD); } VMUptimeDCmd::VMUptimeDCmd(outputStream* output, bool heap) : diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index c901e8ee87a9..76c45f4d438d 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -94,6 +94,15 @@ class PrintSystemPropertiesDCmd : public DCmd { virtual void execute(DCmdSource source, TRAPS); }; +class PrintSecurityPropertiesDCmd : public DCmd { +public: + PrintSecurityPropertiesDCmd(outputStream* output, bool heap) : DCmd(output, heap) { } + static const char* name() { return "VM.security_properties"; } + static const char* description() { return "Print java.security.Security properties."; } + static const char* impact() { return "Low"; } + virtual void execute(DCmdSource source, TRAPS); +}; + // See also: print_flag in attachListener.cpp class PrintVMFlagsDCmd : public DCmdWithParser { protected: diff --git a/src/hotspot/share/utilities/growableArray.cpp b/src/hotspot/share/utilities/growableArray.cpp index 6a1cb0b04142..9cc0813a1f64 100644 --- a/src/hotspot/share/utilities/growableArray.cpp +++ b/src/hotspot/share/utilities/growableArray.cpp @@ -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 @@ -22,6 +22,7 @@ * */ +#include "cds/aotMetaspace.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/javaThread.hpp" @@ -56,7 +57,9 @@ void* GrowableArrayCHeapAllocator::allocate(int max, int element_size, MemTag me } void GrowableArrayCHeapAllocator::deallocate(void* elements) { - FreeHeap(elements); + if (!AOTMetaspace::in_aot_cache(elements)) { + FreeHeap(elements); + } } #ifdef ASSERT diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index e300bea6993f..14b54cfc4ea3 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -116,12 +116,6 @@ class GrowableArrayView : public GrowableArrayBase { ~GrowableArrayView() {} -protected: - // Used by AOTGrowableArray for MetaspaceClosure support. - E** data_addr() { - return &_data; - } - public: bool operator==(const GrowableArrayView& rhs) const { if (_len != rhs._len) @@ -303,6 +297,11 @@ class GrowableArrayView : public GrowableArrayBase { } tty->print("}\n"); } + + // MetaspaceClosure support + E** data_addr() { + return &_data; + } }; template @@ -821,6 +820,8 @@ class GrowableArray : public GrowableArrayWithAllocator> { this->clear_and_deallocate(); } } + + void assert_on_C_heap() { assert(on_C_heap(), "must be on C heap"); } }; // Leaner GrowableArray for CHeap backed data arrays, with compile-time decided MemTag. diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 5e3382f2b696..935854a56be4 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -1357,13 +1357,13 @@ void VMError::report(outputStream* st, bool _verbose) { STEP_IF("printing OS information", _verbose) os::print_os_info(st); - st->cr(); #ifdef __APPLE__ // Avoid large stack allocation on Mac for FD count during signal-handling. os::Bsd::print_open_file_descriptors(st, buf, sizeof(buf)); st->cr(); #else os::print_open_file_descriptors(st); + st->cr(); #endif STEP_IF("printing CPU info", _verbose) @@ -1598,7 +1598,6 @@ void VMError::print_vm_info(outputStream* st) { // STEP("printing OS information") os::print_os_info(st); - st->cr(); os::print_open_file_descriptors(st); st->cr(); diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java index 88bdb99dfd60..df46ffe6ca6a 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -644,12 +644,9 @@ protected Object clone() throws CloneNotSupportedException { * {@code null}, this method has no effect. * @since 9 */ - @ForceInline + @IntrinsicCandidate public static void reachabilityFence(Object ref) { - // Does nothing. This method is annotated with @ForceInline to eliminate - // most of the overhead that using @DontInline would cause with the - // HotSpot JVM, when this fence is used in a wide variety of situations. - // HotSpot JVM retains the ref and does not GC it before a call to - // this method, because the JIT-compilers do not have GC-only safepoints. + // Does nothing. HotSpot JVM retains the ref and does not GC it before a call to this method. + // Using an intrinsic allows JIT-compilers to further optimize it while retaining the correct semantics. } } diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 30a22b05742d..9faa172c8e7a 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -330,6 +330,10 @@ private static void debugLoad(boolean start, Object source) { public Properties getInitialProperties() { return initialSecurityProperties; } + @Override + public Properties getCurrentProperties() { + return props; + } }); } diff --git a/src/java.base/share/classes/java/util/ServiceLoader.java b/src/java.base/share/classes/java/util/ServiceLoader.java index 5137adc1c080..5e4fa4ed2eff 100644 --- a/src/java.base/share/classes/java/util/ServiceLoader.java +++ b/src/java.base/share/classes/java/util/ServiceLoader.java @@ -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 @@ -621,9 +621,9 @@ private Constructor getConstructor(Class clazz) { Constructor ctor = null; try { ctor = clazz.getConstructor(); - } catch (NoSuchMethodException ex) { + } catch (NoSuchMethodException | LinkageError e) { String cn = clazz.getName(); - fail(service, cn + " Unable to get public no-arg constructor", ex); + fail(service, cn + " Unable to get public no-arg constructor", e); } if (inExplicitModule(clazz)) ctor.setAccessible(true); @@ -1086,8 +1086,8 @@ private Class nextProviderClass() { String cn = pending.next(); try { return Class.forName(cn, false, loader); - } catch (ClassNotFoundException x) { - fail(service, "Provider " + cn + " not found"); + } catch (ClassNotFoundException | LinkageError e) { + fail(service, "Provider " + cn + " not found", e); return null; } } diff --git a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java index ebcb9e3204ce..72fb8036f08e 100644 --- a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java +++ b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, 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 @@ -79,11 +79,7 @@ public GZIPInputStream(InputStream in, int size) throws IOException { super(in, createInflater(in, size), size); usesDefaultInflater = true; try { - // we don't expect the stream to be at EOF - // and if it is, then we want readHeader to - // raise an exception, so we pass "true" for - // the "failOnEOF" param. - readHeader(in, true); + readHeader(in); } catch (IOException ioe) { this.inf.end(); throw ioe; @@ -194,40 +190,12 @@ public void close() throws IOException { /* * Reads GZIP member header and returns the total byte number * of this member header. - * If failOnEOF is false and if the given InputStream has already - * reached EOF when this method was invoked, then this method returns - * -1 (indicating that there's no GZIP member header). - * In all other cases of malformed header or EOF being detected - * when reading the header, this method will throw an IOException. */ - private int readHeader(InputStream this_in, boolean failOnEOF) throws IOException { + private int readHeader(InputStream this_in) throws IOException { CheckedInputStream in = new CheckedInputStream(this_in, crc); crc.reset(); - - int magic; - if (!failOnEOF) { - // read an unsigned short value representing the GZIP magic header. - // this is the same as calling readUShort(in), except that here, - // when reading the first byte, we don't raise an EOFException - // if the stream has already reached EOF. - - // read unsigned byte - int b = in.read(); - if (b == -1) { // EOF - crc.reset(); - return -1; // represents no header bytes available - } - checkUnexpectedByte(b); - // read the next unsigned byte to form the unsigned - // short. we throw the usual EOFException/ZipException - // from this point on if there is no more data or - // the data doesn't represent a header. - magic = (readUByte(in) << 8) | b; - } else { - magic = readUShort(in); - } // Check header magic - if (magic != GZIP_MAGIC) { + if (readUShort(in) != GZIP_MAGIC) { throw new ZipException("Not in GZIP format"); } // Check compression method @@ -290,21 +258,23 @@ public void close() throws IOException {} (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL))) throw new ZipException("Corrupt GZIP trailer"); + // If there are more bytes available in "in" or + // the leftover in the "inf" is > 26 bytes: + // this.trailer(8) + next.header.min(10) + next.trailer(8) // try concatenated case - int m = 8; // this.trailer - try { - int numNextHeaderBytes = readHeader(in, false); // next.header (if available) - if (numNextHeaderBytes == -1) { - return true; // end of stream reached + if (this.in.available() > 0 || n > 26) { + int m = 8; // this.trailer + try { + m += readHeader(in); // next.header + } catch (IOException ze) { + return true; // ignore any malformed, do nothing } - m += numNextHeaderBytes; - } catch (IOException ze) { - return true; // ignore any malformed, do nothing + inf.reset(); + if (n > m) + inf.setInput(buf, len - n + m, n - m); + return false; } - inf.reset(); - if (n > m) - inf.setInput(buf, len - n + m, n - m); - return false; + return true; } /* @@ -331,16 +301,12 @@ private int readUByte(InputStream in) throws IOException { if (b == -1) { throw new EOFException(); } - checkUnexpectedByte(b); - return b; - } - - private void checkUnexpectedByte(final int b) throws IOException { if (b < -1 || b > 255) { - // report the InputStream type which returned this unexpected byte + // Report on this.in, not argument in; see read{Header, Trailer}. throw new IOException(this.in.getClass().getName() - + ".read() returned value out of range -1..255: " + b); + + ".read() returned value out of range -1..255: " + b); } + return b; } private byte[] tmpbuf = new byte[128]; diff --git a/src/java.base/share/classes/java/util/zip/ZipEntry.java b/src/java.base/share/classes/java/util/zip/ZipEntry.java index bf0bf55ff98a..0206d2a51549 100644 --- a/src/java.base/share/classes/java/util/zip/ZipEntry.java +++ b/src/java.base/share/classes/java/util/zip/ZipEntry.java @@ -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 @@ -651,8 +651,9 @@ public byte[] getExtra() { } /** - * Sets the optional comment string for the entry. - * @param comment the comment string + * Sets the optional comment string for the entry. If {@code comment} is an + * empty string or {@code null} then the entry will have no comment. + * @param comment the comment string, or an empty string or null for no comment * @throws IllegalArgumentException if the combined length * of the specified entry comment, the {@linkplain #getName() entry name}, * the {@linkplain #getExtra() extra field data}, and the diff --git a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java index 4ea4a103feff..d79b0a1bd9c2 100644 --- a/src/java.base/share/classes/java/util/zip/ZipOutputStream.java +++ b/src/java.base/share/classes/java/util/zip/ZipOutputStream.java @@ -43,6 +43,10 @@ *

Unless otherwise noted, passing a {@code null} argument to a constructor * or method in this class will cause a {@link NullPointerException} to be * thrown. + *

By default, the UTF-8 charset is used to encode entry names and comments. + * {@link #ZipOutputStream(OutputStream, Charset)} may be be used to specify + * an alternative charset. + * * @author David Connelly * @since 1.1 */ @@ -110,10 +114,8 @@ private void ensureOpen() throws IOException { public static final int DEFLATED = ZipEntry.DEFLATED; /** - * Creates a new ZIP output stream. - * - *

The UTF-8 {@link java.nio.charset.Charset charset} is used - * to encode the entry names and comments. + * Creates a new ZIP output stream using the UTF-8 + * {@link Charset charset} to encode entry names and comments. * * @param out the actual output stream */ @@ -122,12 +124,13 @@ public ZipOutputStream(OutputStream out) { } /** - * Creates a new ZIP output stream. + * Creates a new ZIP output stream using the specified + * {@link Charset charset} to encode entry names and comments. * * @param out the actual output stream * * @param charset the {@linkplain java.nio.charset.Charset charset} - * to be used to encode the entry names and comments + * to be used to encode entry names and comments * * @since 1.7 */ @@ -140,10 +143,15 @@ public ZipOutputStream(OutputStream out, Charset charset) { } /** - * Sets the ZIP file comment. - * @param comment the comment string - * @throws IllegalArgumentException if the length of the specified - * ZIP file comment is greater than 0xFFFF bytes + * Sets the ZIP file comment. If {@code comment} is an empty string or + * {@code null} then the output will have no ZIP file comment. + * + * @param comment the comment string, or an empty string or null for no comment + * + * @throws IllegalArgumentException if the length of the specified ZIP file + * comment is greater than 0xFFFF bytes or if the {@code comment} + * contains characters that cannot be mapped by the {@code Charset} + * used to encode entry names and comments */ public void setComment(String comment) { byte[] bytes = null; diff --git a/src/java.base/share/classes/jdk/internal/access/JavaSecurityPropertiesAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaSecurityPropertiesAccess.java index a4875f357e37..2d9dbea052af 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaSecurityPropertiesAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaSecurityPropertiesAccess.java @@ -29,4 +29,5 @@ public interface JavaSecurityPropertiesAccess { Properties getInitialProperties(); + Properties getCurrentProperties(); } diff --git a/src/java.base/share/classes/jdk/internal/vm/VMSupport.java b/src/java.base/share/classes/jdk/internal/vm/VMSupport.java index 197da0d456c4..32c358340af3 100644 --- a/src/java.base/share/classes/jdk/internal/vm/VMSupport.java +++ b/src/java.base/share/classes/jdk/internal/vm/VMSupport.java @@ -98,6 +98,11 @@ public static byte[] serializePropertiesToByteArray() throws IOException { return serializePropertiesToByteArray(onlyStrings(System.getProperties())); } + public static byte[] serializeSecurityPropertiesToByteArray() throws IOException { + Properties p = SharedSecrets.getJavaSecurityPropertiesAccess().getCurrentProperties(); + return serializePropertiesToByteArray(onlyStrings(p)); + } + public static byte[] serializeAgentPropertiesToByteArray() throws IOException { return serializePropertiesToByteArray(onlyStrings(getAgentProperties())); } diff --git a/src/java.base/share/classes/sun/security/provider/DigestBase.java b/src/java.base/share/classes/sun/security/provider/DigestBase.java index 2aaf0a2fac6f..0bb15ef3efe6 100644 --- a/src/java.base/share/classes/sun/security/provider/DigestBase.java +++ b/src/java.base/share/classes/sun/security/provider/DigestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, 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 @@ -242,4 +242,21 @@ public Object clone() throws CloneNotSupportedException { padding = new byte[136]; padding[0] = (byte)0x80; } + + /** + * Digest block-length bytes in a single operation. + * Subclasses are expected to override this method. It is intended + * for fixed-length short input where input includes padding bytes. + * @param input byte array to be digested + * @param inLen the length of the input + * @param output the output buffer + * @param outOffset the offset into output buffer where digest should be written + * @param outLen the length of the output buffer + * @throws UnsupportedOperationException if a subclass does not override this method + */ + void implDigestFixedLengthPreprocessed ( + byte[] input, int inLen, byte[] output, int outOffset, int outLen) + throws UnsupportedOperationException { + throw new UnsupportedOperationException("should not be here"); + } } diff --git a/src/java.base/share/classes/sun/security/provider/HSS.java b/src/java.base/share/classes/sun/security/provider/HSS.java index c1cb5ed6a308..50afba7cab88 100644 --- a/src/java.base/share/classes/sun/security/provider/HSS.java +++ b/src/java.base/share/classes/sun/security/provider/HSS.java @@ -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 @@ -24,16 +24,22 @@ */ package sun.security.provider; +import java.io.ByteArrayOutputStream; +import java.io.InvalidObjectException; +import java.io.Serial; +import java.io.Serializable; +import java.security.SecureRandom; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; + import sun.security.util.*; import sun.security.x509.AlgorithmId; import sun.security.x509.X509Key; -import java.io.*; -import java.security.*; -import java.security.SecureRandom; -import java.security.spec.*; -import java.util.Arrays; - /** * Implementation of the Hierarchical Signature System using the * Leighton-Micali Signatures (HSS/LMS) as described in RFC 8554 and @@ -196,42 +202,94 @@ int keyArrayLength() { static class LMSUtils { static final int LMS_RESERVED = 0; - static final int LMS_SHA256_M32_H5 = 5; - static final int LMS_SHA256_M32_H10 = 6; - static final int LMS_SHA256_M32_H15 = 7; - static final int LMS_SHA256_M32_H20 = 8; - static final int LMS_SHA256_M32_H25 = 9; + static final int LMS_SHA256_M32_H5 = 0x05; + static final int LMS_SHA256_M32_H10 = 0x06; + static final int LMS_SHA256_M32_H15 = 0x07; + static final int LMS_SHA256_M32_H20 = 0x08; + static final int LMS_SHA256_M32_H25 = 0x09; + static final int LMS_SHA256_M24_H5 = 0x0a; + static final int LMS_SHA256_M24_H10 = 0x0b; + static final int LMS_SHA256_M24_H15 = 0x0c; + static final int LMS_SHA256_M24_H20 = 0x0d; + static final int LMS_SHA256_M24_H25 = 0x0e; + static final int LMS_SHAKE_M32_H5 = 0x0f; + static final int LMS_SHAKE_M32_H10 = 0x10; + static final int LMS_SHAKE_M32_H15 = 0x11; + static final int LMS_SHAKE_M32_H20 = 0x12; + static final int LMS_SHAKE_M32_H25 = 0x13; + static final int LMS_SHAKE_M24_H5 = 0x14; + static final int LMS_SHAKE_M24_H10 = 0x15; + static final int LMS_SHAKE_M24_H15 = 0x16; + static final int LMS_SHAKE_M24_H20 = 0x17; + static final int LMS_SHAKE_M24_H25 = 0x18; static String lmsType(int type) { - String typeStr; - switch (type) { - case LMS_RESERVED: typeStr = "LMS_RESERVED"; break; - case LMS_SHA256_M32_H5: typeStr = "LMS_SHA256_M32_H5"; break; - case LMS_SHA256_M32_H10: typeStr = "LMS_SHA256_M32_H10"; break; - case LMS_SHA256_M32_H15: typeStr = "LMS_SHA256_M32_H15"; break; - case LMS_SHA256_M32_H20: typeStr = "LMS_SHA256_M32_H20"; break; - case LMS_SHA256_M32_H25: typeStr = "LMS_SHA256_M32_H25"; break; - default: typeStr = "unrecognized"; - } + String typeStr = switch (type) { + case LMS_RESERVED -> "LMS_RESERVED"; + case LMS_SHA256_M32_H5 -> "LMS_SHA256_M32_H5"; + case LMS_SHA256_M32_H10 -> "LMS_SHA256_M32_H10"; + case LMS_SHA256_M32_H15 -> "LMS_SHA256_M32_H15"; + case LMS_SHA256_M32_H20 -> "LMS_SHA256_M32_H20"; + case LMS_SHA256_M32_H25 -> "LMS_SHA256_M32_H25"; + case LMS_SHA256_M24_H5 -> "LMS_SHA256_M24_H5"; + case LMS_SHA256_M24_H10 -> "LMS_SHA256_M24_H10"; + case LMS_SHA256_M24_H15 -> "LMS_SHA256_M24_H15"; + case LMS_SHA256_M24_H20 -> "LMS_SHA256_M24_H20"; + case LMS_SHA256_M24_H25 -> "LMS_SHA256_M24_H25"; + case LMS_SHAKE_M32_H5 -> "LMS_SHAKE_M32_H5"; + case LMS_SHAKE_M32_H10 -> "LMS_SHAKE_M32_H10"; + case LMS_SHAKE_M32_H15 -> "LMS_SHAKE_M32_H15"; + case LMS_SHAKE_M32_H20 -> "LMS_SHAKE_M32_H20"; + case LMS_SHAKE_M32_H25 -> "LMS_SHAKE_M32_H25"; + case LMS_SHAKE_M24_H5 -> "LMS_SHAKE_M24_H5"; + case LMS_SHAKE_M24_H10 -> "LMS_SHAKE_M24_H10"; + case LMS_SHAKE_M24_H15 -> "LMS_SHAKE_M24_H15"; + case LMS_SHAKE_M24_H20 -> "LMS_SHAKE_M24_H20"; + case LMS_SHAKE_M24_H25 -> "LMS_SHAKE_M24_H25"; + default -> "unrecognized"; + }; return typeStr; } static final int LMOTS_RESERVED = 0; - static final int LMOTS_SHA256_N32_W1 = 1; - static final int LMOTS_SHA256_N32_W2 = 2; - static final int LMOTS_SHA256_N32_W4 = 3; - static final int LMOTS_SHA256_N32_W8 = 4; + static final int LMOTS_SHA256_N32_W1 = 0x01; + static final int LMOTS_SHA256_N32_W2 = 0x02; + static final int LMOTS_SHA256_N32_W4 = 0x03; + static final int LMOTS_SHA256_N32_W8 = 0x04; + static final int LMOTS_SHA256_N24_W1 = 0x05; + static final int LMOTS_SHA256_N24_W2 = 0x06; + static final int LMOTS_SHA256_N24_W4 = 0x07; + static final int LMOTS_SHA256_N24_W8 = 0x08; + static final int LMOTS_SHAKE_N32_W1 = 0x09; + static final int LMOTS_SHAKE_N32_W2 = 0x0a; + static final int LMOTS_SHAKE_N32_W4 = 0x0b; + static final int LMOTS_SHAKE_N32_W8 = 0x0c; + static final int LMOTS_SHAKE_N24_W1 = 0x0d; + static final int LMOTS_SHAKE_N24_W2 = 0x0e; + static final int LMOTS_SHAKE_N24_W4 = 0x0f; + static final int LMOTS_SHAKE_N24_W8 = 0x10; static String lmotsType(int type) { - String typeStr; - switch (type) { - case LMOTS_RESERVED: typeStr = "LMOTS_RESERVED"; break; - case LMOTS_SHA256_N32_W1: typeStr = "LMOTS_SHA256_N32_W1"; break; - case LMOTS_SHA256_N32_W2: typeStr = "LMOTS_SHA256_N32_W2"; break; - case LMOTS_SHA256_N32_W4: typeStr = "LMOTS_SHA256_N32_W4"; break; - case LMOTS_SHA256_N32_W8: typeStr = "LMOTS_SHA256_N32_W8"; break; - default: typeStr = "unrecognized"; - } + String typeStr = switch (type) { + case LMOTS_RESERVED -> "LMOTS_RESERVED"; + case LMOTS_SHA256_N32_W1 -> "LMOTS_SHA256_N32_W1"; + case LMOTS_SHA256_N32_W2 -> "LMOTS_SHA256_N32_W2"; + case LMOTS_SHA256_N32_W4 -> "LMOTS_SHA256_N32_W4"; + case LMOTS_SHA256_N32_W8 -> "LMOTS_SHA256_N32_W8"; + case LMOTS_SHA256_N24_W1 -> "LMOTS_SHA256_N24_W1"; + case LMOTS_SHA256_N24_W2 -> "LMOTS_SHA256_N24_W2"; + case LMOTS_SHA256_N24_W4 -> "LMOTS_SHA256_N24_W4"; + case LMOTS_SHA256_N24_W8 -> "LMOTS_SHA256_N24_W8"; + case LMOTS_SHAKE_N32_W1 -> "LMOTS_SHAKE_N32_W1"; + case LMOTS_SHAKE_N32_W2 -> "LMOTS_SHAKE_N32_W2"; + case LMOTS_SHAKE_N32_W4 -> "LMOTS_SHAKE_N32_W4"; + case LMOTS_SHAKE_N32_W8 -> "LMOTS_SHAKE_N32_W8"; + case LMOTS_SHAKE_N24_W1 -> "LMOTS_SHAKE_N24_W1"; + case LMOTS_SHAKE_N24_W2 -> "LMOTS_SHAKE_N24_W2"; + case LMOTS_SHAKE_N24_W4 -> "LMOTS_SHAKE_N24_W4"; + case LMOTS_SHAKE_N24_W8 -> "LMOTS_SHAKE_N24_W8"; + default -> "unrecognized"; + }; return typeStr; } @@ -352,53 +410,65 @@ void getY(int i, byte[] arr, int pos) { static class LMSParams { final int m; // the number of bytes used from the hash output - final int hashAlg_m = 32; // output length of the LMS tree hash function + final int hashAlg_m; // output length of the LMS tree hash function final int h; // height of the LMS tree final int twoPowh; final String hashAlgStr; - LMSParams(int m, int h, String hashAlgStr) { + private LMSParams(int m, int h, String hashAlgStr, int hashAlg_m) { this.m = m; this.h = h; this.hashAlgStr = hashAlgStr; + this.hashAlg_m = hashAlg_m; twoPowh = 1 << h; } static LMSParams of(int type) { - int m; - int h; - String hashAlgStr; - switch (type) { - case LMSUtils.LMS_SHA256_M32_H5: - m = 32; - h = 5; - hashAlgStr = "SHA-256"; - break; - case LMSUtils.LMS_SHA256_M32_H10: - m = 32; - h = 10; - hashAlgStr = "SHA-256"; - break; - case LMSUtils.LMS_SHA256_M32_H15: - m = 32; - h = 15; - hashAlgStr = "SHA-256"; - break; - case LMSUtils.LMS_SHA256_M32_H20: - m = 32; - h = 20; - hashAlgStr = "SHA-256"; - break; - case LMSUtils.LMS_SHA256_M32_H25: - m = 32; - h = 25; - hashAlgStr = "SHA-256"; - break; - default: + LMSParams params = switch (type) { + case LMSUtils.LMS_SHA256_M32_H5 -> + new LMSParams(32, 5, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M32_H10 -> + new LMSParams(32, 10, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M32_H15 -> + new LMSParams(32, 15, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M32_H20 -> + new LMSParams(32, 20, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M32_H25 -> + new LMSParams(32, 25, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H5 -> + new LMSParams(24, 5, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H10 -> + new LMSParams(24, 10, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H15 -> + new LMSParams(24, 15, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H20 -> + new LMSParams(24, 20, "SHA-256", 32); + case LMSUtils.LMS_SHA256_M24_H25 -> + new LMSParams(24, 25, "SHA-256", 32); + case LMSUtils.LMS_SHAKE_M32_H5 -> + new LMSParams(32, 5, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M32_H10 -> + new LMSParams(32, 10, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M32_H15 -> + new LMSParams(32, 15, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M32_H20 -> + new LMSParams(32, 20, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M32_H25 -> + new LMSParams(32, 25, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H5 -> + new LMSParams(24, 5, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H10 -> + new LMSParams(24, 10, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H15 -> + new LMSParams(24, 15, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H20 -> + new LMSParams(24, 20, "SHAKE256-512", 64); + case LMSUtils.LMS_SHAKE_M24_H25 -> + new LMSParams(24, 25, "SHAKE256-512", 64); + default -> throw new IllegalArgumentException("Unsupported or bad LMS type"); - } - - return new LMSParams(m, h, hashAlgStr); + }; + return params; } boolean hasSameHash(LMSParams other) { @@ -495,7 +565,7 @@ void getPath(int i, byte[] arr, int pos) { static class LMOTSParams { final int lmotSigType; final int n; // the number of bytes used from the hash output - final int hashAlg_n = 32; // the output length of the hash function + int hashAlg_n; // the output length of the hash function final int w; final int twoPowWMinus1; final int ls; @@ -511,6 +581,7 @@ static class LMOTSParams { // back into the buffer. This way, we avoid memory allocations and some // computations that would have to be done otherwise. final byte[] hashBuf; + // Precomputed block for SHA256 when the message size is 55 bytes // (i.e. when SHA256 is used) private static final byte[] hashbufSha256_32 = { @@ -523,10 +594,64 @@ static class LMOTSParams { 0, 0, 0, 0, 0, 0, 0, (byte) 0x80, 0, 0, 0, 0, 0, 0, 1, (byte) 0xb8 }; + // Precomputed block for SHA256 when the message size is 47 bytes + // (i.e. when SHA256-192 is used) + private static final byte[] hashbufSha256_24 = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x80, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0x78 + }; + // Precomputed block for SHAKE256 when the message size is 55 bytes + // (i.e. when SHAKE256 is used) + private static final byte[] hashbufShake256_32 = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x1F, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x80 + }; + // Precomputed block for SHAKE256 when the message size is 47 bytes + // (i.e. when SHAKE256-192 is used) + private static final byte[] hashbufShake256_24 = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x1F, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, (byte) 0x80 + }; private LMOTSParams( int lmotSigType, int hLen, int w, - int ls, int p, String hashAlgName) { + int ls, int p, String hashAlgName, int hashAlg_n) { this.lmotSigType = lmotSigType; this.n = hLen; this.w = w; @@ -534,32 +659,60 @@ private LMOTSParams( this.p = p; twoPowWMinus1 = (1 << w) - 1; this.hashAlgName = hashAlgName; - hashBuf = hashbufSha256_32; + this.hashAlg_n = hashAlg_n; + hashBuf = switch (hashAlgName) { + case "SHAKE256-512" -> { + yield this.n == 24 ? + hashbufShake256_24 : hashbufShake256_32; + } + case "SHA-256" -> { + yield this.n == 24 ? + hashbufSha256_24 : hashbufSha256_32; + } + default -> + throw new IllegalArgumentException( + "Unknown hash algorithm "+hashAlgName); + }; } static LMOTSParams of(int lmotsType) { - LMOTSParams params; - switch (lmotsType) { - case LMSUtils.LMOTS_SHA256_N32_W1: - params = new LMOTSParams( - lmotsType, 32, 1, 7, 265, "SHA-256"); - break; - case LMSUtils.LMOTS_SHA256_N32_W2: - params = new LMOTSParams( - lmotsType, 32, 2, 6, 133, "SHA-256"); - break; - case LMSUtils.LMOTS_SHA256_N32_W4: - params = new LMOTSParams( - lmotsType, 32, 4, 4, 67, "SHA-256"); - break; - case LMSUtils.LMOTS_SHA256_N32_W8: - params = new LMOTSParams( - lmotsType, 32, 8, 0, 34, "SHA-256"); - break; - default: + LMOTSParams params = switch (lmotsType) { + case LMSUtils.LMOTS_SHA256_N32_W1 -> + new LMOTSParams(lmotsType, 32, 1, 7, 265, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N32_W2 -> + new LMOTSParams(lmotsType, 32, 2, 6, 133, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N32_W4 -> + new LMOTSParams(lmotsType, 32, 4, 4, 67, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N32_W8 -> + new LMOTSParams(lmotsType, 32, 8, 0, 34, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N24_W1 -> + new LMOTSParams(lmotsType, 24, 1, 8, 200, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N24_W2 -> + new LMOTSParams(lmotsType, 24, 2, 6, 101, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N24_W4 -> + new LMOTSParams(lmotsType, 24, 4, 4, 51, "SHA-256", 32); + case LMSUtils.LMOTS_SHA256_N24_W8 -> + new LMOTSParams(lmotsType, 24, 8, 0, 26, "SHA-256", 32); + case LMSUtils.LMOTS_SHAKE_N32_W1 -> + new LMOTSParams(lmotsType, 32, 1, 7, 265, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N32_W2 -> + new LMOTSParams(lmotsType, 32, 2, 6, 133, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N32_W4 -> + new LMOTSParams(lmotsType, 32, 4, 4, 67, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N32_W8 -> + new LMOTSParams(lmotsType, 32, 8, 0, 34, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N24_W1 -> + new LMOTSParams(lmotsType, 24, 1, 8, 200, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N24_W2 -> + new LMOTSParams(lmotsType, 24, 2, 6, 101, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N24_W4 -> + new LMOTSParams(lmotsType, 24, 4, 4, 51, "SHAKE256-512", 64); + case LMSUtils.LMOTS_SHAKE_N24_W8 -> + new LMOTSParams(lmotsType, 24, 8, 0, 26, "SHAKE256-512", 64); + default -> throw new IllegalArgumentException( "Unsupported or bad OTS Algorithm Identifier."); - } + }; return params; } @@ -580,13 +733,6 @@ private void addCksm(byte[] S) { S[len + 1] = (byte) (sum & 0xff); } - void digestFixedLengthPreprocessed( - SHA2.SHA256 sha256, byte[] input, int inLen, - byte[] output, int outOffset, int outLen) { - sha256.implDigestFixedLengthPreprocessed( - input, inLen, output, outOffset, outLen); - } - byte[] lmotsPubKeyCandidate( LMSignature lmSig, byte[] message, LMSPublicKey pKey) throws SignatureException { @@ -625,7 +771,13 @@ byte[] lmotsPubKeyCandidate( byte[] preZi = hashBuf.clone(); int hashLen = hashBuf.length; - SHA2.SHA256 sha256 = new SHA2.SHA256(); + + DigestBase db; + if (hashAlgName.startsWith("SHAKE")) { + db = new SHA3.SHAKE256Hash(); + } else { + db = new SHA2.SHA256(); + } pKey.getI(preZi, 0); lmSig.getQArr(preZi, 16); @@ -643,11 +795,11 @@ byte[] lmotsPubKeyCandidate( for (int j = a; j < twoPowWMinus1; j++) { preZi[22] = (byte) j; if (j < twoPowWMinus2) { - digestFixedLengthPreprocessed( - sha256, preZi, hashLen, preZi, 23, n); + db.implDigestFixedLengthPreprocessed(preZi, + hashLen, preZi, 23, n); } else { - digestFixedLengthPreprocessed( - sha256, preZi, hashLen, preCandidate, 22 + i * n, n); + db.implDigestFixedLengthPreprocessed(preZi, + hashLen, preCandidate, 22 + i * n, n); } } } diff --git a/src/java.base/share/classes/sun/security/provider/SHA2.java b/src/java.base/share/classes/sun/security/provider/SHA2.java index e966e6b77f8d..7d8c2840de95 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA2.java +++ b/src/java.base/share/classes/sun/security/provider/SHA2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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,6 +117,7 @@ void implDigest(byte[] out, int ofs) { } + @Override protected void implDigestFixedLengthPreprocessed( byte[] input, int inLen, byte[] output, int outOffset, int outLen) { implReset(); diff --git a/src/java.base/share/classes/sun/security/provider/SHA3.java b/src/java.base/share/classes/sun/security/provider/SHA3.java index a096cac5f504..0578645c1cd5 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA3.java +++ b/src/java.base/share/classes/sun/security/provider/SHA3.java @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -98,6 +98,15 @@ private SHA3(String name, int digestLength, byte suffix, int c) { this.suffix = suffix; } + @Override + protected void implDigestFixedLengthPreprocessed( + byte[] input, int inLen, byte[] output, int outOffset, int outLen) { + implReset(); + + implCompress(input, 0); + implDigest0(output, outOffset, outLen); + } + private void implCompressCheck(byte[] b, int ofs) { Objects.requireNonNull(b); Preconditions.checkIndex(ofs + blockSize - 1, b.length, Preconditions.AIOOBE_FORMATTER); @@ -136,9 +145,6 @@ void finishAbsorb() { * DigestBase calls implReset() when necessary. */ void implDigest(byte[] out, int ofs) { - // Moving this allocation to the block where it is used causes a little - // performance drop, that is why it is here. - byte[] byteState = new byte[8]; if (engineGetDigestLength() == 0) { // This is an XOF, so the digest() call is illegal. throw new ProviderException("Calling digest() is not allowed in an XOF"); @@ -146,8 +152,12 @@ void implDigest(byte[] out, int ofs) { finishAbsorb(); + implDigest0(out, ofs, engineGetDigestLength()); + } + + void implDigest0(byte[] out, int ofs, int outLen) { int availableBytes = blockSize; - int numBytes = engineGetDigestLength(); + int numBytes = outLen; while (numBytes > availableBytes) { for (int i = 0; i < availableBytes / 8; i++) { @@ -163,6 +173,10 @@ void implDigest(byte[] out, int ofs) { asLittleEndian.set(out, ofs, state[i]); ofs += 8; } + + // Moving this allocation to the block where it is used causes a little + // performance drop, that is why it is here. + byte[] byteState = new byte[8]; if (numBytes % 8 != 0) { asLittleEndian.set(byteState, 0, state[numLongs]); System.arraycopy(byteState, 0, out, ofs, numBytes % 8); diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java index 942a91d61b84..e9dabdc5b06f 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -444,15 +444,16 @@ public static ObjectIdentifier hashAlgFromHSS(PublicKey publicKey) // is the LMS public key for the top-level tree. // Section 5.3: LMS public key is u32str(type) || u32str(otstype) || I || T[1] // Section 8: type is the numeric identifier for an LMS specification. - // This RFC defines 5 SHA-256 based types, value from 5 to 9. if (rawKey.length < 8) { throw new NoSuchAlgorithmException("Cannot decode public key"); } int num = ((rawKey[4] & 0xff) << 24) + ((rawKey[5] & 0xff) << 16) + ((rawKey[6] & 0xff) << 8) + (rawKey[7] & 0xff); return switch (num) { - // RFC 8554 only supports SHA_256 hash algorithm + // RFC 8554 only supports SHA_256 hash algorithms case 5, 6, 7, 8, 9 -> AlgorithmId.SHA256_oid; + // RFC 9858 supports SHAKE_256 hash algorithms + case 15, 16, 17, 18, 19 -> AlgorithmId.SHAKE256_512_oid; default -> throw new NoSuchAlgorithmException("Unknown LMS type: " + num); }; } catch (IOException e) { diff --git a/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java b/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java index f3569941321d..78ae70629b60 100644 --- a/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java +++ b/src/java.desktop/share/classes/sun/font/ExtendedTextSourceLabel.java @@ -57,15 +57,16 @@ * Align bounds is a rect that defines how to align this to margins. * it generally allows some overhang that logical bounds would prevent. */ -class ExtendedTextSourceLabel implements TextLineComponent, Decoration.Label { +final class ExtendedTextSourceLabel implements TextLineComponent, Decoration.Label { private final TextSource source; private final Decoration decorator; // caches - private Font font; - private AffineTransform baseTX; - private CoreMetrics cm; + private final Font font; + private final AffineTransform baseTX; + private final CoreMetrics cm; + private final float advTracking; private Rectangle2D lb; private Rectangle2D ab; @@ -74,34 +75,18 @@ class ExtendedTextSourceLabel implements TextLineComponent, Decoration.Label { private StandardGlyphVector gv; private float[] charinfo; - private float advTracking; - /** * Create from a TextSource. */ public ExtendedTextSourceLabel(TextSource source, Decoration decorator) { this.source = source; this.decorator = decorator; - finishInit(); - } - - /** - * Create from a TextSource, optionally using cached data from oldLabel starting at the offset. - * If present oldLabel must have been created from a run of text that includes the text used in - * the new label. Start in source corresponds to logical character offset in oldLabel. - */ - public ExtendedTextSourceLabel(TextSource source, ExtendedTextSourceLabel oldLabel, int offset) { - // currently no optimization. - this.source = source; - this.decorator = oldLabel.decorator; - finishInit(); - } - - private void finishInit() { - font = source.getFont(); + Font font = source.getFont(); Map atts = font.getAttributes(); - baseTX = AttributeValues.getBaselineTransform(atts); + AffineTransform baseTX = AttributeValues.getBaselineTransform(atts); + + CoreMetrics cm; if (baseTX == null){ cm = source.getCoreMetrics(); } else { @@ -110,13 +95,15 @@ private void finishInit() { charTX = new AffineTransform(); } font = font.deriveFont(charTX); - LineMetrics lm = font.getLineMetrics(source.getChars(), source.getStart(), source.getStart() + source.getLength(), source.getFRC()); cm = CoreMetrics.get(lm); } - advTracking = font.getSize() * AttributeValues.getTracking(atts); + this.font = font; + this.baseTX = baseTX; + this.cm = cm; + this.advTracking = font.getSize() * AttributeValues.getTracking(atts); } /** diff --git a/src/java.desktop/share/classes/sun/font/GlyphLayout.java b/src/java.desktop/share/classes/sun/font/GlyphLayout.java index 5bff127f143e..851201fe347a 100644 --- a/src/java.desktop/share/classes/sun/font/GlyphLayout.java +++ b/src/java.desktop/share/classes/sun/font/GlyphLayout.java @@ -125,10 +125,10 @@ public static void done(GlyphLayout gl) { } private static final class SDCache { - public AffineTransform dtx; - public AffineTransform gtx; - public Point2D.Float delta; - public FontStrikeDesc sd; + private final AffineTransform dtx; + private final AffineTransform gtx; + private final Point2D.Float delta; + private final FontStrikeDesc sd; private SDCache(Font font, FontRenderContext frc) { // !!! add getVectorTransform and hasVectorTransform to frc? then diff --git a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java index 32728efde6c3..b28723f94a62 100644 --- a/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java +++ b/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java @@ -33,24 +33,23 @@ import java.awt.KeyboardFocusManager; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.Window; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.print.Book; -import java.awt.print.Pageable; import java.awt.print.PageFormat; +import java.awt.print.Pageable; import java.awt.print.Paper; import java.awt.print.Printable; import java.awt.print.PrinterAbortException; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; -import java.awt.Window; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Locale; -import sun.awt.image.ByteInterleavedRaster; import javax.print.Doc; import javax.print.DocFlavor; @@ -69,8 +68,8 @@ import javax.print.attribute.Size2DSyntax; import javax.print.attribute.standard.Copies; import javax.print.attribute.standard.Destination; -import javax.print.attribute.standard.DialogTypeSelection; import javax.print.attribute.standard.DialogOwner; +import javax.print.attribute.standard.DialogTypeSelection; import javax.print.attribute.standard.Fidelity; import javax.print.attribute.standard.JobName; import javax.print.attribute.standard.JobSheets; @@ -81,15 +80,17 @@ import javax.print.attribute.standard.OrientationRequested; import javax.print.attribute.standard.OutputBin; import javax.print.attribute.standard.PageRanges; +import javax.print.attribute.standard.PrinterIsAcceptingJobs; import javax.print.attribute.standard.PrinterResolution; import javax.print.attribute.standard.PrinterState; import javax.print.attribute.standard.PrinterStateReason; import javax.print.attribute.standard.PrinterStateReasons; -import javax.print.attribute.standard.PrinterIsAcceptingJobs; import javax.print.attribute.standard.RequestingUserName; import javax.print.attribute.standard.SheetCollate; import javax.print.attribute.standard.Sides; +import sun.awt.image.ByteInterleavedRaster; + import static sun.font.FontUtilities.isIgnorableWhitespace; /** @@ -1613,8 +1614,7 @@ public void print(PrintRequestAttributeSet attributes) } catch (PrinterException pe) { throw pe; } catch (Throwable printError) { - throw (PrinterException) - new PrinterException().initCause(printError.getCause()); + throw (PrinterException) new PrinterException().initCause(printError); } finally { // reset previousPaper in case this job is invoked again. previousPaper = null; diff --git a/src/java.desktop/share/legal/libpng.md b/src/java.desktop/share/legal/libpng.md index 034de22bf25f..7783fc7ff032 100644 --- a/src/java.desktop/share/legal/libpng.md +++ b/src/java.desktop/share/legal/libpng.md @@ -1,4 +1,4 @@ -## libpng v1.6.56 +## libpng v1.6.57 ### libpng License

@@ -180,6 +180,7 @@ Authors, for copyright and licensing purposes.
  * Mans Rullgard
  * Matt Sarett
  * Mike Klein
+ * Mohammad Seet
  * Pascal Massimino
  * Paul Schmidt
  * Petr Simecek
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
index 673d4d50420f..ba81df0c0e61 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
@@ -6368,6 +6368,17 @@ Version 1.6.56 [March 25, 2026]
     (Contributed by Bob Friesenhahn and Philippe Antoine.)
   Performed various refactorings and cleanups.
 
+Version 1.6.57 [April 8, 2026]
+  Fixed CVE-2026-34757 (medium severity):
+    Use-after-free in `png_set_PLTE`, `png_set_tRNS` and `png_set_hIST`
+    leading to corrupted chunk data and potential heap information disclosure.
+    Also hardened the append-style setters (`png_set_text`, `png_set_sPLT`,
+    `png_set_unknown_chunks`) against a theoretical variant of the same
+    aliasing pattern.
+    (Reported by Iv4n .)
+  Fixed integer overflow in rowbytes computation in read transforms.
+    (Contributed by Mohammad Seet.)
+
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
 Subscription is required; visit
 
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/README b/src/java.desktop/share/native/libsplashscreen/libpng/README
index d0b085f79334..179b8dc8cb4d 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/README
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/README
@@ -1,4 +1,4 @@
-README for libpng version 1.6.56
+README for libpng version 1.6.57
 ================================
 
 See the note about version numbers near the top of `png.h`.
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.c b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
index fd095b515b91..e4e13b0a6840 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/png.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
@@ -42,7 +42,7 @@
 #include "pngpriv.h"
 
 /* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_1_6_56 Your_png_h_is_not_version_1_6_56;
+typedef png_libpng_version_1_6_57 Your_png_h_is_not_version_1_6_57;
 
 /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the
  * corresponding macro definitions.  This causes a compile time failure if
@@ -849,7 +849,7 @@ png_get_copyright(png_const_structrp png_ptr)
    return PNG_STRING_COPYRIGHT
 #else
    return PNG_STRING_NEWLINE \
-      "libpng version 1.6.56" PNG_STRING_NEWLINE \
+      "libpng version 1.6.57" PNG_STRING_NEWLINE \
       "Copyright (c) 2018-2026 Cosmin Truta" PNG_STRING_NEWLINE \
       "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
       PNG_STRING_NEWLINE \
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.h b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
index 56ec204cd1a4..349e7d073831 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/png.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * libpng version 1.6.56
+ * libpng version 1.6.57
  *
  * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
@@ -43,7 +43,7 @@
  *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
  *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
  *     Glenn Randers-Pehrson
- *   libpng versions 1.6.36, December 2018, through 1.6.56, March 2026:
+ *   libpng versions 1.6.36, December 2018, through 1.6.57, April 2026:
  *     Cosmin Truta
  *   See also "Contributing Authors", below.
  */
@@ -267,7 +267,7 @@
  *    ...
  *    1.5.30                  15    10530  15.so.15.30[.0]
  *    ...
- *    1.6.56                  16    10656  16.so.16.56[.0]
+ *    1.6.57                  16    10657  16.so.16.57[.0]
  *
  *    Henceforth the source version will match the shared-library major and
  *    minor numbers; the shared-library major version number will be used for
@@ -303,7 +303,7 @@
  */
 
 /* Version information for png.h - this should match the version in png.c */
-#define PNG_LIBPNG_VER_STRING "1.6.56"
+#define PNG_LIBPNG_VER_STRING "1.6.57"
 #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n"
 
 /* The versions of shared library builds should stay in sync, going forward */
@@ -314,7 +314,7 @@
 /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
 #define PNG_LIBPNG_VER_MAJOR   1
 #define PNG_LIBPNG_VER_MINOR   6
-#define PNG_LIBPNG_VER_RELEASE 56
+#define PNG_LIBPNG_VER_RELEASE 57
 
 /* This should be zero for a public release, or non-zero for a
  * development version.
@@ -345,7 +345,7 @@
  * From version 1.0.1 it is:
  * XXYYZZ, where XX=major, YY=minor, ZZ=release
  */
-#define PNG_LIBPNG_VER 10656 /* 1.6.56 */
+#define PNG_LIBPNG_VER 10657 /* 1.6.57 */
 
 /* Library configuration: these options cannot be changed after
  * the library has been built.
@@ -455,7 +455,7 @@ extern "C" {
 /* This triggers a compiler error in png.c, if png.c and png.h
  * do not agree upon the version number.
  */
-typedef char *png_libpng_version_1_6_56;
+typedef char *png_libpng_version_1_6_57;
 
 /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
  *
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
index 5772e6ebb1c8..1a5bb7b60f8a 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
@@ -29,7 +29,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  *
- * libpng version 1.6.56
+ * libpng version 1.6.57
  *
  * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
index 4a7e51d112dc..de63c9989279 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
@@ -31,7 +31,7 @@
  * However, the following notice accompanied the original version of this
  * file and, per its terms, should not be removed:
  */
-/* libpng version 1.6.56 */
+/* libpng version 1.6.57 */
 
 /* Copyright (c) 2018-2026 Cosmin Truta */
 /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
index f0972ba9bef9..838c8460f910 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
@@ -2408,7 +2408,7 @@ png_do_unpack(png_row_infop row_info, png_bytep row)
       }
       row_info->bit_depth = 8;
       row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_width * row_info->channels;
+      row_info->rowbytes = (size_t)row_width * row_info->channels;
    }
 }
 #endif
@@ -2610,7 +2610,7 @@ png_do_scale_16_to_8(png_row_infop row_info, png_bytep row)
 
       row_info->bit_depth = 8;
       row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_info->width * row_info->channels;
+      row_info->rowbytes = (size_t)row_info->width * row_info->channels;
    }
 }
 #endif
@@ -2638,7 +2638,7 @@ png_do_chop(png_row_infop row_info, png_bytep row)
 
       row_info->bit_depth = 8;
       row_info->pixel_depth = (png_byte)(8 * row_info->channels);
-      row_info->rowbytes = row_info->width * row_info->channels;
+      row_info->rowbytes = (size_t)row_info->width * row_info->channels;
    }
 }
 #endif
@@ -2874,7 +2874,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = lo_filler;
             row_info->channels = 2;
             row_info->pixel_depth = 16;
-            row_info->rowbytes = row_width * 2;
+            row_info->rowbytes = (size_t)row_width * 2;
          }
 
          else
@@ -2889,7 +2889,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             }
             row_info->channels = 2;
             row_info->pixel_depth = 16;
-            row_info->rowbytes = row_width * 2;
+            row_info->rowbytes = (size_t)row_width * 2;
          }
       }
 
@@ -2912,7 +2912,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = hi_filler;
             row_info->channels = 2;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
 
          else
@@ -2929,7 +2929,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             }
             row_info->channels = 2;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
       }
 #endif
@@ -2953,7 +2953,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = lo_filler;
             row_info->channels = 4;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
 
          else
@@ -2970,7 +2970,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             }
             row_info->channels = 4;
             row_info->pixel_depth = 32;
-            row_info->rowbytes = row_width * 4;
+            row_info->rowbytes = (size_t)row_width * 4;
          }
       }
 
@@ -2997,7 +2997,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
             *(--dp) = hi_filler;
             row_info->channels = 4;
             row_info->pixel_depth = 64;
-            row_info->rowbytes = row_width * 8;
+            row_info->rowbytes = (size_t)row_width * 8;
          }
 
          else
@@ -3019,7 +3019,7 @@ png_do_read_filler(png_row_infop row_info, png_bytep row,
 
             row_info->channels = 4;
             row_info->pixel_depth = 64;
-            row_info->rowbytes = row_width * 8;
+            row_info->rowbytes = (size_t)row_width * 8;
          }
       }
 #endif
@@ -4513,7 +4513,7 @@ png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
                }
                row_info->bit_depth = 8;
                row_info->pixel_depth = 32;
-               row_info->rowbytes = row_width * 4;
+               row_info->rowbytes = (size_t)row_width * 4;
                row_info->color_type = 6;
                row_info->channels = 4;
             }
@@ -4521,7 +4521,7 @@ png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
             else
             {
                sp = row + (size_t)row_width - 1;
-               dp = row + (size_t)(row_width * 3) - 1;
+               dp = row + (size_t)row_width * 3 - 1;
                i = 0;
 #ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
                i = png_do_expand_palette_rgb8_neon(png_ptr, row_info, row,
@@ -4540,7 +4540,7 @@ png_do_expand_palette(png_structrp png_ptr, png_row_infop row_info,
 
                row_info->bit_depth = 8;
                row_info->pixel_depth = 24;
-               row_info->rowbytes = row_width * 3;
+               row_info->rowbytes = (size_t)row_width * 3;
                row_info->color_type = 2;
                row_info->channels = 3;
             }
diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
index 05d18cd06b74..29082a6be089 100644
--- a/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
+++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngset.c
@@ -414,6 +414,7 @@ void PNGAPI
 png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
     png_const_uint_16p hist)
 {
+   png_uint_16 safe_hist[PNG_MAX_PALETTE_LENGTH];
    int i;
 
    png_debug1(1, "in %s storage function", "hIST");
@@ -430,6 +431,13 @@ png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
       return;
    }
 
+   /* Snapshot the caller's hist before freeing, in case it points to
+    * info_ptr->hist (getter-to-setter aliasing).
+    */
+   memcpy(safe_hist, hist, (unsigned int)info_ptr->num_palette *
+       (sizeof (png_uint_16)));
+   hist = safe_hist;
+
    png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
 
    /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in
@@ -771,7 +779,7 @@ void PNGAPI
 png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
     png_const_colorp palette, int num_palette)
 {
-
+   png_color safe_palette[PNG_MAX_PALETTE_LENGTH];
    png_uint_32 max_palette_length;
 
    png_debug1(1, "in %s storage function", "PLTE");
@@ -805,6 +813,15 @@ png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr,
       png_error(png_ptr, "Invalid palette");
    }
 
+   /* Snapshot the caller's palette before freeing, in case it points to
+    * info_ptr->palette (getter-to-setter aliasing).
+    */
+   if (num_palette > 0)
+      memcpy(safe_palette, palette, (unsigned int)num_palette *
+          (sizeof (png_color)));
+
+   palette = safe_palette;
+
    png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
 
    /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead
@@ -966,6 +983,7 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
     png_const_textp text_ptr, int num_text)
 {
    int i;
+   png_textp old_text = NULL;
 
    png_debug1(1, "in text storage function, chunk typeid = 0x%lx",
       png_ptr == NULL ? 0xabadca11UL : (unsigned long)png_ptr->chunk_name);
@@ -1013,7 +1031,10 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
          return 1;
       }
 
-      png_free(png_ptr, info_ptr->text);
+      /* Defer freeing the old array until after the copy loop below,
+       * in case text_ptr aliases info_ptr->text (getter-to-setter).
+       */
+      old_text = info_ptr->text;
 
       info_ptr->text = new_text;
       info_ptr->free_me |= PNG_FREE_TEXT;
@@ -1098,6 +1119,7 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
       {
          png_chunk_report(png_ptr, "text chunk: out of memory",
              PNG_CHUNK_WRITE_ERROR);
+         png_free(png_ptr, old_text);
 
          return 1;
       }
@@ -1151,6 +1173,8 @@ png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr,
       png_debug1(3, "transferred text chunk %d", info_ptr->num_text);
    }
 
+   png_free(png_ptr, old_text);
+
    return 0;
 }
 #endif
@@ -1194,6 +1218,16 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr,
 
    if (trans_alpha != NULL)
    {
+       /* Snapshot the caller's trans_alpha before freeing, in case it
+        * points to info_ptr->trans_alpha (getter-to-setter aliasing).
+        */
+       png_byte safe_trans[PNG_MAX_PALETTE_LENGTH];
+
+       if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
+          memcpy(safe_trans, trans_alpha, (size_t)num_trans);
+
+       trans_alpha = safe_trans;
+
        png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
 
        if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH)
@@ -1278,6 +1312,7 @@ png_set_sPLT(png_const_structrp png_ptr,
  */
 {
    png_sPLT_tp np;
+   png_sPLT_tp old_spalettes;
 
    png_debug1(1, "in %s storage function", "sPLT");
 
@@ -1298,7 +1333,10 @@ png_set_sPLT(png_const_structrp png_ptr,
       return;
    }
 
-   png_free(png_ptr, info_ptr->splt_palettes);
+   /* Defer freeing the old array until after the copy loop below,
+    * in case entries aliases info_ptr->splt_palettes (getter-to-setter).
+    */
+   old_spalettes = info_ptr->splt_palettes;
 
    info_ptr->splt_palettes = np;
    info_ptr->free_me |= PNG_FREE_SPLT;
@@ -1362,6 +1400,8 @@ png_set_sPLT(png_const_structrp png_ptr,
    }
    while (--nentries);
 
+   png_free(png_ptr, old_spalettes);
+
    if (nentries > 0)
       png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR);
 }
@@ -1410,6 +1450,7 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
     png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)
 {
    png_unknown_chunkp np;
+   png_unknown_chunkp old_unknowns;
 
    if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 ||
        unknowns == NULL)
@@ -1456,7 +1497,10 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
       return;
    }
 
-   png_free(png_ptr, info_ptr->unknown_chunks);
+   /* Defer freeing the old array until after the copy loop below,
+    * in case unknowns aliases info_ptr->unknown_chunks (getter-to-setter).
+    */
+   old_unknowns = info_ptr->unknown_chunks;
 
    info_ptr->unknown_chunks = np; /* safe because it is initialized */
    info_ptr->free_me |= PNG_FREE_UNKN;
@@ -1502,6 +1546,8 @@ png_set_unknown_chunks(png_const_structrp png_ptr,
       ++np;
       ++(info_ptr->unknown_chunks_num);
    }
+
+   png_free(png_ptr, old_unknowns);
 }
 
 void PNGAPI
diff --git a/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp b/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp
index 8d016d8b39f7..b18fa5a7e2ce 100644
--- a/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp
+++ b/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp
@@ -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
@@ -522,7 +522,6 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
     AwtComponent *awtParent = (parent != NULL) ? (AwtComponent *)JNI_GET_PDATA(parent) : NULL;
     HWND hwndOwner = awtParent ? awtParent->GetHWnd() : NULL;
 
-    jboolean doIt = JNI_FALSE;
     PAGESETUPDLG setup;
     memset(&setup, 0, sizeof(setup));
 
@@ -578,7 +577,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
          */
         if ((setup.hDevMode == NULL) && (setup.hDevNames == NULL)) {
             CLEANUP_SHOW;
-            return doIt;
+            return JNI_FALSE;
         }
     } else {
         int measure = PSD_INTHOUSANDTHSOFINCHES;
@@ -606,7 +605,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
     pageFormatToSetup(env, self, page, &setup, AwtPrintControl::getPrintDC(env, self));
     if (env->ExceptionCheck()) {
         CLEANUP_SHOW;
-        return doIt;
+        return JNI_FALSE;
     }
 
     setup.lpfnPageSetupHook = reinterpret_cast(pageDlgHook);
@@ -615,89 +614,91 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
     AwtDialog::CheckInstallModalHook();
 
     BOOL ret = ::PageSetupDlg(&setup);
-    if (ret) {
 
-        jobject paper = getPaper(env, page);
-        if (paper == NULL) {
-            CLEANUP_SHOW;
-            return doIt;
-        }
-        int units = setup.Flags & PSD_INTHOUSANDTHSOFINCHES ?
-                                                MM_HIENGLISH :
-                                                MM_HIMETRIC;
-        POINT paperSize;
-        RECT margins;
-        jint orientation;
+    AwtDialog::CheckUninstallModalHook();
+    AwtDialog::ModalActivateNextWindow(NULL, target, peer);
 
-        /* The printer may have been changed, and we track that change,
-         * but then need to get a new DC for the current printer so that
-         * we validate the paper size correctly
-         */
-        if (setup.hDevNames != NULL) {
-            DEVNAMES* names = (DEVNAMES*)::GlobalLock(setup.hDevNames);
-            if (names != NULL) {
-                LPTSTR printer = (LPTSTR)names+names->wDeviceOffset;
-                SAVE_CONTROLWORD
-                HDC newDC = ::CreateDC(TEXT("WINSPOOL"), printer, NULL, NULL);
-                RESTORE_CONTROLWORD
-                if (newDC != NULL) {
-                    HDC oldDC = AwtPrintControl::getPrintDC(env, self);
-                    if (oldDC != NULL) {
-                         ::DeleteDC(oldDC);
-                    }
+    if (!ret) {
+        CLEANUP_SHOW;
+        return JNI_FALSE;
+    }
+
+    jobject paper = getPaper(env, page);
+    if (paper == NULL) {
+        CLEANUP_SHOW;
+        return JNI_FALSE;
+    }
+    int units = setup.Flags & PSD_INTHOUSANDTHSOFINCHES ?
+                                            MM_HIENGLISH :
+                                            MM_HIMETRIC;
+    POINT paperSize;
+    RECT margins;
+    jint orientation;
+
+    /* The printer may have been changed, and we track that change,
+     * but then need to get a new DC for the current printer so that
+     * we validate the paper size correctly
+     */
+    if (setup.hDevNames != NULL) {
+        DEVNAMES* names = (DEVNAMES*)::GlobalLock(setup.hDevNames);
+        if (names != NULL) {
+            LPTSTR printer = (LPTSTR)names+names->wDeviceOffset;
+            SAVE_CONTROLWORD
+            HDC newDC = ::CreateDC(TEXT("WINSPOOL"), printer, NULL, NULL);
+            RESTORE_CONTROLWORD
+            if (newDC != NULL) {
+                HDC oldDC = AwtPrintControl::getPrintDC(env, self);
+                if (oldDC != NULL) {
+                     ::DeleteDC(oldDC);
                 }
-                AwtPrintControl::setPrintDC(env, self, newDC);
             }
-            ::GlobalUnlock(setup.hDevNames);
+            AwtPrintControl::setPrintDC(env, self, newDC);
         }
+        ::GlobalUnlock(setup.hDevNames);
+    }
 
-        /* Get the Windows paper and margins description.
-        */
-        retrievePaperInfo(&setup, &paperSize, &margins, &orientation,
-                          AwtPrintControl::getPrintDC(env, self));
+    /* Get the Windows paper and margins description.
+    */
+    retrievePaperInfo(&setup, &paperSize, &margins, &orientation,
+                      AwtPrintControl::getPrintDC(env, self));
 
-        /* Convert the Windows' paper and margins description
-         * and place them into a Paper instance.
-         */
-        setPaperValues(env, paper, &paperSize, &margins, units);
-        if (env->ExceptionCheck()) {
-            CLEANUP_SHOW;
-            return doIt;
-         }
-        /*
-         * Put the updated Paper instance and the orientation into
-         * the PageFormat.
-         */
-        setPaper(env, page, paper);
-        if (env->ExceptionCheck()) {
-            CLEANUP_SHOW;
-            return doIt;
-        }
-        setPageFormatOrientation(env, page, orientation);
-        if (env->ExceptionCheck()) {
-            CLEANUP_SHOW;
-            return JNI_FALSE;
-        }
-        if (setup.hDevMode != NULL) {
-            DEVMODE *devmode = (DEVMODE *)::GlobalLock(setup.hDevMode);
-            if (devmode != NULL) {
-                if (devmode->dmFields & DM_PAPERSIZE) {
-                    jboolean err = setPrintPaperSize(env, self, devmode->dmPaperSize);
-                    if (err) {
-                        CLEANUP_SHOW;
-                        return doIt;
-                    }
+    /* Convert the Windows' paper and margins description
+     * and place them into a Paper instance.
+     */
+    setPaperValues(env, paper, &paperSize, &margins, units);
+    if (env->ExceptionCheck()) {
+        CLEANUP_SHOW;
+        return JNI_FALSE;
+     }
+    /*
+     * Put the updated Paper instance and the orientation into
+     * the PageFormat.
+     */
+    setPaper(env, page, paper);
+    if (env->ExceptionCheck()) {
+        CLEANUP_SHOW;
+        return JNI_FALSE;
+    }
+    setPageFormatOrientation(env, page, orientation);
+    if (env->ExceptionCheck()) {
+        CLEANUP_SHOW;
+        return JNI_FALSE;
+    }
+    if (setup.hDevMode != NULL) {
+        DEVMODE *devmode = (DEVMODE *)::GlobalLock(setup.hDevMode);
+        if (devmode != NULL) {
+            if (devmode->dmFields & DM_PAPERSIZE) {
+                jboolean err = setPrintPaperSize(env, self, devmode->dmPaperSize);
+                if (err) {
+                    ::GlobalUnlock(setup.hDevMode);
+                    CLEANUP_SHOW;
+                    return JNI_FALSE;
                 }
             }
-            ::GlobalUnlock(setup.hDevMode);
         }
-        doIt = JNI_TRUE;
+        ::GlobalUnlock(setup.hDevMode);
     }
 
-    AwtDialog::CheckUninstallModalHook();
-
-    AwtDialog::ModalActivateNextWindow(NULL, target, peer);
-
     HGLOBAL oldG = AwtPrintControl::getPrintHDMode(env, self);
     if (setup.hDevMode != oldG) {
         AwtPrintControl::setPrintHDMode(env, self, setup.hDevMode);
@@ -710,7 +711,7 @@ Java_sun_awt_windows_WPageDialogPeer__1show(JNIEnv *env, jobject peer)
 
     CLEANUP_SHOW;
 
-    return doIt;
+    return JNI_TRUE;
 
     CATCH_BAD_ALLOC_RET(0);
 }
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
index 1c93c37698a5..7a8e6c6f4f19 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -34,6 +34,7 @@
 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
 import com.sun.tools.javac.util.Assert;
 import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.util.JCDiagnostic.Error;
 import com.sun.tools.javac.util.JCDiagnostic.LintWarning;
@@ -150,24 +151,26 @@ public boolean participatesInPreview(Symtab syms, ModuleSymbol m) {
     /**
      * Report usage of a preview feature. Usages reported through this method will affect the
      * set of sourcefiles with dependencies on preview features.
+     * @param flag a flag to set on the diagnostic
      * @param pos the position at which the preview feature was used.
      * @param feature the preview feature used.
      */
-    public void warnPreview(int pos, Feature feature) {
-        warnPreview(new SimpleDiagnosticPosition(pos), feature);
+    public void warnPreview(DiagnosticFlag flag, int pos, Feature feature) {
+        warnPreview(flag, new SimpleDiagnosticPosition(pos), feature);
     }
 
     /**
      * Report usage of a preview feature. Usages reported through this method will affect the
      * set of sourcefiles with dependencies on preview features.
+     * @param flag a flag to set on the diagnostic
      * @param pos the position at which the preview feature was used.
      * @param feature the preview feature used.
      */
-    public void warnPreview(DiagnosticPosition pos, Feature feature) {
+    public void warnPreview(DiagnosticFlag flag, DiagnosticPosition pos, Feature feature) {
         Assert.check(isEnabled());
         Assert.check(isPreview(feature));
         markUsesPreview(pos);
-        log.warning(pos,
+        log.warning(flag, pos,
             feature.isPlural() ?
                 LintWarnings.PreviewFeatureUsePlural(feature.nameFragment()) :
                 LintWarnings.PreviewFeatureUse(feature.nameFragment()));
@@ -263,7 +266,7 @@ public void checkSourceLevel(DiagnosticPosition pos, Feature feature) {
                 log.error(pos, feature.error(source.name));
             }
             if (isEnabled() && isPreview(feature)) {
-                warnPreview(pos, feature);
+                warnPreview(null, pos, feature);
             }
         }
     }
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
index 444530c72664..89ae68e85bad 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -5270,11 +5270,15 @@ public void visitModifiers(JCModifiers tree) {
 
     public void visitAnnotatedType(JCAnnotatedType tree) {
         attribAnnotationTypes(tree.annotations, env);
-        Type underlyingType = attribType(tree.underlyingType, env);
-        Type annotatedType = underlyingType.preannotatedType();
+        Type underlyingType = attribTree(tree.underlyingType, env, resultInfo);
+        if (underlyingType.getTag() == PACKAGE) {
+            result = tree.type = underlyingType;
+        } else {
+            Type annotatedType = underlyingType.preannotatedType();
 
-        annotate.annotateTypeSecondStage(tree, tree.annotations, annotatedType);
-        result = tree.type = annotatedType;
+            annotate.annotateTypeSecondStage(tree, tree.annotations, annotatedType);
+            result = tree.type = annotatedType;
+        }
     }
 
     public void visitErroneous(JCErroneous tree) {
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java
index 55f2f76e3583..d8b5b1ddd6be 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java
@@ -176,7 +176,7 @@ protected void checkSourceLevel(int pos, Feature feature) {
             lexError(pos, feature.error(source.name));
         } else if (preview.isPreview(feature)) {
             //use of preview feature, warn
-            preview.warnPreview(pos, feature);
+            preview.warnPreview(DiagnosticFlag.SYNTAX, pos, feature);
         }
     }
 
@@ -1040,10 +1040,10 @@ public Token readToken() {
                     // Verify that the incidental indentation is consistent.
                     Set checks = TextBlockSupport.checkWhitespace(string);
                     if (checks.contains(TextBlockSupport.WhitespaceChecks.INCONSISTENT)) {
-                        log.warning(pos, LintWarnings.InconsistentWhiteSpaceIndentation);
+                        log.warning(DiagnosticFlag.SYNTAX, pos, LintWarnings.InconsistentWhiteSpaceIndentation);
                     }
                     if (checks.contains(TextBlockSupport.WhitespaceChecks.TRAILING)) {
-                        log.warning(pos, LintWarnings.TrailingWhiteSpaceWillBeRemoved);
+                        log.warning(DiagnosticFlag.SYNTAX, pos, LintWarnings.TrailingWhiteSpaceWillBeRemoved);
                     }
                     // Remove incidental indentation.
                     try {
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
index df5da5cb954d..b4dfb04766cb 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
@@ -772,7 +772,7 @@ protected Name ident(boolean allowClass, boolean asVariable) {
             }
         } else if (token.kind == UNDERSCORE) {
             if (Feature.UNDERSCORE_IDENTIFIER.allowedInSource(source)) {
-                log.warning(token.pos, Warnings.UnderscoreAsIdentifier);
+                log.warning(DiagnosticFlag.SYNTAX, token.pos, Warnings.UnderscoreAsIdentifier);
             } else if (asVariable) {
                 checkSourceLevel(Feature.UNNAMED_VARIABLES);
                 if (peekToken(LBRACKET)) {
@@ -2339,7 +2339,7 @@ boolean isInvalidUnqualifiedMethodIdentifier(int pos, Name name) {
             if (allowYieldStatement) {
                 return true;
             } else {
-                log.warning(pos, Warnings.InvalidYield);
+                log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.InvalidYield);
             }
         }
         return false;
@@ -3858,35 +3858,35 @@ Source restrictedTypeNameStartingAtSource(Name name, int pos, boolean shouldWarn
             if (Feature.LOCAL_VARIABLE_TYPE_INFERENCE.allowedInSource(source)) {
                 return Source.JDK10;
             } else if (shouldWarn) {
-                log.warning(pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK10));
+                log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK10));
             }
         }
         if (name == names.yield) {
             if (allowYieldStatement) {
                 return Source.JDK14;
             } else if (shouldWarn) {
-                log.warning(pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK14));
+                log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowed(name, Source.JDK14));
             }
         }
         if (name == names.record) {
             if (allowRecords) {
                 return Source.JDK14;
             } else if (shouldWarn) {
-                log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK14));
+                log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK14));
             }
         }
         if (name == names.sealed) {
             if (allowSealedTypes) {
                 return Source.JDK15;
             } else if (shouldWarn) {
-                log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15));
+                log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15));
             }
         }
         if (name == names.permits) {
             if (allowSealedTypes) {
                 return Source.JDK15;
             } else if (shouldWarn) {
-                log.warning(pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15));
+                log.warning(DiagnosticFlag.SYNTAX, pos, Warnings.RestrictedTypeNotAllowedPreview(name, Source.JDK15));
             }
         }
         return null;
@@ -4057,7 +4057,7 @@ public JCTree.JCCompilationUnit parseCompilationUnit() {
                     if (source.compareTo(Source.JDK21) >= 0)
                         reportSyntaxError(semiList.first().pos, Errors.ExtraneousSemicolon);
                     else
-                        log.warning(semiList.first().pos, Warnings.ExtraneousSemicolon);
+                        log.warning(DiagnosticFlag.SYNTAX, semiList.first().pos, Warnings.ExtraneousSemicolon);
                 }
                 seenImport = true;
                 defs.append(importDeclaration());
@@ -4074,7 +4074,7 @@ public JCTree.JCCompilationUnit parseCompilationUnit() {
                         if (source.compareTo(Source.JDK21) >= 0)
                             reportSyntaxError(semiList.first().pos, Errors.ExtraneousSemicolon);
                         else
-                            log.warning(semiList.first().pos, Warnings.ExtraneousSemicolon);
+                            log.warning(DiagnosticFlag.SYNTAX, semiList.first().pos, Warnings.ExtraneousSemicolon);
                     }
                     ModuleKind kind = ModuleKind.STRONG;
                     if (token.name() == names.open) {
@@ -5616,7 +5616,7 @@ protected void checkSourceLevel(int pos, Feature feature) {
             log.error(pos, feature.error(source.name));
         } else if (preview.isPreview(feature)) {
             //use of preview feature, warn
-            preview.warnPreview(pos, feature);
+            preview.warnPreview(DiagnosticFlag.SYNTAX, pos, feature);
         }
     }
 
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
index 809c19d5012e..11fa3a5aebf0 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
@@ -1204,6 +1204,7 @@ void showDiagnostics(boolean showAll) {
         //where:
             private final Predicate ACCEPT_NON_RECOVERABLE_LINTS =
                     d -> !Optional.of(d)
+                            .filter(diag -> !diag.isFlagSet(SYNTAX))
                             .map(JCDiagnostic::getLintCategory)
                             .map(lc -> lc.annotationSuppression ||
                                        lc == Lint.LintCategory.INCUBATING)
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java
index ce3e56f2f3f0..b8b2a3af2546 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractLog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2021, 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
@@ -192,6 +192,16 @@ public void warning(int pos, Warning warningKey) {
         report(diags.warning(null, source, wrap(pos), warningKey));
     }
 
+    /** Report a warning, unless suppressed by the  -nowarn option or the
+     *  maximum number of warnings has been reached.
+     *  @param flag   A flag to set on the diagnostic
+     *  @param pos    The source position at which to report the warning.
+     *  @param warningKey    The key for the localized warning message.
+     */
+    public void warning(DiagnosticFlag flag, int pos, Warning warningKey) {
+        report(diags.warning(flag, source, wrap(pos), warningKey));
+    }
+
     /** Provide a non-fatal notification, unless suppressed by the -nowarn option.
      *  @param noteKey    The key for the localized notification message.
      */
diff --git a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp
index ff011dca8892..5c84b929ef13 100644
--- a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp
+++ b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp
@@ -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
@@ -1375,7 +1375,7 @@ JNIEXPORT jobject JNICALL Java_sun_security_mscapi_CKeyPairGenerator_00024RSA_ge
                 PROV_RSA_FULL,
                 CRYPT_NEWKEYSET) == FALSE)
             {
-                ThrowException(env, KEY_EXCEPTION, GetLastError());
+                ThrowExceptionWithMessageAndErrcode(env, KEY_EXCEPTION, "CryptAcquireContext failure", GetLastError());
                 __leave;
             }
         }
@@ -1387,7 +1387,7 @@ JNIEXPORT jobject JNICALL Java_sun_security_mscapi_CKeyPairGenerator_00024RSA_ge
            dwFlags,
            &hKeyPair) == FALSE)
         {
-            ThrowException(env, KEY_EXCEPTION, GetLastError());
+            ThrowExceptionWithMessageAndErrcode(env, KEY_EXCEPTION, "CryptGenKey failure", GetLastError());
             __leave;
         }
 
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp
index 62dbc84f88c4..cc03f3fc8326 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp
@@ -31,73 +31,54 @@
 #define CHECK_EXCEPTION if (env->ExceptionCheck()) { return; }
 
 static jfieldID p_dwarf_context_ID = 0;
-static jint sa_RAX = -1;
-static jint sa_RDX = -1;
-static jint sa_RCX = -1;
-static jint sa_RBX = -1;
-static jint sa_RSI = -1;
-static jint sa_RDI = -1;
-static jint sa_RBP = -1;
-static jint sa_RSP = -1;
-static jint sa_R8  = -1;
-static jint sa_R9  = -1;
-static jint sa_R10 = -1;
-static jint sa_R11 = -1;
-static jint sa_R12 = -1;
-static jint sa_R13 = -1;
-static jint sa_R14 = -1;
-static jint sa_R15 = -1;
+
+// DWARF_REG macro is used by DWARF_REGLIST.
+#define DWARF_REG(reg, _) \
+  static jint sa_##reg = -1;
+
+DWARF_REGLIST
+
+#undef DWARF_REG
 
 static jlong get_dwarf_context(JNIEnv *env, jobject obj) {
   return env->GetLongField(obj, p_dwarf_context_ID);
 }
 
-#define SET_REG(env, reg, reg_cls) \
-  jfieldID reg##_ID = env->GetStaticFieldID(reg_cls, #reg, "I"); \
-  CHECK_EXCEPTION \
-  sa_##reg = env->GetStaticIntField(reg_cls, reg##_ID); \
-  CHECK_EXCEPTION
-
 /*
- * Class:     sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
  * Method:    init0
  * Signature: ()V
  */
 extern "C"
-JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_init0
+JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_init0
   (JNIEnv *env, jclass this_cls) {
-  jclass cls = env->FindClass("sun/jvm/hotspot/debugger/linux/amd64/DwarfParser");
+  jclass cls = env->FindClass("sun/jvm/hotspot/debugger/linux/DwarfParser");
   CHECK_EXCEPTION
   p_dwarf_context_ID = env->GetFieldID(cls, "p_dwarf_context", "J");
   CHECK_EXCEPTION
 
-  jclass reg_cls = env->FindClass("sun/jvm/hotspot/debugger/amd64/AMD64ThreadContext");
+  jclass reg_cls = env->FindClass(THREAD_CONTEXT_CLASS);
+  CHECK_EXCEPTION
+
+// DWARF_REG macro is used by DWARF_REGLIST.
+#define DWARF_REG(reg, _) \
+  jfieldID reg##_ID = env->GetStaticFieldID(reg_cls, #reg, "I"); \
+  CHECK_EXCEPTION \
+  sa_##reg = env->GetStaticIntField(reg_cls, reg##_ID); \
   CHECK_EXCEPTION
-  SET_REG(env, RAX, reg_cls);
-  SET_REG(env, RDX, reg_cls);
-  SET_REG(env, RCX, reg_cls);
-  SET_REG(env, RBX, reg_cls);
-  SET_REG(env, RSI, reg_cls);
-  SET_REG(env, RDI, reg_cls);
-  SET_REG(env, RBP, reg_cls);
-  SET_REG(env, RSP, reg_cls);
-  SET_REG(env, R8,  reg_cls);
-  SET_REG(env, R9,  reg_cls);
-  SET_REG(env, R10, reg_cls);
-  SET_REG(env, R11, reg_cls);
-  SET_REG(env, R12, reg_cls);
-  SET_REG(env, R13, reg_cls);
-  SET_REG(env, R14, reg_cls);
-  SET_REG(env, R15, reg_cls);
+
+  DWARF_REGLIST
+
+#undef DWARF_REG
 }
 
 /*
- * Class:     sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
  * Method:    createDwarfContext
  * Signature: (J)J
  */
 extern "C"
-JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_createDwarfContext
+JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_createDwarfContext
   (JNIEnv *env, jclass this_cls, jlong lib) {
   DwarfParser *parser = new DwarfParser(reinterpret_cast(lib));
   if (!parser->is_parseable()) {
@@ -113,36 +94,36 @@ JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_cr
 }
 
 /*
- * Class:     sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
  * Method:    destroyDwarfContext
  * Signature: (J)V
  */
 extern "C"
-JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_destroyDwarfContext
+JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_destroyDwarfContext
   (JNIEnv *env, jclass this_cls, jlong context) {
   DwarfParser *parser = reinterpret_cast(context);
   delete parser;
 }
 
 /*
- * Class:     sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
  * Method:    isIn0
  * Signature: (J)Z
  */
 extern "C"
-JNIEXPORT jboolean JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_isIn0
+JNIEXPORT jboolean JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_isIn0
   (JNIEnv *env, jobject this_obj, jlong pc) {
   DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj));
   return static_cast(parser->is_in(pc));
 }
 
 /*
- * Class:     sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
  * Method:    processDwarf0
  * Signature: (J)V
  */
 extern "C"
-JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_processDwarf0
+JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_processDwarf0
   (JNIEnv *env, jobject this_obj, jlong pc) {
   DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj));
   if (!parser->process_dwarf(pc)) {
@@ -155,67 +136,106 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_pro
 }
 
 /*
- * Class:     sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
  * Method:    getCFARegister
  * Signature: ()I
  */
 extern "C"
-JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getCFARegister
+JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getCFARegister
   (JNIEnv *env, jobject this_obj) {
   DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj));
+
   switch (parser->get_cfa_register()) {
-    case RAX: return sa_RAX;
-    case RDX: return sa_RDX;
-    case RCX: return sa_RCX;
-    case RBX: return sa_RBX;
-    case RSI: return sa_RSI;
-    case RDI: return sa_RDI;
-    case RBP: return sa_RBP;
-    case RSP: return sa_RSP;
-    case R8:  return sa_R8;
-    case R9:  return sa_R9;
-    case R10: return sa_R10;
-    case R11: return sa_R11;
-    case R12: return sa_R12;
-    case R13: return sa_R13;
-    case R14: return sa_R14;
-    case R15: return sa_R15;
+// DWARF_REG macro is used by DWARF_REGLIST.
+#define DWARF_REG(reg, _) \
+    case reg: return sa_##reg;
+
+  DWARF_REGLIST
+
+#undef DWARF_REG
+
     default:  return -1;
   }
 }
 
 /*
- * Class:     sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
  * Method:    getCFAOffset
  * Signature: ()I
  */
 extern "C"
-JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getCFAOffset
+JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getCFAOffset
   (JNIEnv *env, jobject this_obj) {
   DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj));
   return parser->get_cfa_offset();
 }
 
 /*
- * Class:     sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
+ * Method:    getOffsetFromCFA
+ * Signature: (I)I
+ */
+extern "C"
+JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getOffsetFromCFA
+  (JNIEnv *env, jobject this_obj, jint sareg) {
+  DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj));
+
+// DWARF_REG macro is used by DWARF_REGLIST.
+#define DWARF_REG(reg, dwreg) \
+    if (sareg == sa_##reg) { \
+      return parser->get_offset_from_cfa(static_cast(dwreg)); \
+    } else
+
+  DWARF_REGLIST
+
+#undef DWARF_REG
+
+  return INT_MAX;
+}
+
+/*
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
+ * Method:    getRARegister
+ * Signature: ()I
+ */
+extern "C"
+JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getRARegister
+  (JNIEnv *env, jobject this_obj) {
+  DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj));
+
+  switch (parser->get_ra_register()) {
+// DWARF_REG macro is used by DWARF_REGLIST.
+#define DWARF_REG(reg, _) \
+    case reg: return sa_##reg;
+
+  DWARF_REGLIST
+
+#undef DWARF_REG
+
+    default:  return -1;
+  }
+}
+
+/*
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
  * Method:    getReturnAddressOffsetFromCFA
  * Signature: ()I
  */
 extern "C"
-JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getReturnAddressOffsetFromCFA
+JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getReturnAddressOffsetFromCFA
   (JNIEnv *env, jobject this_obj) {
   DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj));
   return parser->get_offset_from_cfa(RA);
 }
 
 /*
- * Class:     sun_jvm_hotspot_debugger_linux_amd64_DwarfParser
+ * Class:     sun_jvm_hotspot_debugger_linux_DwarfParser
  * Method:    getBasePointerOffsetFromCFA
  * Signature: ()I
  */
 extern "C"
-JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_getBasePointerOffsetFromCFA
+JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_DwarfParser_getBasePointerOffsetFromCFA
   (JNIEnv *env, jobject this_obj) {
   DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj));
-  return parser->get_offset_from_cfa(RBP);
+  return parser->get_offset_from_cfa(BP);
 }
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp
index caf948019af7..214e2f21ac6e 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/LinuxDebuggerLocal.cpp
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2019, 2021, NTT DATA.
+ * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, NTT DATA.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -289,6 +289,13 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_at
     snprintf(msg, sizeof(msg), "Can't attach to the process: %s", err_buf);
     THROW_NEW_DEBUGGER_EXCEPTION(msg);
   }
+
+#ifdef __aarch64__
+  if (pac_enabled(ph)) {
+    printf("WARNING: PAC is enabled. Stack traces might be incomplete.\n");
+  }
+#endif
+
   env->SetLongField(this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph);
   fillThreadsAndLoadObjects(env, this_obj, ph);
 }
@@ -313,6 +320,13 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_at
   if ( (ph = Pgrab_core(execName_cstr, coreName_cstr)) == NULL) {
     THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the core file. For more information, export LIBSAPROC_DEBUG=1 and try again.");
   }
+
+#ifdef __aarch64__
+  if (pac_enabled(ph)) {
+    printf("WARNING: PAC is enabled. Stack traces might be incomplete.\n");
+  }
+#endif
+
   env->SetLongField(this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph);
   fillThreadsAndLoadObjects(env, this_obj, ph);
 }
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp
index 459e3cc57e9f..28eb92a285f2 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp
@@ -217,6 +217,20 @@ void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const
         _state.offset_from_cfa[reg] = _initial_state.offset_from_cfa[reg];
         break;
       }
+#ifdef __aarch64__
+      // SA hasn't yet supported Pointer Authetication Code (PAC), so following
+      // instructions would be ignored with warning message.
+      //   https://github.com/ARM-software/abi-aa/blob/2025Q4/aadwarf64/aadwarf64.rst
+      case 0x2d: // DW_CFA_AARCH64_negate_ra_state
+        print_debug("DWARF: DW_CFA_AARCH64_negate_ra_state is unimplemented.\n", op);
+        break;
+      case 0x2c: // DW_CFA_AARCH64_negate_ra_state_with_pc
+        print_debug("DWARF: DW_CFA_AARCH64_negate_ra_state_with_pc is unimplemented.\n", op);
+        break;
+      case 0x2b: // DW_CFA_AARCH64_set_ra_state
+        print_debug("DWARF: DW_CFA_AARCH64_set_ra_state is unimplemented.\n", op);
+        break;
+#endif
       default:
         print_debug("DWARF: Unknown opcode: 0x%x\n", op);
         return;
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp
index 0a38c9a0f2e8..2bfdba65a78f 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp
@@ -30,30 +30,21 @@
 
 #include "libproc_impl.h"
 
-/*
- * from System V Application Binary Interface
- *        AMD64 Architecture Processor Supplement
- *          Figure 3.38: DWARF Register Number Mapping
- * https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf
- */
+#ifdef __x86_64__
+#include "dwarf_regs_amd64.h"
+#elif defined(__aarch64__)
+#include "dwarf_regs_aarch64.h"
+#endif
+
 enum DWARF_Register {
-  RAX,
-  RDX,
-  RCX,
-  RBX,
-  RSI,
-  RDI,
-  RBP,
-  RSP,
-  R8,
-  R9,
-  R10,
-  R11,
-  R12,
-  R13,
-  R14,
-  R15,
-  RA,
+// DWARF_REG macro is used by DWARF_REGLIST and DWARF_PSEUDO_REGLIST.
+#define DWARF_REG(reg, no) \
+  reg = no,
+
+  DWARF_REGLIST
+  DWARF_PSEUDO_REGLIST
+
+#undef DWARF_REG
   MAX_VALUE
 };
 
@@ -94,6 +85,7 @@ class DwarfParser {
     bool process_dwarf(const uintptr_t pc);
     enum DWARF_Register get_cfa_register() { return _state.cfa_reg; }
     int get_cfa_offset() { return _state.cfa_offset; }
+    enum DWARF_Register get_ra_register() { return _state.return_address_reg; }
     int get_offset_from_cfa(enum DWARF_Register reg) { return _state.offset_from_cfa[reg]; }
 
     bool is_in(long pc) {
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_aarch64.h b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_aarch64.h
new file mode 100644
index 000000000000..5a95e9405e16
--- /dev/null
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_aarch64.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026, NTT DATA.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef DWARF_REGS_AARCH64_H
+#define DWARF_REGS_AARCH64_H
+
+#define THREAD_CONTEXT_CLASS "sun/jvm/hotspot/debugger/aarch64/AARCH64ThreadContext"
+
+/*
+ * from DWARF for the Arm (R) 64-bit Architecture (AArch64)
+ *   https://github.com/ARM-software/abi-aa/blob/2025Q4/aadwarf64/aadwarf64.rst
+ *       4.1 DWARF register names
+ */
+#define DWARF_REGLIST \
+  DWARF_REG(R0,   0) \
+  DWARF_REG(R1,   1) \
+  DWARF_REG(R2,   2) \
+  DWARF_REG(R3,   3) \
+  DWARF_REG(R4,   4) \
+  DWARF_REG(R5,   5) \
+  DWARF_REG(R6,   6) \
+  DWARF_REG(R7,   7) \
+  DWARF_REG(R8,   8) \
+  DWARF_REG(R9,   9) \
+  DWARF_REG(R10, 10) \
+  DWARF_REG(R11, 11) \
+  DWARF_REG(R12, 12) \
+  DWARF_REG(R13, 13) \
+  DWARF_REG(R14, 14) \
+  DWARF_REG(R15, 15) \
+  DWARF_REG(R16, 16) \
+  DWARF_REG(R17, 17) \
+  DWARF_REG(R18, 18) \
+  DWARF_REG(R19, 19) \
+  DWARF_REG(R20, 20) \
+  DWARF_REG(R21, 21) \
+  DWARF_REG(R22, 22) \
+  DWARF_REG(R23, 23) \
+  DWARF_REG(R24, 24) \
+  DWARF_REG(R25, 25) \
+  DWARF_REG(R26, 26) \
+  DWARF_REG(R27, 27) \
+  DWARF_REG(R28, 28) \
+  DWARF_REG(FP,  29) \
+  DWARF_REG(LR,  30) \
+  DWARF_REG(SP,  31) \
+  DWARF_REG(PC,  32)
+
+// RA_SIGN_STATE might be needed in future to handle PAC.
+#define DWARF_PSEUDO_REGLIST
+
+/* Aliases */
+#define BP FP
+#define RA LR
+
+#endif
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_amd64.h b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_amd64.h
new file mode 100644
index 000000000000..8226bc0864c7
--- /dev/null
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf_regs_amd64.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026, NTT DATA.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef DWARF_REGS_AMD64_H
+#define DWARF_REGS_AMD64_H
+
+#define THREAD_CONTEXT_CLASS "sun/jvm/hotspot/debugger/amd64/AMD64ThreadContext"
+
+/*
+ * from System V Application Binary Interface
+ *      AMD64 Architecture Processor Supplement
+ *   https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
+ *       Figure 3.36: DWARF Register Number Mapping
+ */
+#define DWARF_REGLIST \
+  DWARF_REG(RAX,  0) \
+  DWARF_REG(RDX,  1) \
+  DWARF_REG(RCX,  2) \
+  DWARF_REG(RBX,  3) \
+  DWARF_REG(RSI,  4) \
+  DWARF_REG(RDI,  5) \
+  DWARF_REG(RBP,  6) \
+  DWARF_REG(RSP,  7) \
+  DWARF_REG(R8,   8) \
+  DWARF_REG(R9,   9) \
+  DWARF_REG(R10, 10) \
+  DWARF_REG(R11, 11) \
+  DWARF_REG(R12, 12) \
+  DWARF_REG(R13, 13) \
+  DWARF_REG(R14, 14) \
+  DWARF_REG(R15, 15)
+
+#define DWARF_PSEUDO_REGLIST \
+  DWARF_REG(RA,  16)
+
+/* Aliases */
+#define BP RBP
+
+#endif
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h
index a69496e77a4d..c584131e2852 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2022, 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
@@ -119,6 +119,10 @@ struct ps_prochandle* get_proc_handle(JNIEnv* env, jobject this_obj);
 
 void throw_new_debugger_exception(JNIEnv* env, const char* errMsg);
 
+#ifdef __aarch64__
+bool pac_enabled(struct ps_prochandle* ph);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c
index 029aac1f107b..815902045cff 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c
@@ -478,6 +478,12 @@ struct lib_info *find_lib_by_address(struct ps_prochandle* ph, uintptr_t pc) {
   return NULL;
 }
 
+#ifdef __aarch64__
+bool pac_enabled(struct ps_prochandle* ph) {
+  return ph->pac_enabled;
+}
+#endif
+
 //--------------------------------------------------------------------------
 // proc service functions
 
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h
index 62b1b4d0d6b4..d5aa74e73ad7 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h
@@ -115,6 +115,10 @@ struct ps_prochandle {
    int                num_threads;
    thread_info*       threads;   // head of thread list
    struct core_data*  core;      // data only used for core dumps, NULL for process
+#ifdef __aarch64__
+   // true if the HWCAP_PACA variant of Pointer Authentication Code (PAC) is enabled.
+   bool               pac_enabled;
+#endif
 };
 
 #ifdef __cplusplus
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c
index 6298f569aaf8..c500360f39db 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c
@@ -39,6 +39,12 @@
 #include "proc_service.h"
 #include "salibelf.h"
 
+// HWCAP_PACA was introduced in glibc 2.30
+// https://sourceware.org/git/?p=glibc.git;a=commit;h=a2e57f89a35e6056c9488428e68c4889e114ef71
+#if defined(__aarch64__) && !defined(HWCAP_PACA)
+#define HWCAP_PACA (1 << 30)
+#endif
+
 // This file has the libproc implementation to read core files.
 // For live processes, refer to ps_proc.c. Portions of this is adapted
 // /modelled after Solaris libproc.so (in particular Pcore.c)
@@ -290,6 +296,10 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) {
             break;
           } else if (auxv->a_type == AT_SYSINFO_EHDR) {
             ph->core->vdso_addr = auxv->a_un.a_val;
+#ifdef __aarch64__
+          } else if (auxv->a_type == AT_HWCAP) {
+            ph->pac_enabled = auxv->a_un.a_val & HWCAP_PACA;
+#endif
           }
           auxv++;
         }
@@ -610,23 +620,38 @@ static uintptr_t calc_prelinked_load_address(struct ps_prochandle* ph, int lib_f
   return load_addr;
 }
 
-// Check for vDSO binary in kernel directory (/lib/modules//vdso),
-// rewrite the given lib_name string if found.
-// Otherwise copy vDSO memory in coredump to temporal file generated by tmpfile().
-// Returns FD for vDSO (should be closed by caller), or -1 on error.
-static int handle_vdso(struct ps_prochandle* ph, char* lib_name, size_t lib_name_len) {
+static int handle_vdso_internal(struct ps_prochandle* ph, char* vdso_name, char* lib_name, size_t lib_name_len) {
   int lib_fd;
   struct utsname uts;
   uname(&uts);
 
-  // Check vDSO binary first (for referring debuginfo if possible).
   char *vdso_path = (char*)malloc(lib_name_len);
-  snprintf(vdso_path, lib_name_len, "/lib/modules/%s/vdso/vdso64.so", uts.release);
+  snprintf(vdso_path, lib_name_len, "/lib/modules/%s/vdso/%s", uts.release, vdso_name);
+  print_debug("Try to open vDSO: %s\n", vdso_path);
   lib_fd = pathmap_open(vdso_path);
   if (lib_fd != -1) {
     print_debug("replace vDSO: %s -> %s\n", lib_name, vdso_path);
     strncpy(lib_name, vdso_path, lib_name_len);
-  } else {
+  }
+
+  free(vdso_path);
+  return lib_fd;
+}
+
+// Check for vDSO binary in kernel directory (/lib/modules//vdso),
+// rewrite the given lib_name string if found.
+// Otherwise copy vDSO memory in coredump to temporal file generated by tmpfile().
+// Returns FD for vDSO (should be closed by caller), or -1 on error.
+static int handle_vdso(struct ps_prochandle* ph, char* lib_name, size_t lib_name_len) {
+  int lib_fd;
+
+  // Check vDSO binary first (for referring debuginfo if possible).
+  lib_fd = handle_vdso_internal(ph, "vdso64.so", lib_name, lib_name_len);
+  if (lib_fd == -1) {
+    // Try again with vdso.so
+    lib_fd = handle_vdso_internal(ph, "vdso.so", lib_name, lib_name_len);
+  }
+  if (lib_fd == -1) {
     // Copy vDSO memory segment from core to temporal memory
     // if vDSO binary is not available.
     FILE* tmpf = tmpfile();
@@ -644,7 +669,6 @@ static int handle_vdso(struct ps_prochandle* ph, char* lib_name, size_t lib_name
     }
   }
 
-  free(vdso_path);
   return lib_fd;
 }
 
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c
index fdaa30c3f5d0..9cbde7319f01 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c
@@ -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
@@ -36,6 +36,17 @@
 #include 
 #include "libproc_impl.h"
 
+#ifdef __aarch64__
+#include 
+
+// HWCAP_PACA was introduced in glibc 2.30
+// https://sourceware.org/git/?p=glibc.git;a=commit;h=a2e57f89a35e6056c9488428e68c4889e114ef71
+#ifndef HWCAP_PACA
+#define HWCAP_PACA (1 << 30)
+#endif
+
+#endif
+
 #if defined(x86_64) && !defined(amd64)
 #define amd64 1
 #endif
@@ -460,6 +471,10 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) {
     return NULL;
   }
 
+#ifdef __aarch64__
+  ph->pac_enabled = HWCAP_PACA & getauxval(AT_HWCAP);
+#endif
+
   // initialize ps_prochandle
   ph->pid = pid;
   if (add_thread_info(ph, ph->pid) == NULL) {
diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c
index c8f3fb2ed4cf..8f8ce28be1e7 100644
--- a/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c
+++ b/src/jdk.hotspot.agent/linux/native/libsaproc/symtab.c
@@ -417,10 +417,14 @@ static struct symtab* build_symtab_internal(int fd, const char *filename, bool t
         uintptr_t sym_value;
         char *sym_name = symtab->strs + syms->st_name;
 
-        // skip non-object and non-function symbols
+        // skip non-object and non-function symbols, but STT_NOTYPE is allowed for
+        // signal trampoline.
         int st_type = ELF_ST_TYPE(syms->st_info);
-        if ( st_type != STT_FUNC && st_type != STT_OBJECT)
+        if (st_type != STT_FUNC &&
+            st_type != STT_OBJECT &&
+            st_type != STT_NOTYPE) {
            continue;
+        }
         // skip empty strings and undefined symbols
         if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue;
 
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java
index bc366ef02b5a..86f9e990af8f 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2025, 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
@@ -39,7 +39,7 @@ public interface CFrame {
   public CFrame sender(ThreadProxy th);
 
   /** Find sender frame with given FP and PC */
-  public default CFrame sender(ThreadProxy th, Address sp, Address fp, Address pc) {
+  public default CFrame sender(ThreadProxy th, Address senderSP, Address senderFP, Address senderPC) {
     return sender(th);
   }
 
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java
new file mode 100644
index 000000000000..7baa399ea75e
--- /dev/null
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfCFrame.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026, NTT DATA.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package sun.jvm.hotspot.debugger.linux;
+
+import sun.jvm.hotspot.debugger.Address;
+import sun.jvm.hotspot.debugger.ThreadProxy;
+import sun.jvm.hotspot.debugger.UnalignedAddressException;
+import sun.jvm.hotspot.debugger.UnmappedAddressException;
+import sun.jvm.hotspot.debugger.cdbg.CFrame;
+import sun.jvm.hotspot.debugger.cdbg.ClosestSymbol;
+import sun.jvm.hotspot.debugger.cdbg.basic.BasicCFrame;
+import sun.jvm.hotspot.runtime.VM;
+
+public class DwarfCFrame extends BasicCFrame {
+
+    private Address sp;
+    private Address fp;
+    private Address pc;
+    private Address cfa;
+    private LinuxDebugger linuxDbg;
+    private DwarfParser dwarf;
+    private boolean use1ByteBeforeToLookup;
+
+    /**
+     * @return DwarfParser instance for the PC, null if native library relates to the pc not found.
+     * @throws DebuggerException if DWARF processing is failed.
+     *         For example: pc is not covered in this DWARF, Common Information Entry (CIE) has
+     *         language personality routine and/or Language Data Area (LSDA).
+     */
+    protected static DwarfParser createDwarfParser(LinuxDebugger linuxDbg, Address pc) {
+        Address libptr = linuxDbg.findLibPtrByAddress(pc);
+        if (libptr != null) {
+            DwarfParser dwarf = new DwarfParser(libptr);
+            dwarf.processDwarf(pc);
+            return dwarf;
+        }
+        return null;
+    }
+
+    protected DwarfCFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, DwarfParser dwarf) {
+        this(linuxDbg, sp, fp, cfa, pc, dwarf, false);
+    }
+
+    protected DwarfCFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, DwarfParser dwarf, boolean use1ByteBeforeToLookup) {
+        super(linuxDbg.getCDebugger());
+        this.sp = sp;
+        this.fp = fp;
+        this.cfa = cfa;
+        this.pc = pc;
+        this.linuxDbg = linuxDbg;
+        this.dwarf = dwarf;
+        this.use1ByteBeforeToLookup = use1ByteBeforeToLookup;
+    }
+
+    public Address sp() {
+        return sp;
+    }
+
+    public Address fp() {
+        return fp;
+    }
+
+    public Address pc() {
+        return pc;
+    }
+
+    public LinuxDebugger linuxDbg() {
+        return linuxDbg;
+    }
+
+    public DwarfParser dwarf() {
+        return dwarf;
+    }
+
+    // override base class impl to avoid ELF parsing
+    @Override
+    public ClosestSymbol closestSymbolToPC() {
+        Address symAddr = use1ByteBeforeToLookup ? pc.addOffsetTo(-1) : pc;
+        var sym = linuxDbg.lookup(linuxDbg.getAddressValue(symAddr));
+
+        // Returns a special symbol if the address is signal trampoline,
+        // otherwise returns closest symbol generated by LinuxDebugger.
+        return linuxDbg.isSignalTrampoline(symAddr)
+            ? new ClosestSymbol(sym.getName() + " ", 0)
+            : sym;
+    }
+
+    @Override
+    public Address localVariableBase() {
+        return (dwarf != null && dwarf.isBPOffsetAvailable())
+            ? cfa.addOffsetTo(dwarf.getBasePointerOffsetFromCFA())
+            : fp;
+    }
+
+    protected boolean isValidFrame(Address senderCFA, Address senderFP) {
+        // Both CFA and FP must not be null.
+        if (senderCFA == null && senderFP == null) {
+            return false;
+        }
+
+        // FP must not be null if CFA is null - it happens between Java frame and Native frame.
+        // We cannot validate FP value because it might be used as GPR. Thus returns true
+        // if FP is not null.
+        if (senderCFA == null && senderFP != null) {
+            return true;
+        }
+
+        // senderCFA must be greater than current CFA.
+        if (senderCFA != null && senderCFA.greaterThanOrEqual(cfa)) {
+            return true;
+        }
+
+        // Otherwise, the frame is not valid.
+        return false;
+    }
+
+    protected Address getSenderPC(Address senderPC) {
+        if (senderPC != null) {
+            return senderPC;
+        }
+
+        try {
+            return dwarf == null
+                ? fp.getAddressAt(VM.getVM().getAddressSize()) // Current frame is Java
+                : cfa.getAddressAt(dwarf.getReturnAddressOffsetFromCFA()); // current frame is Native
+        } catch (UnmappedAddressException | UnalignedAddressException _) {
+            // Sender PC is invalid - maybe bottom of stack
+            return null;
+        }
+    }
+
+    protected Address getSenderSP(Address senderSP) {
+        if (senderSP != null) {
+            return senderSP;
+        } else if (dwarf == null) {
+            // Current frame is Java - skip saved BP and RA
+            return fp.addOffsetTo(2 * VM.getVM().getAddressSize());
+        } else {
+            // Current frame is Native
+            // CFA points SP at the call site in the previous frame.
+            // See 6.4 Call Frame Information in DWARF Debugging Information Format
+            //   https://dwarfstd.org/dwarf4std.html
+            return cfa;
+        }
+    }
+
+    protected Address getSenderFP(Address senderFP) {
+        if (senderFP != null) {
+            return senderFP;
+        } else if (dwarf == null) { // Current frame is Java
+            return fp.getAddressAt(0);
+        } else { // Current frame is Native
+            return dwarf.isBPOffsetAvailable()
+                ? cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA())
+                : fp;
+        }
+    }
+
+    @Override
+    public CFrame sender(ThreadProxy th) {
+        return sender(th, null, null, null);
+    }
+
+}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfParser.java
similarity index 95%
rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java
rename to src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfParser.java
index 53351c918d37..3e8099cf75b2 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/DwarfParser.java
@@ -23,7 +23,7 @@
  *
  */
 
-package sun.jvm.hotspot.debugger.linux.amd64;
+package sun.jvm.hotspot.debugger.linux;
 
 import java.lang.ref.Cleaner;
 import sun.jvm.hotspot.debugger.Address;
@@ -75,6 +75,8 @@ public boolean isBPOffsetAvailable() {
 
   public native int getCFARegister();
   public native int getCFAOffset();
+  public native int getOffsetFromCFA(int sareg);
+  public native int getRARegister();
   public native int getReturnAddressOffsetFromCFA();
   public native int getBasePointerOffsetFromCFA();
 }
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java
index 15f6615421cc..57ba419aa9dc 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java
@@ -91,13 +91,7 @@ public CFrame topFrameForThread(ThreadProxy thread) throws DebuggerException {
         return new LinuxPPC64CFrame(dbg, sp, pc, LinuxDebuggerLocal.getAddressSize());
     } else if (cpu.equals("aarch64")) {
        AARCH64ThreadContext context = (AARCH64ThreadContext) thread.getContext();
-       Address sp = context.getRegisterAsAddress(AARCH64ThreadContext.SP);
-       if (sp == null) return null;
-       Address fp = context.getRegisterAsAddress(AARCH64ThreadContext.FP);
-       if (fp == null) return null;
-       Address pc  = context.getRegisterAsAddress(AARCH64ThreadContext.PC);
-       if (pc == null) return null;
-       return new LinuxAARCH64CFrame(dbg, sp, fp, pc);
+       return LinuxAARCH64CFrame.getTopFrame(dbg, context);
     } else if (cpu.equals("riscv64")) {
        RISCV64ThreadContext context = (RISCV64ThreadContext) thread.getContext();
        Address sp = context.getRegisterAsAddress(RISCV64ThreadContext.SP);
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java
index a53b8a0a2824..c09af9881dd7 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebugger.java
@@ -33,12 +33,17 @@
     by the architecture-specific subpackages. */
 
 public interface LinuxDebugger extends JVMDebugger {
-  // SIGHANDLER_NAMES holds the name of signal handler.
-  public static final List SIGHANDLER_NAMES = List.of(
+  // SIGTRAMP_NAMES holds the name of signal trampoline.
+  public static final List SIGTRAMP_NAMES = List.of(
     // For AMD64
     //   - sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c in glibc
     //   - gdb/amd64-linux-tdep.c in GDB
-    "__restore_rt"
+    "__restore_rt",
+
+    // For AArch64
+    //   - arch/arm64/kernel/vdso/vdso.lds.S in Linux kernel
+    "__kernel_rt_sigreturn",
+    "VDSO_sigtramp"
   );
 
   public String       addressValueToString(long address) throws DebuggerException;
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java
index 9a75511e44d3..856981bb73c3 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java
@@ -133,7 +133,7 @@ public Address findLibPtrByAddress(Address pc) {
     @Override
     public boolean isSignalTrampoline(Address pc) {
       var sym = lookup(getAddressValue(pc));
-      return sym == null ? false : SIGHANDLER_NAMES.contains(sym.getName());
+      return sym == null ? false : SIGTRAMP_NAMES.contains(sym.getName());
     }
 
     // Note on Linux threads are really processes. When target process is
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java
index 5f76e6308e9f..c55aca2155c0 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java
@@ -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.
  * Copyright (c) 2015, Red Hat Inc.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -25,73 +25,109 @@
 
 package sun.jvm.hotspot.debugger.linux.aarch64;
 
+import java.util.function.Function;
+
 import sun.jvm.hotspot.debugger.*;
 import sun.jvm.hotspot.debugger.aarch64.*;
 import sun.jvm.hotspot.debugger.linux.*;
 import sun.jvm.hotspot.debugger.cdbg.*;
-import sun.jvm.hotspot.debugger.cdbg.basic.*;
 import sun.jvm.hotspot.code.*;
 import sun.jvm.hotspot.runtime.*;
 import sun.jvm.hotspot.runtime.aarch64.*;
 
-public final class LinuxAARCH64CFrame extends BasicCFrame {
-   public LinuxAARCH64CFrame(LinuxDebugger dbg, Address sp, Address fp, Address pc) {
-      super(dbg.getCDebugger());
-      this.sp = sp;
-      this.fp = fp;
-      this.pc = pc;
-      this.dbg = dbg;
-   }
+public final class LinuxAARCH64CFrame extends DwarfCFrame {
+
+   private Address lr;
+
+   private static LinuxAARCH64CFrame getFrameFromReg(LinuxDebugger linuxDbg, Function getreg) {
+      Address pc = getreg.apply(AARCH64ThreadContext.PC);
+      Address sp = getreg.apply(AARCH64ThreadContext.SP);
+      Address fp = getreg.apply(AARCH64ThreadContext.FP);
+      Address lr = getreg.apply(AARCH64ThreadContext.LR);
+      Address cfa = null;
+      DwarfParser dwarf = createDwarfParser(linuxDbg, pc);
 
-   // override base class impl to avoid ELF parsing
-   public ClosestSymbol closestSymbolToPC() {
-      // try native lookup in debugger.
-      return dbg.lookup(dbg.getAddressValue(pc()));
+      if (dwarf != null) { // Native frame
+        cfa = getreg.apply(dwarf.getCFARegister())
+                    .addOffsetTo(dwarf.getCFAOffset());
+      }
+
+      return (fp == null && cfa == null)
+        ? null
+        : new LinuxAARCH64CFrame(linuxDbg, sp, fp, cfa, pc, lr, dwarf);
    }
 
-   public Address pc() {
-      return pc;
+   public static LinuxAARCH64CFrame getTopFrame(LinuxDebugger linuxDbg, ThreadContext context) {
+      return getFrameFromReg(linuxDbg, context::getRegisterAsAddress);
    }
 
-   public Address localVariableBase() {
-      return fp;
+   private LinuxAARCH64CFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, Address lr, DwarfParser dwarf) {
+      this(linuxDbg, sp, fp, cfa, pc, lr, dwarf, false);
    }
 
-   @Override
-   public CFrame sender(ThreadProxy thread) {
-      return sender(thread, null, null, null);
+   private LinuxAARCH64CFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, DwarfParser dwarf) {
+      this(linuxDbg, sp, fp, cfa, pc, null, dwarf, false);
    }
 
-   @Override
-   public CFrame sender(ThreadProxy thread, Address nextSP, Address nextFP, Address nextPC) {
-      // Check fp
-      // Skip if both nextFP and nextPC are given - do not need to load from fp.
-      if (nextFP == null && nextPC == null) {
-        if (fp == null) {
-          return null;
-        }
+   private LinuxAARCH64CFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, DwarfParser dwarf, boolean use1ByteBeforeToLookup) {
+      this(linuxDbg, sp, fp, cfa, pc, null, dwarf, use1ByteBeforeToLookup);
+   }
 
-        // Check alignment of fp
-        if (dbg.getAddressValue(fp) % (2 * ADDRESS_SIZE) != 0) {
-          return null;
+   private LinuxAARCH64CFrame(LinuxDebugger linuxDbg, Address sp, Address fp, Address cfa, Address pc, Address lr, DwarfParser dwarf, boolean use1ByteBeforeToLookup) {
+      super(linuxDbg, sp, fp, cfa, pc, dwarf, use1ByteBeforeToLookup);
+
+      if (dwarf != null) {
+        // Prioritize to use RA from DWARF instead of LR
+        var senderPCFromDwarf = getSenderPC(null);
+        if (senderPCFromDwarf != null) {
+          lr = senderPCFromDwarf;
+        } else if (lr != null) {
+          // We should set passed lr to LR of this frame,
+          // but throws DebuggerException if lr is not used for RA.
+          var raReg = dwarf.getRARegister();
+          if (raReg != AARCH64ThreadContext.LR) {
+            throw new DebuggerException("Unexpected RA register: " + raReg);
+          }
         }
       }
 
-      if (nextFP == null) {
-        nextFP = fp.getAddressAt(0 * ADDRESS_SIZE);
-      }
-      if (nextFP == null) {
-        return null;
-      }
+      this.lr = lr;
+   }
 
-      if (nextPC == null) {
-        nextPC  = fp.getAddressAt(1 * ADDRESS_SIZE);
+   private Address getSenderCFA(DwarfParser senderDwarf, Address senderSP, Address senderFP) {
+     if (senderDwarf == null) { // Sender frame is Java
+       // CFA is not available on Java frame
+       return null;
+     }
+
+     // Sender frame is Native
+     int senderCFAReg = senderDwarf.getCFARegister();
+     return switch(senderCFAReg){
+       case AARCH64ThreadContext.FP -> senderFP.addOffsetTo(senderDwarf.getCFAOffset());
+       case AARCH64ThreadContext.SP -> senderSP.addOffsetTo(senderDwarf.getCFAOffset());
+       default -> throw new DebuggerException("Unsupported CFA register: " + senderCFAReg);
+     };
+   }
+
+   @Override
+   public CFrame sender(ThreadProxy thread, Address senderSP, Address senderFP, Address senderPC) {
+      if (linuxDbg().isSignalTrampoline(pc())) {
+        // SP points signal context
+        //   https://github.com/torvalds/linux/blob/v6.17/arch/arm64/kernel/signal.c#L1357
+        return getFrameFromReg(linuxDbg(), r -> LinuxAARCH64ThreadContext.getRegFromSignalTrampoline(sp(), r.intValue()));
       }
-      if (nextPC == null) {
-        return null;
+
+      if (senderPC == null) {
+        // Use getSenderPC() if current frame is Java because we cannot rely on lr in this case.
+        senderPC = dwarf() == null ? getSenderPC(null) : lr;
+        if (senderPC == null) {
+          return null;
+        }
       }
 
-      if (nextSP == null) {
+      senderFP = getSenderFP(senderFP);
+
+      if (senderSP == null) {
         CodeCache cc = VM.getVM().getCodeCache();
         CodeBlob currentBlob = cc.findBlobUnsafe(pc());
 
@@ -99,29 +135,62 @@ public CFrame sender(ThreadProxy thread, Address nextSP, Address nextFP, Address
         if (currentBlob != null && (currentBlob.isContinuationStub() || currentBlob.isNativeMethod())) {
           // Use FP since it should always be valid for these cases.
           // TODO: These should be walked as Frames not CFrames.
-          nextSP = fp.addOffsetTo(2 * ADDRESS_SIZE);
+          senderSP = fp().addOffsetTo(2 * VM.getVM().getAddressSize());
         } else {
-          CodeBlob codeBlob = cc.findBlobUnsafe(nextPC);
+          CodeBlob codeBlob = cc.findBlobUnsafe(senderPC);
           boolean useCodeBlob = codeBlob != null && codeBlob.getFrameSize() > 0;
-          nextSP = useCodeBlob ? nextFP.addOffsetTo((2 * ADDRESS_SIZE) - codeBlob.getFrameSize()) : nextFP;
+          senderSP = useCodeBlob ? senderFP.addOffsetTo((2 * VM.getVM().getAddressSize()) - codeBlob.getFrameSize()) : getSenderSP(null);
         }
       }
-      if (nextSP == null) {
+      if (senderSP == null) {
         return null;
       }
 
-      return new LinuxAARCH64CFrame(dbg, nextSP, nextFP, nextPC);
+      DwarfParser senderDwarf = null;
+      boolean fallback = false;
+      try {
+        senderDwarf = createDwarfParser(linuxDbg(), senderPC);
+      } catch (DebuggerException _) {
+        // Try again with PC-1 in case PC is just outside function bounds,
+        // due to function ending with a `call` instruction.
+        try {
+          senderDwarf = createDwarfParser(linuxDbg(), senderPC.addOffsetTo(-1));
+          fallback = true;
+        } catch (DebuggerException _) {
+          if (linuxDbg().isSignalTrampoline(senderPC)) {
+            // We can use the caller frame if it is a signal trampoline.
+            // DWARF processing might fail because vdso.so .eh_frame is not required on aarch64.
+            return new LinuxAARCH64CFrame(linuxDbg(), senderSP, senderFP, null, senderPC, senderDwarf);
+          }
+
+          // DWARF processing should succeed when the frame is native
+          // but it might fail if Common Information Entry (CIE) has language
+          // personality routine and/or Language Specific Data Area (LSDA).
+          return null;
+        }
+      }
+
+      try {
+        Address senderCFA = getSenderCFA(senderDwarf, senderSP, senderFP);
+        return isValidFrame(senderCFA, senderFP)
+          ? new LinuxAARCH64CFrame(linuxDbg(), senderSP, senderFP, senderCFA, senderPC, senderDwarf, fallback)
+          : null;
+      } catch (DebuggerException e) {
+        if (linuxDbg().isSignalTrampoline(senderPC)) {
+          // We can use the caller frame if it is a signal trampoline.
+          // getSenderCFA() might fail because DwarfParser cannot find out CFA register.
+          return new LinuxAARCH64CFrame(linuxDbg(), senderSP, senderFP, null, senderPC, senderDwarf, fallback);
+        }
+
+        // Rethrow the original exception if getSenderCFA() failed
+        // and the caller is not signal trampoline.
+        throw e;
+      }
    }
 
    @Override
    public Frame toFrame() {
-     return new AARCH64Frame(sp, fp, pc);
+     return new AARCH64Frame(sp(), fp(), pc());
    }
 
-   // package/class internals only
-   private static final int ADDRESS_SIZE = 8;
-   private Address pc;
-   private Address sp;
-   private Address fp;
-   private LinuxDebugger dbg;
 }
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64ThreadContext.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64ThreadContext.java
index 77003168671d..422ca001624a 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64ThreadContext.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64ThreadContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2015, Red Hat Inc.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -28,6 +28,7 @@
 import sun.jvm.hotspot.debugger.*;
 import sun.jvm.hotspot.debugger.aarch64.*;
 import sun.jvm.hotspot.debugger.linux.*;
+import sun.jvm.hotspot.runtime.*;
 
 public class LinuxAARCH64ThreadContext extends AARCH64ThreadContext {
   private LinuxDebugger debugger;
@@ -44,4 +45,24 @@ public void setRegisterAsAddress(int index, Address value) {
   public Address getRegisterAsAddress(int index) {
     return debugger.newAddress(getRegister(index));
   }
+
+  public static Address getRegFromSignalTrampoline(Address sp, int index) {
+    // ucontext_t locates at 2nd element of rt_sigframe.
+    // See definition of rt_sigframe in arch/arm/kernel/signal.h
+    // in Linux Kernel.
+    Address addrUContext = sp.addOffsetTo(128); // sizeof(siginfo_t) = 128
+    Address addrUCMContext = addrUContext.addOffsetTo(176); // offsetof(ucontext_t, uc_mcontext) = 176
+
+    Address ptrCallerSP = addrUCMContext.addOffsetTo(256); // offsetof(uc_mcontext, sp) = 256
+    Address ptrCallerPC = addrUCMContext.addOffsetTo(264); // offsetof(uc_mcontext, pc) = 264
+    Address ptrCallerRegs = addrUCMContext.addOffsetTo(8); // offsetof(uc_mcontext, regs) = 8
+
+    return switch(index) {
+      case AARCH64ThreadContext.FP -> ptrCallerRegs.getAddressAt(AARCH64ThreadContext.FP * VM.getVM().getAddressSize());
+      case AARCH64ThreadContext.LR -> ptrCallerRegs.getAddressAt(AARCH64ThreadContext.LR * VM.getVM().getAddressSize());
+      case AARCH64ThreadContext.SP -> ptrCallerSP.getAddressAt(0);
+      case AARCH64ThreadContext.PC -> ptrCallerPC.getAddressAt(0);
+      default -> throw new IllegalArgumentException("Unsupported register index: " + index);
+    };
+  }
 }
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java
index 4d3d9d5998d1..e58e2facdd7c 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java
@@ -29,181 +29,82 @@
 import sun.jvm.hotspot.debugger.*;
 import sun.jvm.hotspot.debugger.amd64.*;
 import sun.jvm.hotspot.debugger.linux.*;
-import sun.jvm.hotspot.debugger.linux.amd64.*;
 import sun.jvm.hotspot.debugger.cdbg.*;
-import sun.jvm.hotspot.debugger.cdbg.basic.*;
 import sun.jvm.hotspot.runtime.*;
 import sun.jvm.hotspot.runtime.amd64.*;
 
-public final class LinuxAMD64CFrame extends BasicCFrame {
+public final class LinuxAMD64CFrame extends DwarfCFrame {
 
-   private static LinuxAMD64CFrame getFrameFromReg(LinuxDebugger dbg, Function getreg) {
+   private static LinuxAMD64CFrame getFrameFromReg(LinuxDebugger linuxDbg, Function getreg) {
       Address rip = getreg.apply(AMD64ThreadContext.RIP);
       Address rsp = getreg.apply(AMD64ThreadContext.RSP);
       Address rbp = getreg.apply(AMD64ThreadContext.RBP);
-      Address libptr = dbg.findLibPtrByAddress(rip);
       Address cfa = null;
-      DwarfParser dwarf = null;
-
-      if (libptr != null) { // Native frame
-        dwarf = new DwarfParser(libptr);
-        try {
-          dwarf.processDwarf(rip);
-        } catch (DebuggerException e) {
-          // DWARF processing should succeed when the frame is native
-          // but it might fail if Common Information Entry (CIE) has language
-          // personality routine and/or Language Specific Data Area (LSDA).
-          return new LinuxAMD64CFrame(dbg, rsp, rbp, cfa, rip, dwarf, true);
-        }
+      DwarfParser dwarf = createDwarfParser(linuxDbg, rip);
 
+      if (dwarf != null) { // Native frame
         cfa = getreg.apply(dwarf.getCFARegister())
                     .addOffsetTo(dwarf.getCFAOffset());
       }
 
       return (rbp == null && cfa == null)
         ? null
-        : new LinuxAMD64CFrame(dbg, rsp, rbp, cfa, rip, dwarf);
-   }
-
-   public static LinuxAMD64CFrame getTopFrame(LinuxDebugger dbg, ThreadContext context) {
-      return getFrameFromReg(dbg, context::getRegisterAsAddress);
-   }
-
-   private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf) {
-      this(dbg, rsp, rbp, cfa, rip, dwarf, false);
-   }
-
-   private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf, boolean use1ByteBeforeToLookup) {
-      super(dbg.getCDebugger());
-      this.rsp = rsp;
-      this.rbp = rbp;
-      this.cfa = cfa;
-      this.rip = rip;
-      this.dbg = dbg;
-      this.dwarf = dwarf;
-      this.use1ByteBeforeToLookup = use1ByteBeforeToLookup;
-   }
-
-   // override base class impl to avoid ELF parsing
-   public ClosestSymbol closestSymbolToPC() {
-      Address symAddr = use1ByteBeforeToLookup ? pc().addOffsetTo(-1) : pc();
-      var sym = dbg.lookup(dbg.getAddressValue(symAddr));
-
-      // Returns a special symbol if the address is signal handler,
-      // otherwise returns closest symbol generated by LinuxDebugger.
-      return dbg.isSignalTrampoline(symAddr)
-        ? new ClosestSymbol(sym.getName() + " ", 0)
-        : sym;
+        : new LinuxAMD64CFrame(linuxDbg, rsp, rbp, cfa, rip, dwarf);
    }
 
-   public Address pc() {
-      return rip;
+   public static LinuxAMD64CFrame getTopFrame(LinuxDebugger linuxDbg, ThreadContext context) {
+      return getFrameFromReg(linuxDbg, context::getRegisterAsAddress);
    }
 
-   public Address localVariableBase() {
-      return (dwarf != null && dwarf.isBPOffsetAvailable())
-        ? cfa.addOffsetTo(dwarf.getBasePointerOffsetFromCFA())
-        : rbp;
+   private LinuxAMD64CFrame(LinuxDebugger linuxDbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf) {
+      this(linuxDbg, rsp, rbp, cfa, rip, dwarf, false);
    }
 
-   private Address getNextPC() {
-     try {
-       return dwarf == null
-         ? rbp.getAddressAt(ADDRESS_SIZE) // Java frame
-         : cfa.getAddressAt(dwarf.getReturnAddressOffsetFromCFA()); // Native frame
-     } catch (UnmappedAddressException | UnalignedAddressException e) {
-       return null;
-     }
+   private LinuxAMD64CFrame(LinuxDebugger linuxDbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf, boolean use1ByteBeforeToLookup) {
+      super(linuxDbg, rsp, rbp, cfa, rip, dwarf, use1ByteBeforeToLookup);
    }
 
-   private boolean isValidFrame(Address nextCFA, Address nextRBP) {
-     // Both CFA and RBP must not be null.
-     if (nextCFA == null && nextRBP == null) {
-       return false;
-     }
-
-     // RBP must not be null if CFA is null - it happens between Java frame and Native frame.
-     // We cannot validate RBP value because it might be used as GPR. Thus returns true
-     // if RBP is not null.
-     if (nextCFA == null && nextRBP != null) {
-       return true;
-     }
-
-     // nextCFA must be greater than current CFA.
-     if (nextCFA != null && nextCFA.greaterThanOrEqual(cfa)) {
-       return true;
-     }
-
-     // Otherwise, the frame is not valid.
-     return false;
-   }
-
-   private Address getNextRSP() {
-     return dwarf == null ? rbp.addOffsetTo(2 * ADDRESS_SIZE) // Java frame - skip saved BP and RA
-                          : cfa.addOffsetTo(dwarf.getReturnAddressOffsetFromCFA())
-                               .addOffsetTo(ADDRESS_SIZE); // Native frame
-   }
-
-   private Address getNextRBP(Address senderFP) {
-     if (senderFP != null) {
-       return senderFP;
-     } else if (dwarf == null) { // Current frame is Java
-       return rbp.getAddressAt(0);
-     } else { // Current frame is Native
-       return dwarf.isBPOffsetAvailable()
-         ? cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA())
-         : rbp;
-     }
-   }
-
-   private Address getNextCFA(DwarfParser nextDwarf, Address senderFP, Address senderPC) {
-     if (nextDwarf == null) { // Next frame is Java
+   private Address getSenderCFA(DwarfParser senderDwarf, Address senderSP, Address senderFP) {
+     if (senderDwarf == null) { // Sender frame is Java
        // CFA is not available on Java frame
        return null;
      }
 
-     // Next frame is Native
-     int nextCFAReg = nextDwarf.getCFARegister();
-     return switch(nextCFAReg){
-       case AMD64ThreadContext.RBP -> getNextRBP(senderFP).addOffsetTo(nextDwarf.getCFAOffset());
-       case AMD64ThreadContext.RSP -> getNextRSP().addOffsetTo(nextDwarf.getCFAOffset());
-       default -> throw new DebuggerException("Unsupported CFA register: " + nextCFAReg);
+     // Sender frame is Native
+     int senderCFAReg = senderDwarf.getCFARegister();
+     return switch(senderCFAReg){
+       case AMD64ThreadContext.RBP -> senderFP.addOffsetTo(senderDwarf.getCFAOffset());
+       case AMD64ThreadContext.RSP -> senderSP.addOffsetTo(senderDwarf.getCFAOffset());
+       default -> throw new DebuggerException("Unsupported CFA register: " + senderCFAReg);
      };
    }
 
    @Override
-   public CFrame sender(ThreadProxy th) {
-     return sender(th, null, null, null);
-   }
-
-   @Override
-   public CFrame sender(ThreadProxy th, Address sp, Address fp, Address pc) {
-     if (dbg.isSignalTrampoline(pc())) {
+   public CFrame sender(ThreadProxy th, Address senderSP, Address senderFP, Address senderPC) {
+     if (linuxDbg().isSignalTrampoline(pc())) {
        // RSP points signal context
        //   https://github.com/torvalds/linux/blob/v6.17/arch/x86/kernel/signal.c#L94
-       return getFrameFromReg(dbg, r -> LinuxAMD64ThreadContext.getRegFromSignalTrampoline(this.rsp, r.intValue()));
+       return getFrameFromReg(linuxDbg(), r -> LinuxAMD64ThreadContext.getRegFromSignalTrampoline(sp(), r.intValue()));
      }
 
-     ThreadContext context = th.getContext();
-
-     Address nextRSP = sp != null ? sp : getNextRSP();
-     if (nextRSP == null) {
+     senderSP = getSenderSP(senderSP);
+     if (senderSP == null) {
        return null;
      }
-     Address nextPC = pc != null ? pc : getNextPC();
-     if (nextPC == null) {
+     senderPC = getSenderPC(senderPC);
+     if (senderPC == null) {
        return null;
      }
 
-     DwarfParser nextDwarf = null;
+     DwarfParser senderDwarf = null;
      boolean fallback = false;
      try {
-       nextDwarf = createDwarfParser(nextPC);
+       senderDwarf = createDwarfParser(linuxDbg(), senderPC);
      } catch (DebuggerException _) {
-       // Try again with RIP-1 in case RIP is just outside function bounds,
+       // Try again with PC-1 in case PC is just outside function bounds,
        // due to function ending with a `call` instruction.
        try {
-         nextDwarf = createDwarfParser(nextPC.addOffsetTo(-1));
+         senderDwarf = createDwarfParser(linuxDbg(), senderPC.addOffsetTo(-1));
          fallback = true;
        } catch (DebuggerException _) {
          // DWARF processing should succeed when the frame is native
@@ -213,56 +114,29 @@ public CFrame sender(ThreadProxy th, Address sp, Address fp, Address pc) {
        }
      }
 
-     Address nextRBP = getNextRBP(fp);
+     senderFP = getSenderFP(senderFP);
 
      try {
-       Address nextCFA = getNextCFA(nextDwarf, fp, nextPC);
-       return isValidFrame(nextCFA, nextRBP)
-         ? new LinuxAMD64CFrame(dbg, nextRSP, nextRBP, nextCFA, nextPC, nextDwarf, fallback)
+       Address senderCFA = getSenderCFA(senderDwarf, senderSP, senderFP);
+       return isValidFrame(senderCFA, senderFP)
+         ? new LinuxAMD64CFrame(linuxDbg(), senderSP, senderFP, senderCFA, senderPC, senderDwarf, fallback)
          : null;
      } catch (DebuggerException e) {
-       if (dbg.isSignalTrampoline(nextPC)) {
-         // We can through the caller frame if it is signal trampoline.
-         // getNextCFA() might fail because DwarfParser cannot find out CFA register.
-         return new LinuxAMD64CFrame(dbg, nextRSP, nextRBP, null, nextPC, nextDwarf, fallback);
+       if (linuxDbg().isSignalTrampoline(senderPC)) {
+         // We can use the caller frame if it is a signal trampoline.
+         // getSenderCFA() might fail because DwarfParser cannot find out CFA register.
+         return new LinuxAMD64CFrame(linuxDbg(), senderSP, senderFP, null, senderPC, senderDwarf, fallback);
        }
 
-       // Rethrow the original exception if getNextCFA() failed
+       // Rethrow the original exception if getSenderCFA() failed
        // and the caller is not signal trampoline.
        throw e;
      }
    }
 
-   private DwarfParser createDwarfParser(Address pc) throws DebuggerException {
-     DwarfParser nextDwarf = null;
-     Address libptr = dbg.findLibPtrByAddress(pc);
-     if (libptr != null) {
-       try {
-         nextDwarf = new DwarfParser(libptr);
-       } catch (DebuggerException _) {
-         // Bail out to Java frame
-       }
-     }
-
-     if (nextDwarf != null) {
-       nextDwarf.processDwarf(pc);
-     }
-
-     return nextDwarf;
-   }
-
    @Override
    public Frame toFrame() {
-     return new AMD64Frame(rsp, localVariableBase(), rip);
+     return new AMD64Frame(sp(), localVariableBase(), pc());
    }
 
-   // package/class internals only
-   private static final int ADDRESS_SIZE = 8;
-   private Address rsp;
-   private Address rbp;
-   private Address rip;
-   private Address cfa;
-   private LinuxDebugger dbg;
-   private DwarfParser dwarf;
-   private boolean use1ByteBeforeToLookup;
 }
diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java
index 2f2d33ab130f..cc5a7ccbdefd 100644
--- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java
+++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java
@@ -560,15 +560,15 @@ static boolean opKind(Operator op, int bit) {
 
 
     /** Produce {@code a<<(n&(ESIZE*8-1))}.  Integral only. */
-    public static final /*bitwise*/ Binary LSHL = binary("LSHL", "<<", VectorSupport.VECTOR_OP_LSHIFT, VO_SHIFT);
+    public static final /*bitwise*/ Binary LSHL = binary("LSHL", "<<", VectorSupport.VECTOR_OP_LSHIFT, VO_SHIFT+VO_NOFP);
     /** Produce {@code a>>(n&(ESIZE*8-1))}.  Integral only. */
-    public static final /*bitwise*/ Binary ASHR = binary("ASHR", ">>", VectorSupport.VECTOR_OP_RSHIFT, VO_SHIFT);
+    public static final /*bitwise*/ Binary ASHR = binary("ASHR", ">>", VectorSupport.VECTOR_OP_RSHIFT, VO_SHIFT+VO_NOFP);
     /** Produce {@code (a&EMASK)>>>(n&(ESIZE*8-1))}.  Integral only. */
-    public static final /*bitwise*/ Binary LSHR = binary("LSHR", ">>>", VectorSupport.VECTOR_OP_URSHIFT, VO_SHIFT);
+    public static final /*bitwise*/ Binary LSHR = binary("LSHR", ">>>", VectorSupport.VECTOR_OP_URSHIFT, VO_SHIFT+VO_NOFP);
     /** Produce {@code rotateLeft(a,n)}.  Integral only. */
-    public static final /*bitwise*/ Binary ROL = binary("ROL", "rotateLeft", VectorSupport.VECTOR_OP_LROTATE, VO_SHIFT);
+    public static final /*bitwise*/ Binary ROL = binary("ROL", "rotateLeft", VectorSupport.VECTOR_OP_LROTATE, VO_SHIFT+VO_NOFP);
     /** Produce {@code rotateRight(a,n)}.  Integral only. */
-    public static final /*bitwise*/ Binary ROR = binary("ROR", "rotateRight", VectorSupport.VECTOR_OP_RROTATE, VO_SHIFT);
+    public static final /*bitwise*/ Binary ROR = binary("ROR", "rotateRight", VectorSupport.VECTOR_OP_RROTATE, VO_SHIFT+VO_NOFP);
     /** Produce {@code compress(a,n)}. Integral, {@code int} and {@code long}, only.
      * @since 19
      */
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java
index 66fcd90e2e84..17d56ff11ba7 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java
@@ -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
@@ -335,16 +335,9 @@ protected void generateOtherFiles(ClassTree classTree)
 
         if (options.createIndex()) {
             copyResource(DocPaths.SEARCH_JS_TEMPLATE, DocPaths.SCRIPT_FILES.resolve(DocPaths.SEARCH_JS), true);
-            copyResource(DocPaths.SEARCH_PAGE_JS, DocPaths.SCRIPT_FILES.resolve(DocPaths.SEARCH_PAGE_JS), true);
             copyResource(DocPaths.GLASS_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.GLASS_SVG), false);
             copyResource(DocPaths.X_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.X_SVG), false);
-            // No newline replacement for JQuery files
-            copyResource(DocPaths.JQUERY_DIR.resolve(DocPaths.JQUERY_JS),
-                    DocPaths.SCRIPT_FILES.resolve(DocPaths.JQUERY_JS), false);
-            copyResource(DocPaths.JQUERY_DIR.resolve(DocPaths.JQUERY_UI_JS),
-                    DocPaths.SCRIPT_FILES.resolve(DocPaths.JQUERY_UI_JS), false);
-            copyResource(DocPaths.JQUERY_DIR.resolve(DocPaths.JQUERY_UI_CSS),
-                    DocPaths.RESOURCE_FILES.resolve(DocPaths.JQUERY_UI_CSS), false);        }
+        }
 
         copyLegalFiles(options.createIndex(), options.syntaxHighlight());
         // Print a notice if the documentation contains diagnostic markers
@@ -369,7 +362,7 @@ private void copyLegalFiles(boolean includeJQuery, boolean includeHighlightJs) t
             case "", "default" -> {
                 // use a known resource as a stand-in, because we cannot get the URL for a resources directory
                 var url = HtmlDoclet.class.getResource(
-                        DocPaths.RESOURCES.resolve(DocPaths.LEGAL).resolve(DocPaths.JQUERY_MD).getPath());
+                        DocPaths.RESOURCES.resolve(DocPaths.LEGAL).resolve(DocPaths.DEJAVU_MD).getPath());
                 if (url != null) {
                     try {
                         legalNoticesDir = Path.of(url.toURI()).getParent();
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java
index a3fba7eca142..50e6207b833b 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -112,6 +112,11 @@ public class HtmlIds {
     static final HtmlId RELATED_PACKAGE_SUMMARY = HtmlId.of("related-package-summary");
     static final HtmlId RESET_SEARCH = HtmlId.of("reset-search");
     static final HtmlId SEARCH_INPUT = HtmlId.of("search-input");
+    static final HtmlId SEARCH_INPUT_CONTAINER = HtmlId.of("search-input-container");
+    static final HtmlId SEARCH_MODULES = HtmlId.of("search-modules");
+    static final HtmlId SEARCH_PAGE_LINK = HtmlId.of("search-page-link");
+    static final HtmlId SEARCH_RESULT_CONTAINER = HtmlId.of("search-result-container");
+    static final HtmlId SEARCH_RESULT_SECTION = HtmlId.of("search-result-section");
     static final HtmlId SERVICES = HtmlId.of("services-summary");
     static final HtmlId SKIP_NAVBAR_TOP = HtmlId.of("skip-navbar-top");
     static final HtmlId THEME_BUTTON = HtmlId.of("theme-button");
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java
index 30318bbaeeab..cde5b287e25c 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -45,6 +45,8 @@
 import jdk.javadoc.internal.html.ContentBuilder;
 import jdk.javadoc.internal.html.Entity;
 import jdk.javadoc.internal.html.HtmlAttr;
+import jdk.javadoc.internal.html.HtmlId;
+import jdk.javadoc.internal.html.HtmlTag;
 import jdk.javadoc.internal.html.HtmlTree;
 import jdk.javadoc.internal.html.Text;
 
@@ -535,6 +537,42 @@ private void addSearch(Content target) {
                 .add(inputText)
                 .add(inputReset);
         target.add(searchDiv);
+        target.add(HtmlTree.DIV(HtmlIds.SEARCH_RESULT_SECTION)
+                .add(HtmlTree.DIV(HtmlStyles.searchForm)
+                        .add(HtmlTree.DIV(HtmlTree.LABEL(HtmlIds.SEARCH_INPUT.name(),
+                                contents.getContent("doclet.search.for"))))
+                        .add(HtmlTree.DIV(HtmlIds.SEARCH_INPUT_CONTAINER).addUnchecked(Text.EMPTY))
+                        .add(createModuleSelector()))
+                .add(HtmlTree.DIV(HtmlIds.SEARCH_RESULT_CONTAINER).addUnchecked(Text.EMPTY))
+                .add(HtmlTree.DIV(HtmlStyles.searchLinks)
+                        .add(HtmlTree.DIV(links.createLink(pathToRoot.resolve(DocPaths.SEARCH_PAGE),
+                                        contents.getContent("doclet.search.linkSearchPageLabel"))
+                                .setId(HtmlIds.SEARCH_PAGE_LINK)))
+                        .add(options.noHelp() || !options.helpFile().isEmpty()
+                                ? HtmlTree.DIV(Text.EMPTY).addUnchecked(Text.EMPTY)
+                                : HtmlTree.DIV(links.createLink(pathToRoot.resolve(DocPaths.HELP_DOC).fragment("search"),
+                                    contents.getContent("doclet.search.linkSearchHelpLabel"))))));
+    }
+
+    private Content createModuleSelector() {
+        if (!configuration.showModules || configuration.modules.size() < 2) {
+            return Text.EMPTY;
+        }
+        var content = new ContentBuilder(HtmlTree.DIV(HtmlTree.LABEL(HtmlIds.SEARCH_MODULES.name(),
+                contents.getContent("doclet.search.in_modules"))));
+        var select = HtmlTree.of(HtmlTag.SELECT)
+                .setId(HtmlIds.SEARCH_MODULES)
+                .put(HtmlAttr.ARIA_LABEL, configuration.getDocResources().getText("doclet.selectModule"))
+                .add(HtmlTree.of(HtmlTag.OPTION)
+                        .put(HtmlAttr.VALUE, "")
+                        .add(contents.getContent("doclet.search.all_modules")));
+
+        for (ModuleElement module : configuration.modules) {
+            select.add(HtmlTree.of(HtmlTag.OPTION)
+                    .put(HtmlAttr.VALUE, module.getQualifiedName().toString())
+                    .add(Text.of(module.getQualifiedName().toString())));
+        }
+        return content.add(HtmlTree.DIV(select));
     }
 
     /**
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java
index 433a641530df..5fa0daacf987 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SearchWriter.java
@@ -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
@@ -92,17 +92,15 @@ protected void addSearchFileContents(Content contentTree) {
                 .add(resourceSection)
                 .add(HtmlTree.P(contents.getContent("doclet.search.loading"))
                         .setId(HtmlId.of("page-search-notify")))
-                .add(HtmlTree.DIV(HtmlTree.DIV(HtmlId.of("result-container"))
+                .add(HtmlTree.DIV(HtmlTree.DIV(HtmlIds.SEARCH_RESULT_CONTAINER)
                                 .addUnchecked(Text.EMPTY))
-                        .setId(HtmlId.of("result-section"))
-                        .put(HtmlAttr.STYLE, "display: none;")
-                        .add(HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.SCRIPT_FILES)
-                                                       .resolve(DocPaths.SEARCH_PAGE_JS).getPath())));
+                        .setId(HtmlIds.SEARCH_RESULT_SECTION)
+                        .put(HtmlAttr.STYLE, "display: none;"));
     }
 
     private Content createModuleSelector() {
 
-        if (!configuration.showModules) {
+        if (!configuration.showModules || configuration.modules.size() < 2) {
             return Text.EMPTY;
         }
 
@@ -118,7 +116,7 @@ private Content createModuleSelector() {
                     .put(HtmlAttr.VALUE, module.getQualifiedName().toString())
                     .add(Text.of(module.getQualifiedName().toString())));
         }
-        return new ContentBuilder(contents.getContent("doclet.search.in", select));
+        return new ContentBuilder(contents.getContent("doclet.search.in_modules"), select);
     }
 
     private Content createResourceSection() {
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java
index cda4bc9a5be4..bff32cbd7bf9 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java
@@ -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
@@ -336,11 +336,6 @@ private Comment getGeneratedBy(boolean timestamp, ZonedDateTime buildDate) {
     }
 
     private void addStylesheets(HtmlTree head) {
-        if (index) {
-            // Add JQuery-UI stylesheet first so its rules can be overridden.
-            addStylesheet(head, DocPaths.RESOURCE_FILES.resolve(DocPaths.JQUERY_UI_CSS));
-        }
-
         if (mainStylesheet == null) {
             mainStylesheet = DocPaths.STYLESHEET;
         }
@@ -381,8 +376,6 @@ private void addScripts(HtmlTree head) {
                         .append("loadScripts();\n")
                         .append("initTheme();\n");
             }
-            addScriptElement(head, DocPaths.JQUERY_JS);
-            addScriptElement(head, DocPaths.JQUERY_UI_JS);
         }
         for (HtmlConfiguration.JavaScriptFile javaScriptFile : additionalScripts) {
             addScriptElement(head, javaScriptFile);
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java
index 9b59cb0cb475..2b154db7de76 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyles.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -734,6 +734,16 @@ public enum HtmlStyles implements HtmlStyle {
      */
     pageSearchInfo,
 
+    /**
+     * The class for a {@code div} element in the search widget containing the search form inputs.
+     */
+    searchForm,
+
+    /**
+     * The class for a {@code div} element in the search widget containing search-related links.
+     */
+    searchLinks,
+
     /**
      * The class for a link in the static "Index" pages to a custom searchable item,
      * such as defined with an {@code @index} tag.
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/jquery-3.7.1.js b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/jquery-3.7.1.js
deleted file mode 100644
index 1a86433c2230..000000000000
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/jquery/jquery-3.7.1.js
+++ /dev/null
@@ -1,10716 +0,0 @@
-/*!
- * jQuery JavaScript Library v3.7.1
- * https://jquery.com/
- *
- * Copyright OpenJS Foundation and other contributors
- * Released under the MIT license
- * https://jquery.org/license
- *
- * Date: 2023-08-28T13:37Z
- */
-( function( global, factory ) {
-
-	"use strict";
-
-	if ( typeof module === "object" && typeof module.exports === "object" ) {
-
-		// For CommonJS and CommonJS-like environments where a proper `window`
-		// is present, execute the factory and get jQuery.
-		// For environments that do not have a `window` with a `document`
-		// (such as Node.js), expose a factory as module.exports.
-		// This accentuates the need for the creation of a real `window`.
-		// e.g. var jQuery = require("jquery")(window);
-		// See ticket trac-14549 for more info.
-		module.exports = global.document ?
-			factory( global, true ) :
-			function( w ) {
-				if ( !w.document ) {
-					throw new Error( "jQuery requires a window with a document" );
-				}
-				return factory( w );
-			};
-	} else {
-		factory( global );
-	}
-
-// Pass this if window is not defined yet
-} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
-
-// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
-// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
-// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
-// enough that all such attempts are guarded in a try block.
-"use strict";
-
-var arr = [];
-
-var getProto = Object.getPrototypeOf;
-
-var slice = arr.slice;
-
-var flat = arr.flat ? function( array ) {
-	return arr.flat.call( array );
-} : function( array ) {
-	return arr.concat.apply( [], array );
-};
-
-
-var push = arr.push;
-
-var indexOf = arr.indexOf;
-
-var class2type = {};
-
-var toString = class2type.toString;
-
-var hasOwn = class2type.hasOwnProperty;
-
-var fnToString = hasOwn.toString;
-
-var ObjectFunctionString = fnToString.call( Object );
-
-var support = {};
-
-var isFunction = function isFunction( obj ) {
-
-		// Support: Chrome <=57, Firefox <=52
-		// In some browsers, typeof returns "function" for HTML  elements
-		// (i.e., `typeof document.createElement( "object" ) === "function"`).
-		// We don't want to classify *any* DOM node as a function.
-		// Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5
-		// Plus for old WebKit, typeof returns "function" for HTML collections
-		// (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756)
-		return typeof obj === "function" && typeof obj.nodeType !== "number" &&
-			typeof obj.item !== "function";
-	};
-
-
-var isWindow = function isWindow( obj ) {
-		return obj != null && obj === obj.window;
-	};
-
-
-var document = window.document;
-
-
-
-	var preservedScriptAttributes = {
-		type: true,
-		src: true,
-		nonce: true,
-		noModule: true
-	};
-
-	function DOMEval( code, node, doc ) {
-		doc = doc || document;
-
-		var i, val,
-			script = doc.createElement( "script" );
-
-		script.text = code;
-		if ( node ) {
-			for ( i in preservedScriptAttributes ) {
-
-				// Support: Firefox 64+, Edge 18+
-				// Some browsers don't support the "nonce" property on scripts.
-				// On the other hand, just using `getAttribute` is not enough as
-				// the `nonce` attribute is reset to an empty string whenever it
-				// becomes browsing-context connected.
-				// See https://github.com/whatwg/html/issues/2369
-				// See https://html.spec.whatwg.org/#nonce-attributes
-				// The `node.getAttribute` check was added for the sake of
-				// `jQuery.globalEval` so that it can fake a nonce-containing node
-				// via an object.
-				val = node[ i ] || node.getAttribute && node.getAttribute( i );
-				if ( val ) {
-					script.setAttribute( i, val );
-				}
-			}
-		}
-		doc.head.appendChild( script ).parentNode.removeChild( script );
-	}
-
-
-function toType( obj ) {
-	if ( obj == null ) {
-		return obj + "";
-	}
-
-	// Support: Android <=2.3 only (functionish RegExp)
-	return typeof obj === "object" || typeof obj === "function" ?
-		class2type[ toString.call( obj ) ] || "object" :
-		typeof obj;
-}
-/* global Symbol */
-// Defining this global in .eslintrc.json would create a danger of using the global
-// unguarded in another place, it seems safer to define global only for this module
-
-
-
-var version = "3.7.1",
-
-	rhtmlSuffix = /HTML$/i,
-
-	// Define a local copy of jQuery
-	jQuery = function( selector, context ) {
-
-		// The jQuery object is actually just the init constructor 'enhanced'
-		// Need init if jQuery is called (just allow error to be thrown if not included)
-		return new jQuery.fn.init( selector, context );
-	};
-
-jQuery.fn = jQuery.prototype = {
-
-	// The current version of jQuery being used
-	jquery: version,
-
-	constructor: jQuery,
-
-	// The default length of a jQuery object is 0
-	length: 0,
-
-	toArray: function() {
-		return slice.call( this );
-	},
-
-	// Get the Nth element in the matched element set OR
-	// Get the whole matched element set as a clean array
-	get: function( num ) {
-
-		// Return all the elements in a clean array
-		if ( num == null ) {
-			return slice.call( this );
-		}
-
-		// Return just the one element from the set
-		return num < 0 ? this[ num + this.length ] : this[ num ];
-	},
-
-	// Take an array of elements and push it onto the stack
-	// (returning the new matched element set)
-	pushStack: function( elems ) {
-
-		// Build a new jQuery matched element set
-		var ret = jQuery.merge( this.constructor(), elems );
-
-		// Add the old object onto the stack (as a reference)
-		ret.prevObject = this;
-
-		// Return the newly-formed element set
-		return ret;
-	},
-
-	// Execute a callback for every element in the matched set.
-	each: function( callback ) {
-		return jQuery.each( this, callback );
-	},
-
-	map: function( callback ) {
-		return this.pushStack( jQuery.map( this, function( elem, i ) {
-			return callback.call( elem, i, elem );
-		} ) );
-	},
-
-	slice: function() {
-		return this.pushStack( slice.apply( this, arguments ) );
-	},
-
-	first: function() {
-		return this.eq( 0 );
-	},
-
-	last: function() {
-		return this.eq( -1 );
-	},
-
-	even: function() {
-		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
-			return ( i + 1 ) % 2;
-		} ) );
-	},
-
-	odd: function() {
-		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
-			return i % 2;
-		} ) );
-	},
-
-	eq: function( i ) {
-		var len = this.length,
-			j = +i + ( i < 0 ? len : 0 );
-		return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
-	},
-
-	end: function() {
-		return this.prevObject || this.constructor();
-	},
-
-	// For internal use only.
-	// Behaves like an Array's method, not like a jQuery method.
-	push: push,
-	sort: arr.sort,
-	splice: arr.splice
-};
-
-jQuery.extend = jQuery.fn.extend = function() {
-	var options, name, src, copy, copyIsArray, clone,
-		target = arguments[ 0 ] || {},
-		i = 1,
-		length = arguments.length,
-		deep = false;
-
-	// Handle a deep copy situation
-	if ( typeof target === "boolean" ) {
-		deep = target;
-
-		// Skip the boolean and the target
-		target = arguments[ i ] || {};
-		i++;
-	}
-
-	// Handle case when target is a string or something (possible in deep copy)
-	if ( typeof target !== "object" && !isFunction( target ) ) {
-		target = {};
-	}
-
-	// Extend jQuery itself if only one argument is passed
-	if ( i === length ) {
-		target = this;
-		i--;
-	}
-
-	for ( ; i < length; i++ ) {
-
-		// Only deal with non-null/undefined values
-		if ( ( options = arguments[ i ] ) != null ) {
-
-			// Extend the base object
-			for ( name in options ) {
-				copy = options[ name ];
-
-				// Prevent Object.prototype pollution
-				// Prevent never-ending loop
-				if ( name === "__proto__" || target === copy ) {
-					continue;
-				}
-
-				// Recurse if we're merging plain objects or arrays
-				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
-					( copyIsArray = Array.isArray( copy ) ) ) ) {
-					src = target[ name ];
-
-					// Ensure proper type for the source value
-					if ( copyIsArray && !Array.isArray( src ) ) {
-						clone = [];
-					} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
-						clone = {};
-					} else {
-						clone = src;
-					}
-					copyIsArray = false;
-
-					// Never move original objects, clone them
-					target[ name ] = jQuery.extend( deep, clone, copy );
-
-				// Don't bring in undefined values
-				} else if ( copy !== undefined ) {
-					target[ name ] = copy;
-				}
-			}
-		}
-	}
-
-	// Return the modified object
-	return target;
-};
-
-jQuery.extend( {
-
-	// Unique for each copy of jQuery on the page
-	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
-
-	// Assume jQuery is ready without the ready module
-	isReady: true,
-
-	error: function( msg ) {
-		throw new Error( msg );
-	},
-
-	noop: function() {},
-
-	isPlainObject: function( obj ) {
-		var proto, Ctor;
-
-		// Detect obvious negatives
-		// Use toString instead of jQuery.type to catch host objects
-		if ( !obj || toString.call( obj ) !== "[object Object]" ) {
-			return false;
-		}
-
-		proto = getProto( obj );
-
-		// Objects with no prototype (e.g., `Object.create( null )`) are plain
-		if ( !proto ) {
-			return true;
-		}
-
-		// Objects with prototype are plain iff they were constructed by a global Object function
-		Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
-		return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
-	},
-
-	isEmptyObject: function( obj ) {
-		var name;
-
-		for ( name in obj ) {
-			return false;
-		}
-		return true;
-	},
-
-	// Evaluates a script in a provided context; falls back to the global one
-	// if not specified.
-	globalEval: function( code, options, doc ) {
-		DOMEval( code, { nonce: options && options.nonce }, doc );
-	},
-
-	each: function( obj, callback ) {
-		var length, i = 0;
-
-		if ( isArrayLike( obj ) ) {
-			length = obj.length;
-			for ( ; i < length; i++ ) {
-				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
-					break;
-				}
-			}
-		} else {
-			for ( i in obj ) {
-				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
-					break;
-				}
-			}
-		}
-
-		return obj;
-	},
-
-
-	// Retrieve the text value of an array of DOM nodes
-	text: function( elem ) {
-		var node,
-			ret = "",
-			i = 0,
-			nodeType = elem.nodeType;
-
-		if ( !nodeType ) {
-
-			// If no nodeType, this is expected to be an array
-			while ( ( node = elem[ i++ ] ) ) {
-
-				// Do not traverse comment nodes
-				ret += jQuery.text( node );
-			}
-		}
-		if ( nodeType === 1 || nodeType === 11 ) {
-			return elem.textContent;
-		}
-		if ( nodeType === 9 ) {
-			return elem.documentElement.textContent;
-		}
-		if ( nodeType === 3 || nodeType === 4 ) {
-			return elem.nodeValue;
-		}
-
-		// Do not include comment or processing instruction nodes
-
-		return ret;
-	},
-
-	// results is for internal usage only
-	makeArray: function( arr, results ) {
-		var ret = results || [];
-
-		if ( arr != null ) {
-			if ( isArrayLike( Object( arr ) ) ) {
-				jQuery.merge( ret,
-					typeof arr === "string" ?
-						[ arr ] : arr
-				);
-			} else {
-				push.call( ret, arr );
-			}
-		}
-
-		return ret;
-	},
-
-	inArray: function( elem, arr, i ) {
-		return arr == null ? -1 : indexOf.call( arr, elem, i );
-	},
-
-	isXMLDoc: function( elem ) {
-		var namespace = elem && elem.namespaceURI,
-			docElem = elem && ( elem.ownerDocument || elem ).documentElement;
-
-		// Assume HTML when documentElement doesn't yet exist, such as inside
-		// document fragments.
-		return !rhtmlSuffix.test( namespace || docElem && docElem.nodeName || "HTML" );
-	},
-
-	// Support: Android <=4.0 only, PhantomJS 1 only
-	// push.apply(_, arraylike) throws on ancient WebKit
-	merge: function( first, second ) {
-		var len = +second.length,
-			j = 0,
-			i = first.length;
-
-		for ( ; j < len; j++ ) {
-			first[ i++ ] = second[ j ];
-		}
-
-		first.length = i;
-
-		return first;
-	},
-
-	grep: function( elems, callback, invert ) {
-		var callbackInverse,
-			matches = [],
-			i = 0,
-			length = elems.length,
-			callbackExpect = !invert;
-
-		// Go through the array, only saving the items
-		// that pass the validator function
-		for ( ; i < length; i++ ) {
-			callbackInverse = !callback( elems[ i ], i );
-			if ( callbackInverse !== callbackExpect ) {
-				matches.push( elems[ i ] );
-			}
-		}
-
-		return matches;
-	},
-
-	// arg is for internal usage only
-	map: function( elems, callback, arg ) {
-		var length, value,
-			i = 0,
-			ret = [];
-
-		// Go through the array, translating each of the items to their new values
-		if ( isArrayLike( elems ) ) {
-			length = elems.length;
-			for ( ; i < length; i++ ) {
-				value = callback( elems[ i ], i, arg );
-
-				if ( value != null ) {
-					ret.push( value );
-				}
-			}
-
-		// Go through every key on the object,
-		} else {
-			for ( i in elems ) {
-				value = callback( elems[ i ], i, arg );
-
-				if ( value != null ) {
-					ret.push( value );
-				}
-			}
-		}
-
-		// Flatten any nested arrays
-		return flat( ret );
-	},
-
-	// A global GUID counter for objects
-	guid: 1,
-
-	// jQuery.support is not used in Core but other projects attach their
-	// properties to it so it needs to exist.
-	support: support
-} );
-
-if ( typeof Symbol === "function" ) {
-	jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
-}
-
-// Populate the class2type map
-jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
-	function( _i, name ) {
-		class2type[ "[object " + name + "]" ] = name.toLowerCase();
-	} );
-
-function isArrayLike( obj ) {
-
-	// Support: real iOS 8.2 only (not reproducible in simulator)
-	// `in` check used to prevent JIT error (gh-2145)
-	// hasOwn isn't used here due to false negatives
-	// regarding Nodelist length in IE
-	var length = !!obj && "length" in obj && obj.length,
-		type = toType( obj );
-
-	if ( isFunction( obj ) || isWindow( obj ) ) {
-		return false;
-	}
-
-	return type === "array" || length === 0 ||
-		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
-}
-
-
-function nodeName( elem, name ) {
-
-	return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
-
-}
-var pop = arr.pop;
-
-
-var sort = arr.sort;
-
-
-var splice = arr.splice;
-
-
-var whitespace = "[\\x20\\t\\r\\n\\f]";
-
-
-var rtrimCSS = new RegExp(
-	"^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$",
-	"g"
-);
-
-
-
-
-// Note: an element does not contain itself
-jQuery.contains = function( a, b ) {
-	var bup = b && b.parentNode;
-
-	return a === bup || !!( bup && bup.nodeType === 1 && (
-
-		// Support: IE 9 - 11+
-		// IE doesn't have `contains` on SVG.
-		a.contains ?
-			a.contains( bup ) :
-			a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
-	) );
-};
-
-
-
-
-// CSS string/identifier serialization
-// https://drafts.csswg.org/cssom/#common-serializing-idioms
-var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
-
-function fcssescape( ch, asCodePoint ) {
-	if ( asCodePoint ) {
-
-		// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
-		if ( ch === "\0" ) {
-			return "\uFFFD";
-		}
-
-		// Control characters and (dependent upon position) numbers get escaped as code points
-		return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
-	}
-
-	// Other potentially-special ASCII characters get backslash-escaped
-	return "\\" + ch;
-}
-
-jQuery.escapeSelector = function( sel ) {
-	return ( sel + "" ).replace( rcssescape, fcssescape );
-};
-
-
-
-
-var preferredDoc = document,
-	pushNative = push;
-
-( function() {
-
-var i,
-	Expr,
-	outermostContext,
-	sortInput,
-	hasDuplicate,
-	push = pushNative,
-
-	// Local document vars
-	document,
-	documentElement,
-	documentIsHTML,
-	rbuggyQSA,
-	matches,
-
-	// Instance-specific data
-	expando = jQuery.expando,
-	dirruns = 0,
-	done = 0,
-	classCache = createCache(),
-	tokenCache = createCache(),
-	compilerCache = createCache(),
-	nonnativeSelectorCache = createCache(),
-	sortOrder = function( a, b ) {
-		if ( a === b ) {
-			hasDuplicate = true;
-		}
-		return 0;
-	},
-
-	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" +
-		"loop|multiple|open|readonly|required|scoped",
-
-	// Regular expressions
-
-	// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
-	identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
-		"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
-
-	// Attribute selectors: https://www.w3.org/TR/selectors/#attribute-selectors
-	attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
-
-		// Operator (capture 2)
-		"*([*^$|!~]?=)" + whitespace +
-
-		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
-		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
-		whitespace + "*\\]",
-
-	pseudos = ":(" + identifier + ")(?:\\((" +
-
-		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
-		// 1. quoted (capture 3; capture 4 or capture 5)
-		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
-
-		// 2. simple (capture 6)
-		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
-
-		// 3. anything else (capture 2)
-		".*" +
-		")\\)|)",
-
-	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
-	rwhitespace = new RegExp( whitespace + "+", "g" ),
-
-	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
-	rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" +
-		whitespace + "*" ),
-	rdescend = new RegExp( whitespace + "|>" ),
-
-	rpseudo = new RegExp( pseudos ),
-	ridentifier = new RegExp( "^" + identifier + "$" ),
-
-	matchExpr = {
-		ID: new RegExp( "^#(" + identifier + ")" ),
-		CLASS: new RegExp( "^\\.(" + identifier + ")" ),
-		TAG: new RegExp( "^(" + identifier + "|[*])" ),
-		ATTR: new RegExp( "^" + attributes ),
-		PSEUDO: new RegExp( "^" + pseudos ),
-		CHILD: new RegExp(
-			"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
-				whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
-				whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
-		bool: new RegExp( "^(?:" + booleans + ")$", "i" ),
-
-		// For use in libraries implementing .is()
-		// We use this for POS matching in `select`
-		needsContext: new RegExp( "^" + whitespace +
-			"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
-			"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
-	},
-
-	rinputs = /^(?:input|select|textarea|button)$/i,
-	rheader = /^h\d$/i,
-
-	// Easily-parseable/retrievable ID or TAG or CLASS selectors
-	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
-
-	rsibling = /[+~]/,
-
-	// CSS escapes
-	// https://www.w3.org/TR/CSS21/syndata.html#escaped-characters
-	runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace +
-		"?|\\\\([^\\r\\n\\f])", "g" ),
-	funescape = function( escape, nonHex ) {
-		var high = "0x" + escape.slice( 1 ) - 0x10000;
-
-		if ( nonHex ) {
-
-			// Strip the backslash prefix from a non-hex escape sequence
-			return nonHex;
-		}
-
-		// Replace a hexadecimal escape sequence with the encoded Unicode code point
-		// Support: IE <=11+
-		// For values outside the Basic Multilingual Plane (BMP), manually construct a
-		// surrogate pair
-		return high < 0 ?
-			String.fromCharCode( high + 0x10000 ) :
-			String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
-	},
-
-	// Used for iframes; see `setDocument`.
-	// Support: IE 9 - 11+, Edge 12 - 18+
-	// Removing the function wrapper causes a "Permission Denied"
-	// error in IE/Edge.
-	unloadHandler = function() {
-		setDocument();
-	},
-
-	inDisabledFieldset = addCombinator(
-		function( elem ) {
-			return elem.disabled === true && nodeName( elem, "fieldset" );
-		},
-		{ dir: "parentNode", next: "legend" }
-	);
-
-// Support: IE <=9 only
-// Accessing document.activeElement can throw unexpectedly
-// https://bugs.jquery.com/ticket/13393
-function safeActiveElement() {
-	try {
-		return document.activeElement;
-	} catch ( err ) { }
-}
-
-// Optimize for push.apply( _, NodeList )
-try {
-	push.apply(
-		( arr = slice.call( preferredDoc.childNodes ) ),
-		preferredDoc.childNodes
-	);
-
-	// Support: Android <=4.0
-	// Detect silently failing push.apply
-	// eslint-disable-next-line no-unused-expressions
-	arr[ preferredDoc.childNodes.length ].nodeType;
-} catch ( e ) {
-	push = {
-		apply: function( target, els ) {
-			pushNative.apply( target, slice.call( els ) );
-		},
-		call: function( target ) {
-			pushNative.apply( target, slice.call( arguments, 1 ) );
-		}
-	};
-}
-
-function find( selector, context, results, seed ) {
-	var m, i, elem, nid, match, groups, newSelector,
-		newContext = context && context.ownerDocument,
-
-		// nodeType defaults to 9, since context defaults to document
-		nodeType = context ? context.nodeType : 9;
-
-	results = results || [];
-
-	// Return early from calls with invalid selector or context
-	if ( typeof selector !== "string" || !selector ||
-		nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
-
-		return results;
-	}
-
-	// Try to shortcut find operations (as opposed to filters) in HTML documents
-	if ( !seed ) {
-		setDocument( context );
-		context = context || document;
-
-		if ( documentIsHTML ) {
-
-			// If the selector is sufficiently simple, try using a "get*By*" DOM method
-			// (excepting DocumentFragment context, where the methods don't exist)
-			if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {
-
-				// ID selector
-				if ( ( m = match[ 1 ] ) ) {
-
-					// Document context
-					if ( nodeType === 9 ) {
-						if ( ( elem = context.getElementById( m ) ) ) {
-
-							// Support: IE 9 only
-							// getElementById can match elements by name instead of ID
-							if ( elem.id === m ) {
-								push.call( results, elem );
-								return results;
-							}
-						} else {
-							return results;
-						}
-
-					// Element context
-					} else {
-
-						// Support: IE 9 only
-						// getElementById can match elements by name instead of ID
-						if ( newContext && ( elem = newContext.getElementById( m ) ) &&
-							find.contains( context, elem ) &&
-							elem.id === m ) {
-
-							push.call( results, elem );
-							return results;
-						}
-					}
-
-				// Type selector
-				} else if ( match[ 2 ] ) {
-					push.apply( results, context.getElementsByTagName( selector ) );
-					return results;
-
-				// Class selector
-				} else if ( ( m = match[ 3 ] ) && context.getElementsByClassName ) {
-					push.apply( results, context.getElementsByClassName( m ) );
-					return results;
-				}
-			}
-
-			// Take advantage of querySelectorAll
-			if ( !nonnativeSelectorCache[ selector + " " ] &&
-				( !rbuggyQSA || !rbuggyQSA.test( selector ) ) ) {
-
-				newSelector = selector;
-				newContext = context;
-
-				// qSA considers elements outside a scoping root when evaluating child or
-				// descendant combinators, which is not what we want.
-				// In such cases, we work around the behavior by prefixing every selector in the
-				// list with an ID selector referencing the scope context.
-				// The technique has to be used as well when a leading combinator is used
-				// as such selectors are not recognized by querySelectorAll.
-				// Thanks to Andrew Dupont for this technique.
-				if ( nodeType === 1 &&
-					( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) {
-
-					// Expand context for sibling selectors
-					newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
-						context;
-
-					// We can use :scope instead of the ID hack if the browser
-					// supports it & if we're not changing the context.
-					// Support: IE 11+, Edge 17 - 18+
-					// IE/Edge sometimes throw a "Permission denied" error when
-					// strict-comparing two documents; shallow comparisons work.
-					// eslint-disable-next-line eqeqeq
-					if ( newContext != context || !support.scope ) {
-
-						// Capture the context ID, setting it first if necessary
-						if ( ( nid = context.getAttribute( "id" ) ) ) {
-							nid = jQuery.escapeSelector( nid );
-						} else {
-							context.setAttribute( "id", ( nid = expando ) );
-						}
-					}
-
-					// Prefix every selector in the list
-					groups = tokenize( selector );
-					i = groups.length;
-					while ( i-- ) {
-						groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
-							toSelector( groups[ i ] );
-					}
-					newSelector = groups.join( "," );
-				}
-
-				try {
-					push.apply( results,
-						newContext.querySelectorAll( newSelector )
-					);
-					return results;
-				} catch ( qsaError ) {
-					nonnativeSelectorCache( selector, true );
-				} finally {
-					if ( nid === expando ) {
-						context.removeAttribute( "id" );
-					}
-				}
-			}
-		}
-	}
-
-	// All others
-	return select( selector.replace( rtrimCSS, "$1" ), context, results, seed );
-}
-
-/**
- * Create key-value caches of limited size
- * @returns {function(string, object)} Returns the Object data after storing it on itself with
- *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
- *	deleting the oldest entry
- */
-function createCache() {
-	var keys = [];
-
-	function cache( key, value ) {
-
-		// Use (key + " ") to avoid collision with native prototype properties
-		// (see https://github.com/jquery/sizzle/issues/157)
-		if ( keys.push( key + " " ) > Expr.cacheLength ) {
-
-			// Only keep the most recent entries
-			delete cache[ keys.shift() ];
-		}
-		return ( cache[ key + " " ] = value );
-	}
-	return cache;
-}
-
-/**
- * Mark a function for special use by jQuery selector module
- * @param {Function} fn The function to mark
- */
-function markFunction( fn ) {
-	fn[ expando ] = true;
-	return fn;
-}
-
-/**
- * Support testing using an element
- * @param {Function} fn Passed the created element and returns a boolean result
- */
-function assert( fn ) {
-	var el = document.createElement( "fieldset" );
-
-	try {
-		return !!fn( el );
-	} catch ( e ) {
-		return false;
-	} finally {
-
-		// Remove from its parent by default
-		if ( el.parentNode ) {
-			el.parentNode.removeChild( el );
-		}
-
-		// release memory in IE
-		el = null;
-	}
-}
-
-/**
- * Returns a function to use in pseudos for input types
- * @param {String} type
- */
-function createInputPseudo( type ) {
-	return function( elem ) {
-		return nodeName( elem, "input" ) && elem.type === type;
-	};
-}
-
-/**
- * Returns a function to use in pseudos for buttons
- * @param {String} type
- */
-function createButtonPseudo( type ) {
-	return function( elem ) {
-		return ( nodeName( elem, "input" ) || nodeName( elem, "button" ) ) &&
-			elem.type === type;
-	};
-}
-
-/**
- * Returns a function to use in pseudos for :enabled/:disabled
- * @param {Boolean} disabled true for :disabled; false for :enabled
- */
-function createDisabledPseudo( disabled ) {
-
-	// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
-	return function( elem ) {
-
-		// Only certain elements can match :enabled or :disabled
-		// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
-		// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
-		if ( "form" in elem ) {
-
-			// Check for inherited disabledness on relevant non-disabled elements:
-			// * listed form-associated elements in a disabled fieldset
-			//   https://html.spec.whatwg.org/multipage/forms.html#category-listed
-			//   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
-			// * option elements in a disabled optgroup
-			//   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
-			// All such elements have a "form" property.
-			if ( elem.parentNode && elem.disabled === false ) {
-
-				// Option elements defer to a parent optgroup if present
-				if ( "label" in elem ) {
-					if ( "label" in elem.parentNode ) {
-						return elem.parentNode.disabled === disabled;
-					} else {
-						return elem.disabled === disabled;
-					}
-				}
-
-				// Support: IE 6 - 11+
-				// Use the isDisabled shortcut property to check for disabled fieldset ancestors
-				return elem.isDisabled === disabled ||
-
-					// Where there is no isDisabled, check manually
-					elem.isDisabled !== !disabled &&
-						inDisabledFieldset( elem ) === disabled;
-			}
-
-			return elem.disabled === disabled;
-
-		// Try to winnow out elements that can't be disabled before trusting the disabled property.
-		// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
-		// even exist on them, let alone have a boolean value.
-		} else if ( "label" in elem ) {
-			return elem.disabled === disabled;
-		}
-
-		// Remaining elements are neither :enabled nor :disabled
-		return false;
-	};
-}
-
-/**
- * Returns a function to use in pseudos for positionals
- * @param {Function} fn
- */
-function createPositionalPseudo( fn ) {
-	return markFunction( function( argument ) {
-		argument = +argument;
-		return markFunction( function( seed, matches ) {
-			var j,
-				matchIndexes = fn( [], seed.length, argument ),
-				i = matchIndexes.length;
-
-			// Match elements found at the specified indexes
-			while ( i-- ) {
-				if ( seed[ ( j = matchIndexes[ i ] ) ] ) {
-					seed[ j ] = !( matches[ j ] = seed[ j ] );
-				}
-			}
-		} );
-	} );
-}
-
-/**
- * Checks a node for validity as a jQuery selector context
- * @param {Element|Object=} context
- * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
- */
-function testContext( context ) {
-	return context && typeof context.getElementsByTagName !== "undefined" && context;
-}
-
-/**
- * Sets document-related variables once based on the current document
- * @param {Element|Object} [node] An element or document object to use to set the document
- * @returns {Object} Returns the current document
- */
-function setDocument( node ) {
-	var subWindow,
-		doc = node ? node.ownerDocument || node : preferredDoc;
-
-	// Return early if doc is invalid or already selected
-	// Support: IE 11+, Edge 17 - 18+
-	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-	// two documents; shallow comparisons work.
-	// eslint-disable-next-line eqeqeq
-	if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {
-		return document;
-	}
-
-	// Update global variables
-	document = doc;
-	documentElement = document.documentElement;
-	documentIsHTML = !jQuery.isXMLDoc( document );
-
-	// Support: iOS 7 only, IE 9 - 11+
-	// Older browsers didn't support unprefixed `matches`.
-	matches = documentElement.matches ||
-		documentElement.webkitMatchesSelector ||
-		documentElement.msMatchesSelector;
-
-	// Support: IE 9 - 11+, Edge 12 - 18+
-	// Accessing iframe documents after unload throws "permission denied" errors
-	// (see trac-13936).
-	// Limit the fix to IE & Edge Legacy; despite Edge 15+ implementing `matches`,
-	// all IE 9+ and Edge Legacy versions implement `msMatchesSelector` as well.
-	if ( documentElement.msMatchesSelector &&
-
-		// Support: IE 11+, Edge 17 - 18+
-		// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-		// two documents; shallow comparisons work.
-		// eslint-disable-next-line eqeqeq
-		preferredDoc != document &&
-		( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {
-
-		// Support: IE 9 - 11+, Edge 12 - 18+
-		subWindow.addEventListener( "unload", unloadHandler );
-	}
-
-	// Support: IE <10
-	// Check if getElementById returns elements by name
-	// The broken getElementById methods don't pick up programmatically-set names,
-	// so use a roundabout getElementsByName test
-	support.getById = assert( function( el ) {
-		documentElement.appendChild( el ).id = jQuery.expando;
-		return !document.getElementsByName ||
-			!document.getElementsByName( jQuery.expando ).length;
-	} );
-
-	// Support: IE 9 only
-	// Check to see if it's possible to do matchesSelector
-	// on a disconnected node.
-	support.disconnectedMatch = assert( function( el ) {
-		return matches.call( el, "*" );
-	} );
-
-	// Support: IE 9 - 11+, Edge 12 - 18+
-	// IE/Edge don't support the :scope pseudo-class.
-	support.scope = assert( function() {
-		return document.querySelectorAll( ":scope" );
-	} );
-
-	// Support: Chrome 105 - 111 only, Safari 15.4 - 16.3 only
-	// Make sure the `:has()` argument is parsed unforgivingly.
-	// We include `*` in the test to detect buggy implementations that are
-	// _selectively_ forgiving (specifically when the list includes at least
-	// one valid selector).
-	// Note that we treat complete lack of support for `:has()` as if it were
-	// spec-compliant support, which is fine because use of `:has()` in such
-	// environments will fail in the qSA path and fall back to jQuery traversal
-	// anyway.
-	support.cssHas = assert( function() {
-		try {
-			document.querySelector( ":has(*,:jqfake)" );
-			return false;
-		} catch ( e ) {
-			return true;
-		}
-	} );
-
-	// ID filter and find
-	if ( support.getById ) {
-		Expr.filter.ID = function( id ) {
-			var attrId = id.replace( runescape, funescape );
-			return function( elem ) {
-				return elem.getAttribute( "id" ) === attrId;
-			};
-		};
-		Expr.find.ID = function( id, context ) {
-			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
-				var elem = context.getElementById( id );
-				return elem ? [ elem ] : [];
-			}
-		};
-	} else {
-		Expr.filter.ID =  function( id ) {
-			var attrId = id.replace( runescape, funescape );
-			return function( elem ) {
-				var node = typeof elem.getAttributeNode !== "undefined" &&
-					elem.getAttributeNode( "id" );
-				return node && node.value === attrId;
-			};
-		};
-
-		// Support: IE 6 - 7 only
-		// getElementById is not reliable as a find shortcut
-		Expr.find.ID = function( id, context ) {
-			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
-				var node, i, elems,
-					elem = context.getElementById( id );
-
-				if ( elem ) {
-
-					// Verify the id attribute
-					node = elem.getAttributeNode( "id" );
-					if ( node && node.value === id ) {
-						return [ elem ];
-					}
-
-					// Fall back on getElementsByName
-					elems = context.getElementsByName( id );
-					i = 0;
-					while ( ( elem = elems[ i++ ] ) ) {
-						node = elem.getAttributeNode( "id" );
-						if ( node && node.value === id ) {
-							return [ elem ];
-						}
-					}
-				}
-
-				return [];
-			}
-		};
-	}
-
-	// Tag
-	Expr.find.TAG = function( tag, context ) {
-		if ( typeof context.getElementsByTagName !== "undefined" ) {
-			return context.getElementsByTagName( tag );
-
-		// DocumentFragment nodes don't have gEBTN
-		} else {
-			return context.querySelectorAll( tag );
-		}
-	};
-
-	// Class
-	Expr.find.CLASS = function( className, context ) {
-		if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
-			return context.getElementsByClassName( className );
-		}
-	};
-
-	/* QSA/matchesSelector
-	---------------------------------------------------------------------- */
-
-	// QSA and matchesSelector support
-
-	rbuggyQSA = [];
-
-	// Build QSA regex
-	// Regex strategy adopted from Diego Perini
-	assert( function( el ) {
-
-		var input;
-
-		documentElement.appendChild( el ).innerHTML =
-			"" +
-			"";
-
-		// Support: iOS <=7 - 8 only
-		// Boolean attributes and "value" are not treated correctly in some XML documents
-		if ( !el.querySelectorAll( "[selected]" ).length ) {
-			rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
-		}
-
-		// Support: iOS <=7 - 8 only
-		if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
-			rbuggyQSA.push( "~=" );
-		}
-
-		// Support: iOS 8 only
-		// https://bugs.webkit.org/show_bug.cgi?id=136851
-		// In-page `selector#id sibling-combinator selector` fails
-		if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
-			rbuggyQSA.push( ".#.+[+~]" );
-		}
-
-		// Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+
-		// In some of the document kinds, these selectors wouldn't work natively.
-		// This is probably OK but for backwards compatibility we want to maintain
-		// handling them through jQuery traversal in jQuery 3.x.
-		if ( !el.querySelectorAll( ":checked" ).length ) {
-			rbuggyQSA.push( ":checked" );
-		}
-
-		// Support: Windows 8 Native Apps
-		// The type and name attributes are restricted during .innerHTML assignment
-		input = document.createElement( "input" );
-		input.setAttribute( "type", "hidden" );
-		el.appendChild( input ).setAttribute( "name", "D" );
-
-		// Support: IE 9 - 11+
-		// IE's :disabled selector does not pick up the children of disabled fieldsets
-		// Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+
-		// In some of the document kinds, these selectors wouldn't work natively.
-		// This is probably OK but for backwards compatibility we want to maintain
-		// handling them through jQuery traversal in jQuery 3.x.
-		documentElement.appendChild( el ).disabled = true;
-		if ( el.querySelectorAll( ":disabled" ).length !== 2 ) {
-			rbuggyQSA.push( ":enabled", ":disabled" );
-		}
-
-		// Support: IE 11+, Edge 15 - 18+
-		// IE 11/Edge don't find elements on a `[name='']` query in some cases.
-		// Adding a temporary attribute to the document before the selection works
-		// around the issue.
-		// Interestingly, IE 10 & older don't seem to have the issue.
-		input = document.createElement( "input" );
-		input.setAttribute( "name", "" );
-		el.appendChild( input );
-		if ( !el.querySelectorAll( "[name='']" ).length ) {
-			rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" +
-				whitespace + "*(?:''|\"\")" );
-		}
-	} );
-
-	if ( !support.cssHas ) {
-
-		// Support: Chrome 105 - 110+, Safari 15.4 - 16.3+
-		// Our regular `try-catch` mechanism fails to detect natively-unsupported
-		// pseudo-classes inside `:has()` (such as `:has(:contains("Foo"))`)
-		// in browsers that parse the `:has()` argument as a forgiving selector list.
-		// https://drafts.csswg.org/selectors/#relational now requires the argument
-		// to be parsed unforgivingly, but browsers have not yet fully adjusted.
-		rbuggyQSA.push( ":has" );
-	}
-
-	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
-
-	/* Sorting
-	---------------------------------------------------------------------- */
-
-	// Document order sorting
-	sortOrder = function( a, b ) {
-
-		// Flag for duplicate removal
-		if ( a === b ) {
-			hasDuplicate = true;
-			return 0;
-		}
-
-		// Sort on method existence if only one input has compareDocumentPosition
-		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
-		if ( compare ) {
-			return compare;
-		}
-
-		// Calculate position if both inputs belong to the same document
-		// Support: IE 11+, Edge 17 - 18+
-		// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-		// two documents; shallow comparisons work.
-		// eslint-disable-next-line eqeqeq
-		compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?
-			a.compareDocumentPosition( b ) :
-
-			// Otherwise we know they are disconnected
-			1;
-
-		// Disconnected nodes
-		if ( compare & 1 ||
-			( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {
-
-			// Choose the first element that is related to our preferred document
-			// Support: IE 11+, Edge 17 - 18+
-			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-			// two documents; shallow comparisons work.
-			// eslint-disable-next-line eqeqeq
-			if ( a === document || a.ownerDocument == preferredDoc &&
-				find.contains( preferredDoc, a ) ) {
-				return -1;
-			}
-
-			// Support: IE 11+, Edge 17 - 18+
-			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-			// two documents; shallow comparisons work.
-			// eslint-disable-next-line eqeqeq
-			if ( b === document || b.ownerDocument == preferredDoc &&
-				find.contains( preferredDoc, b ) ) {
-				return 1;
-			}
-
-			// Maintain original order
-			return sortInput ?
-				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
-				0;
-		}
-
-		return compare & 4 ? -1 : 1;
-	};
-
-	return document;
-}
-
-find.matches = function( expr, elements ) {
-	return find( expr, null, null, elements );
-};
-
-find.matchesSelector = function( elem, expr ) {
-	setDocument( elem );
-
-	if ( documentIsHTML &&
-		!nonnativeSelectorCache[ expr + " " ] &&
-		( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
-
-		try {
-			var ret = matches.call( elem, expr );
-
-			// IE 9's matchesSelector returns false on disconnected nodes
-			if ( ret || support.disconnectedMatch ||
-
-					// As well, disconnected nodes are said to be in a document
-					// fragment in IE 9
-					elem.document && elem.document.nodeType !== 11 ) {
-				return ret;
-			}
-		} catch ( e ) {
-			nonnativeSelectorCache( expr, true );
-		}
-	}
-
-	return find( expr, document, null, [ elem ] ).length > 0;
-};
-
-find.contains = function( context, elem ) {
-
-	// Set document vars if needed
-	// Support: IE 11+, Edge 17 - 18+
-	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-	// two documents; shallow comparisons work.
-	// eslint-disable-next-line eqeqeq
-	if ( ( context.ownerDocument || context ) != document ) {
-		setDocument( context );
-	}
-	return jQuery.contains( context, elem );
-};
-
-
-find.attr = function( elem, name ) {
-
-	// Set document vars if needed
-	// Support: IE 11+, Edge 17 - 18+
-	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-	// two documents; shallow comparisons work.
-	// eslint-disable-next-line eqeqeq
-	if ( ( elem.ownerDocument || elem ) != document ) {
-		setDocument( elem );
-	}
-
-	var fn = Expr.attrHandle[ name.toLowerCase() ],
-
-		// Don't get fooled by Object.prototype properties (see trac-13807)
-		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
-			fn( elem, name, !documentIsHTML ) :
-			undefined;
-
-	if ( val !== undefined ) {
-		return val;
-	}
-
-	return elem.getAttribute( name );
-};
-
-find.error = function( msg ) {
-	throw new Error( "Syntax error, unrecognized expression: " + msg );
-};
-
-/**
- * Document sorting and removing duplicates
- * @param {ArrayLike} results
- */
-jQuery.uniqueSort = function( results ) {
-	var elem,
-		duplicates = [],
-		j = 0,
-		i = 0;
-
-	// Unless we *know* we can detect duplicates, assume their presence
-	//
-	// Support: Android <=4.0+
-	// Testing for detecting duplicates is unpredictable so instead assume we can't
-	// depend on duplicate detection in all browsers without a stable sort.
-	hasDuplicate = !support.sortStable;
-	sortInput = !support.sortStable && slice.call( results, 0 );
-	sort.call( results, sortOrder );
-
-	if ( hasDuplicate ) {
-		while ( ( elem = results[ i++ ] ) ) {
-			if ( elem === results[ i ] ) {
-				j = duplicates.push( i );
-			}
-		}
-		while ( j-- ) {
-			splice.call( results, duplicates[ j ], 1 );
-		}
-	}
-
-	// Clear input after sorting to release objects
-	// See https://github.com/jquery/sizzle/pull/225
-	sortInput = null;
-
-	return results;
-};
-
-jQuery.fn.uniqueSort = function() {
-	return this.pushStack( jQuery.uniqueSort( slice.apply( this ) ) );
-};
-
-Expr = jQuery.expr = {
-
-	// Can be adjusted by the user
-	cacheLength: 50,
-
-	createPseudo: markFunction,
-
-	match: matchExpr,
-
-	attrHandle: {},
-
-	find: {},
-
-	relative: {
-		">": { dir: "parentNode", first: true },
-		" ": { dir: "parentNode" },
-		"+": { dir: "previousSibling", first: true },
-		"~": { dir: "previousSibling" }
-	},
-
-	preFilter: {
-		ATTR: function( match ) {
-			match[ 1 ] = match[ 1 ].replace( runescape, funescape );
-
-			// Move the given value to match[3] whether quoted or unquoted
-			match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" )
-				.replace( runescape, funescape );
-
-			if ( match[ 2 ] === "~=" ) {
-				match[ 3 ] = " " + match[ 3 ] + " ";
-			}
-
-			return match.slice( 0, 4 );
-		},
-
-		CHILD: function( match ) {
-
-			/* matches from matchExpr["CHILD"]
-				1 type (only|nth|...)
-				2 what (child|of-type)
-				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
-				4 xn-component of xn+y argument ([+-]?\d*n|)
-				5 sign of xn-component
-				6 x of xn-component
-				7 sign of y-component
-				8 y of y-component
-			*/
-			match[ 1 ] = match[ 1 ].toLowerCase();
-
-			if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
-
-				// nth-* requires argument
-				if ( !match[ 3 ] ) {
-					find.error( match[ 0 ] );
-				}
-
-				// numeric x and y parameters for Expr.filter.CHILD
-				// remember that false/true cast respectively to 0/1
-				match[ 4 ] = +( match[ 4 ] ?
-					match[ 5 ] + ( match[ 6 ] || 1 ) :
-					2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" )
-				);
-				match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
-
-			// other types prohibit arguments
-			} else if ( match[ 3 ] ) {
-				find.error( match[ 0 ] );
-			}
-
-			return match;
-		},
-
-		PSEUDO: function( match ) {
-			var excess,
-				unquoted = !match[ 6 ] && match[ 2 ];
-
-			if ( matchExpr.CHILD.test( match[ 0 ] ) ) {
-				return null;
-			}
-
-			// Accept quoted arguments as-is
-			if ( match[ 3 ] ) {
-				match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
-
-			// Strip excess characters from unquoted arguments
-			} else if ( unquoted && rpseudo.test( unquoted ) &&
-
-				// Get excess from tokenize (recursively)
-				( excess = tokenize( unquoted, true ) ) &&
-
-				// advance to the next closing parenthesis
-				( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {
-
-				// excess is a negative index
-				match[ 0 ] = match[ 0 ].slice( 0, excess );
-				match[ 2 ] = unquoted.slice( 0, excess );
-			}
-
-			// Return only captures needed by the pseudo filter method (type and argument)
-			return match.slice( 0, 3 );
-		}
-	},
-
-	filter: {
-
-		TAG: function( nodeNameSelector ) {
-			var expectedNodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
-			return nodeNameSelector === "*" ?
-				function() {
-					return true;
-				} :
-				function( elem ) {
-					return nodeName( elem, expectedNodeName );
-				};
-		},
-
-		CLASS: function( className ) {
-			var pattern = classCache[ className + " " ];
-
-			return pattern ||
-				( pattern = new RegExp( "(^|" + whitespace + ")" + className +
-					"(" + whitespace + "|$)" ) ) &&
-				classCache( className, function( elem ) {
-					return pattern.test(
-						typeof elem.className === "string" && elem.className ||
-							typeof elem.getAttribute !== "undefined" &&
-								elem.getAttribute( "class" ) ||
-							""
-					);
-				} );
-		},
-
-		ATTR: function( name, operator, check ) {
-			return function( elem ) {
-				var result = find.attr( elem, name );
-
-				if ( result == null ) {
-					return operator === "!=";
-				}
-				if ( !operator ) {
-					return true;
-				}
-
-				result += "";
-
-				if ( operator === "=" ) {
-					return result === check;
-				}
-				if ( operator === "!=" ) {
-					return result !== check;
-				}
-				if ( operator === "^=" ) {
-					return check && result.indexOf( check ) === 0;
-				}
-				if ( operator === "*=" ) {
-					return check && result.indexOf( check ) > -1;
-				}
-				if ( operator === "$=" ) {
-					return check && result.slice( -check.length ) === check;
-				}
-				if ( operator === "~=" ) {
-					return ( " " + result.replace( rwhitespace, " " ) + " " )
-						.indexOf( check ) > -1;
-				}
-				if ( operator === "|=" ) {
-					return result === check || result.slice( 0, check.length + 1 ) === check + "-";
-				}
-
-				return false;
-			};
-		},
-
-		CHILD: function( type, what, _argument, first, last ) {
-			var simple = type.slice( 0, 3 ) !== "nth",
-				forward = type.slice( -4 ) !== "last",
-				ofType = what === "of-type";
-
-			return first === 1 && last === 0 ?
-
-				// Shortcut for :nth-*(n)
-				function( elem ) {
-					return !!elem.parentNode;
-				} :
-
-				function( elem, _context, xml ) {
-					var cache, outerCache, node, nodeIndex, start,
-						dir = simple !== forward ? "nextSibling" : "previousSibling",
-						parent = elem.parentNode,
-						name = ofType && elem.nodeName.toLowerCase(),
-						useCache = !xml && !ofType,
-						diff = false;
-
-					if ( parent ) {
-
-						// :(first|last|only)-(child|of-type)
-						if ( simple ) {
-							while ( dir ) {
-								node = elem;
-								while ( ( node = node[ dir ] ) ) {
-									if ( ofType ?
-										nodeName( node, name ) :
-										node.nodeType === 1 ) {
-
-										return false;
-									}
-								}
-
-								// Reverse direction for :only-* (if we haven't yet done so)
-								start = dir = type === "only" && !start && "nextSibling";
-							}
-							return true;
-						}
-
-						start = [ forward ? parent.firstChild : parent.lastChild ];
-
-						// non-xml :nth-child(...) stores cache data on `parent`
-						if ( forward && useCache ) {
-
-							// Seek `elem` from a previously-cached index
-							outerCache = parent[ expando ] || ( parent[ expando ] = {} );
-							cache = outerCache[ type ] || [];
-							nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
-							diff = nodeIndex && cache[ 2 ];
-							node = nodeIndex && parent.childNodes[ nodeIndex ];
-
-							while ( ( node = ++nodeIndex && node && node[ dir ] ||
-
-								// Fallback to seeking `elem` from the start
-								( diff = nodeIndex = 0 ) || start.pop() ) ) {
-
-								// When found, cache indexes on `parent` and break
-								if ( node.nodeType === 1 && ++diff && node === elem ) {
-									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
-									break;
-								}
-							}
-
-						} else {
-
-							// Use previously-cached element index if available
-							if ( useCache ) {
-								outerCache = elem[ expando ] || ( elem[ expando ] = {} );
-								cache = outerCache[ type ] || [];
-								nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
-								diff = nodeIndex;
-							}
-
-							// xml :nth-child(...)
-							// or :nth-last-child(...) or :nth(-last)?-of-type(...)
-							if ( diff === false ) {
-
-								// Use the same loop as above to seek `elem` from the start
-								while ( ( node = ++nodeIndex && node && node[ dir ] ||
-									( diff = nodeIndex = 0 ) || start.pop() ) ) {
-
-									if ( ( ofType ?
-										nodeName( node, name ) :
-										node.nodeType === 1 ) &&
-										++diff ) {
-
-										// Cache the index of each encountered element
-										if ( useCache ) {
-											outerCache = node[ expando ] ||
-												( node[ expando ] = {} );
-											outerCache[ type ] = [ dirruns, diff ];
-										}
-
-										if ( node === elem ) {
-											break;
-										}
-									}
-								}
-							}
-						}
-
-						// Incorporate the offset, then check against cycle size
-						diff -= last;
-						return diff === first || ( diff % first === 0 && diff / first >= 0 );
-					}
-				};
-		},
-
-		PSEUDO: function( pseudo, argument ) {
-
-			// pseudo-class names are case-insensitive
-			// https://www.w3.org/TR/selectors/#pseudo-classes
-			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
-			// Remember that setFilters inherits from pseudos
-			var args,
-				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
-					find.error( "unsupported pseudo: " + pseudo );
-
-			// The user may use createPseudo to indicate that
-			// arguments are needed to create the filter function
-			// just as jQuery does
-			if ( fn[ expando ] ) {
-				return fn( argument );
-			}
-
-			// But maintain support for old signatures
-			if ( fn.length > 1 ) {
-				args = [ pseudo, pseudo, "", argument ];
-				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
-					markFunction( function( seed, matches ) {
-						var idx,
-							matched = fn( seed, argument ),
-							i = matched.length;
-						while ( i-- ) {
-							idx = indexOf.call( seed, matched[ i ] );
-							seed[ idx ] = !( matches[ idx ] = matched[ i ] );
-						}
-					} ) :
-					function( elem ) {
-						return fn( elem, 0, args );
-					};
-			}
-
-			return fn;
-		}
-	},
-
-	pseudos: {
-
-		// Potentially complex pseudos
-		not: markFunction( function( selector ) {
-
-			// Trim the selector passed to compile
-			// to avoid treating leading and trailing
-			// spaces as combinators
-			var input = [],
-				results = [],
-				matcher = compile( selector.replace( rtrimCSS, "$1" ) );
-
-			return matcher[ expando ] ?
-				markFunction( function( seed, matches, _context, xml ) {
-					var elem,
-						unmatched = matcher( seed, null, xml, [] ),
-						i = seed.length;
-
-					// Match elements unmatched by `matcher`
-					while ( i-- ) {
-						if ( ( elem = unmatched[ i ] ) ) {
-							seed[ i ] = !( matches[ i ] = elem );
-						}
-					}
-				} ) :
-				function( elem, _context, xml ) {
-					input[ 0 ] = elem;
-					matcher( input, null, xml, results );
-
-					// Don't keep the element
-					// (see https://github.com/jquery/sizzle/issues/299)
-					input[ 0 ] = null;
-					return !results.pop();
-				};
-		} ),
-
-		has: markFunction( function( selector ) {
-			return function( elem ) {
-				return find( selector, elem ).length > 0;
-			};
-		} ),
-
-		contains: markFunction( function( text ) {
-			text = text.replace( runescape, funescape );
-			return function( elem ) {
-				return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1;
-			};
-		} ),
-
-		// "Whether an element is represented by a :lang() selector
-		// is based solely on the element's language value
-		// being equal to the identifier C,
-		// or beginning with the identifier C immediately followed by "-".
-		// The matching of C against the element's language value is performed case-insensitively.
-		// The identifier C does not have to be a valid language name."
-		// https://www.w3.org/TR/selectors/#lang-pseudo
-		lang: markFunction( function( lang ) {
-
-			// lang value must be a valid identifier
-			if ( !ridentifier.test( lang || "" ) ) {
-				find.error( "unsupported lang: " + lang );
-			}
-			lang = lang.replace( runescape, funescape ).toLowerCase();
-			return function( elem ) {
-				var elemLang;
-				do {
-					if ( ( elemLang = documentIsHTML ?
-						elem.lang :
-						elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {
-
-						elemLang = elemLang.toLowerCase();
-						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
-					}
-				} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
-				return false;
-			};
-		} ),
-
-		// Miscellaneous
-		target: function( elem ) {
-			var hash = window.location && window.location.hash;
-			return hash && hash.slice( 1 ) === elem.id;
-		},
-
-		root: function( elem ) {
-			return elem === documentElement;
-		},
-
-		focus: function( elem ) {
-			return elem === safeActiveElement() &&
-				document.hasFocus() &&
-				!!( elem.type || elem.href || ~elem.tabIndex );
-		},
-
-		// Boolean properties
-		enabled: createDisabledPseudo( false ),
-		disabled: createDisabledPseudo( true ),
-
-		checked: function( elem ) {
-
-			// In CSS3, :checked should return both checked and selected elements
-			// https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
-			return ( nodeName( elem, "input" ) && !!elem.checked ) ||
-				( nodeName( elem, "option" ) && !!elem.selected );
-		},
-
-		selected: function( elem ) {
-
-			// Support: IE <=11+
-			// Accessing the selectedIndex property
-			// forces the browser to treat the default option as
-			// selected when in an optgroup.
-			if ( elem.parentNode ) {
-				// eslint-disable-next-line no-unused-expressions
-				elem.parentNode.selectedIndex;
-			}
-
-			return elem.selected === true;
-		},
-
-		// Contents
-		empty: function( elem ) {
-
-			// https://www.w3.org/TR/selectors/#empty-pseudo
-			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
-			//   but not by others (comment: 8; processing instruction: 7; etc.)
-			// nodeType < 6 works because attributes (2) do not appear as children
-			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
-				if ( elem.nodeType < 6 ) {
-					return false;
-				}
-			}
-			return true;
-		},
-
-		parent: function( elem ) {
-			return !Expr.pseudos.empty( elem );
-		},
-
-		// Element/input types
-		header: function( elem ) {
-			return rheader.test( elem.nodeName );
-		},
-
-		input: function( elem ) {
-			return rinputs.test( elem.nodeName );
-		},
-
-		button: function( elem ) {
-			return nodeName( elem, "input" ) && elem.type === "button" ||
-				nodeName( elem, "button" );
-		},
-
-		text: function( elem ) {
-			var attr;
-			return nodeName( elem, "input" ) && elem.type === "text" &&
-
-				// Support: IE <10 only
-				// New HTML5 attribute values (e.g., "search") appear
-				// with elem.type === "text"
-				( ( attr = elem.getAttribute( "type" ) ) == null ||
-					attr.toLowerCase() === "text" );
-		},
-
-		// Position-in-collection
-		first: createPositionalPseudo( function() {
-			return [ 0 ];
-		} ),
-
-		last: createPositionalPseudo( function( _matchIndexes, length ) {
-			return [ length - 1 ];
-		} ),
-
-		eq: createPositionalPseudo( function( _matchIndexes, length, argument ) {
-			return [ argument < 0 ? argument + length : argument ];
-		} ),
-
-		even: createPositionalPseudo( function( matchIndexes, length ) {
-			var i = 0;
-			for ( ; i < length; i += 2 ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		} ),
-
-		odd: createPositionalPseudo( function( matchIndexes, length ) {
-			var i = 1;
-			for ( ; i < length; i += 2 ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		} ),
-
-		lt: createPositionalPseudo( function( matchIndexes, length, argument ) {
-			var i;
-
-			if ( argument < 0 ) {
-				i = argument + length;
-			} else if ( argument > length ) {
-				i = length;
-			} else {
-				i = argument;
-			}
-
-			for ( ; --i >= 0; ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		} ),
-
-		gt: createPositionalPseudo( function( matchIndexes, length, argument ) {
-			var i = argument < 0 ? argument + length : argument;
-			for ( ; ++i < length; ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		} )
-	}
-};
-
-Expr.pseudos.nth = Expr.pseudos.eq;
-
-// Add button/input type pseudos
-for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
-	Expr.pseudos[ i ] = createInputPseudo( i );
-}
-for ( i in { submit: true, reset: true } ) {
-	Expr.pseudos[ i ] = createButtonPseudo( i );
-}
-
-// Easy API for creating new setFilters
-function setFilters() {}
-setFilters.prototype = Expr.filters = Expr.pseudos;
-Expr.setFilters = new setFilters();
-
-function tokenize( selector, parseOnly ) {
-	var matched, match, tokens, type,
-		soFar, groups, preFilters,
-		cached = tokenCache[ selector + " " ];
-
-	if ( cached ) {
-		return parseOnly ? 0 : cached.slice( 0 );
-	}
-
-	soFar = selector;
-	groups = [];
-	preFilters = Expr.preFilter;
-
-	while ( soFar ) {
-
-		// Comma and first run
-		if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
-			if ( match ) {
-
-				// Don't consume trailing commas as valid
-				soFar = soFar.slice( match[ 0 ].length ) || soFar;
-			}
-			groups.push( ( tokens = [] ) );
-		}
-
-		matched = false;
-
-		// Combinators
-		if ( ( match = rleadingCombinator.exec( soFar ) ) ) {
-			matched = match.shift();
-			tokens.push( {
-				value: matched,
-
-				// Cast descendant combinators to space
-				type: match[ 0 ].replace( rtrimCSS, " " )
-			} );
-			soFar = soFar.slice( matched.length );
-		}
-
-		// Filters
-		for ( type in Expr.filter ) {
-			if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
-				( match = preFilters[ type ]( match ) ) ) ) {
-				matched = match.shift();
-				tokens.push( {
-					value: matched,
-					type: type,
-					matches: match
-				} );
-				soFar = soFar.slice( matched.length );
-			}
-		}
-
-		if ( !matched ) {
-			break;
-		}
-	}
-
-	// Return the length of the invalid excess
-	// if we're just parsing
-	// Otherwise, throw an error or return tokens
-	if ( parseOnly ) {
-		return soFar.length;
-	}
-
-	return soFar ?
-		find.error( selector ) :
-
-		// Cache the tokens
-		tokenCache( selector, groups ).slice( 0 );
-}
-
-function toSelector( tokens ) {
-	var i = 0,
-		len = tokens.length,
-		selector = "";
-	for ( ; i < len; i++ ) {
-		selector += tokens[ i ].value;
-	}
-	return selector;
-}
-
-function addCombinator( matcher, combinator, base ) {
-	var dir = combinator.dir,
-		skip = combinator.next,
-		key = skip || dir,
-		checkNonElements = base && key === "parentNode",
-		doneName = done++;
-
-	return combinator.first ?
-
-		// Check against closest ancestor/preceding element
-		function( elem, context, xml ) {
-			while ( ( elem = elem[ dir ] ) ) {
-				if ( elem.nodeType === 1 || checkNonElements ) {
-					return matcher( elem, context, xml );
-				}
-			}
-			return false;
-		} :
-
-		// Check against all ancestor/preceding elements
-		function( elem, context, xml ) {
-			var oldCache, outerCache,
-				newCache = [ dirruns, doneName ];
-
-			// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
-			if ( xml ) {
-				while ( ( elem = elem[ dir ] ) ) {
-					if ( elem.nodeType === 1 || checkNonElements ) {
-						if ( matcher( elem, context, xml ) ) {
-							return true;
-						}
-					}
-				}
-			} else {
-				while ( ( elem = elem[ dir ] ) ) {
-					if ( elem.nodeType === 1 || checkNonElements ) {
-						outerCache = elem[ expando ] || ( elem[ expando ] = {} );
-
-						if ( skip && nodeName( elem, skip ) ) {
-							elem = elem[ dir ] || elem;
-						} else if ( ( oldCache = outerCache[ key ] ) &&
-							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
-
-							// Assign to newCache so results back-propagate to previous elements
-							return ( newCache[ 2 ] = oldCache[ 2 ] );
-						} else {
-
-							// Reuse newcache so results back-propagate to previous elements
-							outerCache[ key ] = newCache;
-
-							// A match means we're done; a fail means we have to keep checking
-							if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
-								return true;
-							}
-						}
-					}
-				}
-			}
-			return false;
-		};
-}
-
-function elementMatcher( matchers ) {
-	return matchers.length > 1 ?
-		function( elem, context, xml ) {
-			var i = matchers.length;
-			while ( i-- ) {
-				if ( !matchers[ i ]( elem, context, xml ) ) {
-					return false;
-				}
-			}
-			return true;
-		} :
-		matchers[ 0 ];
-}
-
-function multipleContexts( selector, contexts, results ) {
-	var i = 0,
-		len = contexts.length;
-	for ( ; i < len; i++ ) {
-		find( selector, contexts[ i ], results );
-	}
-	return results;
-}
-
-function condense( unmatched, map, filter, context, xml ) {
-	var elem,
-		newUnmatched = [],
-		i = 0,
-		len = unmatched.length,
-		mapped = map != null;
-
-	for ( ; i < len; i++ ) {
-		if ( ( elem = unmatched[ i ] ) ) {
-			if ( !filter || filter( elem, context, xml ) ) {
-				newUnmatched.push( elem );
-				if ( mapped ) {
-					map.push( i );
-				}
-			}
-		}
-	}
-
-	return newUnmatched;
-}
-
-function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
-	if ( postFilter && !postFilter[ expando ] ) {
-		postFilter = setMatcher( postFilter );
-	}
-	if ( postFinder && !postFinder[ expando ] ) {
-		postFinder = setMatcher( postFinder, postSelector );
-	}
-	return markFunction( function( seed, results, context, xml ) {
-		var temp, i, elem, matcherOut,
-			preMap = [],
-			postMap = [],
-			preexisting = results.length,
-
-			// Get initial elements from seed or context
-			elems = seed ||
-				multipleContexts( selector || "*",
-					context.nodeType ? [ context ] : context, [] ),
-
-			// Prefilter to get matcher input, preserving a map for seed-results synchronization
-			matcherIn = preFilter && ( seed || !selector ) ?
-				condense( elems, preMap, preFilter, context, xml ) :
-				elems;
-
-		if ( matcher ) {
-
-			// If we have a postFinder, or filtered seed, or non-seed postFilter
-			// or preexisting results,
-			matcherOut = postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
-
-				// ...intermediate processing is necessary
-				[] :
-
-				// ...otherwise use results directly
-				results;
-
-			// Find primary matches
-			matcher( matcherIn, matcherOut, context, xml );
-		} else {
-			matcherOut = matcherIn;
-		}
-
-		// Apply postFilter
-		if ( postFilter ) {
-			temp = condense( matcherOut, postMap );
-			postFilter( temp, [], context, xml );
-
-			// Un-match failing elements by moving them back to matcherIn
-			i = temp.length;
-			while ( i-- ) {
-				if ( ( elem = temp[ i ] ) ) {
-					matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );
-				}
-			}
-		}
-
-		if ( seed ) {
-			if ( postFinder || preFilter ) {
-				if ( postFinder ) {
-
-					// Get the final matcherOut by condensing this intermediate into postFinder contexts
-					temp = [];
-					i = matcherOut.length;
-					while ( i-- ) {
-						if ( ( elem = matcherOut[ i ] ) ) {
-
-							// Restore matcherIn since elem is not yet a final match
-							temp.push( ( matcherIn[ i ] = elem ) );
-						}
-					}
-					postFinder( null, ( matcherOut = [] ), temp, xml );
-				}
-
-				// Move matched elements from seed to results to keep them synchronized
-				i = matcherOut.length;
-				while ( i-- ) {
-					if ( ( elem = matcherOut[ i ] ) &&
-						( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) {
-
-						seed[ temp ] = !( results[ temp ] = elem );
-					}
-				}
-			}
-
-		// Add elements to results, through postFinder if defined
-		} else {
-			matcherOut = condense(
-				matcherOut === results ?
-					matcherOut.splice( preexisting, matcherOut.length ) :
-					matcherOut
-			);
-			if ( postFinder ) {
-				postFinder( null, results, matcherOut, xml );
-			} else {
-				push.apply( results, matcherOut );
-			}
-		}
-	} );
-}
-
-function matcherFromTokens( tokens ) {
-	var checkContext, matcher, j,
-		len = tokens.length,
-		leadingRelative = Expr.relative[ tokens[ 0 ].type ],
-		implicitRelative = leadingRelative || Expr.relative[ " " ],
-		i = leadingRelative ? 1 : 0,
-
-		// The foundational matcher ensures that elements are reachable from top-level context(s)
-		matchContext = addCombinator( function( elem ) {
-			return elem === checkContext;
-		}, implicitRelative, true ),
-		matchAnyContext = addCombinator( function( elem ) {
-			return indexOf.call( checkContext, elem ) > -1;
-		}, implicitRelative, true ),
-		matchers = [ function( elem, context, xml ) {
-
-			// Support: IE 11+, Edge 17 - 18+
-			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-			// two documents; shallow comparisons work.
-			// eslint-disable-next-line eqeqeq
-			var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || (
-				( checkContext = context ).nodeType ?
-					matchContext( elem, context, xml ) :
-					matchAnyContext( elem, context, xml ) );
-
-			// Avoid hanging onto element
-			// (see https://github.com/jquery/sizzle/issues/299)
-			checkContext = null;
-			return ret;
-		} ];
-
-	for ( ; i < len; i++ ) {
-		if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {
-			matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
-		} else {
-			matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
-
-			// Return special upon seeing a positional matcher
-			if ( matcher[ expando ] ) {
-
-				// Find the next relative operator (if any) for proper handling
-				j = ++i;
-				for ( ; j < len; j++ ) {
-					if ( Expr.relative[ tokens[ j ].type ] ) {
-						break;
-					}
-				}
-				return setMatcher(
-					i > 1 && elementMatcher( matchers ),
-					i > 1 && toSelector(
-
-						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
-						tokens.slice( 0, i - 1 )
-							.concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
-					).replace( rtrimCSS, "$1" ),
-					matcher,
-					i < j && matcherFromTokens( tokens.slice( i, j ) ),
-					j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
-					j < len && toSelector( tokens )
-				);
-			}
-			matchers.push( matcher );
-		}
-	}
-
-	return elementMatcher( matchers );
-}
-
-function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
-	var bySet = setMatchers.length > 0,
-		byElement = elementMatchers.length > 0,
-		superMatcher = function( seed, context, xml, results, outermost ) {
-			var elem, j, matcher,
-				matchedCount = 0,
-				i = "0",
-				unmatched = seed && [],
-				setMatched = [],
-				contextBackup = outermostContext,
-
-				// We must always have either seed elements or outermost context
-				elems = seed || byElement && Expr.find.TAG( "*", outermost ),
-
-				// Use integer dirruns iff this is the outermost matcher
-				dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),
-				len = elems.length;
-
-			if ( outermost ) {
-
-				// Support: IE 11+, Edge 17 - 18+
-				// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-				// two documents; shallow comparisons work.
-				// eslint-disable-next-line eqeqeq
-				outermostContext = context == document || context || outermost;
-			}
-
-			// Add elements passing elementMatchers directly to results
-			// Support: iOS <=7 - 9 only
-			// Tolerate NodeList properties (IE: "length"; Safari: ) matching
-			// elements by id. (see trac-14142)
-			for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {
-				if ( byElement && elem ) {
-					j = 0;
-
-					// Support: IE 11+, Edge 17 - 18+
-					// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
-					// two documents; shallow comparisons work.
-					// eslint-disable-next-line eqeqeq
-					if ( !context && elem.ownerDocument != document ) {
-						setDocument( elem );
-						xml = !documentIsHTML;
-					}
-					while ( ( matcher = elementMatchers[ j++ ] ) ) {
-						if ( matcher( elem, context || document, xml ) ) {
-							push.call( results, elem );
-							break;
-						}
-					}
-					if ( outermost ) {
-						dirruns = dirrunsUnique;
-					}
-				}
-
-				// Track unmatched elements for set filters
-				if ( bySet ) {
-
-					// They will have gone through all possible matchers
-					if ( ( elem = !matcher && elem ) ) {
-						matchedCount--;
-					}
-
-					// Lengthen the array for every element, matched or not
-					if ( seed ) {
-						unmatched.push( elem );
-					}
-				}
-			}
-
-			// `i` is now the count of elements visited above, and adding it to `matchedCount`
-			// makes the latter nonnegative.
-			matchedCount += i;
-
-			// Apply set filters to unmatched elements
-			// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
-			// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
-			// no element matchers and no seed.
-			// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
-			// case, which will result in a "00" `matchedCount` that differs from `i` but is also
-			// numerically zero.
-			if ( bySet && i !== matchedCount ) {
-				j = 0;
-				while ( ( matcher = setMatchers[ j++ ] ) ) {
-					matcher( unmatched, setMatched, context, xml );
-				}
-
-				if ( seed ) {
-
-					// Reintegrate element matches to eliminate the need for sorting
-					if ( matchedCount > 0 ) {
-						while ( i-- ) {
-							if ( !( unmatched[ i ] || setMatched[ i ] ) ) {
-								setMatched[ i ] = pop.call( results );
-							}
-						}
-					}
-
-					// Discard index placeholder values to get only actual matches
-					setMatched = condense( setMatched );
-				}
-
-				// Add matches to results
-				push.apply( results, setMatched );
-
-				// Seedless set matches succeeding multiple successful matchers stipulate sorting
-				if ( outermost && !seed && setMatched.length > 0 &&
-					( matchedCount + setMatchers.length ) > 1 ) {
-
-					jQuery.uniqueSort( results );
-				}
-			}
-
-			// Override manipulation of globals by nested matchers
-			if ( outermost ) {
-				dirruns = dirrunsUnique;
-				outermostContext = contextBackup;
-			}
-
-			return unmatched;
-		};
-
-	return bySet ?
-		markFunction( superMatcher ) :
-		superMatcher;
-}
-
-function compile( selector, match /* Internal Use Only */ ) {
-	var i,
-		setMatchers = [],
-		elementMatchers = [],
-		cached = compilerCache[ selector + " " ];
-
-	if ( !cached ) {
-
-		// Generate a function of recursive functions that can be used to check each element
-		if ( !match ) {
-			match = tokenize( selector );
-		}
-		i = match.length;
-		while ( i-- ) {
-			cached = matcherFromTokens( match[ i ] );
-			if ( cached[ expando ] ) {
-				setMatchers.push( cached );
-			} else {
-				elementMatchers.push( cached );
-			}
-		}
-
-		// Cache the compiled function
-		cached = compilerCache( selector,
-			matcherFromGroupMatchers( elementMatchers, setMatchers ) );
-
-		// Save selector and tokenization
-		cached.selector = selector;
-	}
-	return cached;
-}
-
-/**
- * A low-level selection function that works with jQuery's compiled
- *  selector functions
- * @param {String|Function} selector A selector or a pre-compiled
- *  selector function built with jQuery selector compile
- * @param {Element} context
- * @param {Array} [results]
- * @param {Array} [seed] A set of elements to match against
- */
-function select( selector, context, results, seed ) {
-	var i, tokens, token, type, find,
-		compiled = typeof selector === "function" && selector,
-		match = !seed && tokenize( ( selector = compiled.selector || selector ) );
-
-	results = results || [];
-
-	// Try to minimize operations if there is only one selector in the list and no seed
-	// (the latter of which guarantees us context)
-	if ( match.length === 1 ) {
-
-		// Reduce context if the leading compound selector is an ID
-		tokens = match[ 0 ] = match[ 0 ].slice( 0 );
-		if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
-				context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {
-
-			context = ( Expr.find.ID(
-				token.matches[ 0 ].replace( runescape, funescape ),
-				context
-			) || [] )[ 0 ];
-			if ( !context ) {
-				return results;
-
-			// Precompiled matchers will still verify ancestry, so step up a level
-			} else if ( compiled ) {
-				context = context.parentNode;
-			}
-
-			selector = selector.slice( tokens.shift().value.length );
-		}
-
-		// Fetch a seed set for right-to-left matching
-		i = matchExpr.needsContext.test( selector ) ? 0 : tokens.length;
-		while ( i-- ) {
-			token = tokens[ i ];
-
-			// Abort if we hit a combinator
-			if ( Expr.relative[ ( type = token.type ) ] ) {
-				break;
-			}
-			if ( ( find = Expr.find[ type ] ) ) {
-
-				// Search, expanding context for leading sibling combinators
-				if ( ( seed = find(
-					token.matches[ 0 ].replace( runescape, funescape ),
-					rsibling.test( tokens[ 0 ].type ) &&
-						testContext( context.parentNode ) || context
-				) ) ) {
-
-					// If seed is empty or no tokens remain, we can return early
-					tokens.splice( i, 1 );
-					selector = seed.length && toSelector( tokens );
-					if ( !selector ) {
-						push.apply( results, seed );
-						return results;
-					}
-
-					break;
-				}
-			}
-		}
-	}
-
-	// Compile and execute a filtering function if one is not provided
-	// Provide `match` to avoid retokenization if we modified the selector above
-	( compiled || compile( selector, match ) )(
-		seed,
-		context,
-		!documentIsHTML,
-		results,
-		!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
-	);
-	return results;
-}
-
-// One-time assignments
-
-// Support: Android <=4.0 - 4.1+
-// Sort stability
-support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando;
-
-// Initialize against the default document
-setDocument();
-
-// Support: Android <=4.0 - 4.1+
-// Detached nodes confoundingly follow *each other*
-support.sortDetached = assert( function( el ) {
-
-	// Should return 1, but returns 4 (following)
-	return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1;
-} );
-
-jQuery.find = find;
-
-// Deprecated
-jQuery.expr[ ":" ] = jQuery.expr.pseudos;
-jQuery.unique = jQuery.uniqueSort;
-
-// These have always been private, but they used to be documented as part of
-// Sizzle so let's maintain them for now for backwards compatibility purposes.
-find.compile = compile;
-find.select = select;
-find.setDocument = setDocument;
-find.tokenize = tokenize;
-
-find.escape = jQuery.escapeSelector;
-find.getText = jQuery.text;
-find.isXML = jQuery.isXMLDoc;
-find.selectors = jQuery.expr;
-find.support = jQuery.support;
-find.uniqueSort = jQuery.uniqueSort;
-
-	/* eslint-enable */
-
-} )();
-
-
-var dir = function( elem, dir, until ) {
-	var matched = [],
-		truncate = until !== undefined;
-
-	while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
-		if ( elem.nodeType === 1 ) {
-			if ( truncate && jQuery( elem ).is( until ) ) {
-				break;
-			}
-			matched.push( elem );
-		}
-	}
-	return matched;
-};
-
-
-var siblings = function( n, elem ) {
-	var matched = [];
-
-	for ( ; n; n = n.nextSibling ) {
-		if ( n.nodeType === 1 && n !== elem ) {
-			matched.push( n );
-		}
-	}
-
-	return matched;
-};
-
-
-var rneedsContext = jQuery.expr.match.needsContext;
-
-var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
-
-
-
-// Implement the identical functionality for filter and not
-function winnow( elements, qualifier, not ) {
-	if ( isFunction( qualifier ) ) {
-		return jQuery.grep( elements, function( elem, i ) {
-			return !!qualifier.call( elem, i, elem ) !== not;
-		} );
-	}
-
-	// Single element
-	if ( qualifier.nodeType ) {
-		return jQuery.grep( elements, function( elem ) {
-			return ( elem === qualifier ) !== not;
-		} );
-	}
-
-	// Arraylike of elements (jQuery, arguments, Array)
-	if ( typeof qualifier !== "string" ) {
-		return jQuery.grep( elements, function( elem ) {
-			return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
-		} );
-	}
-
-	// Filtered directly for both simple and complex selectors
-	return jQuery.filter( qualifier, elements, not );
-}
-
-jQuery.filter = function( expr, elems, not ) {
-	var elem = elems[ 0 ];
-
-	if ( not ) {
-		expr = ":not(" + expr + ")";
-	}
-
-	if ( elems.length === 1 && elem.nodeType === 1 ) {
-		return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
-	}
-
-	return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
-		return elem.nodeType === 1;
-	} ) );
-};
-
-jQuery.fn.extend( {
-	find: function( selector ) {
-		var i, ret,
-			len = this.length,
-			self = this;
-
-		if ( typeof selector !== "string" ) {
-			return this.pushStack( jQuery( selector ).filter( function() {
-				for ( i = 0; i < len; i++ ) {
-					if ( jQuery.contains( self[ i ], this ) ) {
-						return true;
-					}
-				}
-			} ) );
-		}
-
-		ret = this.pushStack( [] );
-
-		for ( i = 0; i < len; i++ ) {
-			jQuery.find( selector, self[ i ], ret );
-		}
-
-		return len > 1 ? jQuery.uniqueSort( ret ) : ret;
-	},
-	filter: function( selector ) {
-		return this.pushStack( winnow( this, selector || [], false ) );
-	},
-	not: function( selector ) {
-		return this.pushStack( winnow( this, selector || [], true ) );
-	},
-	is: function( selector ) {
-		return !!winnow(
-			this,
-
-			// If this is a positional/relative selector, check membership in the returned set
-			// so $("p:first").is("p:last") won't return true for a doc with two "p".
-			typeof selector === "string" && rneedsContext.test( selector ) ?
-				jQuery( selector ) :
-				selector || [],
-			false
-		).length;
-	}
-} );
-
-
-// Initialize a jQuery object
-
-
-// A central reference to the root jQuery(document)
-var rootjQuery,
-
-	// A simple way to check for HTML strings
-	// Prioritize #id over  to avoid XSS via location.hash (trac-9521)
-	// Strict HTML recognition (trac-11290: must start with <)
-	// Shortcut simple #id case for speed
-	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
-
-	init = jQuery.fn.init = function( selector, context, root ) {
-		var match, elem;
-
-		// HANDLE: $(""), $(null), $(undefined), $(false)
-		if ( !selector ) {
-			return this;
-		}
-
-		// Method init() accepts an alternate rootjQuery
-		// so migrate can support jQuery.sub (gh-2101)
-		root = root || rootjQuery;
-
-		// Handle HTML strings
-		if ( typeof selector === "string" ) {
-			if ( selector[ 0 ] === "<" &&
-				selector[ selector.length - 1 ] === ">" &&
-				selector.length >= 3 ) {
-
-				// Assume that strings that start and end with <> are HTML and skip the regex check
-				match = [ null, selector, null ];
-
-			} else {
-				match = rquickExpr.exec( selector );
-			}
-
-			// Match html or make sure no context is specified for #id
-			if ( match && ( match[ 1 ] || !context ) ) {
-
-				// HANDLE: $(html) -> $(array)
-				if ( match[ 1 ] ) {
-					context = context instanceof jQuery ? context[ 0 ] : context;
-
-					// Option to run scripts is true for back-compat
-					// Intentionally let the error be thrown if parseHTML is not present
-					jQuery.merge( this, jQuery.parseHTML(
-						match[ 1 ],
-						context && context.nodeType ? context.ownerDocument || context : document,
-						true
-					) );
-
-					// HANDLE: $(html, props)
-					if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
-						for ( match in context ) {
-
-							// Properties of context are called as methods if possible
-							if ( isFunction( this[ match ] ) ) {
-								this[ match ]( context[ match ] );
-
-							// ...and otherwise set as attributes
-							} else {
-								this.attr( match, context[ match ] );
-							}
-						}
-					}
-
-					return this;
-
-				// HANDLE: $(#id)
-				} else {
-					elem = document.getElementById( match[ 2 ] );
-
-					if ( elem ) {
-
-						// Inject the element directly into the jQuery object
-						this[ 0 ] = elem;
-						this.length = 1;
-					}
-					return this;
-				}
-
-			// HANDLE: $(expr, $(...))
-			} else if ( !context || context.jquery ) {
-				return ( context || root ).find( selector );
-
-			// HANDLE: $(expr, context)
-			// (which is just equivalent to: $(context).find(expr)
-			} else {
-				return this.constructor( context ).find( selector );
-			}
-
-		// HANDLE: $(DOMElement)
-		} else if ( selector.nodeType ) {
-			this[ 0 ] = selector;
-			this.length = 1;
-			return this;
-
-		// HANDLE: $(function)
-		// Shortcut for document ready
-		} else if ( isFunction( selector ) ) {
-			return root.ready !== undefined ?
-				root.ready( selector ) :
-
-				// Execute immediately if ready is not present
-				selector( jQuery );
-		}
-
-		return jQuery.makeArray( selector, this );
-	};
-
-// Give the init function the jQuery prototype for later instantiation
-init.prototype = jQuery.fn;
-
-// Initialize central reference
-rootjQuery = jQuery( document );
-
-
-var rparentsprev = /^(?:parents|prev(?:Until|All))/,
-
-	// Methods guaranteed to produce a unique set when starting from a unique set
-	guaranteedUnique = {
-		children: true,
-		contents: true,
-		next: true,
-		prev: true
-	};
-
-jQuery.fn.extend( {
-	has: function( target ) {
-		var targets = jQuery( target, this ),
-			l = targets.length;
-
-		return this.filter( function() {
-			var i = 0;
-			for ( ; i < l; i++ ) {
-				if ( jQuery.contains( this, targets[ i ] ) ) {
-					return true;
-				}
-			}
-		} );
-	},
-
-	closest: function( selectors, context ) {
-		var cur,
-			i = 0,
-			l = this.length,
-			matched = [],
-			targets = typeof selectors !== "string" && jQuery( selectors );
-
-		// Positional selectors never match, since there's no _selection_ context
-		if ( !rneedsContext.test( selectors ) ) {
-			for ( ; i < l; i++ ) {
-				for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
-
-					// Always skip document fragments
-					if ( cur.nodeType < 11 && ( targets ?
-						targets.index( cur ) > -1 :
-
-						// Don't pass non-elements to jQuery#find
-						cur.nodeType === 1 &&
-							jQuery.find.matchesSelector( cur, selectors ) ) ) {
-
-						matched.push( cur );
-						break;
-					}
-				}
-			}
-		}
-
-		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
-	},
-
-	// Determine the position of an element within the set
-	index: function( elem ) {
-
-		// No argument, return index in parent
-		if ( !elem ) {
-			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
-		}
-
-		// Index in selector
-		if ( typeof elem === "string" ) {
-			return indexOf.call( jQuery( elem ), this[ 0 ] );
-		}
-
-		// Locate the position of the desired element
-		return indexOf.call( this,
-
-			// If it receives a jQuery object, the first element is used
-			elem.jquery ? elem[ 0 ] : elem
-		);
-	},
-
-	add: function( selector, context ) {
-		return this.pushStack(
-			jQuery.uniqueSort(
-				jQuery.merge( this.get(), jQuery( selector, context ) )
-			)
-		);
-	},
-
-	addBack: function( selector ) {
-		return this.add( selector == null ?
-			this.prevObject : this.prevObject.filter( selector )
-		);
-	}
-} );
-
-function sibling( cur, dir ) {
-	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
-	return cur;
-}
-
-jQuery.each( {
-	parent: function( elem ) {
-		var parent = elem.parentNode;
-		return parent && parent.nodeType !== 11 ? parent : null;
-	},
-	parents: function( elem ) {
-		return dir( elem, "parentNode" );
-	},
-	parentsUntil: function( elem, _i, until ) {
-		return dir( elem, "parentNode", until );
-	},
-	next: function( elem ) {
-		return sibling( elem, "nextSibling" );
-	},
-	prev: function( elem ) {
-		return sibling( elem, "previousSibling" );
-	},
-	nextAll: function( elem ) {
-		return dir( elem, "nextSibling" );
-	},
-	prevAll: function( elem ) {
-		return dir( elem, "previousSibling" );
-	},
-	nextUntil: function( elem, _i, until ) {
-		return dir( elem, "nextSibling", until );
-	},
-	prevUntil: function( elem, _i, until ) {
-		return dir( elem, "previousSibling", until );
-	},
-	siblings: function( elem ) {
-		return siblings( ( elem.parentNode || {} ).firstChild, elem );
-	},
-	children: function( elem ) {
-		return siblings( elem.firstChild );
-	},
-	contents: function( elem ) {
-		if ( elem.contentDocument != null &&
-
-			// Support: IE 11+
-			//  elements with no `data` attribute has an object
-			// `contentDocument` with a `null` prototype.
-			getProto( elem.contentDocument ) ) {
-
-			return elem.contentDocument;
-		}
-
-		// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
-		// Treat the template element as a regular one in browsers that
-		// don't support it.
-		if ( nodeName( elem, "template" ) ) {
-			elem = elem.content || elem;
-		}
-
-		return jQuery.merge( [], elem.childNodes );
-	}
-}, function( name, fn ) {
-	jQuery.fn[ name ] = function( until, selector ) {
-		var matched = jQuery.map( this, fn, until );
-
-		if ( name.slice( -5 ) !== "Until" ) {
-			selector = until;
-		}
-
-		if ( selector && typeof selector === "string" ) {
-			matched = jQuery.filter( selector, matched );
-		}
-
-		if ( this.length > 1 ) {
-
-			// Remove duplicates
-			if ( !guaranteedUnique[ name ] ) {
-				jQuery.uniqueSort( matched );
-			}
-
-			// Reverse order for parents* and prev-derivatives
-			if ( rparentsprev.test( name ) ) {
-				matched.reverse();
-			}
-		}
-
-		return this.pushStack( matched );
-	};
-} );
-var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
-
-
-
-// Convert String-formatted options into Object-formatted ones
-function createOptions( options ) {
-	var object = {};
-	jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
-		object[ flag ] = true;
-	} );
-	return object;
-}
-
-/*
- * Create a callback list using the following parameters:
- *
- *	options: an optional list of space-separated options that will change how
- *			the callback list behaves or a more traditional option object
- *
- * By default a callback list will act like an event callback list and can be
- * "fired" multiple times.
- *
- * Possible options:
- *
- *	once:			will ensure the callback list can only be fired once (like a Deferred)
- *
- *	memory:			will keep track of previous values and will call any callback added
- *					after the list has been fired right away with the latest "memorized"
- *					values (like a Deferred)
- *
- *	unique:			will ensure a callback can only be added once (no duplicate in the list)
- *
- *	stopOnFalse:	interrupt callings when a callback returns false
- *
- */
-jQuery.Callbacks = function( options ) {
-
-	// Convert options from String-formatted to Object-formatted if needed
-	// (we check in cache first)
-	options = typeof options === "string" ?
-		createOptions( options ) :
-		jQuery.extend( {}, options );
-
-	var // Flag to know if list is currently firing
-		firing,
-
-		// Last fire value for non-forgettable lists
-		memory,
-
-		// Flag to know if list was already fired
-		fired,
-
-		// Flag to prevent firing
-		locked,
-
-		// Actual callback list
-		list = [],
-
-		// Queue of execution data for repeatable lists
-		queue = [],
-
-		// Index of currently firing callback (modified by add/remove as needed)
-		firingIndex = -1,
-
-		// Fire callbacks
-		fire = function() {
-
-			// Enforce single-firing
-			locked = locked || options.once;
-
-			// Execute callbacks for all pending executions,
-			// respecting firingIndex overrides and runtime changes
-			fired = firing = true;
-			for ( ; queue.length; firingIndex = -1 ) {
-				memory = queue.shift();
-				while ( ++firingIndex < list.length ) {
-
-					// Run callback and check for early termination
-					if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
-						options.stopOnFalse ) {
-
-						// Jump to end and forget the data so .add doesn't re-fire
-						firingIndex = list.length;
-						memory = false;
-					}
-				}
-			}
-
-			// Forget the data if we're done with it
-			if ( !options.memory ) {
-				memory = false;
-			}
-
-			firing = false;
-
-			// Clean up if we're done firing for good
-			if ( locked ) {
-
-				// Keep an empty list if we have data for future add calls
-				if ( memory ) {
-					list = [];
-
-				// Otherwise, this object is spent
-				} else {
-					list = "";
-				}
-			}
-		},
-
-		// Actual Callbacks object
-		self = {
-
-			// Add a callback or a collection of callbacks to the list
-			add: function() {
-				if ( list ) {
-
-					// If we have memory from a past run, we should fire after adding
-					if ( memory && !firing ) {
-						firingIndex = list.length - 1;
-						queue.push( memory );
-					}
-
-					( function add( args ) {
-						jQuery.each( args, function( _, arg ) {
-							if ( isFunction( arg ) ) {
-								if ( !options.unique || !self.has( arg ) ) {
-									list.push( arg );
-								}
-							} else if ( arg && arg.length && toType( arg ) !== "string" ) {
-
-								// Inspect recursively
-								add( arg );
-							}
-						} );
-					} )( arguments );
-
-					if ( memory && !firing ) {
-						fire();
-					}
-				}
-				return this;
-			},
-
-			// Remove a callback from the list
-			remove: function() {
-				jQuery.each( arguments, function( _, arg ) {
-					var index;
-					while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
-						list.splice( index, 1 );
-
-						// Handle firing indexes
-						if ( index <= firingIndex ) {
-							firingIndex--;
-						}
-					}
-				} );
-				return this;
-			},
-
-			// Check if a given callback is in the list.
-			// If no argument is given, return whether or not list has callbacks attached.
-			has: function( fn ) {
-				return fn ?
-					jQuery.inArray( fn, list ) > -1 :
-					list.length > 0;
-			},
-
-			// Remove all callbacks from the list
-			empty: function() {
-				if ( list ) {
-					list = [];
-				}
-				return this;
-			},
-
-			// Disable .fire and .add
-			// Abort any current/pending executions
-			// Clear all callbacks and values
-			disable: function() {
-				locked = queue = [];
-				list = memory = "";
-				return this;
-			},
-			disabled: function() {
-				return !list;
-			},
-
-			// Disable .fire
-			// Also disable .add unless we have memory (since it would have no effect)
-			// Abort any pending executions
-			lock: function() {
-				locked = queue = [];
-				if ( !memory && !firing ) {
-					list = memory = "";
-				}
-				return this;
-			},
-			locked: function() {
-				return !!locked;
-			},
-
-			// Call all callbacks with the given context and arguments
-			fireWith: function( context, args ) {
-				if ( !locked ) {
-					args = args || [];
-					args = [ context, args.slice ? args.slice() : args ];
-					queue.push( args );
-					if ( !firing ) {
-						fire();
-					}
-				}
-				return this;
-			},
-
-			// Call all the callbacks with the given arguments
-			fire: function() {
-				self.fireWith( this, arguments );
-				return this;
-			},
-
-			// To know if the callbacks have already been called at least once
-			fired: function() {
-				return !!fired;
-			}
-		};
-
-	return self;
-};
-
-
-function Identity( v ) {
-	return v;
-}
-function Thrower( ex ) {
-	throw ex;
-}
-
-function adoptValue( value, resolve, reject, noValue ) {
-	var method;
-
-	try {
-
-		// Check for promise aspect first to privilege synchronous behavior
-		if ( value && isFunction( ( method = value.promise ) ) ) {
-			method.call( value ).done( resolve ).fail( reject );
-
-		// Other thenables
-		} else if ( value && isFunction( ( method = value.then ) ) ) {
-			method.call( value, resolve, reject );
-
-		// Other non-thenables
-		} else {
-
-			// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
-			// * false: [ value ].slice( 0 ) => resolve( value )
-			// * true: [ value ].slice( 1 ) => resolve()
-			resolve.apply( undefined, [ value ].slice( noValue ) );
-		}
-
-	// For Promises/A+, convert exceptions into rejections
-	// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
-	// Deferred#then to conditionally suppress rejection.
-	} catch ( value ) {
-
-		// Support: Android 4.0 only
-		// Strict mode functions invoked without .call/.apply get global-object context
-		reject.apply( undefined, [ value ] );
-	}
-}
-
-jQuery.extend( {
-
-	Deferred: function( func ) {
-		var tuples = [
-
-				// action, add listener, callbacks,
-				// ... .then handlers, argument index, [final state]
-				[ "notify", "progress", jQuery.Callbacks( "memory" ),
-					jQuery.Callbacks( "memory" ), 2 ],
-				[ "resolve", "done", jQuery.Callbacks( "once memory" ),
-					jQuery.Callbacks( "once memory" ), 0, "resolved" ],
-				[ "reject", "fail", jQuery.Callbacks( "once memory" ),
-					jQuery.Callbacks( "once memory" ), 1, "rejected" ]
-			],
-			state = "pending",
-			promise = {
-				state: function() {
-					return state;
-				},
-				always: function() {
-					deferred.done( arguments ).fail( arguments );
-					return this;
-				},
-				"catch": function( fn ) {
-					return promise.then( null, fn );
-				},
-
-				// Keep pipe for back-compat
-				pipe: function( /* fnDone, fnFail, fnProgress */ ) {
-					var fns = arguments;
-
-					return jQuery.Deferred( function( newDefer ) {
-						jQuery.each( tuples, function( _i, tuple ) {
-
-							// Map tuples (progress, done, fail) to arguments (done, fail, progress)
-							var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
-
-							// deferred.progress(function() { bind to newDefer or newDefer.notify })
-							// deferred.done(function() { bind to newDefer or newDefer.resolve })
-							// deferred.fail(function() { bind to newDefer or newDefer.reject })
-							deferred[ tuple[ 1 ] ]( function() {
-								var returned = fn && fn.apply( this, arguments );
-								if ( returned && isFunction( returned.promise ) ) {
-									returned.promise()
-										.progress( newDefer.notify )
-										.done( newDefer.resolve )
-										.fail( newDefer.reject );
-								} else {
-									newDefer[ tuple[ 0 ] + "With" ](
-										this,
-										fn ? [ returned ] : arguments
-									);
-								}
-							} );
-						} );
-						fns = null;
-					} ).promise();
-				},
-				then: function( onFulfilled, onRejected, onProgress ) {
-					var maxDepth = 0;
-					function resolve( depth, deferred, handler, special ) {
-						return function() {
-							var that = this,
-								args = arguments,
-								mightThrow = function() {
-									var returned, then;
-
-									// Support: Promises/A+ section 2.3.3.3.3
-									// https://promisesaplus.com/#point-59
-									// Ignore double-resolution attempts
-									if ( depth < maxDepth ) {
-										return;
-									}
-
-									returned = handler.apply( that, args );
-
-									// Support: Promises/A+ section 2.3.1
-									// https://promisesaplus.com/#point-48
-									if ( returned === deferred.promise() ) {
-										throw new TypeError( "Thenable self-resolution" );
-									}
-
-									// Support: Promises/A+ sections 2.3.3.1, 3.5
-									// https://promisesaplus.com/#point-54
-									// https://promisesaplus.com/#point-75
-									// Retrieve `then` only once
-									then = returned &&
-
-										// Support: Promises/A+ section 2.3.4
-										// https://promisesaplus.com/#point-64
-										// Only check objects and functions for thenability
-										( typeof returned === "object" ||
-											typeof returned === "function" ) &&
-										returned.then;
-
-									// Handle a returned thenable
-									if ( isFunction( then ) ) {
-
-										// Special processors (notify) just wait for resolution
-										if ( special ) {
-											then.call(
-												returned,
-												resolve( maxDepth, deferred, Identity, special ),
-												resolve( maxDepth, deferred, Thrower, special )
-											);
-
-										// Normal processors (resolve) also hook into progress
-										} else {
-
-											// ...and disregard older resolution values
-											maxDepth++;
-
-											then.call(
-												returned,
-												resolve( maxDepth, deferred, Identity, special ),
-												resolve( maxDepth, deferred, Thrower, special ),
-												resolve( maxDepth, deferred, Identity,
-													deferred.notifyWith )
-											);
-										}
-
-									// Handle all other returned values
-									} else {
-
-										// Only substitute handlers pass on context
-										// and multiple values (non-spec behavior)
-										if ( handler !== Identity ) {
-											that = undefined;
-											args = [ returned ];
-										}
-
-										// Process the value(s)
-										// Default process is resolve
-										( special || deferred.resolveWith )( that, args );
-									}
-								},
-
-								// Only normal processors (resolve) catch and reject exceptions
-								process = special ?
-									mightThrow :
-									function() {
-										try {
-											mightThrow();
-										} catch ( e ) {
-
-											if ( jQuery.Deferred.exceptionHook ) {
-												jQuery.Deferred.exceptionHook( e,
-													process.error );
-											}
-
-											// Support: Promises/A+ section 2.3.3.3.4.1
-											// https://promisesaplus.com/#point-61
-											// Ignore post-resolution exceptions
-											if ( depth + 1 >= maxDepth ) {
-
-												// Only substitute handlers pass on context
-												// and multiple values (non-spec behavior)
-												if ( handler !== Thrower ) {
-													that = undefined;
-													args = [ e ];
-												}
-
-												deferred.rejectWith( that, args );
-											}
-										}
-									};
-
-							// Support: Promises/A+ section 2.3.3.3.1
-							// https://promisesaplus.com/#point-57
-							// Re-resolve promises immediately to dodge false rejection from
-							// subsequent errors
-							if ( depth ) {
-								process();
-							} else {
-
-								// Call an optional hook to record the error, in case of exception
-								// since it's otherwise lost when execution goes async
-								if ( jQuery.Deferred.getErrorHook ) {
-									process.error = jQuery.Deferred.getErrorHook();
-
-								// The deprecated alias of the above. While the name suggests
-								// returning the stack, not an error instance, jQuery just passes
-								// it directly to `console.warn` so both will work; an instance
-								// just better cooperates with source maps.
-								} else if ( jQuery.Deferred.getStackHook ) {
-									process.error = jQuery.Deferred.getStackHook();
-								}
-								window.setTimeout( process );
-							}
-						};
-					}
-
-					return jQuery.Deferred( function( newDefer ) {
-
-						// progress_handlers.add( ... )
-						tuples[ 0 ][ 3 ].add(
-							resolve(
-								0,
-								newDefer,
-								isFunction( onProgress ) ?
-									onProgress :
-									Identity,
-								newDefer.notifyWith
-							)
-						);
-
-						// fulfilled_handlers.add( ... )
-						tuples[ 1 ][ 3 ].add(
-							resolve(
-								0,
-								newDefer,
-								isFunction( onFulfilled ) ?
-									onFulfilled :
-									Identity
-							)
-						);
-
-						// rejected_handlers.add( ... )
-						tuples[ 2 ][ 3 ].add(
-							resolve(
-								0,
-								newDefer,
-								isFunction( onRejected ) ?
-									onRejected :
-									Thrower
-							)
-						);
-					} ).promise();
-				},
-
-				// Get a promise for this deferred
-				// If obj is provided, the promise aspect is added to the object
-				promise: function( obj ) {
-					return obj != null ? jQuery.extend( obj, promise ) : promise;
-				}
-			},
-			deferred = {};
-
-		// Add list-specific methods
-		jQuery.each( tuples, function( i, tuple ) {
-			var list = tuple[ 2 ],
-				stateString = tuple[ 5 ];
-
-			// promise.progress = list.add
-			// promise.done = list.add
-			// promise.fail = list.add
-			promise[ tuple[ 1 ] ] = list.add;
-
-			// Handle state
-			if ( stateString ) {
-				list.add(
-					function() {
-
-						// state = "resolved" (i.e., fulfilled)
-						// state = "rejected"
-						state = stateString;
-					},
-
-					// rejected_callbacks.disable
-					// fulfilled_callbacks.disable
-					tuples[ 3 - i ][ 2 ].disable,
-
-					// rejected_handlers.disable
-					// fulfilled_handlers.disable
-					tuples[ 3 - i ][ 3 ].disable,
-
-					// progress_callbacks.lock
-					tuples[ 0 ][ 2 ].lock,
-
-					// progress_handlers.lock
-					tuples[ 0 ][ 3 ].lock
-				);
-			}
-
-			// progress_handlers.fire
-			// fulfilled_handlers.fire
-			// rejected_handlers.fire
-			list.add( tuple[ 3 ].fire );
-
-			// deferred.notify = function() { deferred.notifyWith(...) }
-			// deferred.resolve = function() { deferred.resolveWith(...) }
-			// deferred.reject = function() { deferred.rejectWith(...) }
-			deferred[ tuple[ 0 ] ] = function() {
-				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
-				return this;
-			};
-
-			// deferred.notifyWith = list.fireWith
-			// deferred.resolveWith = list.fireWith
-			// deferred.rejectWith = list.fireWith
-			deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
-		} );
-
-		// Make the deferred a promise
-		promise.promise( deferred );
-
-		// Call given func if any
-		if ( func ) {
-			func.call( deferred, deferred );
-		}
-
-		// All done!
-		return deferred;
-	},
-
-	// Deferred helper
-	when: function( singleValue ) {
-		var
-
-			// count of uncompleted subordinates
-			remaining = arguments.length,
-
-			// count of unprocessed arguments
-			i = remaining,
-
-			// subordinate fulfillment data
-			resolveContexts = Array( i ),
-			resolveValues = slice.call( arguments ),
-
-			// the primary Deferred
-			primary = jQuery.Deferred(),
-
-			// subordinate callback factory
-			updateFunc = function( i ) {
-				return function( value ) {
-					resolveContexts[ i ] = this;
-					resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
-					if ( !( --remaining ) ) {
-						primary.resolveWith( resolveContexts, resolveValues );
-					}
-				};
-			};
-
-		// Single- and empty arguments are adopted like Promise.resolve
-		if ( remaining <= 1 ) {
-			adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject,
-				!remaining );
-
-			// Use .then() to unwrap secondary thenables (cf. gh-3000)
-			if ( primary.state() === "pending" ||
-				isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
-
-				return primary.then();
-			}
-		}
-
-		// Multiple arguments are aggregated like Promise.all array elements
-		while ( i-- ) {
-			adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject );
-		}
-
-		return primary.promise();
-	}
-} );
-
-
-// These usually indicate a programmer mistake during development,
-// warn about them ASAP rather than swallowing them by default.
-var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
-
-// If `jQuery.Deferred.getErrorHook` is defined, `asyncError` is an error
-// captured before the async barrier to get the original error cause
-// which may otherwise be hidden.
-jQuery.Deferred.exceptionHook = function( error, asyncError ) {
-
-	// Support: IE 8 - 9 only
-	// Console exists when dev tools are open, which can happen at any time
-	if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
-		window.console.warn( "jQuery.Deferred exception: " + error.message,
-			error.stack, asyncError );
-	}
-};
-
-
-
-
-jQuery.readyException = function( error ) {
-	window.setTimeout( function() {
-		throw error;
-	} );
-};
-
-
-
-
-// The deferred used on DOM ready
-var readyList = jQuery.Deferred();
-
-jQuery.fn.ready = function( fn ) {
-
-	readyList
-		.then( fn )
-
-		// Wrap jQuery.readyException in a function so that the lookup
-		// happens at the time of error handling instead of callback
-		// registration.
-		.catch( function( error ) {
-			jQuery.readyException( error );
-		} );
-
-	return this;
-};
-
-jQuery.extend( {
-
-	// Is the DOM ready to be used? Set to true once it occurs.
-	isReady: false,
-
-	// A counter to track how many items to wait for before
-	// the ready event fires. See trac-6781
-	readyWait: 1,
-
-	// Handle when the DOM is ready
-	ready: function( wait ) {
-
-		// Abort if there are pending holds or we're already ready
-		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
-			return;
-		}
-
-		// Remember that the DOM is ready
-		jQuery.isReady = true;
-
-		// If a normal DOM Ready event fired, decrement, and wait if need be
-		if ( wait !== true && --jQuery.readyWait > 0 ) {
-			return;
-		}
-
-		// If there are functions bound, to execute
-		readyList.resolveWith( document, [ jQuery ] );
-	}
-} );
-
-jQuery.ready.then = readyList.then;
-
-// The ready event handler and self cleanup method
-function completed() {
-	document.removeEventListener( "DOMContentLoaded", completed );
-	window.removeEventListener( "load", completed );
-	jQuery.ready();
-}
-
-// Catch cases where $(document).ready() is called
-// after the browser event has already occurred.
-// Support: IE <=9 - 10 only
-// Older IE sometimes signals "interactive" too soon
-if ( document.readyState === "complete" ||
-	( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
-
-	// Handle it asynchronously to allow scripts the opportunity to delay ready
-	window.setTimeout( jQuery.ready );
-
-} else {
-
-	// Use the handy event callback
-	document.addEventListener( "DOMContentLoaded", completed );
-
-	// A fallback to window.onload, that will always work
-	window.addEventListener( "load", completed );
-}
-
-
-
-
-// Multifunctional method to get and set values of a collection
-// The value/s can optionally be executed if it's a function
-var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
-	var i = 0,
-		len = elems.length,
-		bulk = key == null;
-
-	// Sets many values
-	if ( toType( key ) === "object" ) {
-		chainable = true;
-		for ( i in key ) {
-			access( elems, fn, i, key[ i ], true, emptyGet, raw );
-		}
-
-	// Sets one value
-	} else if ( value !== undefined ) {
-		chainable = true;
-
-		if ( !isFunction( value ) ) {
-			raw = true;
-		}
-
-		if ( bulk ) {
-
-			// Bulk operations run against the entire set
-			if ( raw ) {
-				fn.call( elems, value );
-				fn = null;
-
-			// ...except when executing function values
-			} else {
-				bulk = fn;
-				fn = function( elem, _key, value ) {
-					return bulk.call( jQuery( elem ), value );
-				};
-			}
-		}
-
-		if ( fn ) {
-			for ( ; i < len; i++ ) {
-				fn(
-					elems[ i ], key, raw ?
-						value :
-						value.call( elems[ i ], i, fn( elems[ i ], key ) )
-				);
-			}
-		}
-	}
-
-	if ( chainable ) {
-		return elems;
-	}
-
-	// Gets
-	if ( bulk ) {
-		return fn.call( elems );
-	}
-
-	return len ? fn( elems[ 0 ], key ) : emptyGet;
-};
-
-
-// Matches dashed string for camelizing
-var rmsPrefix = /^-ms-/,
-	rdashAlpha = /-([a-z])/g;
-
-// Used by camelCase as callback to replace()
-function fcamelCase( _all, letter ) {
-	return letter.toUpperCase();
-}
-
-// Convert dashed to camelCase; used by the css and data modules
-// Support: IE <=9 - 11, Edge 12 - 15
-// Microsoft forgot to hump their vendor prefix (trac-9572)
-function camelCase( string ) {
-	return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
-}
-var acceptData = function( owner ) {
-
-	// Accepts only:
-	//  - Node
-	//    - Node.ELEMENT_NODE
-	//    - Node.DOCUMENT_NODE
-	//  - Object
-	//    - Any
-	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
-};
-
-
-
-
-function Data() {
-	this.expando = jQuery.expando + Data.uid++;
-}
-
-Data.uid = 1;
-
-Data.prototype = {
-
-	cache: function( owner ) {
-
-		// Check if the owner object already has a cache
-		var value = owner[ this.expando ];
-
-		// If not, create one
-		if ( !value ) {
-			value = {};
-
-			// We can accept data for non-element nodes in modern browsers,
-			// but we should not, see trac-8335.
-			// Always return an empty object.
-			if ( acceptData( owner ) ) {
-
-				// If it is a node unlikely to be stringify-ed or looped over
-				// use plain assignment
-				if ( owner.nodeType ) {
-					owner[ this.expando ] = value;
-
-				// Otherwise secure it in a non-enumerable property
-				// configurable must be true to allow the property to be
-				// deleted when data is removed
-				} else {
-					Object.defineProperty( owner, this.expando, {
-						value: value,
-						configurable: true
-					} );
-				}
-			}
-		}
-
-		return value;
-	},
-	set: function( owner, data, value ) {
-		var prop,
-			cache = this.cache( owner );
-
-		// Handle: [ owner, key, value ] args
-		// Always use camelCase key (gh-2257)
-		if ( typeof data === "string" ) {
-			cache[ camelCase( data ) ] = value;
-
-		// Handle: [ owner, { properties } ] args
-		} else {
-
-			// Copy the properties one-by-one to the cache object
-			for ( prop in data ) {
-				cache[ camelCase( prop ) ] = data[ prop ];
-			}
-		}
-		return cache;
-	},
-	get: function( owner, key ) {
-		return key === undefined ?
-			this.cache( owner ) :
-
-			// Always use camelCase key (gh-2257)
-			owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
-	},
-	access: function( owner, key, value ) {
-
-		// In cases where either:
-		//
-		//   1. No key was specified
-		//   2. A string key was specified, but no value provided
-		//
-		// Take the "read" path and allow the get method to determine
-		// which value to return, respectively either:
-		//
-		//   1. The entire cache object
-		//   2. The data stored at the key
-		//
-		if ( key === undefined ||
-				( ( key && typeof key === "string" ) && value === undefined ) ) {
-
-			return this.get( owner, key );
-		}
-
-		// When the key is not a string, or both a key and value
-		// are specified, set or extend (existing objects) with either:
-		//
-		//   1. An object of properties
-		//   2. A key and value
-		//
-		this.set( owner, key, value );
-
-		// Since the "set" path can have two possible entry points
-		// return the expected data based on which path was taken[*]
-		return value !== undefined ? value : key;
-	},
-	remove: function( owner, key ) {
-		var i,
-			cache = owner[ this.expando ];
-
-		if ( cache === undefined ) {
-			return;
-		}
-
-		if ( key !== undefined ) {
-
-			// Support array or space separated string of keys
-			if ( Array.isArray( key ) ) {
-
-				// If key is an array of keys...
-				// We always set camelCase keys, so remove that.
-				key = key.map( camelCase );
-			} else {
-				key = camelCase( key );
-
-				// If a key with the spaces exists, use it.
-				// Otherwise, create an array by matching non-whitespace
-				key = key in cache ?
-					[ key ] :
-					( key.match( rnothtmlwhite ) || [] );
-			}
-
-			i = key.length;
-
-			while ( i-- ) {
-				delete cache[ key[ i ] ];
-			}
-		}
-
-		// Remove the expando if there's no more data
-		if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
-
-			// Support: Chrome <=35 - 45
-			// Webkit & Blink performance suffers when deleting properties
-			// from DOM nodes, so set to undefined instead
-			// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
-			if ( owner.nodeType ) {
-				owner[ this.expando ] = undefined;
-			} else {
-				delete owner[ this.expando ];
-			}
-		}
-	},
-	hasData: function( owner ) {
-		var cache = owner[ this.expando ];
-		return cache !== undefined && !jQuery.isEmptyObject( cache );
-	}
-};
-var dataPriv = new Data();
-
-var dataUser = new Data();
-
-
-
-//	Implementation Summary
-//
-//	1. Enforce API surface and semantic compatibility with 1.9.x branch
-//	2. Improve the module's maintainability by reducing the storage
-//		paths to a single mechanism.
-//	3. Use the same single mechanism to support "private" and "user" data.
-//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
-//	5. Avoid exposing implementation details on user objects (eg. expando properties)
-//	6. Provide a clear path for implementation upgrade to WeakMap in 2014
-
-var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
-	rmultiDash = /[A-Z]/g;
-
-function getData( data ) {
-	if ( data === "true" ) {
-		return true;
-	}
-
-	if ( data === "false" ) {
-		return false;
-	}
-
-	if ( data === "null" ) {
-		return null;
-	}
-
-	// Only convert to a number if it doesn't change the string
-	if ( data === +data + "" ) {
-		return +data;
-	}
-
-	if ( rbrace.test( data ) ) {
-		return JSON.parse( data );
-	}
-
-	return data;
-}
-
-function dataAttr( elem, key, data ) {
-	var name;
-
-	// If nothing was found internally, try to fetch any
-	// data from the HTML5 data-* attribute
-	if ( data === undefined && elem.nodeType === 1 ) {
-		name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
-		data = elem.getAttribute( name );
-
-		if ( typeof data === "string" ) {
-			try {
-				data = getData( data );
-			} catch ( e ) {}
-
-			// Make sure we set the data so it isn't changed later
-			dataUser.set( elem, key, data );
-		} else {
-			data = undefined;
-		}
-	}
-	return data;
-}
-
-jQuery.extend( {
-	hasData: function( elem ) {
-		return dataUser.hasData( elem ) || dataPriv.hasData( elem );
-	},
-
-	data: function( elem, name, data ) {
-		return dataUser.access( elem, name, data );
-	},
-
-	removeData: function( elem, name ) {
-		dataUser.remove( elem, name );
-	},
-
-	// TODO: Now that all calls to _data and _removeData have been replaced
-	// with direct calls to dataPriv methods, these can be deprecated.
-	_data: function( elem, name, data ) {
-		return dataPriv.access( elem, name, data );
-	},
-
-	_removeData: function( elem, name ) {
-		dataPriv.remove( elem, name );
-	}
-} );
-
-jQuery.fn.extend( {
-	data: function( key, value ) {
-		var i, name, data,
-			elem = this[ 0 ],
-			attrs = elem && elem.attributes;
-
-		// Gets all values
-		if ( key === undefined ) {
-			if ( this.length ) {
-				data = dataUser.get( elem );
-
-				if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
-					i = attrs.length;
-					while ( i-- ) {
-
-						// Support: IE 11 only
-						// The attrs elements can be null (trac-14894)
-						if ( attrs[ i ] ) {
-							name = attrs[ i ].name;
-							if ( name.indexOf( "data-" ) === 0 ) {
-								name = camelCase( name.slice( 5 ) );
-								dataAttr( elem, name, data[ name ] );
-							}
-						}
-					}
-					dataPriv.set( elem, "hasDataAttrs", true );
-				}
-			}
-
-			return data;
-		}
-
-		// Sets multiple values
-		if ( typeof key === "object" ) {
-			return this.each( function() {
-				dataUser.set( this, key );
-			} );
-		}
-
-		return access( this, function( value ) {
-			var data;
-
-			// The calling jQuery object (element matches) is not empty
-			// (and therefore has an element appears at this[ 0 ]) and the
-			// `value` parameter was not undefined. An empty jQuery object
-			// will result in `undefined` for elem = this[ 0 ] which will
-			// throw an exception if an attempt to read a data cache is made.
-			if ( elem && value === undefined ) {
-
-				// Attempt to get data from the cache
-				// The key will always be camelCased in Data
-				data = dataUser.get( elem, key );
-				if ( data !== undefined ) {
-					return data;
-				}
-
-				// Attempt to "discover" the data in
-				// HTML5 custom data-* attrs
-				data = dataAttr( elem, key );
-				if ( data !== undefined ) {
-					return data;
-				}
-
-				// We tried really hard, but the data doesn't exist.
-				return;
-			}
-
-			// Set the data...
-			this.each( function() {
-
-				// We always store the camelCased key
-				dataUser.set( this, key, value );
-			} );
-		}, null, value, arguments.length > 1, null, true );
-	},
-
-	removeData: function( key ) {
-		return this.each( function() {
-			dataUser.remove( this, key );
-		} );
-	}
-} );
-
-
-jQuery.extend( {
-	queue: function( elem, type, data ) {
-		var queue;
-
-		if ( elem ) {
-			type = ( type || "fx" ) + "queue";
-			queue = dataPriv.get( elem, type );
-
-			// Speed up dequeue by getting out quickly if this is just a lookup
-			if ( data ) {
-				if ( !queue || Array.isArray( data ) ) {
-					queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
-				} else {
-					queue.push( data );
-				}
-			}
-			return queue || [];
-		}
-	},
-
-	dequeue: function( elem, type ) {
-		type = type || "fx";
-
-		var queue = jQuery.queue( elem, type ),
-			startLength = queue.length,
-			fn = queue.shift(),
-			hooks = jQuery._queueHooks( elem, type ),
-			next = function() {
-				jQuery.dequeue( elem, type );
-			};
-
-		// If the fx queue is dequeued, always remove the progress sentinel
-		if ( fn === "inprogress" ) {
-			fn = queue.shift();
-			startLength--;
-		}
-
-		if ( fn ) {
-
-			// Add a progress sentinel to prevent the fx queue from being
-			// automatically dequeued
-			if ( type === "fx" ) {
-				queue.unshift( "inprogress" );
-			}
-
-			// Clear up the last queue stop function
-			delete hooks.stop;
-			fn.call( elem, next, hooks );
-		}
-
-		if ( !startLength && hooks ) {
-			hooks.empty.fire();
-		}
-	},
-
-	// Not public - generate a queueHooks object, or return the current one
-	_queueHooks: function( elem, type ) {
-		var key = type + "queueHooks";
-		return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
-			empty: jQuery.Callbacks( "once memory" ).add( function() {
-				dataPriv.remove( elem, [ type + "queue", key ] );
-			} )
-		} );
-	}
-} );
-
-jQuery.fn.extend( {
-	queue: function( type, data ) {
-		var setter = 2;
-
-		if ( typeof type !== "string" ) {
-			data = type;
-			type = "fx";
-			setter--;
-		}
-
-		if ( arguments.length < setter ) {
-			return jQuery.queue( this[ 0 ], type );
-		}
-
-		return data === undefined ?
-			this :
-			this.each( function() {
-				var queue = jQuery.queue( this, type, data );
-
-				// Ensure a hooks for this queue
-				jQuery._queueHooks( this, type );
-
-				if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
-					jQuery.dequeue( this, type );
-				}
-			} );
-	},
-	dequeue: function( type ) {
-		return this.each( function() {
-			jQuery.dequeue( this, type );
-		} );
-	},
-	clearQueue: function( type ) {
-		return this.queue( type || "fx", [] );
-	},
-
-	// Get a promise resolved when queues of a certain type
-	// are emptied (fx is the type by default)
-	promise: function( type, obj ) {
-		var tmp,
-			count = 1,
-			defer = jQuery.Deferred(),
-			elements = this,
-			i = this.length,
-			resolve = function() {
-				if ( !( --count ) ) {
-					defer.resolveWith( elements, [ elements ] );
-				}
-			};
-
-		if ( typeof type !== "string" ) {
-			obj = type;
-			type = undefined;
-		}
-		type = type || "fx";
-
-		while ( i-- ) {
-			tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
-			if ( tmp && tmp.empty ) {
-				count++;
-				tmp.empty.add( resolve );
-			}
-		}
-		resolve();
-		return defer.promise( obj );
-	}
-} );
-var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
-
-var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
-
-
-var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
-
-var documentElement = document.documentElement;
-
-
-
-	var isAttached = function( elem ) {
-			return jQuery.contains( elem.ownerDocument, elem );
-		},
-		composed = { composed: true };
-
-	// Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only
-	// Check attachment across shadow DOM boundaries when possible (gh-3504)
-	// Support: iOS 10.0-10.2 only
-	// Early iOS 10 versions support `attachShadow` but not `getRootNode`,
-	// leading to errors. We need to check for `getRootNode`.
-	if ( documentElement.getRootNode ) {
-		isAttached = function( elem ) {
-			return jQuery.contains( elem.ownerDocument, elem ) ||
-				elem.getRootNode( composed ) === elem.ownerDocument;
-		};
-	}
-var isHiddenWithinTree = function( elem, el ) {
-
-		// isHiddenWithinTree might be called from jQuery#filter function;
-		// in that case, element will be second argument
-		elem = el || elem;
-
-		// Inline style trumps all
-		return elem.style.display === "none" ||
-			elem.style.display === "" &&
-
-			// Otherwise, check computed style
-			// Support: Firefox <=43 - 45
-			// Disconnected elements can have computed display: none, so first confirm that elem is
-			// in the document.
-			isAttached( elem ) &&
-
-			jQuery.css( elem, "display" ) === "none";
-	};
-
-
-
-function adjustCSS( elem, prop, valueParts, tween ) {
-	var adjusted, scale,
-		maxIterations = 20,
-		currentValue = tween ?
-			function() {
-				return tween.cur();
-			} :
-			function() {
-				return jQuery.css( elem, prop, "" );
-			},
-		initial = currentValue(),
-		unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
-
-		// Starting value computation is required for potential unit mismatches
-		initialInUnit = elem.nodeType &&
-			( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
-			rcssNum.exec( jQuery.css( elem, prop ) );
-
-	if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
-
-		// Support: Firefox <=54
-		// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
-		initial = initial / 2;
-
-		// Trust units reported by jQuery.css
-		unit = unit || initialInUnit[ 3 ];
-
-		// Iteratively approximate from a nonzero starting point
-		initialInUnit = +initial || 1;
-
-		while ( maxIterations-- ) {
-
-			// Evaluate and update our best guess (doubling guesses that zero out).
-			// Finish if the scale equals or crosses 1 (making the old*new product non-positive).
-			jQuery.style( elem, prop, initialInUnit + unit );
-			if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
-				maxIterations = 0;
-			}
-			initialInUnit = initialInUnit / scale;
-
-		}
-
-		initialInUnit = initialInUnit * 2;
-		jQuery.style( elem, prop, initialInUnit + unit );
-
-		// Make sure we update the tween properties later on
-		valueParts = valueParts || [];
-	}
-
-	if ( valueParts ) {
-		initialInUnit = +initialInUnit || +initial || 0;
-
-		// Apply relative offset (+=/-=) if specified
-		adjusted = valueParts[ 1 ] ?
-			initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
-			+valueParts[ 2 ];
-		if ( tween ) {
-			tween.unit = unit;
-			tween.start = initialInUnit;
-			tween.end = adjusted;
-		}
-	}
-	return adjusted;
-}
-
-
-var defaultDisplayMap = {};
-
-function getDefaultDisplay( elem ) {
-	var temp,
-		doc = elem.ownerDocument,
-		nodeName = elem.nodeName,
-		display = defaultDisplayMap[ nodeName ];
-
-	if ( display ) {
-		return display;
-	}
-
-	temp = doc.body.appendChild( doc.createElement( nodeName ) );
-	display = jQuery.css( temp, "display" );
-
-	temp.parentNode.removeChild( temp );
-
-	if ( display === "none" ) {
-		display = "block";
-	}
-	defaultDisplayMap[ nodeName ] = display;
-
-	return display;
-}
-
-function showHide( elements, show ) {
-	var display, elem,
-		values = [],
-		index = 0,
-		length = elements.length;
-
-	// Determine new display value for elements that need to change
-	for ( ; index < length; index++ ) {
-		elem = elements[ index ];
-		if ( !elem.style ) {
-			continue;
-		}
-
-		display = elem.style.display;
-		if ( show ) {
-
-			// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
-			// check is required in this first loop unless we have a nonempty display value (either
-			// inline or about-to-be-restored)
-			if ( display === "none" ) {
-				values[ index ] = dataPriv.get( elem, "display" ) || null;
-				if ( !values[ index ] ) {
-					elem.style.display = "";
-				}
-			}
-			if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
-				values[ index ] = getDefaultDisplay( elem );
-			}
-		} else {
-			if ( display !== "none" ) {
-				values[ index ] = "none";
-
-				// Remember what we're overwriting
-				dataPriv.set( elem, "display", display );
-			}
-		}
-	}
-
-	// Set the display of the elements in a second loop to avoid constant reflow
-	for ( index = 0; index < length; index++ ) {
-		if ( values[ index ] != null ) {
-			elements[ index ].style.display = values[ index ];
-		}
-	}
-
-	return elements;
-}
-
-jQuery.fn.extend( {
-	show: function() {
-		return showHide( this, true );
-	},
-	hide: function() {
-		return showHide( this );
-	},
-	toggle: function( state ) {
-		if ( typeof state === "boolean" ) {
-			return state ? this.show() : this.hide();
-		}
-
-		return this.each( function() {
-			if ( isHiddenWithinTree( this ) ) {
-				jQuery( this ).show();
-			} else {
-				jQuery( this ).hide();
-			}
-		} );
-	}
-} );
-var rcheckableType = ( /^(?:checkbox|radio)$/i );
-
-var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
-
-var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
-
-
-
-( function() {
-	var fragment = document.createDocumentFragment(),
-		div = fragment.appendChild( document.createElement( "div" ) ),
-		input = document.createElement( "input" );
-
-	// Support: Android 4.0 - 4.3 only
-	// Check state lost if the name is set (trac-11217)
-	// Support: Windows Web Apps (WWA)
-	// `name` and `type` must use .setAttribute for WWA (trac-14901)
-	input.setAttribute( "type", "radio" );
-	input.setAttribute( "checked", "checked" );
-	input.setAttribute( "name", "t" );
-
-	div.appendChild( input );
-
-	// Support: Android <=4.1 only
-	// Older WebKit doesn't clone checked state correctly in fragments
-	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
-
-	// Support: IE <=11 only
-	// Make sure textarea (and checkbox) defaultValue is properly cloned
-	div.innerHTML = "";
-	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
-
-	// Support: IE <=9 only
-	// IE <=9 replaces ";
-	support.option = !!div.lastChild;
-} )();
-
-
-// We have to close these tags to support XHTML (trac-13200)
-var wrapMap = {
-
-	// XHTML parsers do not magically insert elements in the
-	// same way that tag soup parsers do. So we cannot shorten
-	// this by omitting  or other required elements.
-	thead: [ 1, "", "
" ], - col: [ 2, "", "
" ], - tr: [ 2, "", "
" ], - td: [ 3, "", "
" ], - - _default: [ 0, "", "" ] -}; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// Support: IE <=9 only -if ( !support.option ) { - wrapMap.optgroup = wrapMap.option = [ 1, "" ]; -} - - -function getAll( context, tag ) { - - // Support: IE <=9 - 11 only - // Use typeof to avoid zero-argument method invocation on host objects (trac-15151) - var ret; - - if ( typeof context.getElementsByTagName !== "undefined" ) { - ret = context.getElementsByTagName( tag || "*" ); - - } else if ( typeof context.querySelectorAll !== "undefined" ) { - ret = context.querySelectorAll( tag || "*" ); - - } else { - ret = []; - } - - if ( tag === undefined || tag && nodeName( context, tag ) ) { - return jQuery.merge( [ context ], ret ); - } - - return ret; -} - - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - dataPriv.set( - elems[ i ], - "globalEval", - !refElements || dataPriv.get( refElements[ i ], "globalEval" ) - ); - } -} - - -var rhtml = /<|&#?\w+;/; - -function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, attached, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( toType( elem ) === "object" ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (trac-12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( ( elem = nodes[ i++ ] ) ) { - - // Skip elements already in the context collection (trac-4087) - if ( selection && jQuery.inArray( elem, selection ) > -1 ) { - if ( ignored ) { - ignored.push( elem ); - } - continue; - } - - attached = isAttached( elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( attached ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( ( elem = tmp[ j++ ] ) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; -} - - -var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -function on( elem, types, selector, data, fn, one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - on( elem, type, selector, data, types[ type ], one ); - } - return elem; - } - - if ( data == null && fn == null ) { - - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return elem; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return elem.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - } ); -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.get( elem ); - - // Only attach events to objects that accept data - if ( !acceptData( elem ) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Ensure that invalid selectors throw exceptions at attach time - // Evaluate against documentElement in case elem is a non-element node (e.g., document) - if ( selector ) { - jQuery.find.matchesSelector( documentElement, selector ); - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !( events = elemData.events ) ) { - events = elemData.events = Object.create( null ); - } - if ( !( eventHandle = elemData.handle ) ) { - eventHandle = elemData.handle = function( e ) { - - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend( { - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join( "." ) - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !( handlers = events[ type ] ) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || - special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - - if ( !elemData || !( events = elemData.events ) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[ 2 ] && - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || - selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || - special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove data and the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - dataPriv.remove( elem, "handle events" ); - } - }, - - dispatch: function( nativeEvent ) { - - var i, j, ret, matched, handleObj, handlerQueue, - args = new Array( arguments.length ), - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( nativeEvent ), - - handlers = ( - dataPriv.get( this, "events" ) || Object.create( null ) - )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[ 0 ] = event; - - for ( i = 1; i < arguments.length; i++ ) { - args[ i ] = arguments[ i ]; - } - - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( ( handleObj = matched.handlers[ j++ ] ) && - !event.isImmediatePropagationStopped() ) { - - // If the event is namespaced, then each handler is only invoked if it is - // specially universal or its namespaces are a superset of the event's. - if ( !event.rnamespace || handleObj.namespace === false || - event.rnamespace.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || - handleObj.handler ).apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( ( event.result = ret ) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, handleObj, sel, matchedHandlers, matchedSelectors, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - if ( delegateCount && - - // Support: IE <=9 - // Black-hole SVG instance trees (trac-13180) - cur.nodeType && - - // Support: Firefox <=42 - // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) - // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click - // Support: IE 11 only - // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) - !( event.type === "click" && event.button >= 1 ) ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't check non-elements (trac-13208) - // Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764) - if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { - matchedHandlers = []; - matchedSelectors = {}; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (trac-13203) - sel = handleObj.selector + " "; - - if ( matchedSelectors[ sel ] === undefined ) { - matchedSelectors[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) > -1 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matchedSelectors[ sel ] ) { - matchedHandlers.push( handleObj ); - } - } - if ( matchedHandlers.length ) { - handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); - } - } - } - } - - // Add the remaining (directly-bound) handlers - cur = this; - if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); - } - - return handlerQueue; - }, - - addProp: function( name, hook ) { - Object.defineProperty( jQuery.Event.prototype, name, { - enumerable: true, - configurable: true, - - get: isFunction( hook ) ? - function() { - if ( this.originalEvent ) { - return hook( this.originalEvent ); - } - } : - function() { - if ( this.originalEvent ) { - return this.originalEvent[ name ]; - } - }, - - set: function( value ) { - Object.defineProperty( this, name, { - enumerable: true, - configurable: true, - writable: true, - value: value - } ); - } - } ); - }, - - fix: function( originalEvent ) { - return originalEvent[ jQuery.expando ] ? - originalEvent : - new jQuery.Event( originalEvent ); - }, - - special: { - load: { - - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - click: { - - // Utilize native event to ensure correct state for checkable inputs - setup: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Claim the first handler - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - // dataPriv.set( el, "click", ... ) - leverageNative( el, "click", true ); - } - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Force setup before triggering a click - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - leverageNative( el, "click" ); - } - - // Return non-false to allow normal event-path propagation - return true; - }, - - // For cross-browser consistency, suppress native .click() on links - // Also prevent it if we're currently inside a leveraged native-event stack - _default: function( event ) { - var target = event.target; - return rcheckableType.test( target.type ) && - target.click && nodeName( target, "input" ) && - dataPriv.get( target, "click" ) || - nodeName( target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - } -}; - -// Ensure the presence of an event listener that handles manually-triggered -// synthetic events by interrupting progress until reinvoked in response to -// *native* events that it fires directly, ensuring that state changes have -// already occurred before other listeners are invoked. -function leverageNative( el, type, isSetup ) { - - // Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add - if ( !isSetup ) { - if ( dataPriv.get( el, type ) === undefined ) { - jQuery.event.add( el, type, returnTrue ); - } - return; - } - - // Register the controller as a special universal handler for all event namespaces - dataPriv.set( el, type, false ); - jQuery.event.add( el, type, { - namespace: false, - handler: function( event ) { - var result, - saved = dataPriv.get( this, type ); - - if ( ( event.isTrigger & 1 ) && this[ type ] ) { - - // Interrupt processing of the outer synthetic .trigger()ed event - if ( !saved ) { - - // Store arguments for use when handling the inner native event - // There will always be at least one argument (an event object), so this array - // will not be confused with a leftover capture object. - saved = slice.call( arguments ); - dataPriv.set( this, type, saved ); - - // Trigger the native event and capture its result - this[ type ](); - result = dataPriv.get( this, type ); - dataPriv.set( this, type, false ); - - if ( saved !== result ) { - - // Cancel the outer synthetic event - event.stopImmediatePropagation(); - event.preventDefault(); - - return result; - } - - // If this is an inner synthetic event for an event with a bubbling surrogate - // (focus or blur), assume that the surrogate already propagated from triggering - // the native event and prevent that from happening again here. - // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the - // bubbling surrogate propagates *after* the non-bubbling base), but that seems - // less bad than duplication. - } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { - event.stopPropagation(); - } - - // If this is a native event triggered above, everything is now in order - // Fire an inner synthetic event with the original arguments - } else if ( saved ) { - - // ...and capture the result - dataPriv.set( this, type, jQuery.event.trigger( - saved[ 0 ], - saved.slice( 1 ), - this - ) ); - - // Abort handling of the native event by all jQuery handlers while allowing - // native handlers on the same element to run. On target, this is achieved - // by stopping immediate propagation just on the jQuery event. However, - // the native event is re-wrapped by a jQuery one on each level of the - // propagation so the only way to stop it for jQuery is to stop it for - // everyone via native `stopPropagation()`. This is not a problem for - // focus/blur which don't bubble, but it does also stop click on checkboxes - // and radios. We accept this limitation. - event.stopPropagation(); - event.isImmediatePropagationStopped = returnTrue; - } - } - } ); -} - -jQuery.removeEvent = function( elem, type, handle ) { - - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } -}; - -jQuery.Event = function( src, props ) { - - // Allow instantiation without the 'new' keyword - if ( !( this instanceof jQuery.Event ) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - - // Support: Android <=2.3 only - src.returnValue === false ? - returnTrue : - returnFalse; - - // Create target properties - // Support: Safari <=6 - 7 only - // Target should not be a text node (trac-504, trac-13143) - this.target = ( src.target && src.target.nodeType === 3 ) ? - src.target.parentNode : - src.target; - - this.currentTarget = src.currentTarget; - this.relatedTarget = src.relatedTarget; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || Date.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - constructor: jQuery.Event, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - isSimulated: false, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && !this.isSimulated ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Includes all common event props including KeyEvent and MouseEvent specific props -jQuery.each( { - altKey: true, - bubbles: true, - cancelable: true, - changedTouches: true, - ctrlKey: true, - detail: true, - eventPhase: true, - metaKey: true, - pageX: true, - pageY: true, - shiftKey: true, - view: true, - "char": true, - code: true, - charCode: true, - key: true, - keyCode: true, - button: true, - buttons: true, - clientX: true, - clientY: true, - offsetX: true, - offsetY: true, - pointerId: true, - pointerType: true, - screenX: true, - screenY: true, - targetTouches: true, - toElement: true, - touches: true, - which: true -}, jQuery.event.addProp ); - -jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { - - function focusMappedHandler( nativeEvent ) { - if ( document.documentMode ) { - - // Support: IE 11+ - // Attach a single focusin/focusout handler on the document while someone wants - // focus/blur. This is because the former are synchronous in IE while the latter - // are async. In other browsers, all those handlers are invoked synchronously. - - // `handle` from private data would already wrap the event, but we need - // to change the `type` here. - var handle = dataPriv.get( this, "handle" ), - event = jQuery.event.fix( nativeEvent ); - event.type = nativeEvent.type === "focusin" ? "focus" : "blur"; - event.isSimulated = true; - - // First, handle focusin/focusout - handle( nativeEvent ); - - // ...then, handle focus/blur - // - // focus/blur don't bubble while focusin/focusout do; simulate the former by only - // invoking the handler at the lower level. - if ( event.target === event.currentTarget ) { - - // The setup part calls `leverageNative`, which, in turn, calls - // `jQuery.event.add`, so event handle will already have been set - // by this point. - handle( event ); - } - } else { - - // For non-IE browsers, attach a single capturing handler on the document - // while someone wants focusin/focusout. - jQuery.event.simulate( delegateType, nativeEvent.target, - jQuery.event.fix( nativeEvent ) ); - } - } - - jQuery.event.special[ type ] = { - - // Utilize native event if possible so blur/focus sequence is correct - setup: function() { - - var attaches; - - // Claim the first handler - // dataPriv.set( this, "focus", ... ) - // dataPriv.set( this, "blur", ... ) - leverageNative( this, type, true ); - - if ( document.documentMode ) { - - // Support: IE 9 - 11+ - // We use the same native handler for focusin & focus (and focusout & blur) - // so we need to coordinate setup & teardown parts between those events. - // Use `delegateType` as the key as `type` is already used by `leverageNative`. - attaches = dataPriv.get( this, delegateType ); - if ( !attaches ) { - this.addEventListener( delegateType, focusMappedHandler ); - } - dataPriv.set( this, delegateType, ( attaches || 0 ) + 1 ); - } else { - - // Return false to allow normal processing in the caller - return false; - } - }, - trigger: function() { - - // Force setup before trigger - leverageNative( this, type ); - - // Return non-false to allow normal event-path propagation - return true; - }, - - teardown: function() { - var attaches; - - if ( document.documentMode ) { - attaches = dataPriv.get( this, delegateType ) - 1; - if ( !attaches ) { - this.removeEventListener( delegateType, focusMappedHandler ); - dataPriv.remove( this, delegateType ); - } else { - dataPriv.set( this, delegateType, attaches ); - } - } else { - - // Return false to indicate standard teardown should be applied - return false; - } - }, - - // Suppress native focus or blur if we're currently inside - // a leveraged native-event stack - _default: function( event ) { - return dataPriv.get( event.target, type ); - }, - - delegateType: delegateType - }; - - // Support: Firefox <=44 - // Firefox doesn't have focus(in | out) events - // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 - // - // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 - // focus(in | out) events fire after focus & blur events, - // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order - // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 - // - // Support: IE 9 - 11+ - // To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch, - // attach a single handler for both events in IE. - jQuery.event.special[ delegateType ] = { - setup: function() { - - // Handle: regular nodes (via `this.ownerDocument`), window - // (via `this.document`) & document (via `this`). - var doc = this.ownerDocument || this.document || this, - dataHolder = document.documentMode ? this : doc, - attaches = dataPriv.get( dataHolder, delegateType ); - - // Support: IE 9 - 11+ - // We use the same native handler for focusin & focus (and focusout & blur) - // so we need to coordinate setup & teardown parts between those events. - // Use `delegateType` as the key as `type` is already used by `leverageNative`. - if ( !attaches ) { - if ( document.documentMode ) { - this.addEventListener( delegateType, focusMappedHandler ); - } else { - doc.addEventListener( type, focusMappedHandler, true ); - } - } - dataPriv.set( dataHolder, delegateType, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this.document || this, - dataHolder = document.documentMode ? this : doc, - attaches = dataPriv.get( dataHolder, delegateType ) - 1; - - if ( !attaches ) { - if ( document.documentMode ) { - this.removeEventListener( delegateType, focusMappedHandler ); - } else { - doc.removeEventListener( type, focusMappedHandler, true ); - } - dataPriv.remove( dataHolder, delegateType ); - } else { - dataPriv.set( dataHolder, delegateType, attaches ); - } - } - }; -} ); - -// Create mouseenter/leave events using mouseover/out and event-time checks -// so that event delegation works in jQuery. -// Do the same for pointerenter/pointerleave and pointerover/pointerout -// -// Support: Safari 7 only -// Safari sends mouseenter too often; see: -// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 -// for the description of the bug (it existed in older Chrome versions as well). -jQuery.each( { - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mouseenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -} ); - -jQuery.fn.extend( { - - on: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn ); - }, - one: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? - handleObj.origType + "." + handleObj.namespace : - handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each( function() { - jQuery.event.remove( this, types, fn, selector ); - } ); - } -} ); - - -var - - // Support: IE <=10 - 11, Edge 12 - 13 only - // In IE/Edge using regex groups here causes severe slowdowns. - // See https://connect.microsoft.com/IE/feedback/details/1736512/ - rnoInnerhtml = /\s*$/g; - -// Prefer a tbody over its parent table for containing new rows -function manipulationTarget( elem, content ) { - if ( nodeName( elem, "table" ) && - nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - - return jQuery( elem ).children( "tbody" )[ 0 ] || elem; - } - - return elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { - elem.type = elem.type.slice( 5 ); - } else { - elem.removeAttribute( "type" ); - } - - return elem; -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( dataPriv.hasData( src ) ) { - pdataOld = dataPriv.get( src ); - events = pdataOld.events; - - if ( events ) { - dataPriv.remove( dest, "handle events" ); - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( dataUser.hasData( src ) ) { - udataOld = dataUser.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - dataUser.set( dest, udataCur ); - } -} - -// Fix IE bugs, see support tests -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -function domManip( collection, args, callback, ignored ) { - - // Flatten any nested arrays - args = flat( args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = collection.length, - iNoClone = l - 1, - value = args[ 0 ], - valueIsFunction = isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( valueIsFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return collection.each( function( index ) { - var self = collection.eq( index ); - if ( valueIsFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - domManip( self, args, callback, ignored ); - } ); - } - - if ( l ) { - fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - // Require either new content or an interest in ignored elements to invoke the callback - if ( first || ignored ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item - // instead of the first because it can end up - // being emptied incorrectly in certain situations (trac-8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( collection[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Re-enable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !dataPriv.access( node, "globalEval" ) && - jQuery.contains( doc, node ) ) { - - if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { - - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl && !node.noModule ) { - jQuery._evalUrl( node.src, { - nonce: node.nonce || node.getAttribute( "nonce" ) - }, doc ); - } - } else { - - // Unwrap a CDATA section containing script contents. This shouldn't be - // needed as in XML documents they're already not visible when - // inspecting element contents and in HTML documents they have no - // meaning but we're preserving that logic for backwards compatibility. - // This will be removed completely in 4.0. See gh-4904. - DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); - } - } - } - } - } - } - - return collection; -} - -function remove( elem, selector, keepData ) { - var node, - nodes = selector ? jQuery.filter( selector, elem ) : elem, - i = 0; - - for ( ; ( node = nodes[ i ] ) != null; i++ ) { - if ( !keepData && node.nodeType === 1 ) { - jQuery.cleanData( getAll( node ) ); - } - - if ( node.parentNode ) { - if ( keepData && isAttached( node ) ) { - setGlobalEval( getAll( node, "script" ) ); - } - node.parentNode.removeChild( node ); - } - } - - return elem; -} - -jQuery.extend( { - htmlPrefilter: function( html ) { - return html; - }, - - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = isAttached( elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew jQuery#find here for performance reasons: - // https://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - cleanData: function( elems ) { - var data, elem, type, - special = jQuery.event.special, - i = 0; - - for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { - if ( acceptData( elem ) ) { - if ( ( data = elem[ dataPriv.expando ] ) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataPriv.expando ] = undefined; - } - if ( elem[ dataUser.expando ] ) { - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataUser.expando ] = undefined; - } - } - } - } -} ); - -jQuery.fn.extend( { - detach: function( selector ) { - return remove( this, selector, true ); - }, - - remove: function( selector ) { - return remove( this, selector ); - }, - - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each( function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - } ); - }, null, value, arguments.length ); - }, - - append: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - } ); - }, - - prepend: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - } ); - }, - - before: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - } ); - }, - - after: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - } ); - }, - - empty: function() { - var elem, - i = 0; - - for ( ; ( elem = this[ i ] ) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - } ); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = jQuery.htmlPrefilter( value ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch ( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var ignored = []; - - // Make the changes, replacing each non-ignored context element with the new content - return domManip( this, arguments, function( elem ) { - var parent = this.parentNode; - - if ( jQuery.inArray( this, ignored ) < 0 ) { - jQuery.cleanData( getAll( this ) ); - if ( parent ) { - parent.replaceChild( elem, this ); - } - } - - // Force callback invocation - }, ignored ); - } -} ); - -jQuery.each( { - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: Android <=4.0 only, PhantomJS 1 only - // .get() because push.apply(_, arraylike) throws on ancient WebKit - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -} ); -var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); - -var rcustomProp = /^--/; - - -var getStyles = function( elem ) { - - // Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150) - // IE throws on elements created in popups - // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - var view = elem.ownerDocument.defaultView; - - if ( !view || !view.opener ) { - view = window; - } - - return view.getComputedStyle( elem ); - }; - -var swap = function( elem, options, callback ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.call( elem ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; -}; - - -var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); - - - -( function() { - - // Executing both pixelPosition & boxSizingReliable tests require only one layout - // so they're executed at the same time to save the second computation. - function computeStyleTests() { - - // This is a singleton, we need to execute it only once - if ( !div ) { - return; - } - - container.style.cssText = "position:absolute;left:-11111px;width:60px;" + - "margin-top:1px;padding:0;border:0"; - div.style.cssText = - "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + - "margin:auto;border:1px;padding:1px;" + - "width:60%;top:1%"; - documentElement.appendChild( container ).appendChild( div ); - - var divStyle = window.getComputedStyle( div ); - pixelPositionVal = divStyle.top !== "1%"; - - // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; - - // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 - // Some styles come back with percentage values, even though they shouldn't - div.style.right = "60%"; - pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; - - // Support: IE 9 - 11 only - // Detect misreporting of content dimensions for box-sizing:border-box elements - boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; - - // Support: IE 9 only - // Detect overflow:scroll screwiness (gh-3699) - // Support: Chrome <=64 - // Don't get tricked when zoom affects offsetWidth (gh-4029) - div.style.position = "absolute"; - scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; - - documentElement.removeChild( container ); - - // Nullify the div so it wouldn't be stored in the memory and - // it will also be a sign that checks already performed - div = null; - } - - function roundPixelMeasures( measure ) { - return Math.round( parseFloat( measure ) ); - } - - var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, - reliableTrDimensionsVal, reliableMarginLeftVal, - container = document.createElement( "div" ), - div = document.createElement( "div" ); - - // Finish early in limited (non-browser) environments - if ( !div.style ) { - return; - } - - // Support: IE <=9 - 11 only - // Style of cloned element affects source element cloned (trac-8908) - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - jQuery.extend( support, { - boxSizingReliable: function() { - computeStyleTests(); - return boxSizingReliableVal; - }, - pixelBoxStyles: function() { - computeStyleTests(); - return pixelBoxStylesVal; - }, - pixelPosition: function() { - computeStyleTests(); - return pixelPositionVal; - }, - reliableMarginLeft: function() { - computeStyleTests(); - return reliableMarginLeftVal; - }, - scrollboxSize: function() { - computeStyleTests(); - return scrollboxSizeVal; - }, - - // Support: IE 9 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Behavior in IE 9 is more subtle than in newer versions & it passes - // some versions of this test; make sure not to make it pass there! - // - // Support: Firefox 70+ - // Only Firefox includes border widths - // in computed dimensions. (gh-4529) - reliableTrDimensions: function() { - var table, tr, trChild, trStyle; - if ( reliableTrDimensionsVal == null ) { - table = document.createElement( "table" ); - tr = document.createElement( "tr" ); - trChild = document.createElement( "div" ); - - table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; - tr.style.cssText = "box-sizing:content-box;border:1px solid"; - - // Support: Chrome 86+ - // Height set through cssText does not get applied. - // Computed height then comes back as 0. - tr.style.height = "1px"; - trChild.style.height = "9px"; - - // Support: Android 8 Chrome 86+ - // In our bodyBackground.html iframe, - // display for all div elements is set to "inline", - // which causes a problem only in Android 8 Chrome 86. - // Ensuring the div is `display: block` - // gets around this issue. - trChild.style.display = "block"; - - documentElement - .appendChild( table ) - .appendChild( tr ) - .appendChild( trChild ); - - trStyle = window.getComputedStyle( tr ); - reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + - parseInt( trStyle.borderTopWidth, 10 ) + - parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; - - documentElement.removeChild( table ); - } - return reliableTrDimensionsVal; - } - } ); -} )(); - - -function curCSS( elem, name, computed ) { - var width, minWidth, maxWidth, ret, - isCustomProp = rcustomProp.test( name ), - - // Support: Firefox 51+ - // Retrieving style before computed somehow - // fixes an issue with getting wrong values - // on detached elements - style = elem.style; - - computed = computed || getStyles( elem ); - - // getPropertyValue is needed for: - // .css('filter') (IE 9 only, trac-12537) - // .css('--customProperty) (gh-3144) - if ( computed ) { - - // Support: IE <=9 - 11+ - // IE only supports `"float"` in `getPropertyValue`; in computed styles - // it's only available as `"cssFloat"`. We no longer modify properties - // sent to `.css()` apart from camelCasing, so we need to check both. - // Normally, this would create difference in behavior: if - // `getPropertyValue` returns an empty string, the value returned - // by `.css()` would be `undefined`. This is usually the case for - // disconnected elements. However, in IE even disconnected elements - // with no styles return `"none"` for `getPropertyValue( "float" )` - ret = computed.getPropertyValue( name ) || computed[ name ]; - - if ( isCustomProp && ret ) { - - // Support: Firefox 105+, Chrome <=105+ - // Spec requires trimming whitespace for custom properties (gh-4926). - // Firefox only trims leading whitespace. Chrome just collapses - // both leading & trailing whitespace to a single space. - // - // Fall back to `undefined` if empty string returned. - // This collapses a missing definition with property defined - // and set to an empty string but there's no standard API - // allowing us to differentiate them without a performance penalty - // and returning `undefined` aligns with older jQuery. - // - // rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED - // as whitespace while CSS does not, but this is not a problem - // because CSS preprocessing replaces them with U+000A LINE FEED - // (which *is* CSS whitespace) - // https://www.w3.org/TR/css-syntax-3/#input-preprocessing - ret = ret.replace( rtrimCSS, "$1" ) || undefined; - } - - if ( ret === "" && !isAttached( elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Android Browser returns percentage for some values, - // but width seems to be reliably pixels. - // This is against the CSSOM draft spec: - // https://drafts.csswg.org/cssom/#resolved-values - if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret !== undefined ? - - // Support: IE <=9 - 11 only - // IE returns zIndex value as an integer. - ret + "" : - ret; -} - - -function addGetHookIf( conditionFn, hookFn ) { - - // Define the hook, we'll check on the first run if it's really needed. - return { - get: function() { - if ( conditionFn() ) { - - // Hook not needed (or it's not possible to use it due - // to missing dependency), remove it. - delete this.get; - return; - } - - // Hook needed; redefine it so that the support test is not executed again. - return ( this.get = hookFn ).apply( this, arguments ); - } - }; -} - - -var cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style, - vendorProps = {}; - -// Return a vendor-prefixed property or undefined -function vendorPropName( name ) { - - // Check for vendor prefixed names - var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in emptyStyle ) { - return name; - } - } -} - -// Return a potentially-mapped jQuery.cssProps or vendor prefixed property -function finalPropName( name ) { - var final = jQuery.cssProps[ name ] || vendorProps[ name ]; - - if ( final ) { - return final; - } - if ( name in emptyStyle ) { - return name; - } - return vendorProps[ name ] = vendorPropName( name ) || name; -} - - -var - - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }; - -function setPositiveNumber( _elem, value, subtract ) { - - // Any relative (+/-) values have already been - // normalized at this point - var matches = rcssNum.exec( value ); - return matches ? - - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : - value; -} - -function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { - var i = dimension === "width" ? 1 : 0, - extra = 0, - delta = 0, - marginDelta = 0; - - // Adjustment may not be necessary - if ( box === ( isBorderBox ? "border" : "content" ) ) { - return 0; - } - - for ( ; i < 4; i += 2 ) { - - // Both box models exclude margin - // Count margin delta separately to only add it after scroll gutter adjustment. - // This is needed to make negative margins work with `outerHeight( true )` (gh-3982). - if ( box === "margin" ) { - marginDelta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); - } - - // If we get here with a content-box, we're seeking "padding" or "border" or "margin" - if ( !isBorderBox ) { - - // Add padding - delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // For "border" or "margin", add border - if ( box !== "padding" ) { - delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - - // But still keep track of it otherwise - } else { - extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - - // If we get here with a border-box (content + padding + border), we're seeking "content" or - // "padding" or "margin" - } else { - - // For "content", subtract padding - if ( box === "content" ) { - delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // For "content" or "padding", subtract border - if ( box !== "margin" ) { - delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - // Account for positive content-box scroll gutter when requested by providing computedVal - if ( !isBorderBox && computedVal >= 0 ) { - - // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border - // Assuming integer scroll gutter, subtract the rest and round down - delta += Math.max( 0, Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - computedVal - - delta - - extra - - 0.5 - - // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter - // Use an explicit zero to avoid NaN (gh-3964) - ) ) || 0; - } - - return delta + marginDelta; -} - -function getWidthOrHeight( elem, dimension, extra ) { - - // Start with computed style - var styles = getStyles( elem ), - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). - // Fake content-box until we know it's needed to know the true value. - boxSizingNeeded = !support.boxSizingReliable() || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - valueIsBorderBox = isBorderBox, - - val = curCSS( elem, dimension, styles ), - offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); - - // Support: Firefox <=54 - // Return a confounding non-pixel value or feign ignorance, as appropriate. - if ( rnumnonpx.test( val ) ) { - if ( !extra ) { - return val; - } - val = "auto"; - } - - - // Support: IE 9 - 11 only - // Use offsetWidth/offsetHeight for when box sizing is unreliable. - // In those cases, the computed value can be trusted to be border-box. - if ( ( !support.boxSizingReliable() && isBorderBox || - - // Support: IE 10 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Interestingly, in some cases IE 9 doesn't suffer from this issue. - !support.reliableTrDimensions() && nodeName( elem, "tr" ) || - - // Fall back to offsetWidth/offsetHeight when value is "auto" - // This happens for inline elements with no explicit setting (gh-3571) - val === "auto" || - - // Support: Android <=4.1 - 4.3 only - // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) - !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && - - // Make sure the element is visible & connected - elem.getClientRects().length ) { - - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // Where available, offsetWidth/offsetHeight approximate border box dimensions. - // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the - // retrieved value as a content box dimension. - valueIsBorderBox = offsetProp in elem; - if ( valueIsBorderBox ) { - val = elem[ offsetProp ]; - } - } - - // Normalize "" and auto - val = parseFloat( val ) || 0; - - // Adjust for the element's box model - return ( val + - boxModelAdjustment( - elem, - dimension, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles, - - // Provide the current computed size to request scroll gutter calculation (gh-3589) - val - ) - ) + "px"; -} - -jQuery.extend( { - - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - animationIterationCount: true, - aspectRatio: true, - borderImageSlice: true, - columnCount: true, - flexGrow: true, - flexShrink: true, - fontWeight: true, - gridArea: true, - gridColumn: true, - gridColumnEnd: true, - gridColumnStart: true, - gridRow: true, - gridRowEnd: true, - gridRowStart: true, - lineHeight: true, - opacity: true, - order: true, - orphans: true, - scale: true, - widows: true, - zIndex: true, - zoom: true, - - // SVG-related - fillOpacity: true, - floodOpacity: true, - stopOpacity: true, - strokeMiterlimit: true, - strokeOpacity: true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: {}, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ), - style = elem.style; - - // Make sure that we're working with the right name. We don't - // want to query the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Gets hook for the prefixed version, then unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // Convert "+=" or "-=" to relative numbers (trac-7345) - if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { - value = adjustCSS( elem, name, ret ); - - // Fixes bug trac-9237 - type = "number"; - } - - // Make sure that null and NaN values aren't set (trac-7116) - if ( value == null || value !== value ) { - return; - } - - // If a number was passed in, add the unit (except for certain CSS properties) - // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append - // "px" to a few hardcoded values. - if ( type === "number" && !isCustomProp ) { - value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); - } - - // background-* props affect original clone's values - if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !( "set" in hooks ) || - ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - - if ( isCustomProp ) { - style.setProperty( name, value ); - } else { - style[ name ] = value; - } - } - - } else { - - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && - ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { - - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ); - - // Make sure that we're working with the right name. We don't - // want to modify the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Try prefixed name followed by the unprefixed name - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - // Convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Make numeric if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || isFinite( num ) ? num || 0 : val; - } - - return val; - } -} ); - -jQuery.each( [ "height", "width" ], function( _i, dimension ) { - jQuery.cssHooks[ dimension ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - - // Certain elements can have dimension info if we invisibly show them - // but it must have a current display style that would benefit - return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - - // Support: Safari 8+ - // Table columns in Safari have non-zero offsetWidth & zero - // getBoundingClientRect().width unless display is changed. - // Support: IE <=11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, dimension, extra ); - } ) : - getWidthOrHeight( elem, dimension, extra ); - } - }, - - set: function( elem, value, extra ) { - var matches, - styles = getStyles( elem ), - - // Only read styles.position if the test has a chance to fail - // to avoid forcing a reflow. - scrollboxSizeBuggy = !support.scrollboxSize() && - styles.position === "absolute", - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) - boxSizingNeeded = scrollboxSizeBuggy || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - subtract = extra ? - boxModelAdjustment( - elem, - dimension, - extra, - isBorderBox, - styles - ) : - 0; - - // Account for unreliable border-box dimensions by comparing offset* to computed and - // faking a content-box to get border and padding (gh-3699) - if ( isBorderBox && scrollboxSizeBuggy ) { - subtract -= Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - parseFloat( styles[ dimension ] ) - - boxModelAdjustment( elem, dimension, "border", false, styles ) - - 0.5 - ); - } - - // Convert to pixels if value adjustment is needed - if ( subtract && ( matches = rcssNum.exec( value ) ) && - ( matches[ 3 ] || "px" ) !== "px" ) { - - elem.style[ dimension ] = value; - value = jQuery.css( elem, dimension ); - } - - return setPositiveNumber( elem, value, subtract ); - } - }; -} ); - -jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, - function( elem, computed ) { - if ( computed ) { - return ( parseFloat( curCSS( elem, "marginLeft" ) ) || - elem.getBoundingClientRect().left - - swap( elem, { marginLeft: 0 }, function() { - return elem.getBoundingClientRect().left; - } ) - ) + "px"; - } - } -); - -// These hooks are used by animate to expand properties -jQuery.each( { - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i = 0, - expanded = {}, - - // Assumes a single number if not a string - parts = typeof value === "string" ? value.split( " " ) : [ value ]; - - for ( ; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( prefix !== "margin" ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -} ); - -jQuery.fn.extend( { - css: function( name, value ) { - return access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( Array.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - } -} ); - - -function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); -} -jQuery.Tween = Tween; - -Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || jQuery.easing._default; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } -}; - -Tween.prototype.init.prototype = Tween.prototype; - -Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - // Use a property on the element directly when it is not a DOM element, - // or when there is no matching style property that exists. - if ( tween.elem.nodeType !== 1 || - tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { - return tween.elem[ tween.prop ]; - } - - // Passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails. - // Simple values such as "10px" are parsed to Float; - // complex values such as "rotate(1rad)" are returned as-is. - result = jQuery.css( tween.elem, tween.prop, "" ); - - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - - // Use step hook for back compat. - // Use cssHook if its there. - // Use .style if available and use plain properties where available. - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && ( - jQuery.cssHooks[ tween.prop ] || - tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } -}; - -// Support: IE <=9 only -// Panic based approach to setting things on disconnected nodes -Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p * Math.PI ) / 2; - }, - _default: "swing" -}; - -jQuery.fx = Tween.prototype.init; - -// Back compat <1.8 extension point -jQuery.fx.step = {}; - - - - -var - fxNow, inProgress, - rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/; - -function schedule() { - if ( inProgress ) { - if ( document.hidden === false && window.requestAnimationFrame ) { - window.requestAnimationFrame( schedule ); - } else { - window.setTimeout( schedule, jQuery.fx.interval ); - } - - jQuery.fx.tick(); - } -} - -// Animations created synchronously will run synchronously -function createFxNow() { - window.setTimeout( function() { - fxNow = undefined; - } ); - return ( fxNow = Date.now() ); -} - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - i = 0, - attrs = { height: type }; - - // If we include width, step value is 1 to do all cssExpand values, - // otherwise step value is 2 to skip over Left and Right - includeWidth = includeWidth ? 1 : 0; - for ( ; i < 4; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; -} - -function createTween( value, prop, animation ) { - var tween, - collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { - - // We're done with this property - return tween; - } - } -} - -function defaultPrefilter( elem, props, opts ) { - var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, - isBox = "width" in props || "height" in props, - anim = this, - orig = {}, - style = elem.style, - hidden = elem.nodeType && isHiddenWithinTree( elem ), - dataShow = dataPriv.get( elem, "fxshow" ); - - // Queue-skipping animations hijack the fx hooks - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always( function() { - - // Ensure the complete handler is called before this completes - anim.always( function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - } ); - } ); - } - - // Detect show/hide animations - for ( prop in props ) { - value = props[ prop ]; - if ( rfxtypes.test( value ) ) { - delete props[ prop ]; - toggle = toggle || value === "toggle"; - if ( value === ( hidden ? "hide" : "show" ) ) { - - // Pretend to be hidden if this is a "show" and - // there is still data from a stopped show/hide - if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { - hidden = true; - - // Ignore all other no-op show/hide data - } else { - continue; - } - } - orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); - } - } - - // Bail out if this is a no-op like .hide().hide() - propTween = !jQuery.isEmptyObject( props ); - if ( !propTween && jQuery.isEmptyObject( orig ) ) { - return; - } - - // Restrict "overflow" and "display" styles during box animations - if ( isBox && elem.nodeType === 1 ) { - - // Support: IE <=9 - 11, Edge 12 - 15 - // Record all 3 overflow attributes because IE does not infer the shorthand - // from identically-valued overflowX and overflowY and Edge just mirrors - // the overflowX value there. - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Identify a display type, preferring old show/hide data over the CSS cascade - restoreDisplay = dataShow && dataShow.display; - if ( restoreDisplay == null ) { - restoreDisplay = dataPriv.get( elem, "display" ); - } - display = jQuery.css( elem, "display" ); - if ( display === "none" ) { - if ( restoreDisplay ) { - display = restoreDisplay; - } else { - - // Get nonempty value(s) by temporarily forcing visibility - showHide( [ elem ], true ); - restoreDisplay = elem.style.display || restoreDisplay; - display = jQuery.css( elem, "display" ); - showHide( [ elem ] ); - } - } - - // Animate inline elements as inline-block - if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { - if ( jQuery.css( elem, "float" ) === "none" ) { - - // Restore the original display value at the end of pure show/hide animations - if ( !propTween ) { - anim.done( function() { - style.display = restoreDisplay; - } ); - if ( restoreDisplay == null ) { - display = style.display; - restoreDisplay = display === "none" ? "" : display; - } - } - style.display = "inline-block"; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - anim.always( function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - } ); - } - - // Implement show/hide animations - propTween = false; - for ( prop in orig ) { - - // General show/hide setup for this element animation - if ( !propTween ) { - if ( dataShow ) { - if ( "hidden" in dataShow ) { - hidden = dataShow.hidden; - } - } else { - dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); - } - - // Store hidden/visible for toggle so `.stop().toggle()` "reverses" - if ( toggle ) { - dataShow.hidden = !hidden; - } - - // Show elements before animating them - if ( hidden ) { - showHide( [ elem ], true ); - } - - /* eslint-disable no-loop-func */ - - anim.done( function() { - - /* eslint-enable no-loop-func */ - - // The final step of a "hide" animation is actually hiding the element - if ( !hidden ) { - showHide( [ elem ] ); - } - dataPriv.remove( elem, "fxshow" ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - } ); - } - - // Per-property setup - propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = propTween.start; - if ( hidden ) { - propTween.end = propTween.start; - propTween.start = 0; - } - } - } -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( Array.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // Not quite $.extend, this won't overwrite existing keys. - // Reusing 'index' because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } -} - -function Animation( elem, properties, options ) { - var result, - stopped, - index = 0, - length = Animation.prefilters.length, - deferred = jQuery.Deferred().always( function() { - - // Don't match elem in the :animated selector - delete tick.elem; - } ), - tick = function() { - if ( stopped ) { - return false; - } - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - - // Support: Android 2.3 only - // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (trac-12497) - temp = remaining / animation.duration || 0, - percent = 1 - temp, - index = 0, - length = animation.tweens.length; - - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ] ); - - // If there's more to do, yield - if ( percent < 1 && length ) { - return remaining; - } - - // If this was an empty animation, synthesize a final progress notification - if ( !length ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - } - - // Resolve the animation and report its conclusion - deferred.resolveWith( elem, [ animation ] ); - return false; - }, - animation = deferred.promise( { - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { - specialEasing: {}, - easing: jQuery.easing._default - }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - - // If we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - if ( stopped ) { - return this; - } - stopped = true; - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // Resolve when we played the last frame; otherwise, reject - if ( gotoEnd ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - } ), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length; index++ ) { - result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - if ( isFunction( result.stop ) ) { - jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - result.stop.bind( result ); - } - return result; - } - } - - jQuery.map( props, createTween, animation ); - - if ( isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - // Attach callbacks from options - animation - .progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); - - jQuery.fx.timer( - jQuery.extend( tick, { - elem: elem, - anim: animation, - queue: animation.opts.queue - } ) - ); - - return animation; -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweeners: { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }, - - tweener: function( props, callback ) { - if ( isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.match( rnothtmlwhite ); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length; index++ ) { - prop = props[ index ]; - Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; - Animation.tweeners[ prop ].unshift( callback ); - } - }, - - prefilters: [ defaultPrefilter ], - - prefilter: function( callback, prepend ) { - if ( prepend ) { - Animation.prefilters.unshift( callback ); - } else { - Animation.prefilters.push( callback ); - } - } -} ); - -jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !isFunction( easing ) && easing - }; - - // Go to the end state if fx are off - if ( jQuery.fx.off ) { - opt.duration = 0; - - } else { - if ( typeof opt.duration !== "number" ) { - if ( opt.duration in jQuery.fx.speeds ) { - opt.duration = jQuery.fx.speeds[ opt.duration ]; - - } else { - opt.duration = jQuery.fx.speeds._default; - } - } - } - - // Normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.fn.extend( { - fadeTo: function( speed, to, easing, callback ) { - - // Show any hidden elements after setting opacity to 0 - return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() - - // Animate to the value specified - .end().animate( { opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations, or finishing resolves immediately - if ( empty || dataPriv.get( this, "finish" ) ) { - anim.stop( true ); - } - }; - - doAnimation.finish = doAnimation; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue ) { - this.queue( type || "fx", [] ); - } - - return this.each( function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = dataPriv.get( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && - ( type == null || timers[ index ].queue === type ) ) { - - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // Start the next in the queue if the last step wasn't forced. - // Timers currently will call their complete callbacks, which - // will dequeue but only if they were gotoEnd. - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - } ); - }, - finish: function( type ) { - if ( type !== false ) { - type = type || "fx"; - } - return this.each( function() { - var index, - data = dataPriv.get( this ), - queue = data[ type + "queue" ], - hooks = data[ type + "queueHooks" ], - timers = jQuery.timers, - length = queue ? queue.length : 0; - - // Enable finishing flag on private data - data.finish = true; - - // Empty the queue first - jQuery.queue( this, type, [] ); - - if ( hooks && hooks.stop ) { - hooks.stop.call( this, true ); - } - - // Look for any active animations, and finish them - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && timers[ index ].queue === type ) { - timers[ index ].anim.stop( true ); - timers.splice( index, 1 ); - } - } - - // Look for any animations in the old queue and finish them - for ( index = 0; index < length; index++ ) { - if ( queue[ index ] && queue[ index ].finish ) { - queue[ index ].finish.call( this ); - } - } - - // Turn off finishing flag - delete data.finish; - } ); - } -} ); - -jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -} ); - -// Generate shortcuts for custom animations -jQuery.each( { - slideDown: genFx( "show" ), - slideUp: genFx( "hide" ), - slideToggle: genFx( "toggle" ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -} ); - -jQuery.timers = []; -jQuery.fx.tick = function() { - var timer, - i = 0, - timers = jQuery.timers; - - fxNow = Date.now(); - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - - // Run the timer and safely remove it when done (allowing for external removal) - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - fxNow = undefined; -}; - -jQuery.fx.timer = function( timer ) { - jQuery.timers.push( timer ); - jQuery.fx.start(); -}; - -jQuery.fx.interval = 13; -jQuery.fx.start = function() { - if ( inProgress ) { - return; - } - - inProgress = true; - schedule(); -}; - -jQuery.fx.stop = function() { - inProgress = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - - // Default speed - _default: 400 -}; - - -// Based off of the plugin by Clint Helfers, with permission. -jQuery.fn.delay = function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = window.setTimeout( next, time ); - hooks.stop = function() { - window.clearTimeout( timeout ); - }; - } ); -}; - - -( function() { - var input = document.createElement( "input" ), - select = document.createElement( "select" ), - opt = select.appendChild( document.createElement( "option" ) ); - - input.type = "checkbox"; - - // Support: Android <=4.3 only - // Default value for a checkbox should be "on" - support.checkOn = input.value !== ""; - - // Support: IE <=11 only - // Must access selectedIndex to make default options select - support.optSelected = opt.selected; - - // Support: IE <=11 only - // An input loses its value after becoming a radio - input = document.createElement( "input" ); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; -} )(); - - -var boolHook, - attrHandle = jQuery.expr.attrHandle; - -jQuery.fn.extend( { - attr: function( name, value ) { - return access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each( function() { - jQuery.removeAttr( this, name ); - } ); - } -} ); - -jQuery.extend( { - attr: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set attributes on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - // Attribute hooks are determined by the lowercase version - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - hooks = jQuery.attrHooks[ name.toLowerCase() ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); - } - - if ( value !== undefined ) { - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - } - - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - elem.setAttribute( name, value + "" ); - return value; - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? undefined : ret; - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - removeAttr: function( elem, value ) { - var name, - i = 0, - - // Attribute names can contain non-HTML whitespace characters - // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 - attrNames = value && value.match( rnothtmlwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( ( name = attrNames[ i++ ] ) ) { - elem.removeAttribute( name ); - } - } - } -} ); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; - -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { - var getter = attrHandle[ name ] || jQuery.find.attr; - - attrHandle[ name ] = function( elem, name, isXML ) { - var ret, handle, - lowercaseName = name.toLowerCase(); - - if ( !isXML ) { - - // Avoid an infinite loop by temporarily removing this function from the getter - handle = attrHandle[ lowercaseName ]; - attrHandle[ lowercaseName ] = ret; - ret = getter( elem, name, isXML ) != null ? - lowercaseName : - null; - attrHandle[ lowercaseName ] = handle; - } - return ret; - }; -} ); - - - - -var rfocusable = /^(?:input|select|textarea|button)$/i, - rclickable = /^(?:a|area)$/i; - -jQuery.fn.extend( { - prop: function( name, value ) { - return access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each( function() { - delete this[ jQuery.propFix[ name ] || name ]; - } ); - } -} ); - -jQuery.extend( { - prop: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set properties on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - return ( elem[ name ] = value ); - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - return elem[ name ]; - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - - // Support: IE <=9 - 11 only - // elem.tabIndex doesn't always return the - // correct value when it hasn't been explicitly set - // Use proper attribute retrieval (trac-12072) - var tabindex = jQuery.find.attr( elem, "tabindex" ); - - if ( tabindex ) { - return parseInt( tabindex, 10 ); - } - - if ( - rfocusable.test( elem.nodeName ) || - rclickable.test( elem.nodeName ) && - elem.href - ) { - return 0; - } - - return -1; - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - } -} ); - -// Support: IE <=11 only -// Accessing the selectedIndex property -// forces the browser to respect setting selected -// on the option -// The getter ensures a default option is selected -// when in an optgroup -// eslint rule "no-unused-expressions" is disabled for this code -// since it considers such accessions noop -if ( !support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - }, - set: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - }; -} - -jQuery.each( [ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -} ); - - - - - // Strip and collapse whitespace according to HTML spec - // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace - function stripAndCollapse( value ) { - var tokens = value.match( rnothtmlwhite ) || []; - return tokens.join( " " ); - } - - -function getClass( elem ) { - return elem.getAttribute && elem.getAttribute( "class" ) || ""; -} - -function classesToArray( value ) { - if ( Array.isArray( value ) ) { - return value; - } - if ( typeof value === "string" ) { - return value.match( rnothtmlwhite ) || []; - } - return []; -} - -jQuery.fn.extend( { - addClass: function( value ) { - var classNames, cur, curValue, className, i, finalValue; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - classNames = classesToArray( value ); - - if ( classNames.length ) { - return this.each( function() { - curValue = getClass( this ); - cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - if ( cur.indexOf( " " + className + " " ) < 0 ) { - cur += className + " "; - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - this.setAttribute( "class", finalValue ); - } - } - } ); - } - - return this; - }, - - removeClass: function( value ) { - var classNames, cur, curValue, className, i, finalValue; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( !arguments.length ) { - return this.attr( "class", "" ); - } - - classNames = classesToArray( value ); - - if ( classNames.length ) { - return this.each( function() { - curValue = getClass( this ); - - // This expression is here for better compressibility (see addClass) - cur = this.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - - // Remove *all* instances - while ( cur.indexOf( " " + className + " " ) > -1 ) { - cur = cur.replace( " " + className + " ", " " ); - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - this.setAttribute( "class", finalValue ); - } - } - } ); - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var classNames, className, i, self, - type = typeof value, - isValidValue = type === "string" || Array.isArray( value ); - - if ( isFunction( value ) ) { - return this.each( function( i ) { - jQuery( this ).toggleClass( - value.call( this, i, getClass( this ), stateVal ), - stateVal - ); - } ); - } - - if ( typeof stateVal === "boolean" && isValidValue ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - classNames = classesToArray( value ); - - return this.each( function() { - if ( isValidValue ) { - - // Toggle individual class names - self = jQuery( this ); - - for ( i = 0; i < classNames.length; i++ ) { - className = classNames[ i ]; - - // Check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( value === undefined || type === "boolean" ) { - className = getClass( this ); - if ( className ) { - - // Store className if set - dataPriv.set( this, "__className__", className ); - } - - // If the element has a class name or if we're passed `false`, - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - if ( this.setAttribute ) { - this.setAttribute( "class", - className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" - ); - } - } - } ); - }, - - hasClass: function( selector ) { - var className, elem, - i = 0; - - className = " " + selector + " "; - while ( ( elem = this[ i++ ] ) ) { - if ( elem.nodeType === 1 && - ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; - } - } - - return false; - } -} ); - - - - -var rreturn = /\r/g; - -jQuery.fn.extend( { - val: function( value ) { - var hooks, ret, valueIsFunction, - elem = this[ 0 ]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || - jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && - "get" in hooks && - ( ret = hooks.get( elem, "value" ) ) !== undefined - ) { - return ret; - } - - ret = elem.value; - - // Handle most common string cases - if ( typeof ret === "string" ) { - return ret.replace( rreturn, "" ); - } - - // Handle cases where value is null/undef or number - return ret == null ? "" : ret; - } - - return; - } - - valueIsFunction = isFunction( value ); - - return this.each( function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( valueIsFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - - } else if ( typeof val === "number" ) { - val += ""; - - } else if ( Array.isArray( val ) ) { - val = jQuery.map( val, function( value ) { - return value == null ? "" : value + ""; - } ); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - } ); - } -} ); - -jQuery.extend( { - valHooks: { - option: { - get: function( elem ) { - - var val = jQuery.find.attr( elem, "value" ); - return val != null ? - val : - - // Support: IE <=10 - 11 only - // option.text throws exceptions (trac-14686, trac-14858) - // Strip and collapse whitespace - // https://html.spec.whatwg.org/#strip-and-collapse-whitespace - stripAndCollapse( jQuery.text( elem ) ); - } - }, - select: { - get: function( elem ) { - var value, option, i, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one", - values = one ? null : [], - max = one ? index + 1 : options.length; - - if ( index < 0 ) { - i = max; - - } else { - i = one ? index : 0; - } - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Support: IE <=9 only - // IE8-9 doesn't update selected after form reset (trac-2551) - if ( ( option.selected || i === index ) && - - // Don't return options that are disabled or in a disabled optgroup - !option.disabled && - ( !option.parentNode.disabled || - !nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - - /* eslint-disable no-cond-assign */ - - if ( option.selected = - jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 - ) { - optionSet = true; - } - - /* eslint-enable no-cond-assign */ - } - - // Force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - } -} ); - -// Radios and checkboxes getter/setter -jQuery.each( [ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( Array.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); - } - } - }; - if ( !support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - return elem.getAttribute( "value" ) === null ? "on" : elem.value; - }; - } -} ); - - - - -// Return jQuery for attributes-only inclusion -var location = window.location; - -var nonce = { guid: Date.now() }; - -var rquery = ( /\?/ ); - - - -// Cross-browser xml parsing -jQuery.parseXML = function( data ) { - var xml, parserErrorElem; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) {} - - parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; - if ( !xml || parserErrorElem ) { - jQuery.error( "Invalid XML: " + ( - parserErrorElem ? - jQuery.map( parserErrorElem.childNodes, function( el ) { - return el.textContent; - } ).join( "\n" ) : - data - ) ); - } - return xml; -}; - - -var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - stopPropagationCallback = function( e ) { - e.stopPropagation(); - }; - -jQuery.extend( jQuery.event, { - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = lastElement = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (trac-9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724) - if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - lastElement = cur; - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && - dataPriv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( ( !special._default || - special._default.apply( eventPath.pop(), data ) === false ) && - acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name as the event. - // Don't do default actions on window, that's where global variables be (trac-6170) - if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - - if ( event.isPropagationStopped() ) { - lastElement.addEventListener( type, stopPropagationCallback ); - } - - elem[ type ](); - - if ( event.isPropagationStopped() ) { - lastElement.removeEventListener( type, stopPropagationCallback ); - } - - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - // Piggyback on a donor event to simulate a different one - // Used only for `focus(in | out)` events - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - } - ); - - jQuery.event.trigger( e, null, elem ); - } - -} ); - -jQuery.fn.extend( { - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -} ); - - -var - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, - rsubmittable = /^(?:input|select|textarea|keygen)/i; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( Array.isArray( obj ) ) { - - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - - // Item is non-scalar (array or object), encode its numeric index. - buildParams( - prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", - v, - traditional, - add - ); - } - } ); - - } else if ( !traditional && toType( obj ) === "object" ) { - - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - - // Serialize scalar item. - add( prefix, obj ); - } -} - -// Serialize an array of form elements or a set of -// key/values into a query string -jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, valueOrFunction ) { - - // If value is a function, invoke it and use its return value - var value = isFunction( valueOrFunction ) ? - valueOrFunction() : - valueOrFunction; - - s[ s.length ] = encodeURIComponent( key ) + "=" + - encodeURIComponent( value == null ? "" : value ); - }; - - if ( a == null ) { - return ""; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - } ); - - } else { - - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ); -}; - -jQuery.fn.extend( { - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map( function() { - - // Can add propHook for "elements" to filter or add form elements - var elements = jQuery.prop( this, "elements" ); - return elements ? jQuery.makeArray( elements ) : this; - } ).filter( function() { - var type = this.type; - - // Use .is( ":disabled" ) so that fieldset[disabled] works - return this.name && !jQuery( this ).is( ":disabled" ) && - rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckableType.test( type ) ); - } ).map( function( _i, elem ) { - var val = jQuery( this ).val(); - - if ( val == null ) { - return null; - } - - if ( Array.isArray( val ) ) { - return jQuery.map( val, function( val ) { - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ); - } - - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ).get(); - } -} ); - - -var - r20 = /%20/g, - rhash = /#.*$/, - rantiCache = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, - - // trac-7653, trac-8125, trac-8152: local protocol detection - rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (trac-10098); must appease lint and evade compression - allTypes = "*/".concat( "*" ), - - // Anchor tag for parsing the document origin - originAnchor = document.createElement( "a" ); - -originAnchor.href = location.href; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, - i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; - - if ( isFunction( func ) ) { - - // For each dataType in the dataTypeExpression - while ( ( dataType = dataTypes[ i++ ] ) ) { - - // Prepend if requested - if ( dataType[ 0 ] === "+" ) { - dataType = dataType.slice( 1 ) || "*"; - ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); - - // Otherwise append - } else { - ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); - } - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { - - var inspected = {}, - seekingTransport = ( structure === transports ); - - function inspect( dataType ) { - var selected; - inspected[ dataType ] = true; - jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { - var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if ( typeof dataTypeOrTransport === "string" && - !seekingTransport && !inspected[ dataTypeOrTransport ] ) { - - options.dataTypes.unshift( dataTypeOrTransport ); - inspect( dataTypeOrTransport ); - return false; - } else if ( seekingTransport ) { - return !( selected = dataTypeOrTransport ); - } - } ); - return selected; - } - - return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes trac-9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } - - return target; -} - -/* Handles responses to an ajax request: - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes; - - // Remove auto dataType and get content-type in the process - while ( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -/* Chain conversions given the request and the original response - * Also sets the responseXXX fields on the jqXHR instance - */ -function ajaxConvert( s, response, jqXHR, isSuccess ) { - var conv2, current, conv, tmp, prev, - converters = {}, - - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(); - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - current = dataTypes.shift(); - - // Convert to each sequential dataType - while ( current ) { - - if ( s.responseFields[ current ] ) { - jqXHR[ s.responseFields[ current ] ] = response; - } - - // Apply the dataFilter if provided - if ( !prev && isSuccess && s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - prev = current; - current = dataTypes.shift(); - - if ( current ) { - - // There's only work to do if current dataType is non-auto - if ( current === "*" ) { - - current = prev; - - // Convert response if prev dataType is non-auto and differs from current - } else if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split( " " ); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.unshift( tmp[ 1 ] ); - } - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s.throws ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { - state: "parsererror", - error: conv ? e : "No conversion from " + prev + " to " + current - }; - } - } - } - } - } - } - - return { state: "success", data: response }; -} - -jQuery.extend( { - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {}, - - ajaxSettings: { - url: location.href, - type: "GET", - isLocal: rlocalProtocol.test( location.protocol ), - global: true, - processData: true, - async: true, - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - "*": allTypes, - text: "text/plain", - html: "text/html", - xml: "application/xml, text/xml", - json: "application/json, text/javascript" - }, - - contents: { - xml: /\bxml\b/, - html: /\bhtml/, - json: /\bjson\b/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText", - json: "responseJSON" - }, - - // Data converters - // Keys separate source (or catchall "*") and destination types with a single space - converters: { - - // Convert anything to text - "* text": String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": JSON.parse, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - url: true, - context: true - } - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - return settings ? - - // Building a settings object - ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : - - // Extending ajaxSettings - ajaxExtend( jQuery.ajaxSettings, target ); - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var transport, - - // URL without anti-cache param - cacheURL, - - // Response headers - responseHeadersString, - responseHeaders, - - // timeout handle - timeoutTimer, - - // Url cleanup var - urlAnchor, - - // Request state (becomes false upon send and true upon completion) - completed, - - // To know if global events are to be dispatched - fireGlobals, - - // Loop variable - i, - - // uncached part of the url - uncached, - - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - - // Callbacks context - callbackContext = s.context || s, - - // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && - ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, - - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - - // Status-dependent callbacks - statusCode = s.statusCode || {}, - - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - - // Default abort message - strAbort = "canceled", - - // Fake xhr - jqXHR = { - readyState: 0, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( completed ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() + " " ] = - ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) - .concat( match[ 2 ] ); - } - } - match = responseHeaders[ key.toLowerCase() + " " ]; - } - return match == null ? null : match.join( ", " ); - }, - - // Raw string - getAllResponseHeaders: function() { - return completed ? responseHeadersString : null; - }, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( completed == null ) { - name = requestHeadersNames[ name.toLowerCase() ] = - requestHeadersNames[ name.toLowerCase() ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( completed == null ) { - s.mimeType = type; - } - return this; - }, - - // Status-dependent callbacks - statusCode: function( map ) { - var code; - if ( map ) { - if ( completed ) { - - // Execute the appropriate callbacks - jqXHR.always( map[ jqXHR.status ] ); - } else { - - // Lazy-add the new callbacks in a way that preserves old ones - for ( code in map ) { - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - var finalText = statusText || strAbort; - if ( transport ) { - transport.abort( finalText ); - } - done( 0, finalText ); - return this; - } - }; - - // Attach deferreds - deferred.promise( jqXHR ); - - // Add protocol if not provided (prefilters might expect it) - // Handle falsy url in the settings object (trac-10093: consistency with old signature) - // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ) - .replace( rprotocol, location.protocol + "//" ); - - // Alias method option to type as per ticket trac-12004 - s.type = options.method || options.type || s.method || s.type; - - // Extract dataTypes list - s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; - - // A cross-domain request is in order when the origin doesn't match the current origin. - if ( s.crossDomain == null ) { - urlAnchor = document.createElement( "a" ); - - // Support: IE <=8 - 11, Edge 12 - 15 - // IE throws exception on accessing the href property if url is malformed, - // e.g. http://example.com:80x/ - try { - urlAnchor.href = s.url; - - // Support: IE <=8 - 11 only - // Anchor's host property isn't correctly set when s.url is relative - urlAnchor.href = urlAnchor.href; - s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== - urlAnchor.protocol + "//" + urlAnchor.host; - } catch ( e ) { - - // If there is an error parsing the URL, assume it is crossDomain, - // it can be rejected by the transport if it is invalid - s.crossDomain = true; - } - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( completed ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (trac-15118) - fireGlobals = jQuery.event && s.global; - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Save the URL in case we're toying with the If-Modified-Since - // and/or If-None-Match header later on - // Remove hash to simplify url manipulation - cacheURL = s.url.replace( rhash, "" ); - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // Remember the hash so we can put it back - uncached = s.url.slice( cacheURL.length ); - - // If data is available and should be processed, append data to url - if ( s.data && ( s.processData || typeof s.data === "string" ) ) { - cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; - - // trac-9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Add or update anti-cache param if needed - if ( s.cache === false ) { - cacheURL = cacheURL.replace( rantiCache, "$1" ); - uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + - uncached; - } - - // Put hash and anti-cache on the URL that will be requested (gh-1732) - s.url = cacheURL + uncached; - - // Change '%20' to '+' if this is encoded form body content (gh-2658) - } else if ( s.data && s.processData && - ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { - s.data = s.data.replace( r20, "+" ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery.lastModified[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); - } - if ( jQuery.etag[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? - s.accepts[ s.dataTypes[ 0 ] ] + - ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { - - // Abort if not done already and return - return jqXHR.abort(); - } - - // Aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - completeDeferred.add( s.complete ); - jqXHR.done( s.success ); - jqXHR.fail( s.error ); - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - - // If request was aborted inside ajaxSend, stop there - if ( completed ) { - return jqXHR; - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = window.setTimeout( function() { - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - completed = false; - transport.send( requestHeaders, done ); - } catch ( e ) { - - // Rethrow post-completion exceptions - if ( completed ) { - throw e; - } - - // Propagate others as results - done( -1, e ); - } - } - - // Callback for when everything is done - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Ignore repeat invocations - if ( completed ) { - return; - } - - completed = true; - - // Clear timeout if it exists - if ( timeoutTimer ) { - window.clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Determine if successful - isSuccess = status >= 200 && status < 300 || status === 304; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // Use a noop converter for missing script but not if jsonp - if ( !isSuccess && - jQuery.inArray( "script", s.dataTypes ) > -1 && - jQuery.inArray( "json", s.dataTypes ) < 0 ) { - s.converters[ "text script" ] = function() {}; - } - - // Convert no matter what (that way responseXXX fields are always set) - response = ajaxConvert( s, response, jqXHR, isSuccess ); - - // If successful, handle type chaining - if ( isSuccess ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - modified = jqXHR.getResponseHeader( "Last-Modified" ); - if ( modified ) { - jQuery.lastModified[ cacheURL ] = modified; - } - modified = jqXHR.getResponseHeader( "etag" ); - if ( modified ) { - jQuery.etag[ cacheURL ] = modified; - } - } - - // if no content - if ( status === 204 || s.type === "HEAD" ) { - statusText = "nocontent"; - - // if not modified - } else if ( status === 304 ) { - statusText = "notmodified"; - - // If we have data, let's convert it - } else { - statusText = response.state; - success = response.data; - error = response.error; - isSuccess = !error; - } - } else { - - // Extract error from statusText and normalize for non-aborts - error = statusText; - if ( status || !statusText ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = ( nativeStatusText || statusText ) + ""; - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - return jqXHR; - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - } -} ); - -jQuery.each( [ "get", "post" ], function( _i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - - // Shift arguments if data argument was omitted - if ( isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - // The url can be an options object (which then must have .url) - return jQuery.ajax( jQuery.extend( { - url: url, - type: method, - dataType: type, - data: data, - success: callback - }, jQuery.isPlainObject( url ) && url ) ); - }; -} ); - -jQuery.ajaxPrefilter( function( s ) { - var i; - for ( i in s.headers ) { - if ( i.toLowerCase() === "content-type" ) { - s.contentType = s.headers[ i ] || ""; - } - } -} ); - - -jQuery._evalUrl = function( url, options, doc ) { - return jQuery.ajax( { - url: url, - - // Make this explicit, since user can override this through ajaxSetup (trac-11264) - type: "GET", - dataType: "script", - cache: true, - async: false, - global: false, - - // Only evaluate the response if it is successful (gh-4126) - // dataFilter is not invoked for failure responses, so using it instead - // of the default converter is kludgy but it works. - converters: { - "text script": function() {} - }, - dataFilter: function( response ) { - jQuery.globalEval( response, options, doc ); - } - } ); -}; - - -jQuery.fn.extend( { - wrapAll: function( html ) { - var wrap; - - if ( this[ 0 ] ) { - if ( isFunction( html ) ) { - html = html.call( this[ 0 ] ); - } - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map( function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - } ).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( isFunction( html ) ) { - return this.each( function( i ) { - jQuery( this ).wrapInner( html.call( this, i ) ); - } ); - } - - return this.each( function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - } ); - }, - - wrap: function( html ) { - var htmlIsFunction = isFunction( html ); - - return this.each( function( i ) { - jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); - } ); - }, - - unwrap: function( selector ) { - this.parent( selector ).not( "body" ).each( function() { - jQuery( this ).replaceWith( this.childNodes ); - } ); - return this; - } -} ); - - -jQuery.expr.pseudos.hidden = function( elem ) { - return !jQuery.expr.pseudos.visible( elem ); -}; -jQuery.expr.pseudos.visible = function( elem ) { - return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); -}; - - - - -jQuery.ajaxSettings.xhr = function() { - try { - return new window.XMLHttpRequest(); - } catch ( e ) {} -}; - -var xhrSuccessStatus = { - - // File protocol always yields status code 0, assume 200 - 0: 200, - - // Support: IE <=9 only - // trac-1450: sometimes IE returns 1223 when it should be 204 - 1223: 204 - }, - xhrSupported = jQuery.ajaxSettings.xhr(); - -support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); -support.ajax = xhrSupported = !!xhrSupported; - -jQuery.ajaxTransport( function( options ) { - var callback, errorCallback; - - // Cross domain only allowed if supported through XMLHttpRequest - if ( support.cors || xhrSupported && !options.crossDomain ) { - return { - send: function( headers, complete ) { - var i, - xhr = options.xhr(); - - xhr.open( - options.type, - options.url, - options.async, - options.username, - options.password - ); - - // Apply custom fields if provided - if ( options.xhrFields ) { - for ( i in options.xhrFields ) { - xhr[ i ] = options.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( options.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( options.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Set headers - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - - // Callback - callback = function( type ) { - return function() { - if ( callback ) { - callback = errorCallback = xhr.onload = - xhr.onerror = xhr.onabort = xhr.ontimeout = - xhr.onreadystatechange = null; - - if ( type === "abort" ) { - xhr.abort(); - } else if ( type === "error" ) { - - // Support: IE <=9 only - // On a manual native abort, IE9 throws - // errors on any property access that is not readyState - if ( typeof xhr.status !== "number" ) { - complete( 0, "error" ); - } else { - complete( - - // File: protocol always yields status 0; see trac-8605, trac-14207 - xhr.status, - xhr.statusText - ); - } - } else { - complete( - xhrSuccessStatus[ xhr.status ] || xhr.status, - xhr.statusText, - - // Support: IE <=9 only - // IE9 has no XHR2 but throws on binary (trac-11426) - // For XHR2 non-text, let the caller handle it (gh-2498) - ( xhr.responseType || "text" ) !== "text" || - typeof xhr.responseText !== "string" ? - { binary: xhr.response } : - { text: xhr.responseText }, - xhr.getAllResponseHeaders() - ); - } - } - }; - }; - - // Listen to events - xhr.onload = callback(); - errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); - - // Support: IE 9 only - // Use onreadystatechange to replace onabort - // to handle uncaught aborts - if ( xhr.onabort !== undefined ) { - xhr.onabort = errorCallback; - } else { - xhr.onreadystatechange = function() { - - // Check readyState before timeout as it changes - if ( xhr.readyState === 4 ) { - - // Allow onerror to be called first, - // but that will not handle a native abort - // Also, save errorCallback to a variable - // as xhr.onerror cannot be accessed - window.setTimeout( function() { - if ( callback ) { - errorCallback(); - } - } ); - } - }; - } - - // Create the abort callback - callback = callback( "abort" ); - - try { - - // Do send the request (this may raise an exception) - xhr.send( options.hasContent && options.data || null ); - } catch ( e ) { - - // trac-14683: Only rethrow if this hasn't been notified as an error yet - if ( callback ) { - throw e; - } - } - }, - - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } -} ); - - - - -// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) -jQuery.ajaxPrefilter( function( s ) { - if ( s.crossDomain ) { - s.contents.script = false; - } -} ); - -// Install script dataType -jQuery.ajaxSetup( { - accepts: { - script: "text/javascript, application/javascript, " + - "application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /\b(?:java|ecma)script\b/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -} ); - -// Handle cache's special case and crossDomain -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - } -} ); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function( s ) { - - // This transport only deals with cross domain or forced-by-attrs requests - if ( s.crossDomain || s.scriptAttrs ) { - var script, callback; - return { - send: function( _, complete ) { - script = jQuery( " - """, - """ - """, """ const pathtoroot = "./"; loadScripts(); @@ -437,6 +429,10 @@ void checkSearchOutput(String fileName, boolean expectedOutput) { holder="Search documentation (type /)" aria-label="Search in documentation" auto\ complete="off" spellcheck="false">"""); + checkOutput(fileName, false, + "jquery-ui.min.css", + "jquery-3.7.1.min.js", + "jquery-ui.min.js"); } void checkSingleIndex() { @@ -669,14 +665,15 @@ void checkInvalidUsageIndexTag() { "AnotherClass.java:68: warning: invalid usage of tag {@index"); } - void checkJqueryAndImageFiles(boolean expectedOutput) { + void checkImageFiles(boolean expectedOutput) { checkFiles(expectedOutput, "script-files/search.js", - "script-files/jquery-3.7.1.min.js", - "script-files/jquery-ui.min.js", - "resource-files/jquery-ui.min.css", "resource-files/x.svg", "resource-files/glass.svg"); + checkFiles(false, + "script-files/jquery-3.7.1.min.js", + "script-files/jquery-ui.min.js", + "resource-files/jquery-ui.min.css"); } void checkSearchJS() { @@ -689,9 +686,7 @@ void checkSearchJS() { "function getURLPrefix(item, category) {", "url += item.l;"); - checkOutput("script-files/search-page.js", true, - "function renderResults(result) {", - "function selectTab(category) {"); + checkFiles(false, "script-files/search-page.js"); checkCssClasses("script-files/search.js", "resource-files/stylesheet.css"); } @@ -701,8 +696,8 @@ void checkCssClasses(String jsFile, String cssFile) { // are also defined as class selectors somewhere in the stylesheet file. String js = readOutputFile(jsFile); Set cssClasses = new TreeSet<>(); - addMatches(js, Pattern.compile("class=\\\\*\"([^\\\\\"]+)\\\\*\""), cssClasses); - addMatches(js, Pattern.compile("attr\\(\"class\", \"([^\"]+)\"\\)"), cssClasses); + addMatches(js, Pattern.compile("class=[\"']([-\\w]+)[\"']"), cssClasses); + addMatches(js, Pattern.compile("classList.add\\([\"']([-\\w]+)[\"']\\)"), cssClasses); // verify that the regex did find use of CSS class names checking("Checking CSS classes found"); if (cssClasses.isEmpty()) { diff --git a/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java b/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java index 012e9ce00ded..a2c2a6032126 100644 --- a/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java +++ b/test/langtools/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java @@ -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 @@ -132,6 +132,7 @@ public void test(Path base) { min-height:12px; font-size:0; visibility:hidden; + cursor: pointer; }""", """ ::placeholder { diff --git a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java index 71908f34e996..bd8d0cf79531 100644 --- a/test/langtools/jdk/javadoc/tool/api/basic/APITest.java +++ b/test/langtools/jdk/javadoc/tool/api/basic/APITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -207,7 +207,6 @@ protected void error(String msg) { "resource-files/copy.svg", "resource-files/down.svg", "resource-files/glass.svg", - "resource-files/jquery-ui.min.css", "resource-files/left.svg", "resource-files/link.svg", "resource-files/moon.svg", @@ -241,11 +240,8 @@ protected void error(String msg) { "resource-files/fonts/DejaVuLGCSerif-Italic.woff2", "resource-files/fonts/DejaVuLGCSerif.woff", "resource-files/fonts/DejaVuLGCSerif.woff2", - "script-files/jquery-3.7.1.min.js", - "script-files/jquery-ui.min.js", "script-files/script.js", "script-files/search.js", - "script-files/search-page.js", "tag-search-index.js", "type-search-index.js" )); @@ -255,11 +251,8 @@ protected void error(String msg) { !s.endsWith("-search-index.js") && !s.equals("index-all.html") && !s.equals("resource-files/glass.svg") - && !s.equals("resource-files/jquery-ui.min.css") && !s.equals("resource-files/x.svg") - && !s.startsWith("script-files/jquery-") && !s.equals("script-files/search.js") - && !s.equals("script-files/search-page.js") && !s.equals("search.html") && !s.equals("allclasses-index.html") && !s.equals("allpackages-index.html") diff --git a/test/langtools/tools/javac/annotations/8218152/MalformedAnnotationProcessorTests.java b/test/langtools/tools/javac/annotations/8218152/MalformedAnnotationProcessorTests.java index 68e4aea0ab29..85095cc55373 100644 --- a/test/langtools/tools/javac/annotations/8218152/MalformedAnnotationProcessorTests.java +++ b/test/langtools/tools/javac/annotations/8218152/MalformedAnnotationProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -91,8 +91,8 @@ public void testBadAnnotationProcessor(Path base) throws Exception { .getOutputLines(Task.OutputKind.DIRECT); System.out.println(actualErrors.get(0)); - if (!actualErrors.get(0).contains("- compiler.err.proc.cant.load.class: " + - "Incompatible magic value")) { + if (!actualErrors.get(0).contains("- compiler.err.proc.bad.config.file: " + + "javax.annotation.processing.Processor: Provider BadAnnoProcessor not found")) { throw new AssertionError("Unexpected errors reported: " + actualErrors); } } @@ -162,8 +162,8 @@ public void testWrongClassFileVersion(Path base) throws Exception { .writeAll() .getOutputLines(Task.OutputKind.DIRECT); - if (!actualErrors.get(0).contains("- compiler.err.proc.cant.load.class: " + - "WrongClassFileVersion has been compiled by a more recent version")) { + if (!actualErrors.get(0).contains("- compiler.err.proc.bad.config.file: " + + "javax.annotation.processing.Processor: Provider WrongClassFileVersion not found")) { throw new AssertionError("Unexpected errors reported: " + actualErrors); } } diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java index d35351b6f408..5031a74bc5ae 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 8026564 8043226 8334055 + * @bug 8026564 8043226 8334055 8179187 * @summary The parts of a fully-qualified type can't be annotated. * @author Werner Dietl * @compile/fail/ref=CantAnnotatePackages.out -XDrawDiagnostics CantAnnotatePackages.java diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.out index b91d65828b97..6e2b9cb93b0e 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.out @@ -1,5 +1,5 @@ -CantAnnotatePackages.java:16:14: compiler.err.cant.resolve.location: kindname.class, java, , , (compiler.misc.location: kindname.class, CantAnnotatePackages, null) -CantAnnotatePackages.java:17:9: compiler.err.cant.resolve.location: kindname.class, lang, , , (compiler.misc.location: kindname.package, java, null) -CantAnnotatePackages.java:18:14: compiler.err.cant.resolve.location: kindname.class, lang, , , (compiler.misc.location: kindname.package, java, null) CantAnnotatePackages.java:14:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object +CantAnnotatePackages.java:16:14: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object +CantAnnotatePackages.java:17:9: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object +CantAnnotatePackages.java:18:14: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object 4 errors diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.java index 4bdd791909c2..427c1fef3a86 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 8006733 8006775 8043226 8334055 + * @bug 8006733 8006775 8043226 8334055 8179187 * @summary Ensure behavior for nested types is correct. * @author Werner Dietl * @compile/fail/ref=CantAnnotateScoping.out -XDrawDiagnostics CantAnnotateScoping.java diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out index ade5333a446f..2ae736ad315d 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.out @@ -1,12 +1,13 @@ -CantAnnotateScoping.java:63:9: compiler.err.cant.resolve.location: kindname.class, lang, , , (compiler.misc.location: kindname.package, java, null) -CantAnnotateScoping.java:68:9: compiler.err.cant.resolve.location: kindname.class, XXX, , , (compiler.misc.location: kindname.package, java, null) -CantAnnotateScoping.java:71:9: compiler.err.cant.resolve.location: kindname.class, lang, , , (compiler.misc.location: kindname.package, java, null) +CantAnnotateScoping.java:68:18: compiler.err.doesnt.exist: java.XXX CantAnnotateScoping.java:38:14: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), Test.Outer, @TA Test.Outer.SInner CantAnnotateScoping.java:51:18: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object CantAnnotateScoping.java:60:37: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation: @TA,@TA2), java.lang, @DTA @TA @TA2 java.lang.Object CantAnnotateScoping.java:40:14: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), Test.Outer, @TA Test.Outer.SInner +CantAnnotateScoping.java:63:11: compiler.err.annotation.type.not.applicable.to.type: DA +CantAnnotateScoping.java:68:11: compiler.err.annotation.type.not.applicable.to.type: DA +CantAnnotateScoping.java:71:9: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), java.lang, @TA java.lang.Object CantAnnotateScoping.java:44:34: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation: @TA,@TA2), Test.Outer, @TA @TA2 Test.Outer.SInner CantAnnotateScoping.java:44:25: compiler.err.annotation.type.not.applicable.to.type: DA CantAnnotateScoping.java:48:38: compiler.err.type.annotation.inadmissible: (compiler.misc.type.annotation.1: @TA), Test.Outer, @TA Test.Outer.SInner CantAnnotateScoping.java:48:34: compiler.err.annotation.type.not.applicable.to.type: DA -11 errors +12 errors diff --git a/test/langtools/tools/javac/processing/warnings/TestParserWarnings.java b/test/langtools/tools/javac/processing/warnings/TestParserWarnings.java new file mode 100644 index 000000000000..b611e7a8655b --- /dev/null +++ b/test/langtools/tools/javac/processing/warnings/TestParserWarnings.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8381654 + * @summary AP interference with tokenizer warnings + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit ${test.main.class} + */ + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.TypeElement; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class TestParserWarnings { + + static boolean[] apOptions() { + return new boolean[] {false, true}; + } + + final ToolBox tb = new ToolBox(); + Path base, src, classes; + + @ParameterizedTest @MethodSource("apOptions") + public void testPreviewWarning(boolean useProcessor) throws Exception { + tb.writeJavaFiles(src, """ + public record MyRec() {} + """); + + JavacTask task = new JavacTask(tb) + .options("--enable-preview", + "-source", Integer.toString(Runtime.version().feature()), + "-XDforcePreview", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(src)) + .outdir(classes); + if (useProcessor) { + task.processors(new ProcessorImpl()); + } + List log = task + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "- compiler.note.preview.filename: MyRec.java, DEFAULT", + "- compiler.note.preview.recompile" + ); + + tb.checkEqual(expected, log); + } + + @ParameterizedTest @MethodSource("apOptions") + public void testTextBlockWarning(boolean useProcessor) throws Exception { + tb.writeJavaFiles(src, """ + class TextBlockWhitespace { + String m() { + return ""\" + \\u0009\\u0009\\u0009\\u0009tab indentation + \\u0020\\u0020\\u0020\\u0020space indentation and trailing space\\u0020 + \\u0020\\u0020\\u0020\\u0020""\"; + } + } + """); + + JavacTask task = new JavacTask(tb) + .options("-Xlint:text-blocks", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(src)) + .outdir(classes); + if (useProcessor) { + task.processors(new ProcessorImpl()); + } + List log = task + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "TextBlockWhitespace.java:3:16: compiler.warn.inconsistent.white.space.indentation", + "TextBlockWhitespace.java:3:16: compiler.warn.trailing.white.space.will.be.removed", + "2 warnings" + ); + + tb.checkEqual(expected, log); + } + + @Test + public void testAPGeneratedSource() throws Exception { + tb.writeJavaFiles(src, """ + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + @A + class Test {} + + @Target(ElementType.TYPE) + @interface A {} + """); + + List log = new JavacTask(tb) + .options("-Xlint:text-blocks", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(src)) + .outdir(classes) + .processors(new ProcessorImpl()) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "Generated.java:3:16: compiler.warn.inconsistent.white.space.indentation", + "Generated.java:3:16: compiler.warn.trailing.white.space.will.be.removed", + "2 warnings" + ); + + tb.checkEqual(expected, log); + } + + @SupportedAnnotationTypes("*") + private static class ProcessorImpl extends AbstractProcessor { + private boolean done = false; + private Filer filer; + private Messager msgr; + + @Override + public void init(ProcessingEnvironment env) { + filer = env.getFiler(); + msgr = env.getMessager(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (!done && !annotations.isEmpty()) { + try (Writer pw = filer.createSourceFile("Generated").openWriter()) { + pw.write(""" + public class Generated { + String m() { + return ""\" + \\u0009\\u0009\\u0009\\u0009tab indentation + \\u0020\\u0020\\u0020\\u0020space indentation and trailing space\\u0020 + \\u0020\\u0020\\u0020\\u0020""\"; + } + } + """); + pw.flush(); + pw.close(); + done = true; + } catch (IOException ioe) { + msgr.printError(ioe.getMessage()); + return false; + } + return true; + } + return false; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + } + + @BeforeEach + public void setUp(TestInfo info) throws Exception { + base = Path.of(".").resolve(info.getTestMethod().get().getName()); + if (Files.exists(base)) { + tb.cleanDirectory(base); + } + src = base.resolve("src"); + classes = base.resolve("classes"); + Files.createDirectories(classes); + } +} diff --git a/test/lib/jdk/test/lib/Utils.java b/test/lib/jdk/test/lib/Utils.java index 2f46ed873402..95b7a117b2c4 100644 --- a/test/lib/jdk/test/lib/Utils.java +++ b/test/lib/jdk/test/lib/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -105,6 +105,11 @@ public final class Utils { */ public static final String TEST_SRC = System.getProperty("test.src", "").trim(); + /** + * Returns the value of 'test.src.path' system property. + */ + public static final String TEST_SRC_PATH = System.getProperty("test.src.path", "").trim(); + /** * Returns the value of 'test.root' system property. */ diff --git a/test/lib/jdk/test/lib/jfr/EventNames.java b/test/lib/jdk/test/lib/jfr/EventNames.java index 8b0113f75f42..06ee62a2f7c7 100644 --- a/test/lib/jdk/test/lib/jfr/EventNames.java +++ b/test/lib/jdk/test/lib/jfr/EventNames.java @@ -115,6 +115,7 @@ public class EventNames { public static final String ShenandoahHeapRegionInformation = PREFIX + "ShenandoahHeapRegionInformation"; public static final String ShenandoahHeapRegionStateChange = PREFIX + "ShenandoahHeapRegionStateChange"; public static final String ShenandoahEvacuationInformation = PREFIX + "ShenandoahEvacuationInformation"; + public static final String ShenandoahPromotionInformation = PREFIX + "ShenandoahPromotionInformation"; public static final String TenuringDistribution = PREFIX + "TenuringDistribution"; public static final String GarbageCollection = PREFIX + "GarbageCollection"; public static final String ParallelOldGarbageCollection = PREFIX + "ParallelOldGarbageCollection"; diff --git a/test/lib/jdk/test/lib/thread/ThreadWrapper.java b/test/lib/jdk/test/lib/thread/ThreadWrapper.java new file mode 100644 index 000000000000..ab850bf5a4ab --- /dev/null +++ b/test/lib/jdk/test/lib/thread/ThreadWrapper.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package jdk.test.lib.thread; + +import java.time.Duration; +import java.util.Map; + +/* + The ThreadWrapper is a helper class that allows to extend + coverage of virtual threads testing for existing tests with threads. + + Specifically, it is useful for the pattern where Thread is extended + by some class. Example: + + class resumethrd02Thread extends Thread {...} + ... + resumethrd02Thread thr = new resumethrd02Thread(); + + The test can be updated to use this wrapper: + class resumethrd02Thread extends ThreadWrapper {...} + ... + resumethrd02Thread thr = new resumethrd02Thread(); + + So resumethrd02Thread can be run with platform or virtual threads. + + Method getThread() is used to get instance of Thread. + + It is not expected to use this wrapper for new tests or classes that + are not extending Thread. The TestThreadFactory should be used to + create threads in such cases. + */ + +public class ThreadWrapper implements Runnable { + private final Thread thread; + + @SuppressWarnings("this-escape") + public ThreadWrapper() { + // thread is a platform or virtual thread + thread = TestThreadFactory.newThread(this); + } + + @SuppressWarnings("this-escape") + public ThreadWrapper(String name) { + // thread is a platform or virtual thread + thread = TestThreadFactory.newThread(this, name); + } + + public Thread getThread() { + return thread; + } + + public static Thread currentThread() { + return Thread.currentThread(); + } + + public static void yield() { + Thread.yield(); + } + + public static void sleep(long millis) throws InterruptedException { + Thread.sleep(millis); + } + + public static void sleep(long millis, int nanos) throws InterruptedException { + Thread.sleep(millis, nanos); + } + + public static void sleep(Duration duration) throws InterruptedException { + Thread.sleep(duration); + } + + public static void onSpinWait() { + Thread.onSpinWait(); + } + + public static Thread.Builder.OfPlatform ofPlatform() { + return Thread.ofPlatform(); + } + + public static Thread.Builder.OfVirtual ofVirtual() { + return Thread.ofVirtual(); + } + + public static Thread startVirtualThread(Runnable task) { + return Thread.startVirtualThread(task); + } + + public boolean isVirtual() { + return thread.isVirtual(); + } + + public void start() { + thread.start(); + } + + public void run() { + } + + public void interrupt() { + thread.interrupt(); + } + + public static boolean interrupted() { + return Thread.interrupted(); + } + + public boolean isInterrupted() { + return thread.isInterrupted(); + } + + public boolean isAlive() { + return thread.isAlive(); + } + + public void setPriority(int newPriority) { + thread.setPriority(newPriority); + } + + public int getPriority() { + return thread.getPriority(); + } + + public void setName(String name) { + thread.setName(name); + } + + public String getName() { + return thread.getName(); + } + + public ThreadGroup getThreadGroup() { + return thread.getThreadGroup(); + } + + public static int activeCount() { + return Thread.activeCount(); + } + + public static int enumerate(Thread[] tarray) { + return Thread.enumerate(tarray); + } + + public void join(long millis) throws InterruptedException { + thread.join(millis); + } + + public void join(long millis, int nanos) throws InterruptedException { + thread.join(millis, nanos); + } + + public void join() throws InterruptedException { + thread.join(); + } + + public boolean join(Duration duration) throws InterruptedException { + return thread.join(duration); + } + + public static void dumpStack() { + Thread.dumpStack(); + } + + public void setDaemon(boolean on) { + thread.setDaemon(on); + } + + public boolean isDaemon() { + return thread.isDaemon(); + } + + @Override + public String toString() { + return thread.toString(); + } + + public ClassLoader getContextClassLoader() { + return thread.getContextClassLoader(); + } + + public void setContextClassLoader(ClassLoader cl) { + thread.setContextClassLoader(cl); + } + + public static boolean holdsLock(Object obj) { + return Thread.holdsLock(obj); + } + + public StackTraceElement[] getStackTrace() { + return thread.getStackTrace(); + } + + public static Map getAllStackTraces() { + return Thread.getAllStackTraces(); + } + + @Deprecated(since = "19") + public long getId() { + return thread.getId(); + } + + public long threadId() { + return thread.threadId(); + } + + public Thread.State getState() { + return thread.getState(); + } + + public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler ueh) { + Thread.setDefaultUncaughtExceptionHandler(ueh); + } + + public static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { + return Thread.getDefaultUncaughtExceptionHandler(); + } + + public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() { + return thread.getUncaughtExceptionHandler(); + } + + public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler ueh) { + thread.setUncaughtExceptionHandler(ueh); + } +} diff --git a/test/lib/jdk/test/whitebox/code/BlobType.java b/test/lib/jdk/test/whitebox/code/BlobType.java index a2290acc7b61..e2d57f79484d 100644 --- a/test/lib/jdk/test/whitebox/code/BlobType.java +++ b/test/lib/jdk/test/whitebox/code/BlobType.java @@ -46,8 +46,16 @@ public boolean allowTypeWhenOverflow(BlobType type) { || type == BlobType.MethodNonProfiled; } }, + MethodHot(2, "CodeHeap 'hot nmethods'", "HotCodeHeapSize") { + @Override + public boolean allowTypeWhenOverflow(BlobType type) { + return super.allowTypeWhenOverflow(type) + || type == BlobType.MethodNonProfiled + || type == BlobType.MethodProfiled; + } + }, // Non-nmethods like Buffers, Adapters and Runtime Stubs - NonNMethod(2, "CodeHeap 'non-nmethods'", "NonNMethodCodeHeapSize") { + NonNMethod(3, "CodeHeap 'non-nmethods'", "NonNMethodCodeHeapSize") { @Override public boolean allowTypeWhenOverflow(BlobType type) { return super.allowTypeWhenOverflow(type) @@ -56,7 +64,7 @@ public boolean allowTypeWhenOverflow(BlobType type) { } }, // All types (No code cache segmentation) - All(3, "CodeCache", "ReservedCodeCacheSize"); + All(4, "CodeCache", "ReservedCodeCacheSize"); public final int id; public final String sizeOptionName; @@ -99,6 +107,10 @@ public static EnumSet getAvailable() { // there is no MethodProfiled in non tiered world or pure C1 result.remove(MethodProfiled); } + + if (Long.valueOf(0).equals(whiteBox.getVMFlag("HotCodeHeapSize"))) { + result.remove(MethodHot); + } return result; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java index 92c0b58005f1..daf18af528eb 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright 2026 Arm Limited and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -350,4 +351,22 @@ public short dotProductFP16() { } return distRes; } + + @Benchmark + public short reductionAddFP16() { + short result = (short) 0; + for (int i = 0; i < vectorDim; i++) { + result = float16ToRawShortBits(add(shortBitsToFloat16(result), shortBitsToFloat16(vector1[i]))); + } + return result; + } + + @Benchmark + public short reductionMulFP16() { + short result = floatToFloat16(1.0f); + for (int i = 0; i < vectorDim; i++) { + result = float16ToRawShortBits(multiply(shortBitsToFloat16(result), shortBitsToFloat16(vector1[i]))); + } + return result; + } } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/VectorStoreMaskBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/VectorStoreMaskBenchmark.java new file mode 100644 index 000000000000..d4dc321ccba7 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/VectorStoreMaskBenchmark.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package org.openjdk.bench.jdk.incubator.vector; + +import jdk.incubator.vector.*; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; + +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 10, time = 1) +@Fork(value = 1, jvmArgs = {"--add-modules=jdk.incubator.vector"}) +public class VectorStoreMaskBenchmark { + static final int LENGTH = 256; + static final boolean[] mask_arr_input = new boolean[LENGTH]; + static final boolean[] mask_arr_output = new boolean[LENGTH]; + static { + for (int i = 0; i < LENGTH; i++) { + mask_arr_input[i] = (i & 1) == 0; + } + } + + @CompilerControl(CompilerControl.Mode.INLINE) + public void maskLoadCastStoreKernel(VectorSpecies species_from, VectorSpecies species_to) { + for (int i = 0; i < LENGTH; i += species_from.length()) { + VectorMask mask_from = VectorMask.fromArray(species_from, mask_arr_input, i); + VectorMask mask_to = mask_from.cast(species_to); + mask_to.intoArray(mask_arr_output, i); + } + } + + @Benchmark + public void microMaskLoadCastStoreByte64() { + maskLoadCastStoreKernel(ByteVector.SPECIES_64, ShortVector.SPECIES_128); + } + + @Benchmark + public void microMaskLoadCastStoreShort64() { + maskLoadCastStoreKernel(ShortVector.SPECIES_64, IntVector.SPECIES_128); + } + + @Benchmark + public void microMaskLoadCastStoreInt128() { + maskLoadCastStoreKernel(IntVector.SPECIES_128, ShortVector.SPECIES_64); + } + + @Benchmark + public void microMaskLoadCastStoreLong128() { + maskLoadCastStoreKernel(LongVector.SPECIES_128, IntVector.SPECIES_64); + } + + @Benchmark + public void microMaskLoadCastStoreFloat128() { + maskLoadCastStoreKernel(FloatVector.SPECIES_128, ShortVector.SPECIES_64); + } + + @Benchmark + public void microMaskLoadCastStoreDouble128() { + maskLoadCastStoreKernel(DoubleVector.SPECIES_128, IntVector.SPECIES_64); + } +} \ No newline at end of file diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorReduction2.java b/test/micro/org/openjdk/bench/vm/compiler/VectorReduction2.java index 9241aca1dadf..0d11705c8ec6 100644 --- a/test/micro/org/openjdk/bench/vm/compiler/VectorReduction2.java +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorReduction2.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright 2026 Arm Limited and/or its affiliates. * 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,6 +28,7 @@ import java.util.concurrent.TimeUnit; import java.util.Random; +import jdk.incubator.vector.Float16; /** * Note: there is a corresponding IR test: @@ -64,6 +66,9 @@ public abstract class VectorReduction2 { private double[] in1D; private double[] in2D; private double[] in3D; + private short[] in1F16; + private short[] in2F16; + private short[] in3F16; @Param("0") private int seed; @@ -96,6 +101,9 @@ public void init() { in1D = new double[SIZE]; in2D = new double[SIZE]; in3D = new double[SIZE]; + in1F16 = new short[SIZE]; + in2F16 = new short[SIZE]; + in3F16 = new short[SIZE]; for (int i = 0; i < SIZE; i++) { in1B[i] = (byte)r.nextInt(); @@ -121,6 +129,9 @@ public void init() { in1D[i] = r.nextDouble(); in2D[i] = r.nextDouble(); in3D[i] = r.nextDouble(); + in1F16[i] = Float.floatToFloat16(r.nextFloat()); + in2F16[i] = Float.floatToFloat16(r.nextFloat()); + in3F16[i] = Float.floatToFloat16(r.nextFloat()); } } @@ -1449,10 +1460,86 @@ public void doubleMaxBig(Blackhole bh) { bh.consume(acc); } - @Fork(value = 1, jvmArgs = {"-XX:+UseSuperWord"}) + // ---------float16***Simple ------------------------------------------------------------ + @Benchmark + public void float16AddSimple(Blackhole bh) { + short acc = (short)0; // neutral element + for (int i = 0; i < SIZE; i++) { + acc = Float16.float16ToRawShortBits( + Float16.add(Float16.shortBitsToFloat16(acc), Float16.shortBitsToFloat16(in1F16[i]))); + } + bh.consume(acc); + } + + @Benchmark + public void float16MulSimple(Blackhole bh) { + short acc = Float.floatToFloat16(1.0f); // neutral element + for (int i = 0; i < SIZE; i++) { + acc = Float16.float16ToRawShortBits( + Float16.multiply(Float16.shortBitsToFloat16(acc), Float16.shortBitsToFloat16(in1F16[i]))); + } + bh.consume(acc); + } + + // ---------float16***DotProduct ------------------------------------------------------------ + @Benchmark + public void float16AddDotProduct(Blackhole bh) { + short acc = (short)0; // neutral element + for (int i = 0; i < SIZE; i++) { + Float16 val = Float16.multiply(Float16.shortBitsToFloat16(in1F16[i]), + Float16.shortBitsToFloat16(in2F16[i])); + acc = Float16.float16ToRawShortBits( + Float16.add(Float16.shortBitsToFloat16(acc), val)); + } + bh.consume(acc); + } + + @Benchmark + public void float16MulDotProduct(Blackhole bh) { + short acc = Float.floatToFloat16(1.0f); // neutral element + for (int i = 0; i < SIZE; i++) { + Float16 val = Float16.multiply(Float16.shortBitsToFloat16(in1F16[i]), + Float16.shortBitsToFloat16(in2F16[i])); + acc = Float16.float16ToRawShortBits( + Float16.multiply(Float16.shortBitsToFloat16(acc), val)); + } + bh.consume(acc); + } + + // ---------float16***Big ------------------------------------------------------------ + @Benchmark + public void float16AddBig(Blackhole bh) { + short acc = (short)0; // neutral element + for (int i = 0; i < SIZE; i++) { + Float16 a = Float16.shortBitsToFloat16(in1F16[i]); + Float16 b = Float16.shortBitsToFloat16(in2F16[i]); + Float16 c = Float16.shortBitsToFloat16(in3F16[i]); + Float16 val = Float16.add(Float16.multiply(a, b), + Float16.add(Float16.multiply(a, c), Float16.multiply(b, c))); + acc = Float16.float16ToRawShortBits( + Float16.add(Float16.shortBitsToFloat16(acc), val)); + } + bh.consume(acc); + } + + @Benchmark + public void float16MulBig(Blackhole bh) { + short acc = Float.floatToFloat16(1.0f); // neutral element + for (int i = 0; i < SIZE; i++) { + Float16 a = Float16.shortBitsToFloat16(in1F16[i]); + Float16 b = Float16.shortBitsToFloat16(in2F16[i]); + Float16 c = Float16.shortBitsToFloat16(in3F16[i]); + Float16 val = Float16.add(Float16.multiply(a, b), + Float16.add(Float16.multiply(a, c), Float16.multiply(b, c))); + acc = Float16.float16ToRawShortBits( + Float16.multiply(Float16.shortBitsToFloat16(acc), val)); + } + bh.consume(acc); + } + + @Fork(value = 1, jvmArgs = {"--add-modules=jdk.incubator.vector", "-XX:+UseSuperWord"}) public static class WithSuperword extends VectorReduction2 {} - @Fork(value = 1, jvmArgs = {"-XX:-UseSuperWord"}) + @Fork(value = 1, jvmArgs = {"--add-modules=jdk.incubator.vector", "-XX:-UseSuperWord"}) public static class NoSuperword extends VectorReduction2 {} } - diff --git a/src/hotspot/share/cds/aotGrowableArray.inline.hpp b/test/setup_aot/HelloWorld.java similarity index 72% rename from src/hotspot/share/cds/aotGrowableArray.inline.hpp rename to test/setup_aot/HelloWorld.java index 8c6e8cb6503a..e243dfb4e8e7 100644 --- a/src/hotspot/share/cds/aotGrowableArray.inline.hpp +++ b/test/setup_aot/HelloWorld.java @@ -22,16 +22,8 @@ * */ -#ifndef SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP -#define SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP - -#include "cds/aotGrowableArray.hpp" - -#include "memory/metaspaceClosure.hpp" - -template -void AOTGrowableArray::metaspace_pointers_do(MetaspaceClosure* it) { - it->push_c_array(AOTGrowableArray::data_addr(), AOTGrowableArray::capacity()); +public class HelloWorld { + public static void main(String args[]) { + System.out.println("HelloWorld"); + } } - -#endif // SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP