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