You will need X11 libraries suitable for your target system.
-In most cases, using Debian's pre-built libraries work fine.
-
Note that X11 is needed even if you only want to build a headless
-JDK.
+
When not building a headless JDK, you will need X11 libraries
+suitable for your target system. In most cases, using Debian's
+pre-built libraries work fine.
Go to Debian
Package Search, search for the following packages for your
diff --git a/doc/building.md b/doc/building.md
index 11af23d94477..986643094ec2 100644
--- a/doc/building.md
+++ b/doc/building.md
@@ -1173,10 +1173,8 @@ Note that alsa is needed even if you only want to build a headless JDK.
#### X11
-You will need X11 libraries suitable for your *target* system. In most cases,
-using Debian's pre-built libraries work fine.
-
-Note that X11 is needed even if you only want to build a headless JDK.
+When not building a headless JDK, you will need X11 libraries suitable for your
+*target* system. In most cases, using Debian's pre-built libraries work fine.
* Go to [Debian Package Search](https://www.debian.org/distrib/packages),
search for the following packages for your *target* system, and download them
diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4
index 0f5691759b6d..0fa001c5c90d 100644
--- a/make/autoconf/basic_tools.m4
+++ b/make/autoconf/basic_tools.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
@@ -384,6 +384,10 @@ AC_DEFUN_ONCE([BASIC_SETUP_COMPLEX_TOOLS],
IS_GNU_DATE=yes
else
AC_MSG_RESULT([no])
+ # Likely at the AIX provided version of the date utility here, which is not compatible
+ if test "x$OPENJDK_TARGET_OS" = "xaix"; then
+ AC_MSG_ERROR([gnu date from AIX toolbox is required])
+ fi
IS_GNU_DATE=no
fi
AC_SUBST(IS_GNU_DATE)
diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4
index 3a61840e8c97..9bea6b5062e8 100644
--- a/make/autoconf/flags-cflags.m4
+++ b/make/autoconf/flags-cflags.m4
@@ -565,6 +565,11 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER],
TOOLCHAIN_CFLAGS_JDK_CONLY="-fno-strict-aliasing" # technically NOT for CXX
fi
+ if test "x$ENABLE_LINKTIME_GC" = xtrue; then
+ TOOLCHAIN_CFLAGS_JDK="$TOOLCHAIN_CFLAGS_JDK -ffunction-sections -fdata-sections"
+ TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections"
+ fi
+
if test "x$OPENJDK_TARGET_OS" = xaix; then
TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -ftls-model -fno-math-errno"
TOOLCHAIN_CFLAGS_JDK="-ffunction-sections -fsigned-char"
diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4
index 509e0dd825f0..77ea7a693c91 100644
--- a/make/autoconf/flags-ldflags.m4
+++ b/make/autoconf/flags-ldflags.m4
@@ -76,6 +76,10 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
if test "x$CXX_IS_USER_SUPPLIED" = xfalse && test "x$CC_IS_USER_SUPPLIED" = xfalse; then
UTIL_REQUIRE_PROGS(LLD, lld, $TOOLCHAIN_PATH:$PATH)
fi
+
+ if test "x$ENABLE_LINKTIME_GC" = xtrue; then
+ BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,--gc-sections"
+ fi
fi
if test "x$OPENJDK_TARGET_OS" = xaix; then
BASIC_LDFLAGS="-Wl,-b64 -Wl,-brtl -Wl,-bnorwexec -Wl,-blibpath:/usr/lib:lib -Wl,-bnoexpall \
@@ -98,6 +102,9 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
# Setup OS-dependent LDFLAGS
if test "x$OPENJDK_TARGET_OS" = xmacosx && test "x$TOOLCHAIN_TYPE" = xclang; then
+ if test x$DEBUG_LEVEL = xrelease; then
+ BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,-dead_strip"
+ fi
# FIXME: We should really generalize SET_SHARED_LIBRARY_ORIGIN instead.
OS_LDFLAGS_JVM_ONLY="-Wl,-rpath,@loader_path/. -Wl,-rpath,@loader_path/.."
OS_LDFLAGS="-mmacosx-version-min=$MACOSX_VERSION_MIN -Wl,-reproducible"
diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4
index 14e0d91242ff..510d2dc87bf1 100644
--- a/make/autoconf/jdk-options.m4
+++ b/make/autoconf/jdk-options.m4
@@ -102,9 +102,20 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_OPTIONS],
CHECKING_MSG: [if we should build headless-only (no GUI)])
AC_SUBST(ENABLE_HEADLESS_ONLY)
+ # Avoid headless-only on macOS and Windows, it is not supported there
+ if test "x$ENABLE_HEADLESS_ONLY" = xtrue; then
+ if test "x$OPENJDK_TARGET_OS" = xwindows || test "x$OPENJDK_TARGET_OS" = xmacosx; then
+ AC_MSG_ERROR([headless-only is not supported on macOS and Windows])
+ fi
+ fi
+
# should we linktime gc unused code sections in the JDK build ?
- if test "x$OPENJDK_TARGET_OS" = "xlinux" && test "x$OPENJDK_TARGET_CPU" = xs390x; then
- LINKTIME_GC_DEFAULT=true
+ if test "x$OPENJDK_TARGET_OS" = "xlinux"; then
+ if test "x$OPENJDK_TARGET_CPU" = "xs390x" || test "x$OPENJDK_TARGET_CPU" = "xppc64le"; then
+ LINKTIME_GC_DEFAULT=true
+ else
+ LINKTIME_GC_DEFAULT=false
+ fi
else
LINKTIME_GC_DEFAULT=false
fi
diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4
index 8dc3d55ed0c8..5daacdc1ced5 100644
--- a/make/autoconf/libraries.m4
+++ b/make/autoconf/libraries.m4
@@ -42,12 +42,12 @@ m4_include([lib-tests.m4])
AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES],
[
# Check if X11 is needed
- if test "x$OPENJDK_TARGET_OS" = xwindows || test "x$OPENJDK_TARGET_OS" = xmacosx; then
- # No X11 support on windows or macosx
+ if test "x$OPENJDK_TARGET_OS" = xwindows ||
+ test "x$OPENJDK_TARGET_OS" = xmacosx ||
+ test "x$ENABLE_HEADLESS_ONLY" = xtrue; then
NEEDS_LIB_X11=false
else
- # All other instances need X11, even if building headless only, libawt still
- # needs X11 headers.
+ # All other instances need X11 for libawt.
NEEDS_LIB_X11=true
fi
diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf
index adaf6ce24534..46b6c430f542 100644
--- a/make/conf/version-numbers.conf
+++ b/make/conf/version-numbers.conf
@@ -28,15 +28,15 @@
DEFAULT_VERSION_FEATURE=25
DEFAULT_VERSION_INTERIM=0
-DEFAULT_VERSION_UPDATE=3
+DEFAULT_VERSION_UPDATE=4
DEFAULT_VERSION_PATCH=0
DEFAULT_VERSION_EXTRA1=0
DEFAULT_VERSION_EXTRA2=0
DEFAULT_VERSION_EXTRA3=0
-DEFAULT_VERSION_DATE=2026-04-21
+DEFAULT_VERSION_DATE=2026-07-21
DEFAULT_VERSION_CLASSFILE_MAJOR=69 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
DEFAULT_VERSION_CLASSFILE_MINOR=0
DEFAULT_VERSION_DOCS_API_SINCE=11
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="24 25"
DEFAULT_JDK_SOURCE_TARGET_VERSION=25
-DEFAULT_PROMOTED_VERSION_PRE=
+DEFAULT_PROMOTED_VERSION_PRE=ea
diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk
index f7a7732993a2..0e5cb31302e1 100644
--- a/make/hotspot/lib/CompileJvm.gmk
+++ b/make/hotspot/lib/CompileJvm.gmk
@@ -204,6 +204,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \
DISABLED_WARNINGS_gcc_jvmtiTagMap.cpp := stringop-overflow, \
DISABLED_WARNINGS_gcc_macroAssembler_ppc_sha.cpp := unused-const-variable, \
DISABLED_WARNINGS_gcc_postaloc.cpp := address, \
+ DISABLED_WARNINGS_gcc_safepointMechanism.cpp := stringop-overflow, \
DISABLED_WARNINGS_gcc_shenandoahLock.cpp := stringop-overflow, \
DISABLED_WARNINGS_gcc_stubGenerator_s390.cpp := unused-const-variable, \
DISABLED_WARNINGS_gcc_synchronizer.cpp := stringop-overflow, \
diff --git a/make/modules/java.desktop/lib/AwtLibraries.gmk b/make/modules/java.desktop/lib/AwtLibraries.gmk
index 463e09e12dce..1aa2578d2e2e 100644
--- a/make/modules/java.desktop/lib/AwtLibraries.gmk
+++ b/make/modules/java.desktop/lib/AwtLibraries.gmk
@@ -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
@@ -88,6 +88,10 @@ LIBAWT_EXTRA_HEADER_DIRS := \
LIBAWT_CFLAGS := -D__MEDIALIB_OLD_NAMES -D__USE_J2D_NAMES -DMLIB_NO_LIBSUNMATH
+ifeq ($(ENABLE_HEADLESS_ONLY), true)
+ LIBAWT_CFLAGS += -DHEADLESS
+endif
+
ifeq ($(call isTargetOs, windows), true)
LIBAWT_CFLAGS += -EHsc -DUNICODE -D_UNICODE -DMLIB_OS64BIT
LIBAWT_RCFLAGS ?= -I$(TOPDIR)/src/java.base/windows/native/launcher/icons
@@ -167,11 +171,18 @@ ifeq ($(call isTargetOs, windows macosx), false)
$(TOPDIR)/src/$(MODULE)/$(OPENJDK_TARGET_OS_TYPE)/native/common/awt \
#
+ LIBAWT_HEADLESS_EXCLUDE_FILES := \
+ GLXGraphicsConfig.c \
+ GLXSurfaceData.c \
+ X11PMBlitLoops.c \
+ X11Renderer.c \
+ X11SurfaceData.c \
+ #
+
LIBAWT_HEADLESS_EXTRA_HEADER_DIRS := \
$(LIBAWT_DEFAULT_HEADER_DIRS) \
common/awt/debug \
common/font \
- common/java2d/opengl \
java.base:libjvm \
#
@@ -191,7 +202,8 @@ ifeq ($(call isTargetOs, windows macosx), false)
$(eval $(call SetupJdkLibrary, BUILD_LIBAWT_HEADLESS, \
NAME := awt_headless, \
EXTRA_SRC := $(LIBAWT_HEADLESS_EXTRA_SRC), \
- EXCLUDES := medialib, \
+ EXCLUDES := medialib opengl, \
+ EXCLUDE_FILES := $(LIBAWT_HEADLESS_EXCLUDE_FILES), \
ONLY_EXPORTED := $(LIBAWT_HEADLESS_ONLY_EXPORTED), \
OPTIMIZATION := LOW, \
CFLAGS := -DHEADLESS=true $(CUPS_CFLAGS) $(FONTCONFIG_CFLAGS) \
@@ -411,6 +423,9 @@ endif
ifeq ($(call isTargetOs, linux)+$(ENABLE_HEADLESS_ONLY), true+true)
LIBJAWT_CFLAGS += -DHEADLESS
endif
+ifeq ($(call isTargetOs, aix)+$(ENABLE_HEADLESS_ONLY), true+true)
+ LIBJAWT_CFLAGS += -DHEADLESS
+endif
ifeq ($(call isTargetOs, windows)+$(call isTargetCpu, x86), true+true)
LIBJAWT_LIBS_windows := kernel32.lib
diff --git a/src/hotspot/cpu/arm/arm_32.ad b/src/hotspot/cpu/arm/arm_32.ad
index 1c15d55fbc3d..be4dc825e9c7 100644
--- a/src/hotspot/cpu/arm/arm_32.ad
+++ b/src/hotspot/cpu/arm/arm_32.ad
@@ -62,22 +62,22 @@ register %{
// Integer/Long Registers
// ----------------------------
-reg_def R_R0 (SOC, SOC, Op_RegI, 0, R(0)->as_VMReg());
-reg_def R_R1 (SOC, SOC, Op_RegI, 1, R(1)->as_VMReg());
-reg_def R_R2 (SOC, SOC, Op_RegI, 2, R(2)->as_VMReg());
-reg_def R_R3 (SOC, SOC, Op_RegI, 3, R(3)->as_VMReg());
-reg_def R_R4 (SOC, SOE, Op_RegI, 4, R(4)->as_VMReg());
-reg_def R_R5 (SOC, SOE, Op_RegI, 5, R(5)->as_VMReg());
-reg_def R_R6 (SOC, SOE, Op_RegI, 6, R(6)->as_VMReg());
-reg_def R_R7 (SOC, SOE, Op_RegI, 7, R(7)->as_VMReg());
-reg_def R_R8 (SOC, SOE, Op_RegI, 8, R(8)->as_VMReg());
-reg_def R_R9 (SOC, SOE, Op_RegI, 9, R(9)->as_VMReg());
-reg_def R_R10(NS, SOE, Op_RegI, 10, R(10)->as_VMReg());
-reg_def R_R11(NS, SOE, Op_RegI, 11, R(11)->as_VMReg());
-reg_def R_R12(SOC, SOC, Op_RegI, 12, R(12)->as_VMReg());
-reg_def R_R13(NS, NS, Op_RegI, 13, R(13)->as_VMReg());
-reg_def R_R14(SOC, SOC, Op_RegI, 14, R(14)->as_VMReg());
-reg_def R_R15(NS, NS, Op_RegI, 15, R(15)->as_VMReg());
+reg_def R_R0 (SOC, SOC, Op_RegI, 0, as_Register(0)->as_VMReg());
+reg_def R_R1 (SOC, SOC, Op_RegI, 1, as_Register(1)->as_VMReg());
+reg_def R_R2 (SOC, SOC, Op_RegI, 2, as_Register(2)->as_VMReg());
+reg_def R_R3 (SOC, SOC, Op_RegI, 3, as_Register(3)->as_VMReg());
+reg_def R_R4 (SOC, SOE, Op_RegI, 4, as_Register(4)->as_VMReg());
+reg_def R_R5 (SOC, SOE, Op_RegI, 5, as_Register(5)->as_VMReg());
+reg_def R_R6 (SOC, SOE, Op_RegI, 6, as_Register(6)->as_VMReg());
+reg_def R_R7 (SOC, SOE, Op_RegI, 7, as_Register(7)->as_VMReg());
+reg_def R_R8 (SOC, SOE, Op_RegI, 8, as_Register(8)->as_VMReg());
+reg_def R_R9 (SOC, SOE, Op_RegI, 9, as_Register(9)->as_VMReg());
+reg_def R_R10(NS, SOE, Op_RegI, 10, as_Register(10)->as_VMReg());
+reg_def R_R11(NS, SOE, Op_RegI, 11, as_Register(11)->as_VMReg());
+reg_def R_R12(SOC, SOC, Op_RegI, 12, as_Register(12)->as_VMReg());
+reg_def R_R13(NS, NS, Op_RegI, 13, as_Register(13)->as_VMReg());
+reg_def R_R14(SOC, SOC, Op_RegI, 14, as_Register(14)->as_VMReg());
+reg_def R_R15(NS, NS, Op_RegI, 15, as_Register(15)->as_VMReg());
// ----------------------------
// Float/Double Registers
diff --git a/src/hotspot/cpu/arm/assembler_arm_32.hpp b/src/hotspot/cpu/arm/assembler_arm_32.hpp
index ae13644ecf9e..d6524f086800 100644
--- a/src/hotspot/cpu/arm/assembler_arm_32.hpp
+++ b/src/hotspot/cpu/arm/assembler_arm_32.hpp
@@ -114,7 +114,7 @@ class RegisterSet {
}
RegisterSet(Register first, Register last) {
- assert(first < last, "encoding constraint");
+ assert(first->encoding() < last->encoding(), "encoding constraint");
_encoding = (1 << (last->encoding() + 1)) - (1 << first->encoding());
}
diff --git a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp
index 5683bc59d5c0..60eae37e2193 100644
--- a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp
+++ b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp
@@ -181,7 +181,7 @@ void MonitorEnterStub::emit_code(LIR_Assembler* ce) {
const Register lock_reg = _lock_reg->as_pointer_register();
ce->verify_reserved_argument_area_size(2);
- if (obj_reg < lock_reg) {
+ if (obj_reg->encoding() < lock_reg->encoding()) {
__ stmia(SP, RegisterSet(obj_reg) | RegisterSet(lock_reg));
} else {
__ str(obj_reg, Address(SP));
diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
index 66c7b916f1ff..2f7901db82e3 100644
--- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
+++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
@@ -2655,11 +2655,11 @@ void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type,
const Register src_hi = src->as_register_hi();
assert(addr->index()->is_illegal() && addr->disp() == 0, "The address is simple already");
- if (src_lo < src_hi) {
+ if (src_lo->encoding() < src_hi->encoding()) {
null_check_offset = __ offset();
__ stmia(addr->base()->as_register(), RegisterSet(src_lo) | RegisterSet(src_hi));
} else {
- assert(src_lo < Rtemp, "Rtemp is higher than any allocatable register");
+ assert(src_lo->encoding() < Rtemp->encoding(), "Rtemp is higher than any allocatable register");
__ mov(Rtemp, src_hi);
null_check_offset = __ offset();
__ stmia(addr->base()->as_register(), RegisterSet(src_lo) | RegisterSet(Rtemp));
@@ -2672,10 +2672,10 @@ void LIR_Assembler::volatile_move_op(LIR_Opr src, LIR_Opr dest, BasicType type,
assert(addr->index()->is_illegal() && addr->disp() == 0, "The address is simple already");
null_check_offset = __ offset();
- if (dest_lo < dest_hi) {
+ if (dest_lo->encoding() < dest_hi->encoding()) {
__ ldmia(addr->base()->as_register(), RegisterSet(dest_lo) | RegisterSet(dest_hi));
} else {
- assert(dest_lo < Rtemp, "Rtemp is higher than any allocatable register");
+ assert(dest_lo->encoding() < Rtemp->encoding(), "Rtemp is higher than any allocatable register");
__ ldmia(addr->base()->as_register(), RegisterSet(dest_lo) | RegisterSet(Rtemp));
__ mov(dest_hi, Rtemp);
}
diff --git a/src/hotspot/cpu/arm/interp_masm_arm.cpp b/src/hotspot/cpu/arm/interp_masm_arm.cpp
index e9e6187a6d18..69f3f726ab2d 100644
--- a/src/hotspot/cpu/arm/interp_masm_arm.cpp
+++ b/src/hotspot/cpu/arm/interp_masm_arm.cpp
@@ -409,7 +409,7 @@ void InterpreterMacroAssembler::pop_i(Register r) {
void InterpreterMacroAssembler::pop_l(Register lo, Register hi) {
assert_different_registers(lo, hi);
- assert(lo < hi, "lo must be < hi");
+ assert(lo->encoding() < hi->encoding(), "lo must be < hi");
pop(RegisterSet(lo) | RegisterSet(hi));
}
@@ -459,7 +459,7 @@ void InterpreterMacroAssembler::push_i(Register r) {
void InterpreterMacroAssembler::push_l(Register lo, Register hi) {
assert_different_registers(lo, hi);
- assert(lo < hi, "lo must be < hi");
+ assert(lo->encoding() < hi->encoding(), "lo must be < hi");
push(RegisterSet(lo) | RegisterSet(hi));
}
diff --git a/src/hotspot/cpu/arm/register_arm.cpp b/src/hotspot/cpu/arm/register_arm.cpp
index ea3ef87e6708..296c55e2e164 100644
--- a/src/hotspot/cpu/arm/register_arm.cpp
+++ b/src/hotspot/cpu/arm/register_arm.cpp
@@ -25,12 +25,19 @@
#include "register_arm.hpp"
#include "utilities/debug.hpp"
-const int ConcreteRegisterImpl::max_gpr = ConcreteRegisterImpl::num_gpr;
-const int ConcreteRegisterImpl::max_fpr = ConcreteRegisterImpl::num_fpr +
- ConcreteRegisterImpl::max_gpr;
+Register::RegisterImpl all_RegisterImpls [Register::number_of_registers + 1];
+FloatRegister::FloatRegisterImpl all_FloatRegisterImpls [FloatRegister::number_of_registers + 1];
+VFPSystemRegister::VFPSystemRegisterImpl all_VFPSystemRegisterImpls [VFPSystemRegister::number_of_registers + 1] {
+ { -1 }, //vfpsnoreg
+ { VFPSystemRegister::FPSID },
+ { VFPSystemRegister::FPSCR },
+ { VFPSystemRegister::MVFR0 },
+ { VFPSystemRegister::MVFR1 }
+};
-const char* RegisterImpl::name() const {
- const char* names[number_of_registers] = {
+const char* Register::RegisterImpl::name() const {
+ static const char* names[number_of_registers + 1] = {
+ "noreg",
"r0", "r1", "r2", "r3", "r4", "r5", "r6",
#if (FP_REG_NUM == 7)
"fp",
@@ -45,13 +52,14 @@ const char* RegisterImpl::name() const {
#endif
"r12", "sp", "lr", "pc"
};
- return is_valid() ? names[encoding()] : "noreg";
+ return names[encoding() + 1];
}
-const char* FloatRegisterImpl::name() const {
- const char* names[number_of_registers] = {
- "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
- "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
+const char* FloatRegister::FloatRegisterImpl::name() const {
+ static const char* names[number_of_registers + 1] = {
+ "fnoreg",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
"s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
"s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31"
#ifdef COMPILER2
@@ -61,5 +69,5 @@ const char* FloatRegisterImpl::name() const {
"s56", "s57?","s58", "s59?","s60", "s61?","s62", "s63?"
#endif
};
- return is_valid() ? names[encoding()] : "fnoreg";
+ return names[encoding() + 1];
}
diff --git a/src/hotspot/cpu/arm/register_arm.hpp b/src/hotspot/cpu/arm/register_arm.hpp
index fca23d07fee5..b7286bb68a45 100644
--- a/src/hotspot/cpu/arm/register_arm.hpp
+++ b/src/hotspot/cpu/arm/register_arm.hpp
@@ -31,26 +31,6 @@
class VMRegImpl;
typedef VMRegImpl* VMReg;
-// These are declared ucontext.h
-#undef R0
-#undef R1
-#undef R2
-#undef R3
-#undef R4
-#undef R5
-#undef R6
-#undef R7
-#undef R8
-#undef R9
-#undef R10
-#undef R11
-#undef R12
-#undef R13
-#undef R14
-#undef R15
-
-#define R(r) ((Register)(r))
-
/////////////////////////////////
// Support for different ARM ABIs
// Note: default ABI is for linux
@@ -94,25 +74,86 @@ typedef VMRegImpl* VMReg;
#define ALIGN_WIDE_ARGUMENTS 1
#endif
-#define R0 ((Register)0)
-#define R1 ((Register)1)
-#define R2 ((Register)2)
-#define R3 ((Register)3)
-#define R4 ((Register)4)
-#define R5 ((Register)5)
-#define R6 ((Register)6)
-#define R7 ((Register)7)
-#define R8 ((Register)8)
-#define R9 ((Register)9)
-#define R10 ((Register)10)
-#define R11 ((Register)11)
-#define R12 ((Register)12)
-#define R13 ((Register)13)
-#define R14 ((Register)14)
-#define R15 ((Register)15)
-
-
-#define FP ((Register)FP_REG_NUM)
+class Register {
+ private:
+ int _encoding;
+
+ constexpr explicit Register(int encoding) : _encoding(encoding) {}
+
+ public:
+ enum {
+ number_of_registers = 16,
+ max_slots_per_register = 1
+ };
+
+ class RegisterImpl : public AbstractRegisterImpl {
+ friend class Register;
+
+ static constexpr const RegisterImpl* first();
+
+ public:
+
+ // accessors and testers
+ int raw_encoding() const { return this - first(); }
+ int encoding() const { assert(is_valid(), "invalid register"); return raw_encoding(); }
+ bool is_valid() const { return 0 <= raw_encoding() && raw_encoding() < number_of_registers; }
+
+ inline Register successor() const;
+
+ VMReg as_VMReg() const;
+
+ const char* name() const;
+ };
+
+
+ inline friend constexpr Register as_Register(int encoding);
+
+ constexpr Register() : _encoding(-1) {} //noreg
+
+ int operator==(const Register r) const { return _encoding == r._encoding; }
+ int operator!=(const Register r) const { return _encoding != r._encoding; }
+
+ const RegisterImpl* operator->() const { return RegisterImpl::first() + _encoding; }
+};
+
+extern Register::RegisterImpl all_RegisterImpls[Register::number_of_registers + 1] INTERNAL_VISIBILITY;
+
+inline constexpr const Register::RegisterImpl* Register::RegisterImpl::first() {
+ return all_RegisterImpls + 1;
+}
+
+constexpr Register noreg = Register();
+
+inline constexpr Register as_Register(int encoding) {
+ if (0 <= encoding && encoding < Register::number_of_registers) {
+ return Register(encoding);
+ }
+ return noreg;
+}
+
+inline Register Register::RegisterImpl::successor() const {
+ assert(is_valid(), "sainty");
+ return as_Register(encoding() + 1);
+}
+
+constexpr Register R0 = as_Register( 0);
+constexpr Register R1 = as_Register( 1);
+constexpr Register R2 = as_Register( 2);
+constexpr Register R3 = as_Register( 3);
+constexpr Register R4 = as_Register( 4);
+constexpr Register R5 = as_Register( 5);
+constexpr Register R6 = as_Register( 6);
+constexpr Register R7 = as_Register( 7);
+constexpr Register R8 = as_Register( 8);
+constexpr Register R9 = as_Register( 9);
+constexpr Register R10 = as_Register(10);
+constexpr Register R11 = as_Register(11);
+constexpr Register R12 = as_Register(12);
+constexpr Register R13 = as_Register(13);
+constexpr Register R14 = as_Register(14);
+constexpr Register R15 = as_Register(15);
+
+constexpr Register FP = as_Register(FP_REG_NUM);
// Safe use of registers which may be FP on some platforms.
//
@@ -122,185 +163,170 @@ typedef VMRegImpl* VMReg;
// as FP on supported ABIs (and replace R# by altFP_#_11). altFP_#_11
// must be #define to R11 if and only if # is FP_REG_NUM.
#if (FP_REG_NUM == 7)
-#define altFP_7_11 ((Register)11)
+constexpr Register altFP_7_11 = R11;
#else
-#define altFP_7_11 ((Register)7)
+constexpr Register altFP_7_11 = R7;
#endif
-#define SP R13
-#define LR R14
-#define PC R15
+constexpr Register SP = R13;
+constexpr Register LR = R14;
+constexpr Register PC = R15;
-class RegisterImpl;
-typedef RegisterImpl* Register;
+class FloatRegister {
+ private:
+ int _encoding;
-inline Register as_Register(int encoding) {
- return (Register)(intptr_t)encoding;
-}
+ constexpr explicit FloatRegister(int encoding) : _encoding(encoding) {}
-class RegisterImpl : public AbstractRegisterImpl {
public:
enum {
- number_of_registers = 16
+ number_of_registers = NOT_COMPILER2(32) COMPILER2_PRESENT(64),
+ max_slots_per_register = 1
};
- Register successor() const { return as_Register(encoding() + 1); }
+ class FloatRegisterImpl : public AbstractRegisterImpl {
+ friend class FloatRegister;
- inline friend Register as_Register(int encoding);
+ static constexpr const FloatRegisterImpl* first();
- VMReg as_VMReg();
+ public:
- // accessors
- int encoding() const { assert(is_valid(), "invalid register"); return value(); }
- const char* name() const;
+ // accessors and testers
+ int raw_encoding() const { return this - first(); }
+ int encoding() const { assert(is_valid(), "invalid register"); return raw_encoding(); }
+ bool is_valid() const { return 0 <= raw_encoding() && raw_encoding() < number_of_registers; }
+ inline FloatRegister successor() const;
- // testers
- bool is_valid() const { return 0 <= value() && value() < number_of_registers; }
-
-};
+ VMReg as_VMReg() const;
-CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1));
+ int hi_bits() const {
+ return (encoding() >> 1) & 0xf;
+ }
+ int lo_bit() const {
+ return encoding() & 1;
+ }
-// Use FloatRegister as shortcut
-class FloatRegisterImpl;
-typedef FloatRegisterImpl* FloatRegister;
+ int hi_bit() const {
+ return encoding() >> 5;
+ }
-inline FloatRegister as_FloatRegister(int encoding) {
- return (FloatRegister)(intptr_t)encoding;
-}
-
-class FloatRegisterImpl : public AbstractRegisterImpl {
- public:
- enum {
- number_of_registers = NOT_COMPILER2(32) COMPILER2_PRESENT(64)
+ const char* name() const;
};
- inline friend FloatRegister as_FloatRegister(int encoding);
+ inline friend constexpr FloatRegister as_FloatRegister(int encoding);
- VMReg as_VMReg();
+ constexpr FloatRegister() : _encoding(-1) {} // fnoreg
- int encoding() const { assert(is_valid(), "invalid register"); return value(); }
- bool is_valid() const { return 0 <= (intx)this && (intx)this < number_of_registers; }
- FloatRegister successor() const { return as_FloatRegister(encoding() + 1); }
+ int operator==(const FloatRegister r) const { return _encoding == r._encoding; }
+ int operator!=(const FloatRegister r) const { return _encoding != r._encoding; }
- const char* name() const;
+ const FloatRegisterImpl* operator->() const { return FloatRegisterImpl::first() + _encoding; }
+};
- int hi_bits() const {
- return (encoding() >> 1) & 0xf;
- }
+extern FloatRegister::FloatRegisterImpl all_FloatRegisterImpls[FloatRegister::number_of_registers + 1] INTERNAL_VISIBILITY;
- int lo_bit() const {
- return encoding() & 1;
- }
+inline constexpr const FloatRegister::FloatRegisterImpl* FloatRegister::FloatRegisterImpl::first() {
+ return all_FloatRegisterImpls + 1;
+}
+
+constexpr FloatRegister fnoreg = FloatRegister();
- int hi_bit() const {
- return encoding() >> 5;
+inline constexpr FloatRegister as_FloatRegister(int encoding) {
+ if (0 <= encoding && encoding < FloatRegister::number_of_registers) {
+ return FloatRegister(encoding);
}
-};
+ return fnoreg;
+}
-CONSTANT_REGISTER_DECLARATION(FloatRegister, fnoreg, (-1));
+inline FloatRegister FloatRegister::FloatRegisterImpl::successor() const {
+ assert(is_valid(), "sainty");
+ return as_FloatRegister(encoding() + 1);
+}
/*
* S1-S6 are named with "_reg" suffix to avoid conflict with
* constants defined in sharedRuntimeTrig.cpp
*/
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S0, ( 0));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S1_reg, ( 1));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S2_reg, ( 2));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S3_reg, ( 3));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S4_reg, ( 4));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S5_reg, ( 5));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S6_reg, ( 6));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S7, ( 7));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S8, ( 8));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S9, ( 9));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S10, (10));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S11, (11));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S12, (12));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S13, (13));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S14, (14));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S15, (15));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S16, (16));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S17, (17));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S18, (18));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S19, (19));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S20, (20));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S21, (21));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S22, (22));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S23, (23));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S24, (24));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S25, (25));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S26, (26));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S27, (27));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S28, (28));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S29, (29));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S30, (30));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, S31, (31));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, Stemp, (30));
-
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D0, ( 0));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D1, ( 2));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D2, ( 4));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D3, ( 6));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D4, ( 8));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D5, ( 10));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D6, ( 12));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D7, ( 14));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D8, ( 16));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D9, ( 18));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D10, ( 20));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D11, ( 22));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D12, ( 24));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D13, ( 26));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D14, ( 28));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D15, (30));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D16, (32));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D17, (34));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D18, (36));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D19, (38));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D20, (40));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D21, (42));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D22, (44));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D23, (46));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D24, (48));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D25, (50));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D26, (52));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D27, (54));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D28, (56));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D29, (58));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D30, (60));
-CONSTANT_REGISTER_DECLARATION(FloatRegister, D31, (62));
+constexpr FloatRegister S0 = as_FloatRegister( 0);
+constexpr FloatRegister S1_reg = as_FloatRegister(1);
+constexpr FloatRegister S2_reg = as_FloatRegister(2);
+constexpr FloatRegister S3_reg = as_FloatRegister(3);
+constexpr FloatRegister S4_reg = as_FloatRegister(4);
+constexpr FloatRegister S5_reg = as_FloatRegister(5);
+constexpr FloatRegister S6_reg = as_FloatRegister(6);
+constexpr FloatRegister S7 = as_FloatRegister( 7);
+constexpr FloatRegister S8 = as_FloatRegister( 8);
+constexpr FloatRegister S9 = as_FloatRegister( 9);
+constexpr FloatRegister S10 = as_FloatRegister(10);
+constexpr FloatRegister S11 = as_FloatRegister(11);
+constexpr FloatRegister S12 = as_FloatRegister(12);
+constexpr FloatRegister S13 = as_FloatRegister(13);
+constexpr FloatRegister S14 = as_FloatRegister(14);
+constexpr FloatRegister S15 = as_FloatRegister(15);
+constexpr FloatRegister S16 = as_FloatRegister(16);
+constexpr FloatRegister S17 = as_FloatRegister(17);
+constexpr FloatRegister S18 = as_FloatRegister(18);
+constexpr FloatRegister S19 = as_FloatRegister(19);
+constexpr FloatRegister S20 = as_FloatRegister(20);
+constexpr FloatRegister S21 = as_FloatRegister(21);
+constexpr FloatRegister S22 = as_FloatRegister(22);
+constexpr FloatRegister S23 = as_FloatRegister(23);
+constexpr FloatRegister S24 = as_FloatRegister(24);
+constexpr FloatRegister S25 = as_FloatRegister(25);
+constexpr FloatRegister S26 = as_FloatRegister(26);
+constexpr FloatRegister S27 = as_FloatRegister(27);
+constexpr FloatRegister S28 = as_FloatRegister(28);
+constexpr FloatRegister S29 = as_FloatRegister(29);
+constexpr FloatRegister S30 = as_FloatRegister(30);
+constexpr FloatRegister S31 = as_FloatRegister(31);
+constexpr FloatRegister Stemp = S30;
+
+constexpr FloatRegister D0 = as_FloatRegister( 0);
+constexpr FloatRegister D1 = as_FloatRegister( 2);
+constexpr FloatRegister D2 = as_FloatRegister( 4);
+constexpr FloatRegister D3 = as_FloatRegister( 6);
+constexpr FloatRegister D4 = as_FloatRegister( 8);
+constexpr FloatRegister D5 = as_FloatRegister(10);
+constexpr FloatRegister D6 = as_FloatRegister(12);
+constexpr FloatRegister D7 = as_FloatRegister(14);
+constexpr FloatRegister D8 = as_FloatRegister(16);
+constexpr FloatRegister D9 = as_FloatRegister(18);
+constexpr FloatRegister D10 = as_FloatRegister(20);
+constexpr FloatRegister D11 = as_FloatRegister(22);
+constexpr FloatRegister D12 = as_FloatRegister(24);
+constexpr FloatRegister D13 = as_FloatRegister(26);
+constexpr FloatRegister D14 = as_FloatRegister(28);
+constexpr FloatRegister D15 = as_FloatRegister(30);
+constexpr FloatRegister D16 = as_FloatRegister(32);
+constexpr FloatRegister D17 = as_FloatRegister(34);
+constexpr FloatRegister D18 = as_FloatRegister(36);
+constexpr FloatRegister D19 = as_FloatRegister(38);
+constexpr FloatRegister D20 = as_FloatRegister(40);
+constexpr FloatRegister D21 = as_FloatRegister(42);
+constexpr FloatRegister D22 = as_FloatRegister(44);
+constexpr FloatRegister D23 = as_FloatRegister(46);
+constexpr FloatRegister D24 = as_FloatRegister(48);
+constexpr FloatRegister D25 = as_FloatRegister(50);
+constexpr FloatRegister D26 = as_FloatRegister(52);
+constexpr FloatRegister D27 = as_FloatRegister(54);
+constexpr FloatRegister D28 = as_FloatRegister(56);
+constexpr FloatRegister D29 = as_FloatRegister(58);
+constexpr FloatRegister D30 = as_FloatRegister(60);
+constexpr FloatRegister D31 = as_FloatRegister(62);
class ConcreteRegisterImpl : public AbstractRegisterImpl {
public:
enum {
- log_vmregs_per_word = LogBytesPerWord - LogBytesPerInt, // VMRegs are of 4-byte size
-#ifdef COMPILER2
- log_bytes_per_fpr = 2, // quad vectors
-#else
- log_bytes_per_fpr = 2, // double vectors
-#endif
- log_words_per_fpr = log_bytes_per_fpr - LogBytesPerWord,
- words_per_fpr = 1 << log_words_per_fpr,
- log_vmregs_per_fpr = log_bytes_per_fpr - LogBytesPerInt,
- log_vmregs_per_gpr = log_vmregs_per_word,
- vmregs_per_gpr = 1 << log_vmregs_per_gpr,
- vmregs_per_fpr = 1 << log_vmregs_per_fpr,
-
- num_gpr = RegisterImpl::number_of_registers << log_vmregs_per_gpr,
- max_gpr0 = num_gpr,
- num_fpr = FloatRegisterImpl::number_of_registers << log_vmregs_per_fpr,
- max_fpr0 = max_gpr0 + num_fpr,
- number_of_registers = num_gpr + num_fpr + 1+1 // APSR and FPSCR so that c2's REG_COUNT <= ConcreteRegisterImpl::number_of_registers
- };
+ max_gpr = Register::number_of_registers * Register::max_slots_per_register,
+ max_fpr = max_gpr + FloatRegister::number_of_registers * FloatRegister::max_slots_per_register,
- static const int max_gpr;
- static const int max_fpr;
+ number_of_registers = max_fpr + 1+1 // APSR and FPSCR so that c2's REG_COUNT <= ConcreteRegisterImpl::number_of_registers
+ };
};
typedef AbstractRegSet RegSet;
@@ -328,101 +354,157 @@ inline FloatRegister AbstractRegSet::last() {
-class VFPSystemRegisterImpl;
-typedef VFPSystemRegisterImpl* VFPSystemRegister;
-class VFPSystemRegisterImpl : public AbstractRegisterImpl {
+class VFPSystemRegister {
+ private:
+ int _store_idx;
+
+ constexpr explicit VFPSystemRegister(int store_idx) : _store_idx(store_idx) {}
+
+ enum {
+ _FPSID_store_idx = 0,
+ _FPSCR_store_idx = 1,
+ _MVFR0_store_idx = 2,
+ _MVFR1_store_idx = 3
+ };
+
public:
- int encoding() const { return value(); }
+ enum {
+ FPSID = 0,
+ FPSCR = 1,
+ MVFR0 = 6,
+ MVFR1 = 7,
+ number_of_registers = 4
+ };
+
+ class VFPSystemRegisterImpl : public AbstractRegisterImpl {
+ friend class VFPSystemRegister;
+
+ int _encoding;
+
+ static constexpr const VFPSystemRegisterImpl* first();
+
+ public:
+ constexpr VFPSystemRegisterImpl(int encoding) : _encoding(encoding) {}
+
+ int encoding() const { return _encoding; }
+ };
+
+ inline friend constexpr VFPSystemRegister as_VFPSystemRegister(int encoding);
+
+ constexpr VFPSystemRegister() : _store_idx(-1) {} // vfpsnoreg
+
+ int operator==(const VFPSystemRegister r) const { return _store_idx == r._store_idx; }
+ int operator!=(const VFPSystemRegister r) const { return _store_idx != r._store_idx; }
+
+ const VFPSystemRegisterImpl* operator->() const { return VFPSystemRegisterImpl::first() + _store_idx; }
};
-#define FPSID ((VFPSystemRegister)0)
-#define FPSCR ((VFPSystemRegister)1)
-#define MVFR0 ((VFPSystemRegister)0x6)
-#define MVFR1 ((VFPSystemRegister)0x7)
+extern VFPSystemRegister::VFPSystemRegisterImpl all_VFPSystemRegisterImpls[VFPSystemRegister::number_of_registers + 1] INTERNAL_VISIBILITY;
+
+inline constexpr const VFPSystemRegister::VFPSystemRegisterImpl* VFPSystemRegister::VFPSystemRegisterImpl::first() {
+ return all_VFPSystemRegisterImpls + 1;
+}
+
+constexpr VFPSystemRegister vfpsnoreg = VFPSystemRegister();
+
+inline constexpr VFPSystemRegister as_VFPSystemRegister(int encoding) {
+ switch (encoding) {
+ case VFPSystemRegister::FPSID: return VFPSystemRegister(VFPSystemRegister::_FPSID_store_idx);
+ case VFPSystemRegister::FPSCR: return VFPSystemRegister(VFPSystemRegister::_FPSCR_store_idx);
+ case VFPSystemRegister::MVFR0: return VFPSystemRegister(VFPSystemRegister::_MVFR0_store_idx);
+ case VFPSystemRegister::MVFR1: return VFPSystemRegister(VFPSystemRegister::_MVFR1_store_idx);
+ default: return vfpsnoreg;
+ }
+}
+
+constexpr VFPSystemRegister FPSID = as_VFPSystemRegister(VFPSystemRegister::FPSID);
+constexpr VFPSystemRegister FPSCR = as_VFPSystemRegister(VFPSystemRegister::FPSCR);
+constexpr VFPSystemRegister MVFR0 = as_VFPSystemRegister(VFPSystemRegister::MVFR0);
+constexpr VFPSystemRegister MVFR1 = as_VFPSystemRegister(VFPSystemRegister::MVFR1);
/*
* Register definitions shared across interpreter and compiler
*/
-#define Rexception_obj R4
-#define Rexception_pc R5
+constexpr Register Rexception_obj = R4;
+constexpr Register Rexception_pc = R5;
/*
* Interpreter register definitions common to C++ and template interpreters.
*/
-#define Rlocals R8
-#define Rmethod R9
-#define Rthread R10
-#define Rtemp R12
+constexpr Register Rlocals = R8;
+constexpr Register Rmethod = R9;
+constexpr Register Rthread = R10;
+constexpr Register Rtemp = R12;
// Interpreter calling conventions
-#define Rparams SP
-#define Rsender_sp R4
+constexpr Register Rparams = SP;
+constexpr Register Rsender_sp = R4;
// JSR292
// Note: R5_mh is needed only during the call setup, including adapters
// This does not seem to conflict with Rexception_pc
// In case of issues, R3 might be OK but adapters calling the runtime would have to save it
-#define R5_mh R5 // MethodHandle register, used during the call setup
-#define Rmh_SP_save FP // for C1
+constexpr Register R5_mh = R5; // MethodHandle register, used during the call setup
+constexpr Register Rmh_SP_save = FP; // for C1
/*
* C++ Interpreter Register Defines
*/
-#define Rsave0 R4
-#define Rsave1 R5
-#define Rsave2 R6
-#define Rstate altFP_7_11 // R7 or R11
-#define Ricklass R8
+constexpr Register Rsave0 = R4;
+constexpr Register Rsave1 = R5;
+constexpr Register Rsave2 = R6;
+constexpr Register Rstate = altFP_7_11; // R7 or R11
+constexpr Register Ricklass = R8;
/*
* TemplateTable Interpreter Register Usage
*/
// Temporary registers
-#define R0_tmp R0
-#define R1_tmp R1
-#define R2_tmp R2
-#define R3_tmp R3
-#define R4_tmp R4
-#define R5_tmp R5
-#define R12_tmp R12
-#define LR_tmp LR
+constexpr Register R0_tmp = R0;
+constexpr Register R1_tmp = R1;
+constexpr Register R2_tmp = R2;
+constexpr Register R3_tmp = R3;
+constexpr Register R4_tmp = R4;
+constexpr Register R5_tmp = R5;
+constexpr Register R12_tmp = R12;
+constexpr Register LR_tmp = LR;
-#define S0_tmp S0
-#define S1_tmp S1_reg
+constexpr FloatRegister S0_tmp = S0;
+constexpr FloatRegister S1_tmp = S1_reg;
-#define D0_tmp D0
-#define D1_tmp D1
+constexpr FloatRegister D0_tmp = D0;
+constexpr FloatRegister D1_tmp = D1;
// Temporary registers saved across VM calls (according to C calling conventions)
-#define Rtmp_save0 R4
-#define Rtmp_save1 R5
+constexpr Register Rtmp_save0 = R4;
+constexpr Register Rtmp_save1 = R5;
// Cached TOS value
-#define R0_tos R0
+constexpr Register R0_tos = R0;
-#define R0_tos_lo R0
-#define R1_tos_hi R1
+constexpr Register R0_tos_lo = R0;
+constexpr Register R1_tos_hi = R1;
-#define S0_tos S0
-#define D0_tos D0
+constexpr FloatRegister S0_tos = S0;
+constexpr FloatRegister D0_tos = D0;
// Dispatch table
-#define RdispatchTable R6
+constexpr Register RdispatchTable = R6;
// Bytecode pointer
-#define Rbcp altFP_7_11
+constexpr Register Rbcp = altFP_7_11;
// Pre-loaded next bytecode for the dispatch
-#define R3_bytecode R3
+constexpr Register R3_bytecode = R3;
// Conventions between bytecode templates and stubs
-#define R2_ClassCastException_obj R2
-#define R4_ArrayIndexOutOfBounds_index R4
+constexpr Register R2_ClassCastException_obj = R2;
+constexpr Register R4_ArrayIndexOutOfBounds_index = R4;
// Interpreter expression stack top
-#define Rstack_top SP
+constexpr Register Rstack_top = SP;
/*
* Linux 32-bit ARM C ABI Register calling conventions
@@ -445,10 +527,11 @@ class VFPSystemRegisterImpl : public AbstractRegisterImpl {
* R14 (LR) Link register
* R15 (PC) Program Counter
*/
-#define c_rarg0 R0
-#define c_rarg1 R1
-#define c_rarg2 R2
-#define c_rarg3 R3
+
+constexpr Register c_rarg0 = R0;
+constexpr Register c_rarg1 = R1;
+constexpr Register c_rarg2 = R2;
+constexpr Register c_rarg3 = R3;
#define GPR_PARAMS 4
@@ -456,10 +539,10 @@ class VFPSystemRegisterImpl : public AbstractRegisterImpl {
// Java ABI
// XXX Is this correct?
-#define j_rarg0 c_rarg0
-#define j_rarg1 c_rarg1
-#define j_rarg2 c_rarg2
-#define j_rarg3 c_rarg3
+constexpr Register j_rarg0 = c_rarg0;
+constexpr Register j_rarg1 = c_rarg1;
+constexpr Register j_rarg2 = c_rarg2;
+constexpr Register j_rarg3 = c_rarg3;
#endif // CPU_ARM_REGISTER_ARM_HPP
diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp
index 8ba847e7e328..2fc317cbb286 100644
--- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp
+++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp
@@ -70,7 +70,7 @@ class RegisterSaver {
enum RegisterLayout {
- fpu_save_size = FloatRegisterImpl::number_of_registers,
+ fpu_save_size = FloatRegister::number_of_registers,
#ifndef __SOFTFP__
D0_offset = 0,
#endif
@@ -139,8 +139,8 @@ OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm,
if (VM_Version::has_vfp3_32()) {
__ fpush(FloatRegisterSet(D16, 16));
} else {
- if (FloatRegisterImpl::number_of_registers > 32) {
- assert(FloatRegisterImpl::number_of_registers == 64, "nb fp registers should be 64");
+ if (FloatRegister::number_of_registers > 32) {
+ assert(FloatRegister::number_of_registers == 64, "nb fp registers should be 64");
__ sub(SP, SP, 32 * wordSize);
}
}
@@ -182,8 +182,8 @@ void RegisterSaver::restore_live_registers(MacroAssembler* masm, bool restore_lr
if (VM_Version::has_vfp3_32()) {
__ fpop(FloatRegisterSet(D16, 16));
} else {
- if (FloatRegisterImpl::number_of_registers > 32) {
- assert(FloatRegisterImpl::number_of_registers == 64, "nb fp registers should be 64");
+ if (FloatRegister::number_of_registers > 32) {
+ assert(FloatRegister::number_of_registers == 64, "nb fp registers should be 64");
__ add(SP, SP, 32 * wordSize);
}
}
diff --git a/src/hotspot/cpu/arm/vmreg_arm.cpp b/src/hotspot/cpu/arm/vmreg_arm.cpp
index 4ce1dd0be20f..efaf38ef7297 100644
--- a/src/hotspot/cpu/arm/vmreg_arm.cpp
+++ b/src/hotspot/cpu/arm/vmreg_arm.cpp
@@ -30,14 +30,14 @@ void VMRegImpl::set_regName() {
Register reg = ::as_Register(0);
int i;
for (i = 0; i < ConcreteRegisterImpl::max_gpr; reg = reg->successor()) {
- for (int j = 0; j < (1 << ConcreteRegisterImpl::log_vmregs_per_gpr); j++) {
+ for (int j = 0; j < Register::max_slots_per_register; j++) {
regName[i++] = reg->name();
}
}
#ifndef __SOFTFP__
FloatRegister freg = ::as_FloatRegister(0);
for ( ; i < ConcreteRegisterImpl::max_fpr ; ) {
- for (int j = 0; j < (1 << ConcreteRegisterImpl::log_vmregs_per_fpr); j++) {
+ for (int j = 0; j < Register::max_slots_per_register; j++) {
regName[i++] = freg->name();
}
freg = freg->successor();
diff --git a/src/hotspot/cpu/arm/vmreg_arm.hpp b/src/hotspot/cpu/arm/vmreg_arm.hpp
index c13f443b804f..f1dfd09a1e69 100644
--- a/src/hotspot/cpu/arm/vmreg_arm.hpp
+++ b/src/hotspot/cpu/arm/vmreg_arm.hpp
@@ -36,20 +36,20 @@
inline Register as_Register() {
assert(is_Register(), "must be");
assert(is_concrete(), "concrete register expected");
- return ::as_Register(value() >> ConcreteRegisterImpl::log_vmregs_per_gpr);
+ return ::as_Register(value() / Register::max_slots_per_register);
}
inline FloatRegister as_FloatRegister() {
assert(is_FloatRegister(), "must be");
assert(is_concrete(), "concrete register expected");
- return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) >> ConcreteRegisterImpl::log_vmregs_per_fpr);
+ return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) / FloatRegister::max_slots_per_register);
}
inline bool is_concrete() {
if (is_Register()) {
- return ((value() & right_n_bits(ConcreteRegisterImpl::log_vmregs_per_gpr)) == 0);
+ return (value() % Register::max_slots_per_register == 0);
} else if (is_FloatRegister()) {
- return (((value() - ConcreteRegisterImpl::max_gpr) & right_n_bits(ConcreteRegisterImpl::log_vmregs_per_fpr)) == 0);
+ return (value() % FloatRegister::max_slots_per_register == 0); // Single slot
} else {
return false;
}
diff --git a/src/hotspot/cpu/arm/vmreg_arm.inline.hpp b/src/hotspot/cpu/arm/vmreg_arm.inline.hpp
index f122b9ede701..3e5c18dbda0a 100644
--- a/src/hotspot/cpu/arm/vmreg_arm.inline.hpp
+++ b/src/hotspot/cpu/arm/vmreg_arm.inline.hpp
@@ -25,11 +25,11 @@
#ifndef CPU_ARM_VMREG_ARM_INLINE_HPP
#define CPU_ARM_VMREG_ARM_INLINE_HPP
-inline VMReg RegisterImpl::as_VMReg() {
- return VMRegImpl::as_VMReg(encoding() << ConcreteRegisterImpl::log_vmregs_per_gpr);
+inline VMReg Register::RegisterImpl::as_VMReg() const {
+ return VMRegImpl::as_VMReg(encoding() * Register::max_slots_per_register);
}
-inline VMReg FloatRegisterImpl::as_VMReg() {
- return VMRegImpl::as_VMReg((encoding() << ConcreteRegisterImpl::log_vmregs_per_fpr) + ConcreteRegisterImpl::max_gpr);
+inline VMReg FloatRegister::FloatRegisterImpl::as_VMReg() const {
+ return VMRegImpl::as_VMReg((encoding() * FloatRegister::max_slots_per_register) + ConcreteRegisterImpl::max_gpr);
}
#endif // CPU_ARM_VMREG_ARM_INLINE_HPP
diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
index 32a7011ac261..405ac4b2310e 100644
--- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
@@ -333,9 +333,9 @@ int SaveLiveRegisters::iterate_over_register_mask(IterationAction action, int of
}
} else if (vm_reg->is_ConditionRegister()) {
// NOP. Conditions registers are covered by save_LR_CR
- } else if (vm_reg->is_VectorSRegister()) {
+ } else if (vm_reg->is_VectorRegister()) {
assert(SuperwordUseVSX, "or should not reach here");
- VectorSRegister vs_reg = vm_reg->as_VectorSRegister();
+ VectorSRegister vs_reg = (vm_reg->as_VectorRegister()).to_vsr();
if (vs_reg->encoding() >= VSR32->encoding() && vs_reg->encoding() <= VSR51->encoding()) {
reg_save_index += (2 + (reg_save_index & 1)); // 2 slots + alignment if needed
diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
index 396a50427f85..bcb2df203a2c 100644
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
@@ -3436,23 +3436,19 @@ void MacroAssembler::store_klass_gap(Register dst_oop, Register val) {
}
}
-int MacroAssembler::instr_size_for_decode_klass_not_null() {
+int MacroAssembler::instr_size_for_load_klass() {
static int computed_size = -1;
// Not yet computed?
if (computed_size == -1) {
- if (!UseCompressedClassPointers) {
- computed_size = 0;
- } else {
- // Determine by scratch emit.
- ResourceMark rm;
- int code_size = 8 * BytesPerInstWord;
- CodeBuffer cb("decode_klass_not_null scratch buffer", code_size, 0);
- MacroAssembler* a = new MacroAssembler(&cb);
- a->decode_klass_not_null(R11_scratch1);
- computed_size = a->offset();
- }
+ // Determine by scratch emit.
+ ResourceMark rm;
+ int code_size = 16 * BytesPerInstWord;
+ CodeBuffer cb("load_klass scratch buffer", code_size, 0);
+ MacroAssembler* a = new MacroAssembler(&cb);
+ a->load_klass(R11_scratch1, R11_scratch1);
+ computed_size = a->offset();
}
return computed_size;
@@ -4752,7 +4748,7 @@ void MacroAssembler::push_cont_fastpath() {
Label done;
ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread);
cmpld(CR0, R1_SP, R0);
- ble(CR0, done);
+ ble(CR0, done); // if (SP <= _cont_fastpath) goto done;
st_ptr(R1_SP, JavaThread::cont_fastpath_offset(), R16_thread);
bind(done);
}
@@ -4763,7 +4759,7 @@ void MacroAssembler::pop_cont_fastpath() {
Label done;
ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread);
cmpld(CR0, R1_SP, R0);
- ble(CR0, done);
+ blt(CR0, done); // if (SP < _cont_fastpath) goto done;
li(R0, 0);
st_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread);
bind(done);
diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
index 471ebb7459a1..9f272e96bcfc 100644
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
@@ -818,7 +818,7 @@ class MacroAssembler: public Assembler {
MacroAssembler::PreservationLevel preservation_level);
void load_method_holder(Register holder, Register method);
- static int instr_size_for_decode_klass_not_null();
+ static int instr_size_for_load_klass();
void decode_klass_not_null(Register dst, Register src = noreg);
Register encode_klass_not_null(Register dst, Register src = noreg);
diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad
index 18a99e5bc9af..c4a8172cf927 100644
--- a/src/hotspot/cpu/ppc/ppc.ad
+++ b/src/hotspot/cpu/ppc/ppc.ad
@@ -255,329 +255,168 @@ register %{
reg_def SR_PPR( SOC, SOC, Op_RegP, 5, SR_PPR->as_VMReg()); // v
// ----------------------------
-// Vector-Scalar Registers
+// Vector Registers
// ----------------------------
- // 1st 32 VSRs are aliases for the FPRs which are already defined above.
- reg_def VSR0 (SOC, SOC, Op_RegF, 0, VMRegImpl::Bad());
- reg_def VSR0_H (SOC, SOC, Op_RegF, 0, VMRegImpl::Bad());
- reg_def VSR0_J (SOC, SOC, Op_RegF, 0, VMRegImpl::Bad());
- reg_def VSR0_K (SOC, SOC, Op_RegF, 0, VMRegImpl::Bad());
-
- reg_def VSR1 (SOC, SOC, Op_RegF, 1, VMRegImpl::Bad());
- reg_def VSR1_H (SOC, SOC, Op_RegF, 1, VMRegImpl::Bad());
- reg_def VSR1_J (SOC, SOC, Op_RegF, 1, VMRegImpl::Bad());
- reg_def VSR1_K (SOC, SOC, Op_RegF, 1, VMRegImpl::Bad());
-
- reg_def VSR2 (SOC, SOC, Op_RegF, 2, VMRegImpl::Bad());
- reg_def VSR2_H (SOC, SOC, Op_RegF, 2, VMRegImpl::Bad());
- reg_def VSR2_J (SOC, SOC, Op_RegF, 2, VMRegImpl::Bad());
- reg_def VSR2_K (SOC, SOC, Op_RegF, 2, VMRegImpl::Bad());
-
- reg_def VSR3 (SOC, SOC, Op_RegF, 3, VMRegImpl::Bad());
- reg_def VSR3_H (SOC, SOC, Op_RegF, 3, VMRegImpl::Bad());
- reg_def VSR3_J (SOC, SOC, Op_RegF, 3, VMRegImpl::Bad());
- reg_def VSR3_K (SOC, SOC, Op_RegF, 3, VMRegImpl::Bad());
-
- reg_def VSR4 (SOC, SOC, Op_RegF, 4, VMRegImpl::Bad());
- reg_def VSR4_H (SOC, SOC, Op_RegF, 4, VMRegImpl::Bad());
- reg_def VSR4_J (SOC, SOC, Op_RegF, 4, VMRegImpl::Bad());
- reg_def VSR4_K (SOC, SOC, Op_RegF, 4, VMRegImpl::Bad());
-
- reg_def VSR5 (SOC, SOC, Op_RegF, 5, VMRegImpl::Bad());
- reg_def VSR5_H (SOC, SOC, Op_RegF, 5, VMRegImpl::Bad());
- reg_def VSR5_J (SOC, SOC, Op_RegF, 5, VMRegImpl::Bad());
- reg_def VSR5_K (SOC, SOC, Op_RegF, 5, VMRegImpl::Bad());
-
- reg_def VSR6 (SOC, SOC, Op_RegF, 6, VMRegImpl::Bad());
- reg_def VSR6_H (SOC, SOC, Op_RegF, 6, VMRegImpl::Bad());
- reg_def VSR6_J (SOC, SOC, Op_RegF, 6, VMRegImpl::Bad());
- reg_def VSR6_K (SOC, SOC, Op_RegF, 6, VMRegImpl::Bad());
-
- reg_def VSR7 (SOC, SOC, Op_RegF, 7, VMRegImpl::Bad());
- reg_def VSR7_H (SOC, SOC, Op_RegF, 7, VMRegImpl::Bad());
- reg_def VSR7_J (SOC, SOC, Op_RegF, 7, VMRegImpl::Bad());
- reg_def VSR7_K (SOC, SOC, Op_RegF, 7, VMRegImpl::Bad());
-
- reg_def VSR8 (SOC, SOC, Op_RegF, 8, VMRegImpl::Bad());
- reg_def VSR8_H (SOC, SOC, Op_RegF, 8, VMRegImpl::Bad());
- reg_def VSR8_J (SOC, SOC, Op_RegF, 8, VMRegImpl::Bad());
- reg_def VSR8_K (SOC, SOC, Op_RegF, 8, VMRegImpl::Bad());
-
- reg_def VSR9 (SOC, SOC, Op_RegF, 9, VMRegImpl::Bad());
- reg_def VSR9_H (SOC, SOC, Op_RegF, 9, VMRegImpl::Bad());
- reg_def VSR9_J (SOC, SOC, Op_RegF, 9, VMRegImpl::Bad());
- reg_def VSR9_K (SOC, SOC, Op_RegF, 9, VMRegImpl::Bad());
-
- reg_def VSR10 (SOC, SOC, Op_RegF, 10, VMRegImpl::Bad());
- reg_def VSR10_H(SOC, SOC, Op_RegF, 10, VMRegImpl::Bad());
- reg_def VSR10_J(SOC, SOC, Op_RegF, 10, VMRegImpl::Bad());
- reg_def VSR10_K(SOC, SOC, Op_RegF, 10, VMRegImpl::Bad());
-
- reg_def VSR11 (SOC, SOC, Op_RegF, 11, VMRegImpl::Bad());
- reg_def VSR11_H(SOC, SOC, Op_RegF, 11, VMRegImpl::Bad());
- reg_def VSR11_J(SOC, SOC, Op_RegF, 11, VMRegImpl::Bad());
- reg_def VSR11_K(SOC, SOC, Op_RegF, 11, VMRegImpl::Bad());
-
- reg_def VSR12 (SOC, SOC, Op_RegF, 12, VMRegImpl::Bad());
- reg_def VSR12_H(SOC, SOC, Op_RegF, 12, VMRegImpl::Bad());
- reg_def VSR12_J(SOC, SOC, Op_RegF, 12, VMRegImpl::Bad());
- reg_def VSR12_K(SOC, SOC, Op_RegF, 12, VMRegImpl::Bad());
-
- reg_def VSR13 (SOC, SOC, Op_RegF, 13, VMRegImpl::Bad());
- reg_def VSR13_H(SOC, SOC, Op_RegF, 13, VMRegImpl::Bad());
- reg_def VSR13_J(SOC, SOC, Op_RegF, 13, VMRegImpl::Bad());
- reg_def VSR13_K(SOC, SOC, Op_RegF, 13, VMRegImpl::Bad());
-
- reg_def VSR14 (SOC, SOC, Op_RegF, 14, VMRegImpl::Bad());
- reg_def VSR14_H(SOC, SOC, Op_RegF, 14, VMRegImpl::Bad());
- reg_def VSR14_J(SOC, SOC, Op_RegF, 14, VMRegImpl::Bad());
- reg_def VSR14_K(SOC, SOC, Op_RegF, 14, VMRegImpl::Bad());
-
- reg_def VSR15 (SOC, SOC, Op_RegF, 15, VMRegImpl::Bad());
- reg_def VSR15_H(SOC, SOC, Op_RegF, 15, VMRegImpl::Bad());
- reg_def VSR15_J(SOC, SOC, Op_RegF, 15, VMRegImpl::Bad());
- reg_def VSR15_K(SOC, SOC, Op_RegF, 15, VMRegImpl::Bad());
-
- reg_def VSR16 (SOC, SOC, Op_RegF, 16, VMRegImpl::Bad());
- reg_def VSR16_H(SOC, SOC, Op_RegF, 16, VMRegImpl::Bad());
- reg_def VSR16_J(SOC, SOC, Op_RegF, 16, VMRegImpl::Bad());
- reg_def VSR16_K(SOC, SOC, Op_RegF, 16, VMRegImpl::Bad());
-
- reg_def VSR17 (SOC, SOC, Op_RegF, 17, VMRegImpl::Bad());
- reg_def VSR17_H(SOC, SOC, Op_RegF, 17, VMRegImpl::Bad());
- reg_def VSR17_J(SOC, SOC, Op_RegF, 17, VMRegImpl::Bad());
- reg_def VSR17_K(SOC, SOC, Op_RegF, 17, VMRegImpl::Bad());
-
- reg_def VSR18 (SOC, SOC, Op_RegF, 18, VMRegImpl::Bad());
- reg_def VSR18_H(SOC, SOC, Op_RegF, 18, VMRegImpl::Bad());
- reg_def VSR18_J(SOC, SOC, Op_RegF, 18, VMRegImpl::Bad());
- reg_def VSR18_K(SOC, SOC, Op_RegF, 18, VMRegImpl::Bad());
-
- reg_def VSR19 (SOC, SOC, Op_RegF, 19, VMRegImpl::Bad());
- reg_def VSR19_H(SOC, SOC, Op_RegF, 19, VMRegImpl::Bad());
- reg_def VSR19_J(SOC, SOC, Op_RegF, 19, VMRegImpl::Bad());
- reg_def VSR19_K(SOC, SOC, Op_RegF, 19, VMRegImpl::Bad());
-
- reg_def VSR20 (SOC, SOC, Op_RegF, 20, VMRegImpl::Bad());
- reg_def VSR20_H(SOC, SOC, Op_RegF, 20, VMRegImpl::Bad());
- reg_def VSR20_J(SOC, SOC, Op_RegF, 20, VMRegImpl::Bad());
- reg_def VSR20_K(SOC, SOC, Op_RegF, 20, VMRegImpl::Bad());
-
- reg_def VSR21 (SOC, SOC, Op_RegF, 21, VMRegImpl::Bad());
- reg_def VSR21_H(SOC, SOC, Op_RegF, 21, VMRegImpl::Bad());
- reg_def VSR21_J(SOC, SOC, Op_RegF, 21, VMRegImpl::Bad());
- reg_def VSR21_K(SOC, SOC, Op_RegF, 21, VMRegImpl::Bad());
-
- reg_def VSR22 (SOC, SOC, Op_RegF, 22, VMRegImpl::Bad());
- reg_def VSR22_H(SOC, SOC, Op_RegF, 22, VMRegImpl::Bad());
- reg_def VSR22_J(SOC, SOC, Op_RegF, 22, VMRegImpl::Bad());
- reg_def VSR22_K(SOC, SOC, Op_RegF, 22, VMRegImpl::Bad());
-
- reg_def VSR23 (SOC, SOC, Op_RegF, 23, VMRegImpl::Bad());
- reg_def VSR23_H(SOC, SOC, Op_RegF, 23, VMRegImpl::Bad());
- reg_def VSR23_J(SOC, SOC, Op_RegF, 23, VMRegImpl::Bad());
- reg_def VSR23_K(SOC, SOC, Op_RegF, 23, VMRegImpl::Bad());
-
- reg_def VSR24 (SOC, SOC, Op_RegF, 24, VMRegImpl::Bad());
- reg_def VSR24_H(SOC, SOC, Op_RegF, 24, VMRegImpl::Bad());
- reg_def VSR24_J(SOC, SOC, Op_RegF, 24, VMRegImpl::Bad());
- reg_def VSR24_K(SOC, SOC, Op_RegF, 24, VMRegImpl::Bad());
-
- reg_def VSR25 (SOC, SOC, Op_RegF, 25, VMRegImpl::Bad());
- reg_def VSR25_H(SOC, SOC, Op_RegF, 25, VMRegImpl::Bad());
- reg_def VSR25_J(SOC, SOC, Op_RegF, 25, VMRegImpl::Bad());
- reg_def VSR25_K(SOC, SOC, Op_RegF, 25, VMRegImpl::Bad());
-
- reg_def VSR26 (SOC, SOC, Op_RegF, 26, VMRegImpl::Bad());
- reg_def VSR26_H(SOC, SOC, Op_RegF, 26, VMRegImpl::Bad());
- reg_def VSR26_J(SOC, SOC, Op_RegF, 26, VMRegImpl::Bad());
- reg_def VSR26_K(SOC, SOC, Op_RegF, 26, VMRegImpl::Bad());
-
- reg_def VSR27 (SOC, SOC, Op_RegF, 27, VMRegImpl::Bad());
- reg_def VSR27_H(SOC, SOC, Op_RegF, 27, VMRegImpl::Bad());
- reg_def VSR27_J(SOC, SOC, Op_RegF, 27, VMRegImpl::Bad());
- reg_def VSR27_K(SOC, SOC, Op_RegF, 27, VMRegImpl::Bad());
-
- reg_def VSR28 (SOC, SOC, Op_RegF, 28, VMRegImpl::Bad());
- reg_def VSR28_H(SOC, SOC, Op_RegF, 28, VMRegImpl::Bad());
- reg_def VSR28_J(SOC, SOC, Op_RegF, 28, VMRegImpl::Bad());
- reg_def VSR28_K(SOC, SOC, Op_RegF, 28, VMRegImpl::Bad());
-
- reg_def VSR29 (SOC, SOC, Op_RegF, 29, VMRegImpl::Bad());
- reg_def VSR29_H(SOC, SOC, Op_RegF, 29, VMRegImpl::Bad());
- reg_def VSR29_J(SOC, SOC, Op_RegF, 29, VMRegImpl::Bad());
- reg_def VSR29_K(SOC, SOC, Op_RegF, 29, VMRegImpl::Bad());
-
- reg_def VSR30 (SOC, SOC, Op_RegF, 30, VMRegImpl::Bad());
- reg_def VSR30_H(SOC, SOC, Op_RegF, 30, VMRegImpl::Bad());
- reg_def VSR30_J(SOC, SOC, Op_RegF, 30, VMRegImpl::Bad());
- reg_def VSR30_K(SOC, SOC, Op_RegF, 30, VMRegImpl::Bad());
-
- reg_def VSR31 (SOC, SOC, Op_RegF, 31, VMRegImpl::Bad());
- reg_def VSR31_H(SOC, SOC, Op_RegF, 31, VMRegImpl::Bad());
- reg_def VSR31_J(SOC, SOC, Op_RegF, 31, VMRegImpl::Bad());
- reg_def VSR31_K(SOC, SOC, Op_RegF, 31, VMRegImpl::Bad());
-
- // 2nd 32 VSRs are aliases for the VRs which are only defined here.
- reg_def VSR32 (SOC, SOC, Op_RegF, 32, VSR32->as_VMReg() );
- reg_def VSR32_H(SOC, SOC, Op_RegF, 32, VSR32->as_VMReg()->next() );
- reg_def VSR32_J(SOC, SOC, Op_RegF, 32, VSR32->as_VMReg()->next(2));
- reg_def VSR32_K(SOC, SOC, Op_RegF, 32, VSR32->as_VMReg()->next(3));
-
- reg_def VSR33 (SOC, SOC, Op_RegF, 33, VSR33->as_VMReg() );
- reg_def VSR33_H(SOC, SOC, Op_RegF, 33, VSR33->as_VMReg()->next() );
- reg_def VSR33_J(SOC, SOC, Op_RegF, 33, VSR33->as_VMReg()->next(2));
- reg_def VSR33_K(SOC, SOC, Op_RegF, 33, VSR33->as_VMReg()->next(3));
-
- reg_def VSR34 (SOC, SOC, Op_RegF, 34, VSR34->as_VMReg() );
- reg_def VSR34_H(SOC, SOC, Op_RegF, 34, VSR34->as_VMReg()->next() );
- reg_def VSR34_J(SOC, SOC, Op_RegF, 34, VSR34->as_VMReg()->next(2));
- reg_def VSR34_K(SOC, SOC, Op_RegF, 34, VSR34->as_VMReg()->next(3));
-
- reg_def VSR35 (SOC, SOC, Op_RegF, 35, VSR35->as_VMReg() );
- reg_def VSR35_H(SOC, SOC, Op_RegF, 35, VSR35->as_VMReg()->next() );
- reg_def VSR35_J(SOC, SOC, Op_RegF, 35, VSR35->as_VMReg()->next(2));
- reg_def VSR35_K(SOC, SOC, Op_RegF, 35, VSR35->as_VMReg()->next(3));
-
- reg_def VSR36 (SOC, SOC, Op_RegF, 36, VSR36->as_VMReg() );
- reg_def VSR36_H(SOC, SOC, Op_RegF, 36, VSR36->as_VMReg()->next() );
- reg_def VSR36_J(SOC, SOC, Op_RegF, 36, VSR36->as_VMReg()->next(2));
- reg_def VSR36_K(SOC, SOC, Op_RegF, 36, VSR36->as_VMReg()->next(3));
-
- reg_def VSR37 (SOC, SOC, Op_RegF, 37, VSR37->as_VMReg() );
- reg_def VSR37_H(SOC, SOC, Op_RegF, 37, VSR37->as_VMReg()->next() );
- reg_def VSR37_J(SOC, SOC, Op_RegF, 37, VSR37->as_VMReg()->next(2));
- reg_def VSR37_K(SOC, SOC, Op_RegF, 37, VSR37->as_VMReg()->next(3));
-
- reg_def VSR38 (SOC, SOC, Op_RegF, 38, VSR38->as_VMReg() );
- reg_def VSR38_H(SOC, SOC, Op_RegF, 38, VSR38->as_VMReg()->next() );
- reg_def VSR38_J(SOC, SOC, Op_RegF, 38, VSR38->as_VMReg()->next(2));
- reg_def VSR38_K(SOC, SOC, Op_RegF, 38, VSR38->as_VMReg()->next(3));
-
- reg_def VSR39 (SOC, SOC, Op_RegF, 39, VSR39->as_VMReg() );
- reg_def VSR39_H(SOC, SOC, Op_RegF, 39, VSR39->as_VMReg()->next() );
- reg_def VSR39_J(SOC, SOC, Op_RegF, 39, VSR39->as_VMReg()->next(2));
- reg_def VSR39_K(SOC, SOC, Op_RegF, 39, VSR39->as_VMReg()->next(3));
-
- reg_def VSR40 (SOC, SOC, Op_RegF, 40, VSR40->as_VMReg() );
- reg_def VSR40_H(SOC, SOC, Op_RegF, 40, VSR40->as_VMReg()->next() );
- reg_def VSR40_J(SOC, SOC, Op_RegF, 40, VSR40->as_VMReg()->next(2));
- reg_def VSR40_K(SOC, SOC, Op_RegF, 40, VSR40->as_VMReg()->next(3));
-
- reg_def VSR41 (SOC, SOC, Op_RegF, 41, VSR41->as_VMReg() );
- reg_def VSR41_H(SOC, SOC, Op_RegF, 41, VSR41->as_VMReg()->next() );
- reg_def VSR41_J(SOC, SOC, Op_RegF, 41, VSR41->as_VMReg()->next(2));
- reg_def VSR41_K(SOC, SOC, Op_RegF, 41, VSR41->as_VMReg()->next(3));
-
- reg_def VSR42 (SOC, SOC, Op_RegF, 42, VSR42->as_VMReg() );
- reg_def VSR42_H(SOC, SOC, Op_RegF, 42, VSR42->as_VMReg()->next() );
- reg_def VSR42_J(SOC, SOC, Op_RegF, 42, VSR42->as_VMReg()->next(2));
- reg_def VSR42_K(SOC, SOC, Op_RegF, 42, VSR42->as_VMReg()->next(3));
-
- reg_def VSR43 (SOC, SOC, Op_RegF, 43, VSR43->as_VMReg() );
- reg_def VSR43_H(SOC, SOC, Op_RegF, 43, VSR43->as_VMReg()->next() );
- reg_def VSR43_J(SOC, SOC, Op_RegF, 43, VSR43->as_VMReg()->next(2));
- reg_def VSR43_K(SOC, SOC, Op_RegF, 43, VSR43->as_VMReg()->next(3));
-
- reg_def VSR44 (SOC, SOC, Op_RegF, 44, VSR44->as_VMReg() );
- reg_def VSR44_H(SOC, SOC, Op_RegF, 44, VSR44->as_VMReg()->next() );
- reg_def VSR44_J(SOC, SOC, Op_RegF, 44, VSR44->as_VMReg()->next(2));
- reg_def VSR44_K(SOC, SOC, Op_RegF, 44, VSR44->as_VMReg()->next(3));
-
- reg_def VSR45 (SOC, SOC, Op_RegF, 45, VSR45->as_VMReg() );
- reg_def VSR45_H(SOC, SOC, Op_RegF, 45, VSR45->as_VMReg()->next() );
- reg_def VSR45_J(SOC, SOC, Op_RegF, 45, VSR45->as_VMReg()->next(2));
- reg_def VSR45_K(SOC, SOC, Op_RegF, 45, VSR45->as_VMReg()->next(3));
-
- reg_def VSR46 (SOC, SOC, Op_RegF, 46, VSR46->as_VMReg() );
- reg_def VSR46_H(SOC, SOC, Op_RegF, 46, VSR46->as_VMReg()->next() );
- reg_def VSR46_J(SOC, SOC, Op_RegF, 46, VSR46->as_VMReg()->next(2));
- reg_def VSR46_K(SOC, SOC, Op_RegF, 46, VSR46->as_VMReg()->next(3));
-
- reg_def VSR47 (SOC, SOC, Op_RegF, 47, VSR47->as_VMReg() );
- reg_def VSR47_H(SOC, SOC, Op_RegF, 47, VSR47->as_VMReg()->next() );
- reg_def VSR47_J(SOC, SOC, Op_RegF, 47, VSR47->as_VMReg()->next(2));
- reg_def VSR47_K(SOC, SOC, Op_RegF, 47, VSR47->as_VMReg()->next(3));
-
- reg_def VSR48 (SOC, SOC, Op_RegF, 48, VSR48->as_VMReg() );
- reg_def VSR48_H(SOC, SOC, Op_RegF, 48, VSR48->as_VMReg()->next() );
- reg_def VSR48_J(SOC, SOC, Op_RegF, 48, VSR48->as_VMReg()->next(2));
- reg_def VSR48_K(SOC, SOC, Op_RegF, 48, VSR48->as_VMReg()->next(3));
-
- reg_def VSR49 (SOC, SOC, Op_RegF, 49, VSR49->as_VMReg() );
- reg_def VSR49_H(SOC, SOC, Op_RegF, 49, VSR49->as_VMReg()->next() );
- reg_def VSR49_J(SOC, SOC, Op_RegF, 49, VSR49->as_VMReg()->next(2));
- reg_def VSR49_K(SOC, SOC, Op_RegF, 49, VSR49->as_VMReg()->next(3));
-
- reg_def VSR50 (SOC, SOC, Op_RegF, 50, VSR50->as_VMReg() );
- reg_def VSR50_H(SOC, SOC, Op_RegF, 50, VSR50->as_VMReg()->next() );
- reg_def VSR50_J(SOC, SOC, Op_RegF, 50, VSR50->as_VMReg()->next(2));
- reg_def VSR50_K(SOC, SOC, Op_RegF, 50, VSR50->as_VMReg()->next(3));
-
- reg_def VSR51 (SOC, SOC, Op_RegF, 51, VSR51->as_VMReg() );
- reg_def VSR51_H(SOC, SOC, Op_RegF, 51, VSR51->as_VMReg()->next() );
- reg_def VSR51_J(SOC, SOC, Op_RegF, 51, VSR51->as_VMReg()->next(2));
- reg_def VSR51_K(SOC, SOC, Op_RegF, 51, VSR51->as_VMReg()->next(3));
-
- reg_def VSR52 (SOC, SOE, Op_RegF, 52, VSR52->as_VMReg() );
- reg_def VSR52_H(SOC, SOE, Op_RegF, 52, VSR52->as_VMReg()->next() );
- reg_def VSR52_J(SOC, SOE, Op_RegF, 52, VSR52->as_VMReg()->next(2));
- reg_def VSR52_K(SOC, SOE, Op_RegF, 52, VSR52->as_VMReg()->next(3));
-
- reg_def VSR53 (SOC, SOE, Op_RegF, 53, VSR53->as_VMReg() );
- reg_def VSR53_H(SOC, SOE, Op_RegF, 53, VSR53->as_VMReg()->next() );
- reg_def VSR53_J(SOC, SOE, Op_RegF, 53, VSR53->as_VMReg()->next(2));
- reg_def VSR53_K(SOC, SOE, Op_RegF, 53, VSR53->as_VMReg()->next(3));
-
- reg_def VSR54 (SOC, SOE, Op_RegF, 54, VSR54->as_VMReg() );
- reg_def VSR54_H(SOC, SOE, Op_RegF, 54, VSR54->as_VMReg()->next() );
- reg_def VSR54_J(SOC, SOE, Op_RegF, 54, VSR54->as_VMReg()->next(2));
- reg_def VSR54_K(SOC, SOE, Op_RegF, 54, VSR54->as_VMReg()->next(3));
-
- reg_def VSR55 (SOC, SOE, Op_RegF, 55, VSR55->as_VMReg() );
- reg_def VSR55_H(SOC, SOE, Op_RegF, 55, VSR55->as_VMReg()->next() );
- reg_def VSR55_J(SOC, SOE, Op_RegF, 55, VSR55->as_VMReg()->next(2));
- reg_def VSR55_K(SOC, SOE, Op_RegF, 55, VSR55->as_VMReg()->next(3));
-
- reg_def VSR56 (SOC, SOE, Op_RegF, 56, VSR56->as_VMReg() );
- reg_def VSR56_H(SOC, SOE, Op_RegF, 56, VSR56->as_VMReg()->next() );
- reg_def VSR56_J(SOC, SOE, Op_RegF, 56, VSR56->as_VMReg()->next(2));
- reg_def VSR56_K(SOC, SOE, Op_RegF, 56, VSR56->as_VMReg()->next(3));
-
- reg_def VSR57 (SOC, SOE, Op_RegF, 57, VSR57->as_VMReg() );
- reg_def VSR57_H(SOC, SOE, Op_RegF, 57, VSR57->as_VMReg()->next() );
- reg_def VSR57_J(SOC, SOE, Op_RegF, 57, VSR57->as_VMReg()->next(2));
- reg_def VSR57_K(SOC, SOE, Op_RegF, 57, VSR57->as_VMReg()->next(3));
-
- reg_def VSR58 (SOC, SOE, Op_RegF, 58, VSR58->as_VMReg() );
- reg_def VSR58_H(SOC, SOE, Op_RegF, 58, VSR58->as_VMReg()->next() );
- reg_def VSR58_J(SOC, SOE, Op_RegF, 58, VSR58->as_VMReg()->next(2));
- reg_def VSR58_K(SOC, SOE, Op_RegF, 58, VSR58->as_VMReg()->next(3));
-
- reg_def VSR59 (SOC, SOE, Op_RegF, 59, VSR59->as_VMReg() );
- reg_def VSR59_H(SOC, SOE, Op_RegF, 59, VSR59->as_VMReg()->next() );
- reg_def VSR59_J(SOC, SOE, Op_RegF, 59, VSR59->as_VMReg()->next(2));
- reg_def VSR59_K(SOC, SOE, Op_RegF, 59, VSR59->as_VMReg()->next(3));
-
- reg_def VSR60 (SOC, SOE, Op_RegF, 60, VSR60->as_VMReg() );
- reg_def VSR60_H(SOC, SOE, Op_RegF, 60, VSR60->as_VMReg()->next() );
- reg_def VSR60_J(SOC, SOE, Op_RegF, 60, VSR60->as_VMReg()->next(2));
- reg_def VSR60_K(SOC, SOE, Op_RegF, 60, VSR60->as_VMReg()->next(3));
-
- reg_def VSR61 (SOC, SOE, Op_RegF, 61, VSR61->as_VMReg() );
- reg_def VSR61_H(SOC, SOE, Op_RegF, 61, VSR61->as_VMReg()->next() );
- reg_def VSR61_J(SOC, SOE, Op_RegF, 61, VSR61->as_VMReg()->next(2));
- reg_def VSR61_K(SOC, SOE, Op_RegF, 61, VSR61->as_VMReg()->next(3));
-
- reg_def VSR62 (SOC, SOE, Op_RegF, 62, VSR62->as_VMReg() );
- reg_def VSR62_H(SOC, SOE, Op_RegF, 62, VSR62->as_VMReg()->next() );
- reg_def VSR62_J(SOC, SOE, Op_RegF, 62, VSR62->as_VMReg()->next(2));
- reg_def VSR62_K(SOC, SOE, Op_RegF, 62, VSR62->as_VMReg()->next(3));
-
- reg_def VSR63 (SOC, SOE, Op_RegF, 63, VSR63->as_VMReg() );
- reg_def VSR63_H(SOC, SOE, Op_RegF, 63, VSR63->as_VMReg()->next() );
- reg_def VSR63_J(SOC, SOE, Op_RegF, 63, VSR63->as_VMReg()->next(2));
- reg_def VSR63_K(SOC, SOE, Op_RegF, 63, VSR63->as_VMReg()->next(3));
+
+ reg_def VR0 (SOC, SOC, Op_RegF, 0, VR0->as_VMReg() );
+ reg_def VR0_H(SOC, SOC, Op_RegF, 0, VR0->as_VMReg()->next() );
+ reg_def VR0_J(SOC, SOC, Op_RegF, 0, VR0->as_VMReg()->next(2));
+ reg_def VR0_K(SOC, SOC, Op_RegF, 0, VR0->as_VMReg()->next(3));
+
+ reg_def VR1 (SOC, SOC, Op_RegF, 1, VR1->as_VMReg() );
+ reg_def VR1_H(SOC, SOC, Op_RegF, 1, VR1->as_VMReg()->next() );
+ reg_def VR1_J(SOC, SOC, Op_RegF, 1, VR1->as_VMReg()->next(2));
+ reg_def VR1_K(SOC, SOC, Op_RegF, 1, VR1->as_VMReg()->next(3));
+
+ reg_def VR2 (SOC, SOC, Op_RegF, 2, VR2->as_VMReg() );
+ reg_def VR2_H(SOC, SOC, Op_RegF, 2, VR2->as_VMReg()->next() );
+ reg_def VR2_J(SOC, SOC, Op_RegF, 2, VR2->as_VMReg()->next(2));
+ reg_def VR2_K(SOC, SOC, Op_RegF, 2, VR2->as_VMReg()->next(3));
+
+ reg_def VR3 (SOC, SOC, Op_RegF, 3, VR3->as_VMReg() );
+ reg_def VR3_H(SOC, SOC, Op_RegF, 3, VR3->as_VMReg()->next() );
+ reg_def VR3_J(SOC, SOC, Op_RegF, 3, VR3->as_VMReg()->next(2));
+ reg_def VR3_K(SOC, SOC, Op_RegF, 3, VR3->as_VMReg()->next(3));
+
+ reg_def VR4 (SOC, SOC, Op_RegF, 4, VR4->as_VMReg() );
+ reg_def VR4_H(SOC, SOC, Op_RegF, 4, VR4->as_VMReg()->next() );
+ reg_def VR4_J(SOC, SOC, Op_RegF, 4, VR4->as_VMReg()->next(2));
+ reg_def VR4_K(SOC, SOC, Op_RegF, 4, VR4->as_VMReg()->next(3));
+
+ reg_def VR5 (SOC, SOC, Op_RegF, 5, VR5->as_VMReg() );
+ reg_def VR5_H(SOC, SOC, Op_RegF, 5, VR5->as_VMReg()->next() );
+ reg_def VR5_J(SOC, SOC, Op_RegF, 5, VR5->as_VMReg()->next(2));
+ reg_def VR5_K(SOC, SOC, Op_RegF, 5, VR5->as_VMReg()->next(3));
+
+ reg_def VR6 (SOC, SOC, Op_RegF, 6, VR6->as_VMReg() );
+ reg_def VR6_H(SOC, SOC, Op_RegF, 6, VR6->as_VMReg()->next() );
+ reg_def VR6_J(SOC, SOC, Op_RegF, 6, VR6->as_VMReg()->next(2));
+ reg_def VR6_K(SOC, SOC, Op_RegF, 6, VR6->as_VMReg()->next(3));
+
+ reg_def VR7 (SOC, SOC, Op_RegF, 7, VR7->as_VMReg() );
+ reg_def VR7_H(SOC, SOC, Op_RegF, 7, VR7->as_VMReg()->next() );
+ reg_def VR7_J(SOC, SOC, Op_RegF, 7, VR7->as_VMReg()->next(2));
+ reg_def VR7_K(SOC, SOC, Op_RegF, 7, VR7->as_VMReg()->next(3));
+
+ reg_def VR8 (SOC, SOC, Op_RegF, 8, VR8->as_VMReg() );
+ reg_def VR8_H(SOC, SOC, Op_RegF, 8, VR8->as_VMReg()->next() );
+ reg_def VR8_J(SOC, SOC, Op_RegF, 8, VR8->as_VMReg()->next(2));
+ reg_def VR8_K(SOC, SOC, Op_RegF, 8, VR8->as_VMReg()->next(3));
+
+ reg_def VR9 (SOC, SOC, Op_RegF, 9, VR9->as_VMReg() );
+ reg_def VR9_H(SOC, SOC, Op_RegF, 9, VR9->as_VMReg()->next() );
+ reg_def VR9_J(SOC, SOC, Op_RegF, 9, VR9->as_VMReg()->next(2));
+ reg_def VR9_K(SOC, SOC, Op_RegF, 9, VR9->as_VMReg()->next(3));
+
+ reg_def VR10 (SOC, SOC, Op_RegF, 10, VR10->as_VMReg() );
+ reg_def VR10_H(SOC, SOC, Op_RegF, 10, VR10->as_VMReg()->next() );
+ reg_def VR10_J(SOC, SOC, Op_RegF, 10, VR10->as_VMReg()->next(2));
+ reg_def VR10_K(SOC, SOC, Op_RegF, 10, VR10->as_VMReg()->next(3));
+
+ reg_def VR11 (SOC, SOC, Op_RegF, 11, VR11->as_VMReg() );
+ reg_def VR11_H(SOC, SOC, Op_RegF, 11, VR11->as_VMReg()->next() );
+ reg_def VR11_J(SOC, SOC, Op_RegF, 11, VR11->as_VMReg()->next(2));
+ reg_def VR11_K(SOC, SOC, Op_RegF, 11, VR11->as_VMReg()->next(3));
+
+ reg_def VR12 (SOC, SOC, Op_RegF, 12, VR12->as_VMReg() );
+ reg_def VR12_H(SOC, SOC, Op_RegF, 12, VR12->as_VMReg()->next() );
+ reg_def VR12_J(SOC, SOC, Op_RegF, 12, VR12->as_VMReg()->next(2));
+ reg_def VR12_K(SOC, SOC, Op_RegF, 12, VR12->as_VMReg()->next(3));
+
+ reg_def VR13 (SOC, SOC, Op_RegF, 13, VR13->as_VMReg() );
+ reg_def VR13_H(SOC, SOC, Op_RegF, 13, VR13->as_VMReg()->next() );
+ reg_def VR13_J(SOC, SOC, Op_RegF, 13, VR13->as_VMReg()->next(2));
+ reg_def VR13_K(SOC, SOC, Op_RegF, 13, VR13->as_VMReg()->next(3));
+
+ reg_def VR14 (SOC, SOC, Op_RegF, 14, VR14->as_VMReg() );
+ reg_def VR14_H(SOC, SOC, Op_RegF, 14, VR14->as_VMReg()->next() );
+ reg_def VR14_J(SOC, SOC, Op_RegF, 14, VR14->as_VMReg()->next(2));
+ reg_def VR14_K(SOC, SOC, Op_RegF, 14, VR14->as_VMReg()->next(3));
+
+ reg_def VR15 (SOC, SOC, Op_RegF, 15, VR15->as_VMReg() );
+ reg_def VR15_H(SOC, SOC, Op_RegF, 15, VR15->as_VMReg()->next() );
+ reg_def VR15_J(SOC, SOC, Op_RegF, 15, VR15->as_VMReg()->next(2));
+ reg_def VR15_K(SOC, SOC, Op_RegF, 15, VR15->as_VMReg()->next(3));
+
+ reg_def VR16 (SOC, SOC, Op_RegF, 16, VR16->as_VMReg() );
+ reg_def VR16_H(SOC, SOC, Op_RegF, 16, VR16->as_VMReg()->next() );
+ reg_def VR16_J(SOC, SOC, Op_RegF, 16, VR16->as_VMReg()->next(2));
+ reg_def VR16_K(SOC, SOC, Op_RegF, 16, VR16->as_VMReg()->next(3));
+
+ reg_def VR17 (SOC, SOC, Op_RegF, 17, VR17->as_VMReg() );
+ reg_def VR17_H(SOC, SOC, Op_RegF, 17, VR17->as_VMReg()->next() );
+ reg_def VR17_J(SOC, SOC, Op_RegF, 17, VR17->as_VMReg()->next(2));
+ reg_def VR17_K(SOC, SOC, Op_RegF, 17, VR17->as_VMReg()->next(3));
+
+ reg_def VR18 (SOC, SOC, Op_RegF, 18, VR18->as_VMReg() );
+ reg_def VR18_H(SOC, SOC, Op_RegF, 18, VR18->as_VMReg()->next() );
+ reg_def VR18_J(SOC, SOC, Op_RegF, 18, VR18->as_VMReg()->next(2));
+ reg_def VR18_K(SOC, SOC, Op_RegF, 18, VR18->as_VMReg()->next(3));
+
+ reg_def VR19 (SOC, SOC, Op_RegF, 19, VR19->as_VMReg() );
+ reg_def VR19_H(SOC, SOC, Op_RegF, 19, VR19->as_VMReg()->next() );
+ reg_def VR19_J(SOC, SOC, Op_RegF, 19, VR19->as_VMReg()->next(2));
+ reg_def VR19_K(SOC, SOC, Op_RegF, 19, VR19->as_VMReg()->next(3));
+
+ reg_def VR20 (SOC, SOE, Op_RegF, 20, VR20->as_VMReg() );
+ reg_def VR20_H(SOC, SOE, Op_RegF, 20, VR20->as_VMReg()->next() );
+ reg_def VR20_J(SOC, SOE, Op_RegF, 20, VR20->as_VMReg()->next(2));
+ reg_def VR20_K(SOC, SOE, Op_RegF, 20, VR20->as_VMReg()->next(3));
+
+ reg_def VR21 (SOC, SOE, Op_RegF, 21, VR21->as_VMReg() );
+ reg_def VR21_H(SOC, SOE, Op_RegF, 21, VR21->as_VMReg()->next() );
+ reg_def VR21_J(SOC, SOE, Op_RegF, 21, VR21->as_VMReg()->next(2));
+ reg_def VR21_K(SOC, SOE, Op_RegF, 21, VR21->as_VMReg()->next(3));
+
+ reg_def VR22 (SOC, SOE, Op_RegF, 22, VR22->as_VMReg() );
+ reg_def VR22_H(SOC, SOE, Op_RegF, 22, VR22->as_VMReg()->next() );
+ reg_def VR22_J(SOC, SOE, Op_RegF, 22, VR22->as_VMReg()->next(2));
+ reg_def VR22_K(SOC, SOE, Op_RegF, 22, VR22->as_VMReg()->next(3));
+
+ reg_def VR23 (SOC, SOE, Op_RegF, 23, VR23->as_VMReg() );
+ reg_def VR23_H(SOC, SOE, Op_RegF, 23, VR23->as_VMReg()->next() );
+ reg_def VR23_J(SOC, SOE, Op_RegF, 23, VR23->as_VMReg()->next(2));
+ reg_def VR23_K(SOC, SOE, Op_RegF, 23, VR23->as_VMReg()->next(3));
+
+ reg_def VR24 (SOC, SOE, Op_RegF, 24, VR24->as_VMReg() );
+ reg_def VR24_H(SOC, SOE, Op_RegF, 24, VR24->as_VMReg()->next() );
+ reg_def VR24_J(SOC, SOE, Op_RegF, 24, VR24->as_VMReg()->next(2));
+ reg_def VR24_K(SOC, SOE, Op_RegF, 24, VR24->as_VMReg()->next(3));
+
+ reg_def VR25 (SOC, SOE, Op_RegF, 25, VR25->as_VMReg() );
+ reg_def VR25_H(SOC, SOE, Op_RegF, 25, VR25->as_VMReg()->next() );
+ reg_def VR25_J(SOC, SOE, Op_RegF, 25, VR25->as_VMReg()->next(2));
+ reg_def VR25_K(SOC, SOE, Op_RegF, 25, VR25->as_VMReg()->next(3));
+
+ reg_def VR26 (SOC, SOE, Op_RegF, 26, VR26->as_VMReg() );
+ reg_def VR26_H(SOC, SOE, Op_RegF, 26, VR26->as_VMReg()->next() );
+ reg_def VR26_J(SOC, SOE, Op_RegF, 26, VR26->as_VMReg()->next(2));
+ reg_def VR26_K(SOC, SOE, Op_RegF, 26, VR26->as_VMReg()->next(3));
+
+ reg_def VR27 (SOC, SOE, Op_RegF, 27, VR27->as_VMReg() );
+ reg_def VR27_H(SOC, SOE, Op_RegF, 27, VR27->as_VMReg()->next() );
+ reg_def VR27_J(SOC, SOE, Op_RegF, 27, VR27->as_VMReg()->next(2));
+ reg_def VR27_K(SOC, SOE, Op_RegF, 27, VR27->as_VMReg()->next(3));
+
+ reg_def VR28 (SOC, SOE, Op_RegF, 28, VR28->as_VMReg() );
+ reg_def VR28_H(SOC, SOE, Op_RegF, 28, VR28->as_VMReg()->next() );
+ reg_def VR28_J(SOC, SOE, Op_RegF, 28, VR28->as_VMReg()->next(2));
+ reg_def VR28_K(SOC, SOE, Op_RegF, 28, VR28->as_VMReg()->next(3));
+
+ reg_def VR29 (SOC, SOE, Op_RegF, 29, VR29->as_VMReg() );
+ reg_def VR29_H(SOC, SOE, Op_RegF, 29, VR29->as_VMReg()->next() );
+ reg_def VR29_J(SOC, SOE, Op_RegF, 29, VR29->as_VMReg()->next(2));
+ reg_def VR29_K(SOC, SOE, Op_RegF, 29, VR29->as_VMReg()->next(3));
+
+ reg_def VR30 (SOC, SOE, Op_RegF, 30, VR30->as_VMReg() );
+ reg_def VR30_H(SOC, SOE, Op_RegF, 30, VR30->as_VMReg()->next() );
+ reg_def VR30_J(SOC, SOE, Op_RegF, 30, VR30->as_VMReg()->next(2));
+ reg_def VR30_K(SOC, SOE, Op_RegF, 30, VR30->as_VMReg()->next(3));
+
+ reg_def VR31 (SOC, SOE, Op_RegF, 31, VR31->as_VMReg() );
+ reg_def VR31_H(SOC, SOE, Op_RegF, 31, VR31->as_VMReg()->next() );
+ reg_def VR31_J(SOC, SOE, Op_RegF, 31, VR31->as_VMReg()->next(2));
+ reg_def VR31_K(SOC, SOE, Op_RegF, 31, VR31->as_VMReg()->next(3));
// ----------------------------
// Specify priority of register selection within phases of register
@@ -696,70 +535,38 @@ alloc_class chunk1 (
);
alloc_class chunk2 (
- VSR0 , VSR0_H , VSR0_J , VSR0_K ,
- VSR1 , VSR1_H , VSR1_J , VSR1_K ,
- VSR2 , VSR2_H , VSR2_J , VSR2_K ,
- VSR3 , VSR3_H , VSR3_J , VSR3_K ,
- VSR4 , VSR4_H , VSR4_J , VSR4_K ,
- VSR5 , VSR5_H , VSR5_J , VSR5_K ,
- VSR6 , VSR6_H , VSR6_J , VSR6_K ,
- VSR7 , VSR7_H , VSR7_J , VSR7_K ,
- VSR8 , VSR8_H , VSR8_J , VSR8_K ,
- VSR9 , VSR9_H , VSR9_J , VSR9_K ,
- VSR10, VSR10_H, VSR10_J, VSR10_K,
- VSR11, VSR11_H, VSR11_J, VSR11_K,
- VSR12, VSR12_H, VSR12_J, VSR12_K,
- VSR13, VSR13_H, VSR13_J, VSR13_K,
- VSR14, VSR14_H, VSR14_J, VSR14_K,
- VSR15, VSR15_H, VSR15_J, VSR15_K,
- VSR16, VSR16_H, VSR16_J, VSR16_K,
- VSR17, VSR17_H, VSR17_J, VSR17_K,
- VSR18, VSR18_H, VSR18_J, VSR18_K,
- VSR19, VSR19_H, VSR19_J, VSR19_K,
- VSR20, VSR20_H, VSR20_J, VSR20_K,
- VSR21, VSR21_H, VSR21_J, VSR21_K,
- VSR22, VSR22_H, VSR22_J, VSR22_K,
- VSR23, VSR23_H, VSR23_J, VSR23_K,
- VSR24, VSR24_H, VSR24_J, VSR24_K,
- VSR25, VSR25_H, VSR25_J, VSR25_K,
- VSR26, VSR26_H, VSR26_J, VSR26_K,
- VSR27, VSR27_H, VSR27_J, VSR27_K,
- VSR28, VSR28_H, VSR28_J, VSR28_K,
- VSR29, VSR29_H, VSR29_J, VSR29_K,
- VSR30, VSR30_H, VSR30_J, VSR30_K,
- VSR31, VSR31_H, VSR31_J, VSR31_K,
- VSR32, VSR32_H, VSR32_J, VSR32_K,
- VSR33, VSR33_H, VSR33_J, VSR33_K,
- VSR34, VSR34_H, VSR34_J, VSR34_K,
- VSR35, VSR35_H, VSR35_J, VSR35_K,
- VSR36, VSR36_H, VSR36_J, VSR36_K,
- VSR37, VSR37_H, VSR37_J, VSR37_K,
- VSR38, VSR38_H, VSR38_J, VSR38_K,
- VSR39, VSR39_H, VSR39_J, VSR39_K,
- VSR40, VSR40_H, VSR40_J, VSR40_K,
- VSR41, VSR41_H, VSR41_J, VSR41_K,
- VSR42, VSR42_H, VSR42_J, VSR42_K,
- VSR43, VSR43_H, VSR43_J, VSR43_K,
- VSR44, VSR44_H, VSR44_J, VSR44_K,
- VSR45, VSR45_H, VSR45_J, VSR45_K,
- VSR46, VSR46_H, VSR46_J, VSR46_K,
- VSR47, VSR47_H, VSR47_J, VSR47_K,
- VSR48, VSR48_H, VSR48_J, VSR48_K,
- VSR49, VSR49_H, VSR49_J, VSR49_K,
- VSR50, VSR50_H, VSR50_J, VSR50_K,
- VSR51, VSR51_H, VSR51_J, VSR51_K,
- VSR52, VSR52_H, VSR52_J, VSR52_K,
- VSR53, VSR53_H, VSR53_J, VSR53_K,
- VSR54, VSR54_H, VSR54_J, VSR54_K,
- VSR55, VSR55_H, VSR55_J, VSR55_K,
- VSR56, VSR56_H, VSR56_J, VSR56_K,
- VSR57, VSR57_H, VSR57_J, VSR57_K,
- VSR58, VSR58_H, VSR58_J, VSR58_K,
- VSR59, VSR59_H, VSR59_J, VSR59_K,
- VSR60, VSR60_H, VSR60_J, VSR60_K,
- VSR61, VSR61_H, VSR61_J, VSR61_K,
- VSR62, VSR62_H, VSR62_J, VSR62_K,
- VSR63, VSR63_H, VSR63_J, VSR63_K
+ VR0 , VR0_H , VR0_J , VR0_K ,
+ VR1 , VR1_H , VR1_J , VR1_K ,
+ VR2 , VR2_H , VR2_J , VR2_K ,
+ VR3 , VR3_H , VR3_J , VR3_K ,
+ VR4 , VR4_H , VR4_J , VR4_K ,
+ VR5 , VR5_H , VR5_J , VR5_K ,
+ VR6 , VR6_H , VR6_J , VR6_K ,
+ VR7 , VR7_H , VR7_J , VR7_K ,
+ VR8 , VR8_H , VR8_J , VR8_K ,
+ VR9 , VR9_H , VR9_J , VR9_K ,
+ VR10, VR10_H, VR10_J, VR10_K,
+ VR11, VR11_H, VR11_J, VR11_K,
+ VR12, VR12_H, VR12_J, VR12_K,
+ VR13, VR13_H, VR13_J, VR13_K,
+ VR14, VR14_H, VR14_J, VR14_K,
+ VR15, VR15_H, VR15_J, VR15_K,
+ VR16, VR16_H, VR16_J, VR16_K,
+ VR17, VR17_H, VR17_J, VR17_K,
+ VR18, VR18_H, VR18_J, VR18_K,
+ VR19, VR19_H, VR19_J, VR19_K,
+ VR20, VR20_H, VR20_J, VR20_K,
+ VR21, VR21_H, VR21_J, VR21_K,
+ VR22, VR22_H, VR22_J, VR22_K,
+ VR23, VR23_H, VR23_J, VR23_K,
+ VR24, VR24_H, VR24_J, VR24_K,
+ VR25, VR25_H, VR25_J, VR25_K,
+ VR26, VR26_H, VR26_J, VR26_K,
+ VR27, VR27_H, VR27_J, VR27_K,
+ VR28, VR28_H, VR28_J, VR28_K,
+ VR29, VR29_H, VR29_J, VR29_K,
+ VR30, VR30_H, VR30_J, VR30_K,
+ VR31, VR31_H, VR31_J, VR31_K
);
alloc_class chunk3 (
@@ -1163,39 +970,39 @@ reg_class dbl_reg(
// Vector-Scalar Register Class
// ----------------------------
-reg_class vs_reg(
- VSR32, VSR32_H, VSR32_J, VSR32_K,
- VSR33, VSR33_H, VSR33_J, VSR33_K,
- VSR34, VSR34_H, VSR34_J, VSR34_K,
- VSR35, VSR35_H, VSR35_J, VSR35_K,
- VSR36, VSR36_H, VSR36_J, VSR36_K,
- VSR37, VSR37_H, VSR37_J, VSR37_K,
- VSR38, VSR38_H, VSR38_J, VSR38_K,
- VSR39, VSR39_H, VSR39_J, VSR39_K,
- VSR40, VSR40_H, VSR40_J, VSR40_K,
- VSR41, VSR41_H, VSR41_J, VSR41_K,
- VSR42, VSR42_H, VSR42_J, VSR42_K,
- VSR43, VSR43_H, VSR43_J, VSR43_K,
- VSR44, VSR44_H, VSR44_J, VSR44_K,
- VSR45, VSR45_H, VSR45_J, VSR45_K,
- VSR46, VSR46_H, VSR46_J, VSR46_K,
- VSR47, VSR47_H, VSR47_J, VSR47_K,
- VSR48, VSR48_H, VSR48_J, VSR48_K,
- VSR49, VSR49_H, VSR49_J, VSR49_K,
- VSR50, VSR50_H, VSR50_J, VSR50_K,
- VSR51, VSR51_H, VSR51_J, VSR51_K,
- VSR52, VSR52_H, VSR52_J, VSR52_K, // non-volatile
- VSR53, VSR53_H, VSR53_J, VSR53_K, // non-volatile
- VSR54, VSR54_H, VSR54_J, VSR54_K, // non-volatile
- VSR55, VSR55_H, VSR55_J, VSR55_K, // non-volatile
- VSR56, VSR56_H, VSR56_J, VSR56_K, // non-volatile
- VSR57, VSR57_H, VSR57_J, VSR57_K, // non-volatile
- VSR58, VSR58_H, VSR58_J, VSR58_K, // non-volatile
- VSR59, VSR59_H, VSR59_J, VSR59_K, // non-volatile
- VSR60, VSR60_H, VSR60_J, VSR60_K, // non-volatile
- VSR61, VSR61_H, VSR61_J, VSR61_K, // non-volatile
- VSR62, VSR62_H, VSR62_J, VSR62_K, // non-volatile
- VSR63, VSR63_H, VSR63_J, VSR63_K // non-volatile
+reg_class v_reg(
+ VR0 , VR0_H , VR0_J , VR0_K ,
+ VR1 , VR1_H , VR1_J , VR1_K ,
+ VR2 , VR2_H , VR2_J , VR2_K ,
+ VR3 , VR3_H , VR3_J , VR3_K ,
+ VR4 , VR4_H , VR4_J , VR4_K ,
+ VR5 , VR5_H , VR5_J , VR5_K ,
+ VR6 , VR6_H , VR6_J , VR6_K ,
+ VR7 , VR7_H , VR7_J , VR7_K ,
+ VR8 , VR8_H , VR8_J , VR8_K ,
+ VR9 , VR9_H , VR9_J , VR9_K ,
+ VR10, VR10_H, VR10_J, VR10_K,
+ VR11, VR11_H, VR11_J, VR11_K,
+ VR12, VR12_H, VR12_J, VR12_K,
+ VR13, VR13_H, VR13_J, VR13_K,
+ VR14, VR14_H, VR14_J, VR14_K,
+ VR15, VR15_H, VR15_J, VR15_K,
+ VR16, VR16_H, VR16_J, VR16_K,
+ VR17, VR17_H, VR17_J, VR17_K,
+ VR18, VR18_H, VR18_J, VR18_K,
+ VR19, VR19_H, VR19_J, VR19_K,
+ VR20, VR20_H, VR20_J, VR20_K,
+ VR21, VR21_H, VR21_J, VR21_K,
+ VR22, VR22_H, VR22_J, VR22_K,
+ VR23, VR23_H, VR23_J, VR23_K,
+ VR24, VR24_H, VR24_J, VR24_K,
+ VR25, VR25_H, VR25_J, VR25_K,
+ VR26, VR26_H, VR26_J, VR26_K,
+ VR27, VR27_H, VR27_J, VR27_K,
+ VR28, VR28_H, VR28_J, VR28_K,
+ VR29, VR29_H, VR29_J, VR29_K,
+ VR30, VR30_H, VR30_J, VR30_K,
+ VR31, VR31_H, VR31_J, VR31_K
);
%}
@@ -1380,7 +1187,7 @@ int MachCallDynamicJavaNode::ret_addr_offset() {
assert(vtable_index == Method::invalid_vtable_index, "correct sentinel value");
return 12;
} else {
- return 24 + MacroAssembler::instr_size_for_decode_klass_not_null();
+ return 20 + MacroAssembler::instr_size_for_load_klass();
}
}
@@ -1908,9 +1715,9 @@ const Pipeline * MachEpilogNode::pipeline() const {
// =============================================================================
-// Figure out which register class each belongs in: rc_int, rc_float, rc_vs or
+// Figure out which register class each belongs in: rc_int, rc_float, rc_vec or
// rc_stack.
-enum RC { rc_bad, rc_int, rc_float, rc_vs, rc_stack };
+enum RC { rc_bad, rc_int, rc_float, rc_vec, rc_stack };
static enum RC rc_class(OptoReg::Name reg) {
// Return the register class for the given register. The given register
@@ -1924,12 +1731,12 @@ static enum RC rc_class(OptoReg::Name reg) {
if (reg < ConcreteRegisterImpl::max_gpr) return rc_int;
// We have 64 floating-point register halves, starting at index 64.
- STATIC_ASSERT((int)ConcreteRegisterImpl::max_fpr == (int)MachRegisterNumbers::VSR0_num);
+ STATIC_ASSERT((int)ConcreteRegisterImpl::max_fpr == (int)MachRegisterNumbers::VR0_num);
if (reg < ConcreteRegisterImpl::max_fpr) return rc_float;
// We have 64 vector-scalar registers, starting at index 128.
- STATIC_ASSERT((int)ConcreteRegisterImpl::max_vsr == (int)MachRegisterNumbers::CR0_num);
- if (reg < ConcreteRegisterImpl::max_vsr) return rc_vs;
+ STATIC_ASSERT((int)ConcreteRegisterImpl::max_vr == (int)MachRegisterNumbers::CR0_num);
+ if (reg < ConcreteRegisterImpl::max_vr) return rc_vec;
// Condition and special purpose registers are not allocated. We only accept stack from here.
assert(OptoReg::is_stack(reg), "what else is it?");
@@ -2005,9 +1812,9 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
}
size += 16;
}
- // VectorSRegister->Memory Spill.
- else if (src_lo_rc == rc_vs && dst_lo_rc == rc_stack) {
- VectorSRegister Rsrc = as_VectorSRegister(Matcher::_regEncode[src_lo]);
+ // VectorRegister->Memory Spill.
+ else if (src_lo_rc == rc_vec && dst_lo_rc == rc_stack) {
+ VectorSRegister Rsrc = as_VectorRegister(Matcher::_regEncode[src_lo]).to_vsr();
int dst_offset = ra_->reg2offset(dst_lo);
if (PowerArchitecturePPC64 >= 9) {
if (is_aligned(dst_offset, 16)) {
@@ -2032,9 +1839,9 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
size += 8;
}
}
- // Memory->VectorSRegister Spill.
- else if (src_lo_rc == rc_stack && dst_lo_rc == rc_vs) {
- VectorSRegister Rdst = as_VectorSRegister(Matcher::_regEncode[dst_lo]);
+ // Memory->VectorRegister Spill.
+ else if (src_lo_rc == rc_stack && dst_lo_rc == rc_vec) {
+ VectorSRegister Rdst = as_VectorRegister(Matcher::_regEncode[dst_lo]).to_vsr();
int src_offset = ra_->reg2offset(src_lo);
if (PowerArchitecturePPC64 >= 9) {
if (is_aligned(src_offset, 16)) {
@@ -2057,17 +1864,17 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
size += 8;
}
}
- // VectorSRegister->VectorSRegister.
- else if (src_lo_rc == rc_vs && dst_lo_rc == rc_vs) {
- VectorSRegister Rsrc = as_VectorSRegister(Matcher::_regEncode[src_lo]);
- VectorSRegister Rdst = as_VectorSRegister(Matcher::_regEncode[dst_lo]);
+ // VectorRegister->VectorRegister.
+ else if (src_lo_rc == rc_vec && dst_lo_rc == rc_vec) {
+ VectorSRegister Rsrc = as_VectorRegister(Matcher::_regEncode[src_lo]).to_vsr();
+ VectorSRegister Rdst = as_VectorRegister(Matcher::_regEncode[dst_lo]).to_vsr();
if (masm) {
__ xxlor(Rdst, Rsrc, Rsrc);
}
size += 4;
}
else {
- ShouldNotReachHere(); // No VSR spill.
+ ShouldNotReachHere(); // No VR spill.
}
return size;
}
@@ -4044,7 +3851,7 @@ ins_attrib ins_field_load_ic_node(0);
// Formats are generated automatically for constants and base registers.
operand vecX() %{
- constraint(ALLOC_IN_RC(vs_reg));
+ constraint(ALLOC_IN_RC(v_reg));
match(VecX);
format %{ %}
@@ -5620,7 +5427,7 @@ instruct loadV16_Power8(vecX dst, indirect mem) %{
format %{ "LXVD2X $dst, $mem \t// load 16-byte Vector" %}
size(4);
ins_encode %{
- __ lxvd2x($dst$$VectorSRegister, $mem$$Register);
+ __ lxvd2x($dst$$VectorRegister.to_vsr(), $mem$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -5633,7 +5440,7 @@ instruct loadV16_Power9(vecX dst, memoryAlg16 mem) %{
format %{ "LXV $dst, $mem \t// load 16-byte Vector" %}
size(4);
ins_encode %{
- __ lxv($dst$$VectorSRegister, $mem$$disp, $mem$$Register);
+ __ lxv($dst$$VectorRegister.to_vsr(), $mem$$disp, $mem$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -6660,7 +6467,7 @@ instruct storeV16_Power8(indirect mem, vecX src) %{
format %{ "STXVD2X $mem, $src \t// store 16-byte Vector" %}
size(4);
ins_encode %{
- __ stxvd2x($src$$VectorSRegister, $mem$$Register);
+ __ stxvd2x($src$$VectorRegister.to_vsr(), $mem$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -6673,7 +6480,7 @@ instruct storeV16_Power9(memoryAlg16 mem, vecX src) %{
format %{ "STXV $mem, $src \t// store 16-byte Vector" %}
size(4);
ins_encode %{
- __ stxv($src$$VectorSRegister, $mem$$disp, $mem$$Register);
+ __ stxv($src$$VectorRegister.to_vsr(), $mem$$disp, $mem$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -12658,9 +12465,9 @@ instruct bytes_reverse_int_vec(iRegIdst dst, iRegIsrc src, vecX tmpV) %{
"\tMFVSRWZ $dst, $tmpV" %}
ins_encode %{
- __ mtvsrwz($tmpV$$VectorSRegister, $src$$Register);
- __ xxbrw($tmpV$$VectorSRegister, $tmpV$$VectorSRegister);
- __ mfvsrwz($dst$$Register, $tmpV$$VectorSRegister);
+ __ mtvsrwz($tmpV$$VectorRegister.to_vsr(), $src$$Register);
+ __ xxbrw($tmpV$$VectorRegister.to_vsr(), $tmpV$$VectorRegister->to_vsr());
+ __ mfvsrwz($dst$$Register, $tmpV$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -12730,9 +12537,9 @@ instruct bytes_reverse_long_vec(iRegLdst dst, iRegLsrc src, vecX tmpV) %{
"\tMFVSRD $dst, $tmpV" %}
ins_encode %{
- __ mtvsrd($tmpV$$VectorSRegister, $src$$Register);
- __ xxbrd($tmpV$$VectorSRegister, $tmpV$$VectorSRegister);
- __ mfvsrd($dst$$Register, $tmpV$$VectorSRegister);
+ __ mtvsrd($tmpV$$VectorRegister->to_vsr(), $src$$Register);
+ __ xxbrd($tmpV$$VectorRegister->to_vsr(), $tmpV$$VectorRegister->to_vsr());
+ __ mfvsrd($dst$$Register, $tmpV$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -12959,7 +12766,7 @@ instruct mtvsrwz(vecX temp1, iRegIsrc src) %{
format %{ "MTVSRWZ $temp1, $src \t// Move to 16-byte register" %}
size(4);
ins_encode %{
- __ mtvsrwz($temp1$$VectorSRegister, $src$$Register);
+ __ mtvsrwz($temp1$$VectorRegister->to_vsr(), $src$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -12970,7 +12777,7 @@ instruct xxspltw(vecX dst, vecX src, immI8 imm1) %{
format %{ "XXSPLTW $dst, $src, $imm1 \t// Splat word" %}
size(4);
ins_encode %{
- __ xxspltw($dst$$VectorSRegister, $src$$VectorSRegister, $imm1$$constant);
+ __ xxspltw($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr(), $imm1$$constant);
%}
ins_pipe(pipe_class_default);
%}
@@ -12981,7 +12788,7 @@ instruct xscvdpspn_regF(vecX dst, regF src) %{
format %{ "XSCVDPSPN $dst, $src \t// Convert scalar single precision to vector single precision" %}
size(4);
ins_encode %{
- __ xscvdpspn($dst$$VectorSRegister, $src$$FloatRegister->to_vsr());
+ __ xscvdpspn($dst$$VectorRegister->to_vsr(), $src$$FloatRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13088,7 +12895,7 @@ instruct repl16B_immI0(vecX dst, immI_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate16B" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13101,7 +12908,7 @@ instruct repl16B_immIminus1(vecX dst, immI_minus1 src) %{
format %{ "XXLEQV $dst, $src \t// replicate16B" %}
size(4);
ins_encode %{
- __ xxleqv($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxleqv($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13166,7 +12973,7 @@ instruct repl8S_immI0(vecX dst, immI_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate8S" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13179,7 +12986,7 @@ instruct repl8S_immIminus1(vecX dst, immI_minus1 src) %{
format %{ "XXLEQV $dst, $src \t// replicate8S" %}
size(4);
ins_encode %{
- __ xxleqv($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxleqv($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13244,7 +13051,7 @@ instruct repl4I_immI0(vecX dst, immI_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate4I" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13257,7 +13064,7 @@ instruct repl4I_immIminus1(vecX dst, immI_minus1 src) %{
format %{ "XXLEQV $dst, $dst, $dst \t// replicate4I" %}
size(4);
ins_encode %{
- __ xxleqv($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxleqv($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13313,7 +13120,7 @@ instruct vadd16B_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDUBM $dst,$src1,$src2\t// add packed16B" %}
size(4);
ins_encode %{
- __ vaddubm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vaddubm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13324,7 +13131,7 @@ instruct vadd8S_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDUHM $dst,$src1,$src2\t// add packed8S" %}
size(4);
ins_encode %{
- __ vadduhm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vadduhm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13335,7 +13142,7 @@ instruct vadd4I_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDUWM $dst,$src1,$src2\t// add packed4I" %}
size(4);
ins_encode %{
- __ vadduwm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vadduwm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13346,7 +13153,7 @@ instruct vadd4F_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDFP $dst,$src1,$src2\t// add packed4F" %}
size(4);
ins_encode %{
- __ vaddfp($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vaddfp($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13357,7 +13164,7 @@ instruct vadd2L_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VADDUDM $dst,$src1,$src2\t// add packed2L" %}
size(4);
ins_encode %{
- __ vaddudm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vaddudm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13368,7 +13175,7 @@ instruct vadd2D_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVADDDP $dst,$src1,$src2\t// add packed2D" %}
size(4);
ins_encode %{
- __ xvadddp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvadddp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13381,7 +13188,7 @@ instruct vsub16B_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBUBM $dst,$src1,$src2\t// sub packed16B" %}
size(4);
ins_encode %{
- __ vsububm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsububm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13392,7 +13199,7 @@ instruct vsub8S_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBUHM $dst,$src1,$src2\t// sub packed8S" %}
size(4);
ins_encode %{
- __ vsubuhm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsubuhm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13403,7 +13210,7 @@ instruct vsub4I_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBUWM $dst,$src1,$src2\t// sub packed4I" %}
size(4);
ins_encode %{
- __ vsubuwm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsubuwm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13414,7 +13221,7 @@ instruct vsub4F_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBFP $dst,$src1,$src2\t// sub packed4F" %}
size(4);
ins_encode %{
- __ vsubfp($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsubfp($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13425,7 +13232,7 @@ instruct vsub2L_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VSUBUDM $dst,$src1,$src2\t// sub packed2L" %}
size(4);
ins_encode %{
- __ vsubudm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vsubudm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13436,7 +13243,7 @@ instruct vsub2D_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVSUBDP $dst,$src1,$src2\t// sub packed2D" %}
size(4);
ins_encode %{
- __ xvsubdp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvsubdp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13451,8 +13258,8 @@ instruct vmul8S_reg(vecX dst, vecX src1, vecX src2, vecX tmp) %{
format %{ "VMLADDUHM $dst,$src1,$src2\t// mul packed8S" %}
size(8);
ins_encode %{
- __ vspltish($tmp$$VectorSRegister->to_vr(), 0);
- __ vmladduhm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr(), $tmp$$VectorSRegister->to_vr());
+ __ vspltish($tmp$$VectorRegister, 0);
+ __ vmladduhm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister, $tmp$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13463,7 +13270,7 @@ instruct vmul4I_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "VMULUWM $dst,$src1,$src2\t// mul packed4I" %}
size(4);
ins_encode %{
- __ vmuluwm($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vmuluwm($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13474,7 +13281,7 @@ instruct vmul4F_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVMULSP $dst,$src1,$src2\t// mul packed4F" %}
size(4);
ins_encode %{
- __ xvmulsp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmulsp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13485,7 +13292,7 @@ instruct vmul2D_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVMULDP $dst,$src1,$src2\t// mul packed2D" %}
size(4);
ins_encode %{
- __ xvmuldp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmuldp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13498,7 +13305,7 @@ instruct vdiv4F_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVDIVSP $dst,$src1,$src2\t// div packed4F" %}
size(4);
ins_encode %{
- __ xvdivsp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvdivsp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13509,7 +13316,7 @@ instruct vdiv2D_reg(vecX dst, vecX src1, vecX src2) %{
format %{ "XVDIVDP $dst,$src1,$src2\t// div packed2D" %}
size(4);
ins_encode %{
- __ xvdivdp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvdivdp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13524,10 +13331,10 @@ instruct vmin_reg(vecX dst, vecX src1, vecX src2) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_INT:
- __ vminsw($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vminsw($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
break;
case T_LONG:
- __ vminsd($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vminsd($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13544,10 +13351,10 @@ instruct vmax_reg(vecX dst, vecX src1, vecX src2) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_INT:
- __ vmaxsw($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vmaxsw($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
break;
case T_LONG:
- __ vmaxsd($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vmaxsd($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13561,7 +13368,7 @@ instruct vand(vecX dst, vecX src1, vecX src2) %{
size(4);
format %{ "VAND $dst,$src1,$src2\t// and vectors" %}
ins_encode %{
- __ vand($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vand($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13571,7 +13378,7 @@ instruct vor(vecX dst, vecX src1, vecX src2) %{
size(4);
format %{ "VOR $dst,$src1,$src2\t// or vectors" %}
ins_encode %{
- __ vor($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vor($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13581,7 +13388,7 @@ instruct vxor(vecX dst, vecX src1, vecX src2) %{
size(4);
format %{ "VXOR $dst,$src1,$src2\t// xor vectors" %}
ins_encode %{
- __ vxor($dst$$VectorSRegister->to_vr(), $src1$$VectorSRegister->to_vr(), $src2$$VectorSRegister->to_vr());
+ __ vxor($dst$$VectorRegister, $src1$$VectorRegister, $src2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13599,8 +13406,8 @@ instruct reductionI_arith_logic(iRegIdst dst, iRegIsrc srcInt, vecX srcVec, vecX
size(24);
ins_encode %{
int opcode = this->ideal_Opcode();
- __ reduceI(opcode, $dst$$Register, $srcInt$$Register, $srcVec$$VectorSRegister->to_vr(),
- $tmp1$$VectorSRegister->to_vr(), $tmp2$$VectorSRegister->to_vr());
+ __ reduceI(opcode, $dst$$Register, $srcInt$$Register, $srcVec$$VectorRegister,
+ $tmp1$$VectorRegister, $tmp2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13615,8 +13422,8 @@ instruct reductionI_min_max(iRegIdst dst, iRegIsrc srcInt, vecX srcVec, vecX tmp
size(28);
ins_encode %{
int opcode = this->ideal_Opcode();
- __ reduceI(opcode, $dst$$Register, $srcInt$$Register, $srcVec$$VectorSRegister->to_vr(),
- $tmp1$$VectorSRegister->to_vr(), $tmp2$$VectorSRegister->to_vr());
+ __ reduceI(opcode, $dst$$Register, $srcInt$$Register, $srcVec$$VectorRegister,
+ $tmp1$$VectorRegister, $tmp2$$VectorRegister);
%}
ins_pipe(pipe_class_default);
%}
@@ -13629,7 +13436,7 @@ instruct vabs4F_reg(vecX dst, vecX src) %{
format %{ "XVABSSP $dst,$src\t// absolute packed4F" %}
size(4);
ins_encode %{
- __ xvabssp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvabssp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13640,7 +13447,7 @@ instruct vabs2D_reg(vecX dst, vecX src) %{
format %{ "XVABSDP $dst,$src\t// absolute packed2D" %}
size(4);
ins_encode %{
- __ xvabsdp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvabsdp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13677,13 +13484,13 @@ instruct vround2D_reg(vecX dst, vecX src, immI8 rmode) %{
ins_encode %{
switch ($rmode$$constant) {
case RoundDoubleModeNode::rmode_rint:
- __ xvrdpic($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvrdpic($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
break;
case RoundDoubleModeNode::rmode_floor:
- __ xvrdpim($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvrdpim($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
break;
case RoundDoubleModeNode::rmode_ceil:
- __ xvrdpip($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvrdpip($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
break;
default:
ShouldNotReachHere();
@@ -13700,7 +13507,7 @@ instruct vneg4F_reg(vecX dst, vecX src) %{
format %{ "XVNEGSP $dst,$src\t// negate packed4F" %}
size(4);
ins_encode %{
- __ xvnegsp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvnegsp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13711,7 +13518,7 @@ instruct vneg2D_reg(vecX dst, vecX src) %{
format %{ "XVNEGDP $dst,$src\t// negate packed2D" %}
size(4);
ins_encode %{
- __ xvnegdp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvnegdp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13724,7 +13531,7 @@ instruct vsqrt4F_reg(vecX dst, vecX src) %{
format %{ "XVSQRTSP $dst,$src\t// sqrt packed4F" %}
size(4);
ins_encode %{
- __ xvsqrtsp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvsqrtsp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13735,7 +13542,7 @@ instruct vsqrt2D_reg(vecX dst, vecX src) %{
format %{ "XVSQRTDP $dst,$src\t// sqrt packed2D" %}
size(4);
ins_encode %{
- __ xvsqrtdp($dst$$VectorSRegister, $src$$VectorSRegister);
+ __ xvsqrtdp($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13751,16 +13558,16 @@ instruct vpopcnt_reg(vecX dst, vecX src) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_BYTE:
- __ vpopcntb($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vpopcntb($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_SHORT:
- __ vpopcnth($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vpopcnth($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_INT:
- __ vpopcntw($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vpopcntw($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_LONG:
- __ vpopcntd($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vpopcntd($dst$$VectorRegister, $src$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13777,16 +13584,16 @@ instruct vcount_leading_zeros_reg(vecX dst, vecX src) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_BYTE:
- __ vclzb($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vclzb($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_SHORT:
- __ vclzh($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vclzh($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_INT:
- __ vclzw($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vclzw($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_LONG:
- __ vclzd($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vclzd($dst$$VectorRegister, $src$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13803,16 +13610,16 @@ instruct vcount_trailing_zeros_reg(vecX dst, vecX src) %{
BasicType bt = Matcher::vector_element_basic_type(this);
switch (bt) {
case T_BYTE:
- __ vctzb($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vctzb($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_SHORT:
- __ vctzh($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vctzh($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_INT:
- __ vctzw($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vctzw($dst$$VectorRegister, $src$$VectorRegister);
break;
case T_LONG:
- __ vctzd($dst$$VectorSRegister->to_vr(), $src$$VectorSRegister->to_vr());
+ __ vctzd($dst$$VectorRegister, $src$$VectorRegister);
break;
default:
ShouldNotReachHere();
@@ -13832,7 +13639,7 @@ instruct vfma4F(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvmaddasp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmaddasp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13848,7 +13655,7 @@ instruct vfma4F_neg1(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvnmsubasp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvnmsubasp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13863,7 +13670,7 @@ instruct vfma4F_neg2(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvmsubasp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmsubasp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13878,7 +13685,7 @@ instruct vfma2D(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvmaddadp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmaddadp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13894,7 +13701,7 @@ instruct vfma2D_neg1(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvnmsubadp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvnmsubadp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13909,7 +13716,7 @@ instruct vfma2D_neg2(vecX dst, vecX src1, vecX src2) %{
size(4);
ins_encode %{
assert(UseFMA, "Needs FMA instructions support.");
- __ xvmsubadp($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister);
+ __ xvmsubadp($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -13999,7 +13806,7 @@ instruct repl4F_immF0(vecX dst, immF_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate4F" %}
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -14012,7 +13819,7 @@ instruct repl2D_reg_Ex(vecX dst, regD src) %{
format %{ "XXPERMDI $dst, $src, $src, 0 \t// Splat doubleword" %}
size(4);
ins_encode %{
- __ xxpermdi($dst$$VectorSRegister, $src$$FloatRegister->to_vsr(), $src$$FloatRegister->to_vsr(), 0);
+ __ xxpermdi($dst$$VectorRegister->to_vsr(), $src$$FloatRegister->to_vsr(), $src$$FloatRegister->to_vsr(), 0);
%}
ins_pipe(pipe_class_default);
%}
@@ -14025,7 +13832,7 @@ instruct repl2D_immD0(vecX dst, immD_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate2D" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -14037,7 +13844,7 @@ instruct mtvsrd(vecX dst, iRegLsrc src) %{
format %{ "MTVSRD $dst, $src \t// Move to 16-byte register" %}
size(4);
ins_encode %{
- __ mtvsrd($dst$$VectorSRegister, $src$$Register);
+ __ mtvsrd($dst$$VectorRegister->to_vsr(), $src$$Register);
%}
ins_pipe(pipe_class_default);
%}
@@ -14048,7 +13855,7 @@ instruct xxspltd(vecX dst, vecX src, immI8 zero) %{
format %{ "XXSPLATD $dst, $src, $zero \t// Splat doubleword" %}
size(4);
ins_encode %{
- __ xxpermdi($dst$$VectorSRegister, $src$$VectorSRegister, $src$$VectorSRegister, $zero$$constant);
+ __ xxpermdi($dst$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr(), $src$$VectorRegister->to_vsr(), $zero$$constant);
%}
ins_pipe(pipe_class_default);
%}
@@ -14059,7 +13866,7 @@ instruct xxpermdi(vecX dst, vecX src1, vecX src2, immI8 zero) %{
format %{ "XXPERMDI $dst, $src1, $src2, $zero \t// Splat doubleword" %}
size(4);
ins_encode %{
- __ xxpermdi($dst$$VectorSRegister, $src1$$VectorSRegister, $src2$$VectorSRegister, $zero$$constant);
+ __ xxpermdi($dst$$VectorRegister->to_vsr(), $src1$$VectorRegister->to_vsr(), $src2$$VectorRegister->to_vsr(), $zero$$constant);
%}
ins_pipe(pipe_class_default);
%}
@@ -14084,7 +13891,7 @@ instruct repl2L_immI0(vecX dst, immI_0 zero) %{
format %{ "XXLXOR $dst, $zero \t// replicate2L" %}
size(4);
ins_encode %{
- __ xxlxor($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxlxor($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
@@ -14097,7 +13904,7 @@ instruct repl2L_immIminus1(vecX dst, immI_minus1 src) %{
format %{ "XXLEQV $dst, $src \t// replicate2L" %}
size(4);
ins_encode %{
- __ xxleqv($dst$$VectorSRegister, $dst$$VectorSRegister, $dst$$VectorSRegister);
+ __ xxleqv($dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr(), $dst$$VectorRegister->to_vsr());
%}
ins_pipe(pipe_class_default);
%}
diff --git a/src/hotspot/cpu/ppc/registerMap_ppc.cpp b/src/hotspot/cpu/ppc/registerMap_ppc.cpp
new file mode 100644
index 000000000000..2e7f8af89d35
--- /dev/null
+++ b/src/hotspot/cpu/ppc/registerMap_ppc.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * 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 "runtime/registerMap.hpp"
+
+address RegisterMap::pd_location(VMReg base_reg, int slot_idx) const {
+ if (base_reg->is_VectorRegister()) {
+ // Not all physical slots belonging to a VectorRegister have corresponding
+ // valid VMReg locations in the RegisterMap.
+ // (See RegisterSaver::push_frame_reg_args_and_save_live_registers.)
+ // However, the slots are always saved to the stack in a contiguous region
+ // of memory so we can calculate the address of the upper slots by
+ // offsetting from the base address.
+ assert(base_reg->is_concrete(), "must pass base reg");
+ address base_location = location(base_reg, nullptr);
+ if (base_location != nullptr) {
+ intptr_t offset_in_bytes = slot_idx * VMRegImpl::stack_slot_size;
+ return base_location + offset_in_bytes;
+ } else {
+ return nullptr;
+ }
+ } else {
+ return location(base_reg->next(slot_idx), nullptr);
+ }
+}
diff --git a/src/hotspot/cpu/ppc/registerMap_ppc.hpp b/src/hotspot/cpu/ppc/registerMap_ppc.hpp
index 01eb642107cb..607c712d10f2 100644
--- a/src/hotspot/cpu/ppc/registerMap_ppc.hpp
+++ b/src/hotspot/cpu/ppc/registerMap_ppc.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2013 SAP SE. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,9 +35,7 @@
// Since there is none, we just return null.
address pd_location(VMReg reg) const { return nullptr; }
- address pd_location(VMReg base_reg, int slot_idx) const {
- return location(base_reg->next(slot_idx), nullptr);
- }
+ address pd_location(VMReg base_reg, int slot_idx) const;
// no PD state to clear or copy:
void pd_clear() {}
diff --git a/src/hotspot/cpu/ppc/register_ppc.hpp b/src/hotspot/cpu/ppc/register_ppc.hpp
index b7949750dcc9..2613d6c98222 100644
--- a/src/hotspot/cpu/ppc/register_ppc.hpp
+++ b/src/hotspot/cpu/ppc/register_ppc.hpp
@@ -321,6 +321,7 @@ class VectorRegister {
// accessors
constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; }
+ inline VMReg as_VMReg() const;
// testers
constexpr bool is_valid() const { return (0 <= _encoding && _encoding < number_of_registers); }
@@ -392,7 +393,6 @@ class VectorSRegister {
// accessors
constexpr int encoding() const { assert(is_valid(), "invalid register"); return _encoding; }
- inline VMReg as_VMReg() const;
VectorSRegister successor() const { return VectorSRegister(encoding() + 1); }
// testers
@@ -484,8 +484,8 @@ class ConcreteRegisterImpl : public AbstractRegisterImpl {
enum {
max_gpr = Register::number_of_registers * 2,
max_fpr = max_gpr + FloatRegister::number_of_registers * 2,
- max_vsr = max_fpr + VectorSRegister::number_of_registers * 4,
- max_cnd = max_vsr + ConditionRegister::number_of_registers,
+ max_vr = max_fpr + VectorRegister::number_of_registers * 4,
+ max_cnd = max_vr + ConditionRegister::number_of_registers,
max_spr = max_cnd + SpecialRegister::number_of_registers,
// This number must be large enough to cover REG_COUNT (defined by c2) registers.
// There is no requirement that any ordering here matches any ordering c2 gives
diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
index 4ec2483b267c..8d34f494d96b 100644
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
@@ -103,7 +103,7 @@ class RegisterSaver {
// During deoptimization only the result registers need to be restored
// all the other values have already been extracted.
- static void restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes);
+ static void restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes, bool save_vectors);
// Constants and data structures:
@@ -111,13 +111,13 @@ class RegisterSaver {
int_reg,
float_reg,
special_reg,
- vs_reg
+ vec_reg
} RegisterType;
typedef enum {
reg_size = 8,
half_reg_size = reg_size / 2,
- vs_reg_size = 16
+ vec_reg_size = 16
} RegisterConstants;
typedef struct {
@@ -137,8 +137,8 @@ class RegisterSaver {
#define RegisterSaver_LiveSpecialReg(regname) \
{ RegisterSaver::special_reg, regname->encoding(), regname->as_VMReg() }
-#define RegisterSaver_LiveVSReg(regname) \
- { RegisterSaver::vs_reg, regname->encoding(), regname->as_VMReg() }
+#define RegisterSaver_LiveVecReg(regname) \
+ { RegisterSaver::vec_reg, regname->encoding(), regname->as_VMReg() }
static const RegisterSaver::LiveRegType RegisterSaver_LiveRegs[] = {
// Live registers which get spilled to the stack. Register
@@ -220,42 +220,42 @@ static const RegisterSaver::LiveRegType RegisterSaver_LiveRegs[] = {
RegisterSaver_LiveIntReg( R31 ) // must be the last register (see save/restore functions below)
};
-static const RegisterSaver::LiveRegType RegisterSaver_LiveVSRegs[] = {
+static const RegisterSaver::LiveRegType RegisterSaver_LiveVecRegs[] = {
//
- // live vector scalar registers (optional, only these ones are used by C2):
+ // live vector registers (optional, only these ones are used by C2):
//
- RegisterSaver_LiveVSReg( VSR32 ),
- RegisterSaver_LiveVSReg( VSR33 ),
- RegisterSaver_LiveVSReg( VSR34 ),
- RegisterSaver_LiveVSReg( VSR35 ),
- RegisterSaver_LiveVSReg( VSR36 ),
- RegisterSaver_LiveVSReg( VSR37 ),
- RegisterSaver_LiveVSReg( VSR38 ),
- RegisterSaver_LiveVSReg( VSR39 ),
- RegisterSaver_LiveVSReg( VSR40 ),
- RegisterSaver_LiveVSReg( VSR41 ),
- RegisterSaver_LiveVSReg( VSR42 ),
- RegisterSaver_LiveVSReg( VSR43 ),
- RegisterSaver_LiveVSReg( VSR44 ),
- RegisterSaver_LiveVSReg( VSR45 ),
- RegisterSaver_LiveVSReg( VSR46 ),
- RegisterSaver_LiveVSReg( VSR47 ),
- RegisterSaver_LiveVSReg( VSR48 ),
- RegisterSaver_LiveVSReg( VSR49 ),
- RegisterSaver_LiveVSReg( VSR50 ),
- RegisterSaver_LiveVSReg( VSR51 ),
- RegisterSaver_LiveVSReg( VSR52 ),
- RegisterSaver_LiveVSReg( VSR53 ),
- RegisterSaver_LiveVSReg( VSR54 ),
- RegisterSaver_LiveVSReg( VSR55 ),
- RegisterSaver_LiveVSReg( VSR56 ),
- RegisterSaver_LiveVSReg( VSR57 ),
- RegisterSaver_LiveVSReg( VSR58 ),
- RegisterSaver_LiveVSReg( VSR59 ),
- RegisterSaver_LiveVSReg( VSR60 ),
- RegisterSaver_LiveVSReg( VSR61 ),
- RegisterSaver_LiveVSReg( VSR62 ),
- RegisterSaver_LiveVSReg( VSR63 )
+ RegisterSaver_LiveVecReg( VR0 ),
+ RegisterSaver_LiveVecReg( VR1 ),
+ RegisterSaver_LiveVecReg( VR2 ),
+ RegisterSaver_LiveVecReg( VR3 ),
+ RegisterSaver_LiveVecReg( VR4 ),
+ RegisterSaver_LiveVecReg( VR5 ),
+ RegisterSaver_LiveVecReg( VR6 ),
+ RegisterSaver_LiveVecReg( VR7 ),
+ RegisterSaver_LiveVecReg( VR8 ),
+ RegisterSaver_LiveVecReg( VR9 ),
+ RegisterSaver_LiveVecReg( VR10 ),
+ RegisterSaver_LiveVecReg( VR11 ),
+ RegisterSaver_LiveVecReg( VR12 ),
+ RegisterSaver_LiveVecReg( VR13 ),
+ RegisterSaver_LiveVecReg( VR14 ),
+ RegisterSaver_LiveVecReg( VR15 ),
+ RegisterSaver_LiveVecReg( VR16 ),
+ RegisterSaver_LiveVecReg( VR17 ),
+ RegisterSaver_LiveVecReg( VR18 ),
+ RegisterSaver_LiveVecReg( VR19 ),
+ RegisterSaver_LiveVecReg( VR20 ),
+ RegisterSaver_LiveVecReg( VR21 ),
+ RegisterSaver_LiveVecReg( VR22 ),
+ RegisterSaver_LiveVecReg( VR23 ),
+ RegisterSaver_LiveVecReg( VR24 ),
+ RegisterSaver_LiveVecReg( VR25 ),
+ RegisterSaver_LiveVecReg( VR26 ),
+ RegisterSaver_LiveVecReg( VR27 ),
+ RegisterSaver_LiveVecReg( VR28 ),
+ RegisterSaver_LiveVecReg( VR29 ),
+ RegisterSaver_LiveVecReg( VR30 ),
+ RegisterSaver_LiveVecReg( VR31 )
};
@@ -277,10 +277,10 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble
// calculate frame size
const int regstosave_num = sizeof(RegisterSaver_LiveRegs) /
sizeof(RegisterSaver::LiveRegType);
- const int vsregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVSRegs) /
+ const int vecregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVecRegs) /
sizeof(RegisterSaver::LiveRegType))
: 0;
- const int register_save_size = regstosave_num * reg_size + vsregstosave_num * vs_reg_size;
+ const int register_save_size = regstosave_num * reg_size + vecregstosave_num * vec_reg_size;
const int frame_size_in_bytes = align_up(register_save_size, frame::alignment_in_bytes)
+ frame::native_abi_reg_args_size;
@@ -298,8 +298,8 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble
// Save some registers in the last (non-vector) slots of the new frame so we
// can use them as scratch regs or to determine the return pc.
- __ std(R31, frame_size_in_bytes - reg_size - vsregstosave_num * vs_reg_size, R1_SP);
- __ std(R30, frame_size_in_bytes - 2*reg_size - vsregstosave_num * vs_reg_size, R1_SP);
+ __ std(R31, frame_size_in_bytes - reg_size - vecregstosave_num * vec_reg_size, R1_SP);
+ __ std(R30, frame_size_in_bytes - 2*reg_size - vecregstosave_num * vec_reg_size, R1_SP);
// save the flags
// Do the save_LR by hand and adjust the return pc if requested.
@@ -355,42 +355,44 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble
}
// Note that generate_oop_map in the following loop is only used for the
- // polling_page_vectors_safepoint_handler_blob.
+ // polling_page_vectors_safepoint_handler_blob and the deopt_blob.
// The order in which the vector contents are stored depends on Endianess and
// the utilized instructions (PowerArchitecturePPC64).
assert(is_aligned(offset, StackAlignmentInBytes), "should be");
if (PowerArchitecturePPC64 >= 10) {
- assert(is_even(vsregstosave_num), "expectation");
- for (int i = 0; i < vsregstosave_num; i += 2) {
- int reg_num = RegisterSaver_LiveVSRegs[i].reg_num;
- assert(RegisterSaver_LiveVSRegs[i + 1].reg_num == reg_num + 1, "or use other instructions!");
+ assert(is_even(vecregstosave_num), "expectation");
+ for (int i = 0; i < vecregstosave_num; i += 2) {
+ int reg_num = RegisterSaver_LiveVecRegs[i].reg_num;
+ assert(RegisterSaver_LiveVecRegs[i + 1].reg_num == reg_num + 1, "or use other instructions!");
- __ stxvp(as_VectorSRegister(reg_num), offset, R1_SP);
+ __ stxvp(as_VectorRegister(reg_num).to_vsr(), offset, R1_SP);
// Note: The contents were read in the same order (see loadV16_Power9 node in ppc.ad).
+ // RegisterMap::pd_location only uses the first VMReg for each VectorRegister.
if (generate_oop_map) {
map->set_callee_saved(VMRegImpl::stack2reg(offset >> 2),
- RegisterSaver_LiveVSRegs[i LITTLE_ENDIAN_ONLY(+1) ].vmreg);
- map->set_callee_saved(VMRegImpl::stack2reg((offset + vs_reg_size) >> 2),
- RegisterSaver_LiveVSRegs[i BIG_ENDIAN_ONLY(+1) ].vmreg);
+ RegisterSaver_LiveVecRegs[i LITTLE_ENDIAN_ONLY(+1) ].vmreg);
+ map->set_callee_saved(VMRegImpl::stack2reg((offset + vec_reg_size) >> 2),
+ RegisterSaver_LiveVecRegs[i BIG_ENDIAN_ONLY(+1) ].vmreg);
}
- offset += (2 * vs_reg_size);
+ offset += (2 * vec_reg_size);
}
} else {
- for (int i = 0; i < vsregstosave_num; i++) {
- int reg_num = RegisterSaver_LiveVSRegs[i].reg_num;
+ for (int i = 0; i < vecregstosave_num; i++) {
+ int reg_num = RegisterSaver_LiveVecRegs[i].reg_num;
if (PowerArchitecturePPC64 >= 9) {
- __ stxv(as_VectorSRegister(reg_num), offset, R1_SP);
+ __ stxv(as_VectorRegister(reg_num)->to_vsr(), offset, R1_SP);
} else {
__ li(R31, offset);
- __ stxvd2x(as_VectorSRegister(reg_num), R31, R1_SP);
+ __ stxvd2x(as_VectorRegister(reg_num)->to_vsr(), R31, R1_SP);
}
// Note: The contents were read in the same order (see loadV16_Power8 / loadV16_Power9 node in ppc.ad).
+ // RegisterMap::pd_location only uses the first VMReg for each VectorRegister.
if (generate_oop_map) {
- VMReg vsr = RegisterSaver_LiveVSRegs[i].vmreg;
+ VMReg vsr = RegisterSaver_LiveVecRegs[i].vmreg;
map->set_callee_saved(VMRegImpl::stack2reg(offset >> 2), vsr);
}
- offset += vs_reg_size;
+ offset += vec_reg_size;
}
}
@@ -411,10 +413,10 @@ void RegisterSaver::restore_live_registers_and_pop_frame(MacroAssembler* masm,
bool save_vectors) {
const int regstosave_num = sizeof(RegisterSaver_LiveRegs) /
sizeof(RegisterSaver::LiveRegType);
- const int vsregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVSRegs) /
+ const int vecregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVecRegs) /
sizeof(RegisterSaver::LiveRegType))
: 0;
- const int register_save_size = regstosave_num * reg_size + vsregstosave_num * vs_reg_size;
+ const int register_save_size = regstosave_num * reg_size + vecregstosave_num * vec_reg_size;
const int register_save_offset = frame_size_in_bytes - register_save_size;
@@ -456,26 +458,26 @@ void RegisterSaver::restore_live_registers_and_pop_frame(MacroAssembler* masm,
assert(is_aligned(offset, StackAlignmentInBytes), "should be");
if (PowerArchitecturePPC64 >= 10) {
- for (int i = 0; i < vsregstosave_num; i += 2) {
- int reg_num = RegisterSaver_LiveVSRegs[i].reg_num;
- assert(RegisterSaver_LiveVSRegs[i + 1].reg_num == reg_num + 1, "or use other instructions!");
+ for (int i = 0; i < vecregstosave_num; i += 2) {
+ int reg_num = RegisterSaver_LiveVecRegs[i].reg_num;
+ assert(RegisterSaver_LiveVecRegs[i + 1].reg_num == reg_num + 1, "or use other instructions!");
- __ lxvp(as_VectorSRegister(reg_num), offset, R1_SP);
+ __ lxvp(as_VectorRegister(reg_num).to_vsr(), offset, R1_SP);
- offset += (2 * vs_reg_size);
+ offset += (2 * vec_reg_size);
}
} else {
- for (int i = 0; i < vsregstosave_num; i++) {
- int reg_num = RegisterSaver_LiveVSRegs[i].reg_num;
+ for (int i = 0; i < vecregstosave_num; i++) {
+ int reg_num = RegisterSaver_LiveVecRegs[i].reg_num;
if (PowerArchitecturePPC64 >= 9) {
- __ lxv(as_VectorSRegister(reg_num), offset, R1_SP);
+ __ lxv(as_VectorRegister(reg_num).to_vsr(), offset, R1_SP);
} else {
__ li(R31, offset);
- __ lxvd2x(as_VectorSRegister(reg_num), R31, R1_SP);
+ __ lxvd2x(as_VectorRegister(reg_num).to_vsr(), R31, R1_SP);
}
- offset += vs_reg_size;
+ offset += vec_reg_size;
}
}
@@ -486,7 +488,7 @@ void RegisterSaver::restore_live_registers_and_pop_frame(MacroAssembler* masm,
__ mtlr(R31);
// restore scratch register's value
- __ ld(R31, frame_size_in_bytes - reg_size - vsregstosave_num * vs_reg_size, R1_SP);
+ __ ld(R31, frame_size_in_bytes - reg_size - vecregstosave_num * vec_reg_size, R1_SP);
// pop the frame
__ addi(R1_SP, R1_SP, frame_size_in_bytes);
@@ -572,10 +574,14 @@ void RegisterSaver::restore_argument_registers_and_pop_frame(MacroAssembler*masm
}
// Restore the registers that might be holding a result.
-void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes) {
+void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes, bool save_vectors) {
const int regstosave_num = sizeof(RegisterSaver_LiveRegs) /
sizeof(RegisterSaver::LiveRegType);
- const int register_save_size = regstosave_num * reg_size; // VS registers not relevant here.
+ const int vecregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVecRegs) /
+ sizeof(RegisterSaver::LiveRegType))
+ : 0;
+ const int register_save_size = regstosave_num * reg_size + vecregstosave_num * vec_reg_size;
+
const int register_save_offset = frame_size_in_bytes - register_save_size;
// restore all result registers (ints and floats)
@@ -604,7 +610,7 @@ void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_siz
offset += reg_size;
}
- assert(offset == frame_size_in_bytes, "consistency check");
+ assert(offset == frame_size_in_bytes - (save_vectors ? vecregstosave_num * vec_reg_size : 0), "consistency check");
}
// Is vector's size (in bytes) bigger than a size saved by default?
@@ -2993,7 +2999,8 @@ void SharedRuntime::generate_deopt_blob() {
&first_frame_size_in_bytes,
/*generate_oop_map=*/ true,
return_pc_adjustment_no_exception,
- RegisterSaver::return_pc_is_lr);
+ RegisterSaver::return_pc_is_lr,
+ /*save_vectors*/ SuperwordUseVSX);
assert(map != nullptr, "OopMap must have been created");
__ li(exec_mode_reg, Deoptimization::Unpack_deopt);
@@ -3028,7 +3035,8 @@ void SharedRuntime::generate_deopt_blob() {
&first_frame_size_in_bytes,
/*generate_oop_map=*/ false,
/*return_pc_adjustment_exception=*/ 0,
- RegisterSaver::return_pc_is_pre_saved);
+ RegisterSaver::return_pc_is_pre_saved,
+ /*save_vectors*/ SuperwordUseVSX);
// Deopt during an exception. Save exec mode for unpack_frames.
__ li(exec_mode_reg, Deoptimization::Unpack_exception);
@@ -3046,7 +3054,8 @@ void SharedRuntime::generate_deopt_blob() {
&first_frame_size_in_bytes,
/*generate_oop_map=*/ false,
/*return_pc_adjustment_reexecute=*/ 0,
- RegisterSaver::return_pc_is_pre_saved);
+ RegisterSaver::return_pc_is_pre_saved,
+ /*save_vectors*/ SuperwordUseVSX);
__ li(exec_mode_reg, Deoptimization::Unpack_reexecute);
#endif
@@ -3072,7 +3081,7 @@ void SharedRuntime::generate_deopt_blob() {
// Restore only the result registers that have been saved
// by save_volatile_registers(...).
- RegisterSaver::restore_result_registers(masm, first_frame_size_in_bytes);
+ RegisterSaver::restore_result_registers(masm, first_frame_size_in_bytes, /*save_vectors*/ SuperwordUseVSX);
// reload the exec mode from the UnrollBlock (it might have changed)
__ lwz(exec_mode_reg, in_bytes(Deoptimization::UnrollBlock::unpack_kind_offset()), unroll_block_reg);
diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp
index 721364ce412b..d076308026b6 100644
--- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp
+++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp
@@ -25,6 +25,7 @@
#include "asm/assembler.inline.hpp"
#include "asm/macroAssembler.inline.hpp"
+#include "compiler/compilerDefinitions.inline.hpp"
#include "compiler/disassembler.hpp"
#include "jvm.h"
#include "memory/resourceArea.hpp"
@@ -103,7 +104,7 @@ void VM_Version::initialize() {
if (PowerArchitecturePPC64 >= 9) {
// Performance is good since Power9.
- if (FLAG_IS_DEFAULT(SuperwordUseVSX)) {
+ if (FLAG_IS_DEFAULT(SuperwordUseVSX) && CompilerConfig::is_c2_enabled()) {
FLAG_SET_ERGO(SuperwordUseVSX, true);
}
}
diff --git a/src/hotspot/cpu/ppc/vmreg_ppc.cpp b/src/hotspot/cpu/ppc/vmreg_ppc.cpp
index 2ed68578a80a..0edbf700ec61 100644
--- a/src/hotspot/cpu/ppc/vmreg_ppc.cpp
+++ b/src/hotspot/cpu/ppc/vmreg_ppc.cpp
@@ -47,7 +47,7 @@ void VMRegImpl::set_regName() {
}
VectorSRegister vsreg = ::as_VectorSRegister(0);
- for ( ; i < ConcreteRegisterImpl::max_vsr; ) {
+ for ( ; i < ConcreteRegisterImpl::max_vr; ) {
regName[i++] = vsreg->name();
regName[i++] = vsreg->name();
regName[i++] = vsreg->name();
diff --git a/src/hotspot/cpu/ppc/vmreg_ppc.hpp b/src/hotspot/cpu/ppc/vmreg_ppc.hpp
index 4e25c8b3cead..194b5fd93ef6 100644
--- a/src/hotspot/cpu/ppc/vmreg_ppc.hpp
+++ b/src/hotspot/cpu/ppc/vmreg_ppc.hpp
@@ -35,13 +35,13 @@ inline bool is_FloatRegister() {
value() < ConcreteRegisterImpl::max_fpr;
}
-inline bool is_VectorSRegister() {
+inline bool is_VectorRegister() {
return value() >= ConcreteRegisterImpl::max_fpr &&
- value() < ConcreteRegisterImpl::max_vsr;
+ value() < ConcreteRegisterImpl::max_vr;
}
inline bool is_ConditionRegister() {
- return value() >= ConcreteRegisterImpl::max_vsr &&
+ return value() >= ConcreteRegisterImpl::max_vr &&
value() < ConcreteRegisterImpl::max_cnd;
}
@@ -60,15 +60,15 @@ inline FloatRegister as_FloatRegister() {
return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) >> 1);
}
-inline VectorSRegister as_VectorSRegister() {
- assert(is_VectorSRegister(), "must be");
- return ::as_VectorSRegister((value() - ConcreteRegisterImpl::max_fpr) >> 2);
+inline VectorRegister as_VectorRegister() {
+ assert(is_VectorRegister(), "must be");
+ return ::as_VectorRegister((value() - ConcreteRegisterImpl::max_fpr) >> 2);
}
inline bool is_concrete() {
assert(is_reg(), "must be");
if (is_Register() || is_FloatRegister()) return is_even(value());
- if (is_VectorSRegister()) {
+ if (is_VectorRegister()) {
int base = value() - ConcreteRegisterImpl::max_fpr;
return (base & 3) == 0;
}
diff --git a/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp b/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp
index 2424df8da01e..a7810266b897 100644
--- a/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp
+++ b/src/hotspot/cpu/ppc/vmreg_ppc.inline.hpp
@@ -40,13 +40,13 @@ inline VMReg FloatRegister::as_VMReg() const {
return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_gpr);
}
-inline VMReg VectorSRegister::as_VMReg() const {
+inline VMReg VectorRegister::as_VMReg() const {
// Four halves, multiply by 4.
return VMRegImpl::as_VMReg((encoding() << 2) + ConcreteRegisterImpl::max_fpr);
}
inline VMReg ConditionRegister::as_VMReg() const {
- return VMRegImpl::as_VMReg((encoding()) + ConcreteRegisterImpl::max_vsr);
+ return VMRegImpl::as_VMReg((encoding()) + ConcreteRegisterImpl::max_vr);
}
inline VMReg SpecialRegister::as_VMReg() const {
diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
index 4c1056e75a55..f936ae9ba27b 100644
--- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
@@ -301,7 +301,7 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm,
} else {
assert(is_phantom, "only remaining strength");
assert(!is_narrow, "phantom access cannot be narrow");
- target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak);
+ target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom);
}
__ rt_call(target);
__ mv(t0, x10);
diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp
index 09dea62b6d18..a294fba8e1ff 100644
--- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp
@@ -713,6 +713,7 @@ class ZSetupArguments {
#define __ masm->
void ZBarrierSetAssembler::generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const {
+ Assembler::InlineSkippedInstructionsCounter skipped_counter(masm);
BLOCK_COMMENT("ZLoadBarrierStubC2");
// Stub entry
@@ -732,6 +733,7 @@ void ZBarrierSetAssembler::generate_c2_load_barrier_stub(MacroAssembler* masm, Z
}
void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const {
+ Assembler::InlineSkippedInstructionsCounter skipped_counter(masm);
BLOCK_COMMENT("ZStoreBarrierStubC2");
// Stub entry
diff --git a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad
index fd9a1d43afc6..4a17e512ae3c 100644
--- a/src/hotspot/cpu/riscv/gc/z/z_riscv.ad
+++ b/src/hotspot/cpu/riscv/gc/z/z_riscv.ad
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
+// Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
// Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
@@ -57,6 +57,7 @@ static void check_color(MacroAssembler* masm, Register ref, bool on_non_strong,
}
static void z_load_barrier(MacroAssembler* masm, const MachNode* node, Address ref_addr, Register ref, Register tmp) {
+ Assembler::InlineSkippedInstructionsCounter skipped_counter(masm);
const bool on_non_strong =
((node->barrier_data() & ZBarrierWeak) != 0) ||
((node->barrier_data() & ZBarrierPhantom) != 0);
@@ -78,6 +79,7 @@ static void z_load_barrier(MacroAssembler* masm, const MachNode* node, Address r
}
static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address ref_addr, Register rnew_zaddress, Register rnew_zpointer, Register tmp, bool is_atomic) {
+ Assembler::InlineSkippedInstructionsCounter skipped_counter(masm);
if (node->barrier_data() == ZBarrierElided) {
z_color(masm, node, rnew_zpointer, rnew_zaddress, tmp);
} else {
diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp
index 8fc9c2f9eaa8..32675a12cb07 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.cpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.cpp
@@ -140,7 +140,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
Label detect_486, cpu486, detect_586, std_cpuid1, std_cpuid4, std_cpuid24, std_cpuid29;
Label sef_cpuid, sefsl1_cpuid, ext_cpuid, ext_cpuid1, ext_cpuid5, ext_cpuid7;
- Label ext_cpuid8, done, wrapup, vector_save_restore, apx_save_restore_warning;
+ Label ext_cpuid8, done, wrapup, vector_save_restore, apx_save_restore_warning, apx_xstate;
Label legacy_setup, save_restore_except, legacy_save_restore, start_simd_check;
StubCodeMark mark(this, "VM_Version", "get_cpu_info_stub");
@@ -465,6 +465,20 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
__ movq(Address(rsi, 0), r16);
__ movq(Address(rsi, 8), r31);
+ //
+ // Query CPUID 0xD.19 for APX XSAVE offset
+ // Extended State Enumeration Sub-leaf 19 (APX)
+ // EAX = size of APX state (should be 128)
+ // EBX = offset in standard XSAVE format
+ //
+ __ movl(rax, 0xD);
+ __ movl(rcx, 19);
+ __ cpuid();
+ __ lea(rsi, Address(rbp, in_bytes(VM_Version::apx_xstate_size_offset())));
+ __ movl(Address(rsi, 0), rax);
+ __ lea(rsi, Address(rbp, in_bytes(VM_Version::apx_xstate_offset_offset())));
+ __ movl(Address(rsi, 0), rbx);
+
UseAPX = save_apx;
__ bind(vector_save_restore);
//
diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp
index 78ae45ef4f8c..64b9102b5cb8 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.hpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.hpp
@@ -672,6 +672,10 @@ class VM_Version : public Abstract_VM_Version {
// Space to save apx registers after signal handle
jlong apx_save[2]; // Save r16 and r31
+ // cpuid function 0xD, subleaf 19 (APX extended state)
+ uint32_t apx_xstate_size; // EAX: size of APX state (128)
+ uint32_t apx_xstate_offset; // EBX: offset in standard XSAVE area
+
VM_Features feature_flags() const;
// Asserts
@@ -735,6 +739,11 @@ class VM_Version : public Abstract_VM_Version {
static ByteSize ymm_save_offset() { return byte_offset_of(CpuidInfo, ymm_save); }
static ByteSize zmm_save_offset() { return byte_offset_of(CpuidInfo, zmm_save); }
static ByteSize apx_save_offset() { return byte_offset_of(CpuidInfo, apx_save); }
+ static ByteSize apx_xstate_offset_offset() { return byte_offset_of(CpuidInfo, apx_xstate_offset); }
+ static ByteSize apx_xstate_size_offset() { return byte_offset_of(CpuidInfo, apx_xstate_size); }
+
+ static uint32_t apx_xstate_offset() { return _cpuid_info.apx_xstate_offset; }
+ static uint32_t apx_xstate_size() { return _cpuid_info.apx_xstate_size; }
// The value used to check ymm register after signal handle
static int ymm_test_value() { return 0xCAFEBABE; }
diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp
index 50a219f0824e..5cf34a894c33 100644
--- a/src/hotspot/os/aix/os_aix.cpp
+++ b/src/hotspot/os/aix/os_aix.cpp
@@ -169,7 +169,7 @@ static void vmembk_print_on(outputStream* os);
////////////////////////////////////////////////////////////////////////////////
// global variables (for a description see os_aix.hpp)
-julong os::Aix::_physical_memory = 0;
+physical_memory_size_type os::Aix::_physical_memory = 0;
pthread_t os::Aix::_main_thread = ((pthread_t)0);
@@ -254,40 +254,43 @@ static bool is_close_to_brk(address a) {
return false;
}
-julong os::free_memory() {
- return Aix::available_memory();
+bool os::free_memory(physical_memory_size_type& value) {
+ return Aix::available_memory(value);
}
-julong os::available_memory() {
- return Aix::available_memory();
+bool os::available_memory(physical_memory_size_type& value) {
+ return Aix::available_memory(value);
}
-julong os::Aix::available_memory() {
+bool os::Aix::available_memory(physical_memory_size_type& value) {
os::Aix::meminfo_t mi;
if (os::Aix::get_meminfo(&mi)) {
- return mi.real_free;
+ value = static_cast(mi.real_free);
+ return true;
} else {
- return ULONG_MAX;
+ return false;
}
}
-jlong os::total_swap_space() {
+bool os::total_swap_space(physical_memory_size_type& value) {
perfstat_memory_total_t memory_info;
if (libperfstat::perfstat_memory_total(nullptr, &memory_info, sizeof(perfstat_memory_total_t), 1) == -1) {
- return -1;
+ return false;
}
- return (jlong)(memory_info.pgsp_total * 4 * K);
+ value = static_cast(memory_info.pgsp_total * 4 * K);
+ return true;
}
-jlong os::free_swap_space() {
+bool os::free_swap_space(physical_memory_size_type& value) {
perfstat_memory_total_t memory_info;
if (libperfstat::perfstat_memory_total(nullptr, &memory_info, sizeof(perfstat_memory_total_t), 1) == -1) {
- return -1;
+ return false;
}
- return (jlong)(memory_info.pgsp_free * 4 * K);
+ value = static_cast(memory_info.pgsp_free * 4 * K);
+ return true;
}
-julong os::physical_memory() {
+physical_memory_size_type os::physical_memory() {
return Aix::physical_memory();
}
@@ -326,7 +329,7 @@ void os::Aix::initialize_system_info() {
if (!os::Aix::get_meminfo(&mi)) {
assert(false, "os::Aix::get_meminfo failed.");
}
- _physical_memory = (julong) mi.real_total;
+ _physical_memory = static_cast(mi.real_total);
}
// Helper function for tracing page sizes.
@@ -1046,6 +1049,8 @@ static void* dll_load_library(const char *filename, int *eno, char *ebuf, int eb
dflags |= RTLD_MEMBER;
}
+ Events::log_dll_message(nullptr, "Attempting to load shared library %s", filename);
+
void* result;
const char* error_report = nullptr;
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
@@ -2267,7 +2272,7 @@ jint os::init_2(void) {
os::Posix::init_2();
trcVerbose("processor count: %d", os::_processor_count);
- trcVerbose("physical memory: %lu", Aix::_physical_memory);
+ trcVerbose("physical memory: " PHYS_MEM_TYPE_FORMAT, Aix::_physical_memory);
// Initially build up the loaded dll map.
LoadedLibraries::reload();
diff --git a/src/hotspot/os/aix/os_aix.hpp b/src/hotspot/os/aix/os_aix.hpp
index d17c022e4113..a7bac40e79b9 100644
--- a/src/hotspot/os/aix/os_aix.hpp
+++ b/src/hotspot/os/aix/os_aix.hpp
@@ -35,7 +35,7 @@ class os::Aix {
private:
- static julong _physical_memory;
+ static physical_memory_size_type _physical_memory;
static pthread_t _main_thread;
// 0 = uninitialized, otherwise 16 bit number:
@@ -54,9 +54,9 @@ class os::Aix {
// 1 - EXTSHM=ON
static int _extshm;
- static julong available_memory();
- static julong free_memory();
- static julong physical_memory() { return _physical_memory; }
+ static bool available_memory(physical_memory_size_type& value);
+ static bool free_memory(physical_memory_size_type& value);
+ static physical_memory_size_type physical_memory() { return _physical_memory; }
static void initialize_system_info();
// OS recognitions (AIX OS level) call this before calling Aix::os_version().
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index 868725fdb671..e58e1e085113 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -114,7 +114,7 @@
////////////////////////////////////////////////////////////////////////////////
// global variables
-julong os::Bsd::_physical_memory = 0;
+physical_memory_size_type os::Bsd::_physical_memory = 0;
#ifdef __APPLE__
mach_timebase_info_data_t os::Bsd::_timebase_info = {0, 0};
@@ -133,19 +133,19 @@ static volatile int processor_id_next = 0;
////////////////////////////////////////////////////////////////////////////////
// utility functions
-julong os::available_memory() {
- return Bsd::available_memory();
+bool os::available_memory(physical_memory_size_type& value) {
+ return Bsd::available_memory(value);
}
-julong os::free_memory() {
- return Bsd::available_memory();
+bool os::free_memory(physical_memory_size_type& value) {
+ return Bsd::available_memory(value);
}
// Available here means free. Note that this number is of no much use. As an estimate
// for future memory pressure it is far too conservative, since MacOS will use a lot
// of unused memory for caches, and return it willingly in case of needs.
-julong os::Bsd::available_memory() {
- uint64_t available = physical_memory() >> 2;
+bool os::Bsd::available_memory(physical_memory_size_type& value) {
+ physical_memory_size_type available = physical_memory() >> 2;
#ifdef __APPLE__
mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
vm_statistics64_data_t vmstat;
@@ -156,9 +156,12 @@ julong os::Bsd::available_memory() {
if (kerr == KERN_SUCCESS) {
// free_count is just a lowerbound, other page categories can be freed too and make memory available
available = (vmstat.free_count + vmstat.inactive_count + vmstat.purgeable_count) * os::vm_page_size();
+ } else {
+ return false;
}
#endif
- return available;
+ value = available;
+ return true;
}
// for more info see :
@@ -177,33 +180,35 @@ void os::Bsd::print_uptime_info(outputStream* st) {
}
}
-jlong os::total_swap_space() {
+bool os::total_swap_space(physical_memory_size_type& value) {
#if defined(__APPLE__)
struct xsw_usage vmusage;
size_t size = sizeof(vmusage);
if (sysctlbyname("vm.swapusage", &vmusage, &size, nullptr, 0) != 0) {
- return -1;
+ return false;
}
- return (jlong)vmusage.xsu_total;
+ value = static_cast(vmusage.xsu_total);
+ return true;
#else
- return -1;
+ return false;
#endif
}
-jlong os::free_swap_space() {
+bool os::free_swap_space(physical_memory_size_type& value) {
#if defined(__APPLE__)
struct xsw_usage vmusage;
size_t size = sizeof(vmusage);
if (sysctlbyname("vm.swapusage", &vmusage, &size, nullptr, 0) != 0) {
- return -1;
+ return false;
}
- return (jlong)vmusage.xsu_avail;
+ value = static_cast(vmusage.xsu_avail);
+ return true;
#else
- return -1;
+ return false;
#endif
}
-julong os::physical_memory() {
+physical_memory_size_type os::physical_memory() {
return Bsd::physical_memory();
}
@@ -281,7 +286,7 @@ void os::Bsd::initialize_system_info() {
len = sizeof(mem_val);
if (sysctl(mib, 2, &mem_val, &len, nullptr, 0) != -1) {
assert(len == sizeof(mem_val), "unexpected data size");
- _physical_memory = mem_val;
+ _physical_memory = static_cast(mem_val);
} else {
_physical_memory = 256 * 1024 * 1024; // fallback (XXXBSD?)
}
@@ -292,7 +297,7 @@ void os::Bsd::initialize_system_info() {
// datasize rlimit restricts us anyway.
struct rlimit limits;
getrlimit(RLIMIT_DATA, &limits);
- _physical_memory = MIN2(_physical_memory, (julong)limits.rlim_cur);
+ _physical_memory = MIN2(_physical_memory, static_cast(limits.rlim_cur));
}
#endif
}
@@ -1042,6 +1047,8 @@ void *os::Bsd::dlopen_helper(const char *filename, int mode, char *ebuf, int ebu
assert(rtn == 0, "fegetenv must succeed");
#endif // IA32
+ Events::log_dll_message(nullptr, "Attempting to load shared library %s", filename);
+
void* result;
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
result = ::dlopen(filename, RTLD_LAZY);
@@ -1465,11 +1472,13 @@ void os::print_memory_info(outputStream* st) {
st->print("Memory:");
st->print(" %zuk page", os::vm_page_size()>>10);
-
- st->print(", physical " UINT64_FORMAT "k",
- os::physical_memory() >> 10);
- st->print("(" UINT64_FORMAT "k free)",
- os::available_memory() >> 10);
+ physical_memory_size_type phys_mem = os::physical_memory();
+ st->print(", physical " PHYS_MEM_TYPE_FORMAT "k",
+ phys_mem >> 10);
+ physical_memory_size_type avail_mem = 0;
+ (void)os::available_memory(avail_mem);
+ st->print("(" PHYS_MEM_TYPE_FORMAT "k free)",
+ avail_mem >> 10);
if((sysctlbyname("vm.swapusage", &swap_usage, &size, nullptr, 0) == 0) || (errno == ENOMEM)) {
if (size >= offset_of(xsw_usage, xsu_used)) {
diff --git a/src/hotspot/os/bsd/os_bsd.hpp b/src/hotspot/os/bsd/os_bsd.hpp
index 72de9ca59719..82002917f39d 100644
--- a/src/hotspot/os/bsd/os_bsd.hpp
+++ b/src/hotspot/os/bsd/os_bsd.hpp
@@ -42,12 +42,12 @@ class os::Bsd {
protected:
- static julong _physical_memory;
+ static physical_memory_size_type _physical_memory;
static pthread_t _main_thread;
- static julong available_memory();
- static julong free_memory();
- static julong physical_memory() { return _physical_memory; }
+ static bool available_memory(physical_memory_size_type& value);
+ static bool free_memory(physical_memory_size_type& value);
+ static physical_memory_size_type physical_memory() { return _physical_memory; }
static void initialize_system_info();
static void rebuild_cpu_to_node_map();
diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
index a9cabc873356..3186d97ec614 100644
--- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
+++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
@@ -670,8 +670,8 @@ jlong CgroupSubsystem::memory_limit_in_bytes() {
if (!memory_limit->should_check_metric()) {
return memory_limit->value();
}
- jlong phys_mem = os::Linux::physical_memory();
- log_trace(os, container)("total physical memory: " JLONG_FORMAT, phys_mem);
+ julong phys_mem = static_cast(os::Linux::physical_memory());
+ log_trace(os, container)("total physical memory: " JULONG_FORMAT, phys_mem);
jlong mem_limit = contrl->controller()->read_memory_limit_in_bytes(phys_mem);
// Update cached metric to avoid re-reading container settings too often
memory_limit->set_value(mem_limit, OSCONTAINER_CACHE_TIMEOUT);
@@ -841,19 +841,19 @@ jlong CgroupController::limit_from_str(char* limit_str) {
// CgroupSubsystem implementations
jlong CgroupSubsystem::memory_and_swap_limit_in_bytes() {
- julong phys_mem = os::Linux::physical_memory();
+ julong phys_mem = static_cast(os::Linux::physical_memory());
julong host_swap = os::Linux::host_swap();
return memory_controller()->controller()->memory_and_swap_limit_in_bytes(phys_mem, host_swap);
}
jlong CgroupSubsystem::memory_and_swap_usage_in_bytes() {
- julong phys_mem = os::Linux::physical_memory();
+ julong phys_mem = static_cast(os::Linux::physical_memory());
julong host_swap = os::Linux::host_swap();
return memory_controller()->controller()->memory_and_swap_usage_in_bytes(phys_mem, host_swap);
}
jlong CgroupSubsystem::memory_soft_limit_in_bytes() {
- julong phys_mem = os::Linux::physical_memory();
+ julong phys_mem = static_cast(os::Linux::physical_memory());
return memory_controller()->controller()->memory_soft_limit_in_bytes(phys_mem);
}
@@ -894,6 +894,6 @@ jlong CgroupSubsystem::cpu_usage_in_micros() {
}
void CgroupSubsystem::print_version_specific_info(outputStream* st) {
- julong phys_mem = os::Linux::physical_memory();
+ julong phys_mem = static_cast(os::Linux::physical_memory());
memory_controller()->controller()->print_version_specific_info(st, phys_mem);
}
diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp
index b52ef87dcaee..72dda36504d3 100644
--- a/src/hotspot/os/linux/cgroupUtil_linux.cpp
+++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp
@@ -65,7 +65,7 @@ void CgroupUtil::adjust_controller(CgroupMemoryController* mem) {
char* cg_path = os::strdup(orig);
char* last_slash;
assert(cg_path[0] == '/', "cgroup path must start with '/'");
- julong phys_mem = os::Linux::physical_memory();
+ julong phys_mem = static_cast(os::Linux::physical_memory());
char* limit_cg_path = nullptr;
jlong limit = mem->read_memory_limit_in_bytes(phys_mem);
jlong lowest_limit = limit < 0 ? phys_mem : limit;
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index 47c6d4fee8b8..577a7e2a54de 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -157,7 +157,7 @@ enum CoredumpFilterBit {
////////////////////////////////////////////////////////////////////////////////
// global variables
-julong os::Linux::_physical_memory = 0;
+physical_memory_size_type os::Linux::_physical_memory = 0;
address os::Linux::_initial_thread_stack_bottom = nullptr;
uintptr_t os::Linux::_initial_thread_stack_size = 0;
@@ -232,15 +232,16 @@ julong os::Linux::available_memory_in_container() {
return avail_mem;
}
-julong os::available_memory() {
- return Linux::available_memory();
+bool os::available_memory(physical_memory_size_type& value) {
+ return Linux::available_memory(value);
}
-julong os::Linux::available_memory() {
+bool os::Linux::available_memory(physical_memory_size_type& value) {
julong avail_mem = available_memory_in_container();
if (avail_mem != static_cast(-1L)) {
log_trace(os)("available container memory: " JULONG_FORMAT, avail_mem);
- return avail_mem;
+ value = static_cast(avail_mem);
+ return true;
}
FILE *fp = os::fopen("/proc/meminfo", "r");
@@ -255,66 +256,88 @@ julong os::Linux::available_memory() {
fclose(fp);
}
if (avail_mem == static_cast(-1L)) {
- avail_mem = free_memory();
+ physical_memory_size_type free_mem = 0;
+ if (!free_memory(free_mem)) {
+ return false;
+ }
+ avail_mem = static_cast(free_mem);
}
log_trace(os)("available memory: " JULONG_FORMAT, avail_mem);
- return avail_mem;
+ value = static_cast(avail_mem);
+ return true;
}
-julong os::free_memory() {
- return Linux::free_memory();
+bool os::free_memory(physical_memory_size_type& value) {
+ return Linux::free_memory(value);
}
-julong os::Linux::free_memory() {
+bool os::Linux::free_memory(physical_memory_size_type& value) {
// values in struct sysinfo are "unsigned long"
struct sysinfo si;
julong free_mem = available_memory_in_container();
if (free_mem != static_cast(-1L)) {
log_trace(os)("free container memory: " JULONG_FORMAT, free_mem);
- return free_mem;
+ value = static_cast(free_mem);
+ return true;
}
- sysinfo(&si);
+ int ret = sysinfo(&si);
+ if (ret != 0) {
+ return false;
+ }
free_mem = (julong)si.freeram * si.mem_unit;
log_trace(os)("free memory: " JULONG_FORMAT, free_mem);
- return free_mem;
+ value = static_cast(free_mem);
+ return true;
}
-jlong os::total_swap_space() {
+bool os::total_swap_space(physical_memory_size_type& value) {
if (OSContainer::is_containerized()) {
- if (OSContainer::memory_limit_in_bytes() > 0) {
- return (jlong)(OSContainer::memory_and_swap_limit_in_bytes() - OSContainer::memory_limit_in_bytes());
+ jlong memory_and_swap_limit_in_bytes = OSContainer::memory_and_swap_limit_in_bytes();
+ jlong memory_limit_in_bytes = OSContainer::memory_limit_in_bytes();
+ if (memory_limit_in_bytes > 0 && memory_and_swap_limit_in_bytes > 0) {
+ value = static_cast(memory_and_swap_limit_in_bytes - memory_limit_in_bytes);
+ return true;
}
- }
+ } // fallback to the host swap space if the container did return the unbound value of -1
struct sysinfo si;
int ret = sysinfo(&si);
if (ret != 0) {
- return -1;
+ assert(false, "sysinfo failed in total_swap_space(): %s", os::strerror(errno));
+ return false;
}
- return (jlong)(si.totalswap * si.mem_unit);
+ value = static_cast(si.totalswap) * si.mem_unit;
+ return true;
}
-static jlong host_free_swap() {
+static bool host_free_swap_f(physical_memory_size_type& value) {
struct sysinfo si;
int ret = sysinfo(&si);
if (ret != 0) {
- return -1;
+ assert(false, "sysinfo failed in host_free_swap_f(): %s", os::strerror(errno));
+ return false;
}
- return (jlong)(si.freeswap * si.mem_unit);
+ value = static_cast(si.freeswap) * si.mem_unit;
+ return true;
}
-jlong os::free_swap_space() {
+bool os::free_swap_space(physical_memory_size_type& value) {
// os::total_swap_space() might return the containerized limit which might be
// less than host_free_swap(). The upper bound of free swap needs to be the lower of the two.
- jlong host_free_swap_val = MIN2(os::total_swap_space(), host_free_swap());
- assert(host_free_swap_val >= 0, "sysinfo failed?");
+ physical_memory_size_type total_swap_space = 0;
+ physical_memory_size_type host_free_swap = 0;
+ if (!os::total_swap_space(total_swap_space) || !host_free_swap_f(host_free_swap)) {
+ return false;
+ }
+ physical_memory_size_type host_free_swap_val = MIN2(total_swap_space, host_free_swap);
if (OSContainer::is_containerized()) {
jlong mem_swap_limit = OSContainer::memory_and_swap_limit_in_bytes();
jlong mem_limit = OSContainer::memory_limit_in_bytes();
if (mem_swap_limit >= 0 && mem_limit >= 0) {
jlong delta_limit = mem_swap_limit - mem_limit;
if (delta_limit <= 0) {
- return 0;
+ value = 0;
+ return true;
}
jlong mem_swap_usage = OSContainer::memory_and_swap_usage_in_bytes();
jlong mem_usage = OSContainer::memory_usage_in_bytes();
@@ -322,30 +345,31 @@ jlong os::free_swap_space() {
jlong delta_usage = mem_swap_usage - mem_usage;
if (delta_usage >= 0) {
jlong free_swap = delta_limit - delta_usage;
- return free_swap >= 0 ? free_swap : 0;
+ value = free_swap >= 0 ? static_cast(free_swap) : 0;
+ return true;
}
}
}
// unlimited or not supported. Fall through to return host value
log_trace(os,container)("os::free_swap_space: container_swap_limit=" JLONG_FORMAT
- " container_mem_limit=" JLONG_FORMAT " returning host value: " JLONG_FORMAT,
+ " container_mem_limit=" JLONG_FORMAT " returning host value: " PHYS_MEM_TYPE_FORMAT,
mem_swap_limit, mem_limit, host_free_swap_val);
}
- return host_free_swap_val;
+ value = host_free_swap_val;
+ return true;
}
-julong os::physical_memory() {
- jlong phys_mem = 0;
+physical_memory_size_type os::physical_memory() {
if (OSContainer::is_containerized()) {
jlong mem_limit;
if ((mem_limit = OSContainer::memory_limit_in_bytes()) > 0) {
log_trace(os)("total container memory: " JLONG_FORMAT, mem_limit);
- return mem_limit;
+ return static_cast(mem_limit);
}
}
- phys_mem = Linux::physical_memory();
- log_trace(os)("total system memory: " JLONG_FORMAT, phys_mem);
+ physical_memory_size_type phys_mem = Linux::physical_memory();
+ log_trace(os)("total system memory: " PHYS_MEM_TYPE_FORMAT, phys_mem);
return phys_mem;
}
@@ -527,7 +551,7 @@ void os::Linux::initialize_system_info() {
fclose(fp);
}
}
- _physical_memory = (julong)sysconf(_SC_PHYS_PAGES) * (julong)sysconf(_SC_PAGESIZE);
+ _physical_memory = static_cast(sysconf(_SC_PHYS_PAGES)) * static_cast(sysconf(_SC_PAGESIZE));
assert(processor_count() > 0, "linux error");
}
@@ -1925,6 +1949,8 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
assert(rtn == 0, "fegetenv must succeed");
#endif // IA32
+ Events::log_dll_message(nullptr, "Attempting to load shared library %s", filename);
+
void* result;
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
result = ::dlopen(filename, RTLD_LAZY);
@@ -2677,10 +2703,13 @@ void os::print_memory_info(outputStream* st) {
struct sysinfo si;
int ret = sysinfo(&si);
assert(ret == 0, "sysinfo failed: %s", os::strerror(errno));
- st->print(", physical " UINT64_FORMAT "k",
- os::physical_memory() >> 10);
- st->print("(" UINT64_FORMAT "k free)",
- os::available_memory() >> 10);
+ physical_memory_size_type phys_mem = physical_memory();
+ st->print(", physical " PHYS_MEM_TYPE_FORMAT "k",
+ phys_mem >> 10);
+ physical_memory_size_type avail_mem = 0;
+ (void)os::available_memory(avail_mem);
+ st->print("(" PHYS_MEM_TYPE_FORMAT "k free)",
+ avail_mem >> 10);
if (ret == 0) {
st->print(", swap " UINT64_FORMAT "k",
((jlong)si.totalswap * si.mem_unit) >> 10);
diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp
index 8acbe6476045..91f057b469d4 100644
--- a/src/hotspot/os/linux/os_linux.hpp
+++ b/src/hotspot/os/linux/os_linux.hpp
@@ -50,11 +50,11 @@ class os::Linux {
protected:
- static julong _physical_memory;
+ static physical_memory_size_type _physical_memory;
static pthread_t _main_thread;
- static julong available_memory();
- static julong free_memory();
+ static bool available_memory(physical_memory_size_type& value);
+ static bool free_memory(physical_memory_size_type& value);
static void initialize_system_info();
@@ -119,7 +119,7 @@ class os::Linux {
static address initial_thread_stack_bottom(void) { return _initial_thread_stack_bottom; }
static uintptr_t initial_thread_stack_size(void) { return _initial_thread_stack_size; }
- static julong physical_memory() { return _physical_memory; }
+ static physical_memory_size_type physical_memory() { return _physical_memory; }
static julong host_swap();
static intptr_t* ucontext_get_sp(const ucontext_t* uc);
diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp
index e3483781794e..3deb7efeb9bc 100644
--- a/src/hotspot/os/posix/perfMemory_posix.cpp
+++ b/src/hotspot/os/posix/perfMemory_posix.cpp
@@ -111,6 +111,10 @@ static void save_memory_to_file(char* addr, size_t size) {
result = ::close(fd);
if (result == OS_ERR) {
warning("Could not close %s: %s\n", destfile, os::strerror(errno));
+ } else {
+ if (!successful_write) {
+ remove(destfile);
+ }
}
}
FREE_C_HEAP_ARRAY(char, destfile);
@@ -953,6 +957,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size
warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename);
}
result = OS_ERR;
+ remove(filename);
break;
}
}
diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index e602acbfae3d..fee71654cf40 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -848,39 +848,56 @@ jlong os::elapsed_frequency() {
}
-julong os::available_memory() {
- return win32::available_memory();
+bool os::available_memory(physical_memory_size_type& value) {
+ return win32::available_memory(value);
}
-julong os::free_memory() {
- return win32::available_memory();
+bool os::free_memory(physical_memory_size_type& value) {
+ return win32::available_memory(value);
}
-julong os::win32::available_memory() {
+bool os::win32::available_memory(physical_memory_size_type& value) {
// Use GlobalMemoryStatusEx() because GlobalMemoryStatus() may return incorrect
// value if total memory is larger than 4GB
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
- GlobalMemoryStatusEx(&ms);
-
- return (julong)ms.ullAvailPhys;
+ BOOL res = GlobalMemoryStatusEx(&ms);
+ if (res == TRUE) {
+ value = static_cast(ms.ullAvailPhys);
+ return true;
+ } else {
+ assert(false, "GlobalMemoryStatusEx failed in os::win32::available_memory(): %lu", ::GetLastError());
+ return false;
+ }
}
-jlong os::total_swap_space() {
+bool os::total_swap_space(physical_memory_size_type& value) {
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
- GlobalMemoryStatusEx(&ms);
- return (jlong) ms.ullTotalPageFile;
+ BOOL res = GlobalMemoryStatusEx(&ms);
+ if (res == TRUE) {
+ value = static_cast(ms.ullTotalPageFile);
+ return true;
+ } else {
+ assert(false, "GlobalMemoryStatusEx failed in os::total_swap_space(): %lu", ::GetLastError());
+ return false;
+ }
}
-jlong os::free_swap_space() {
+bool os::free_swap_space(physical_memory_size_type& value) {
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
- GlobalMemoryStatusEx(&ms);
- return (jlong) ms.ullAvailPageFile;
+ BOOL res = GlobalMemoryStatusEx(&ms);
+ if (res == TRUE) {
+ value = static_cast(ms.ullAvailPageFile);
+ return true;
+ } else {
+ assert(false, "GlobalMemoryStatusEx failed in os::free_swap_space(): %lu", ::GetLastError());
+ return false;
+ }
}
-julong os::physical_memory() {
+physical_memory_size_type os::physical_memory() {
return win32::physical_memory();
}
@@ -1702,6 +1719,8 @@ static int _print_module(const char* fname, address base_address,
// same architecture as Hotspot is running on
void * os::dll_load(const char *name, char *ebuf, int ebuflen) {
log_info(os)("attempting shared library load of %s", name);
+ Events::log_dll_message(nullptr, "Attempting to load shared library %s", name);
+
void* result;
JFR_ONLY(NativeLibraryLoadEvent load_event(name, &result);)
result = LoadLibrary(name);
@@ -3895,25 +3914,25 @@ int os::current_process_id() {
return (_initial_pid ? _initial_pid : _getpid());
}
-int os::win32::_processor_type = 0;
+int os::win32::_processor_type = 0;
// Processor level is not available on non-NT systems, use vm_version instead
-int os::win32::_processor_level = 0;
-julong os::win32::_physical_memory = 0;
+int os::win32::_processor_level = 0;
+physical_memory_size_type os::win32::_physical_memory = 0;
-bool os::win32::_is_windows_server = false;
+bool os::win32::_is_windows_server = false;
// 6573254
// Currently, the bug is observed across all the supported Windows releases,
// including the latest one (as of this writing - Windows Server 2012 R2)
-bool os::win32::_has_exit_bug = true;
+bool os::win32::_has_exit_bug = true;
-int os::win32::_major_version = 0;
-int os::win32::_minor_version = 0;
-int os::win32::_build_number = 0;
-int os::win32::_build_minor = 0;
+int os::win32::_major_version = 0;
+int os::win32::_minor_version = 0;
+int os::win32::_build_number = 0;
+int os::win32::_build_minor = 0;
-bool os::win32::_processor_group_warning_displayed = false;
-bool os::win32::_job_object_processor_group_warning_displayed = false;
+bool os::win32::_processor_group_warning_displayed = false;
+bool os::win32::_job_object_processor_group_warning_displayed = false;
void getWindowsInstallationType(char* buffer, int bufferSize) {
HKEY hKey;
@@ -4128,8 +4147,11 @@ void os::win32::initialize_system_info() {
// also returns dwAvailPhys (free physical memory bytes), dwTotalVirtual, dwAvailVirtual,
// dwMemoryLoad (% of memory in use)
- GlobalMemoryStatusEx(&ms);
- _physical_memory = ms.ullTotalPhys;
+ BOOL res = GlobalMemoryStatusEx(&ms);
+ if (res != TRUE) {
+ assert(false, "GlobalMemoryStatusEx failed in os::win32::initialize_system_info(): %lu", ::GetLastError());
+ }
+ _physical_memory = static_cast(ms.ullTotalPhys);
if (FLAG_IS_DEFAULT(MaxRAM)) {
// Adjust MaxRAM according to the maximum virtual address space available.
diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp
index 1aba43fb3d25..efb7b4149897 100644
--- a/src/hotspot/os/windows/os_windows.hpp
+++ b/src/hotspot/os/windows/os_windows.hpp
@@ -38,18 +38,18 @@ class os::win32 {
friend class os;
protected:
- static int _processor_type;
- static int _processor_level;
- static julong _physical_memory;
- static bool _is_windows_server;
- static bool _has_exit_bug;
- static bool _processor_group_warning_displayed;
- static bool _job_object_processor_group_warning_displayed;
-
- static int _major_version;
- static int _minor_version;
- static int _build_number;
- static int _build_minor;
+ static int _processor_type;
+ static int _processor_level;
+ static physical_memory_size_type _physical_memory;
+ static bool _is_windows_server;
+ static bool _has_exit_bug;
+ static bool _processor_group_warning_displayed;
+ static bool _job_object_processor_group_warning_displayed;
+
+ static int _major_version;
+ static int _minor_version;
+ static int _build_number;
+ static int _build_minor;
static void print_windows_version(outputStream* st);
static void print_uptime_info(outputStream* st);
@@ -102,9 +102,9 @@ class os::win32 {
static int processor_level() {
return _processor_level;
}
- static julong available_memory();
- static julong free_memory();
- static julong physical_memory() { return _physical_memory; }
+ static bool available_memory(physical_memory_size_type& value);
+ static bool free_memory(physical_memory_size_type& value);
+ static physical_memory_size_type physical_memory() { return _physical_memory; }
// load dll from Windows system directory or Windows directory
static HINSTANCE load_Windows_dll(const char* name, char *ebuf, int ebuflen);
diff --git a/src/hotspot/os_cpu/linux_arm/macroAssembler_linux_arm_32.cpp b/src/hotspot/os_cpu/linux_arm/macroAssembler_linux_arm_32.cpp
index e74daaa6d666..e4737191cfcd 100644
--- a/src/hotspot/os_cpu/linux_arm/macroAssembler_linux_arm_32.cpp
+++ b/src/hotspot/os_cpu/linux_arm/macroAssembler_linux_arm_32.cpp
@@ -246,9 +246,9 @@ void MacroAssembler::atomic_cas64(Register memval_lo, Register memval_hi, Regist
Label loop;
assert_different_registers(memval_lo, memval_hi, result, oldval_lo,
oldval_hi, newval_lo, newval_hi, base);
- assert(memval_hi == memval_lo + 1 && memval_lo < R9, "cmpxchg_long: illegal registers");
- assert(oldval_hi == oldval_lo + 1 && oldval_lo < R9, "cmpxchg_long: illegal registers");
- assert(newval_hi == newval_lo + 1 && newval_lo < R9, "cmpxchg_long: illegal registers");
+ assert(memval_hi == as_Register(memval_lo->encoding() + 1) && memval_lo->encoding() < R9->encoding(), "cmpxchg_long: illegal registers");
+ assert(oldval_hi == as_Register(oldval_lo->encoding() + 1) && oldval_lo->encoding() < R9->encoding(), "cmpxchg_long: illegal registers");
+ assert(newval_hi == as_Register(newval_lo->encoding() + 1) && newval_lo->encoding() < R9->encoding(), "cmpxchg_long: illegal registers");
assert(result != R10, "cmpxchg_long: illegal registers");
assert(base != R10, "cmpxchg_long: illegal registers");
diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp
index ff7fce234c4c..5a84cfef7bda 100644
--- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp
+++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp
@@ -51,6 +51,7 @@
#include "utilities/debug.hpp"
#include "utilities/events.hpp"
#include "utilities/vmError.hpp"
+#include "runtime/vm_version.hpp"
// put OS-includes here
# include
@@ -516,6 +517,43 @@ size_t os::Posix::default_stack_size(os::ThreadType thr_type) {
/////////////////////////////////////////////////////////////////////////////
// helper functions for fatal error handler
+// XSAVE constants - from Intel SDM Vol. 1, Chapter 13
+#define XSAVE_HDR_OFFSET 512
+#define XFEATURE_APX (1ULL << 19)
+
+// XSAVE header structure
+// See: Intel SDM Vol. 1, Section 13.4.2 "XSAVE Header"
+// Also: Linux kernel arch/x86/include/asm/fpu/types.h
+struct xstate_header {
+ uint64_t xfeatures;
+ uint64_t xcomp_bv;
+ uint64_t reserved[6];
+};
+
+// APX extended state - R16-R31 (16 x 64-bit registers)
+// See: Intel APX Architecture Specification
+struct apx_state {
+ uint64_t regs[16]; // r16-r31
+};
+
+static apx_state* get_apx_state(const ucontext_t* uc) {
+ uint32_t offset = VM_Version::apx_xstate_offset();
+ if (offset == 0 || uc->uc_mcontext.fpregs == nullptr) {
+ return nullptr;
+ }
+
+ char* xsave = (char*)uc->uc_mcontext.fpregs;
+ xstate_header* hdr = (xstate_header*)(xsave + XSAVE_HDR_OFFSET);
+
+ // Check if APX state is present in this context
+ if (!(hdr->xfeatures & XFEATURE_APX)) {
+ return nullptr;
+ }
+
+ return (apx_state*)(xsave + offset);
+}
+
+
void os::print_context(outputStream *st, const void *context) {
if (context == nullptr) return;
@@ -543,6 +581,14 @@ void os::print_context(outputStream *st, const void *context) {
st->print(", R14=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_R14]);
st->print(", R15=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_R15]);
st->cr();
+ // Dump APX EGPRs (R16-R31)
+ apx_state* apx = UseAPX ? get_apx_state(uc) : nullptr;
+ if (apx != nullptr) {
+ for (int i = 0; i < 16; i++) {
+ st->print("%sR%d=" INTPTR_FORMAT, (i % 4 == 0) ? "" : ", ", 16 + i, (intptr_t)apx->regs[i]);
+ if (i % 4 == 3) st->cr();
+ }
+ }
st->print( "RIP=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_RIP]);
st->print(", EFLAGS=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_EFL]);
st->print(", CSGSFS=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_CSGSFS]);
@@ -584,36 +630,44 @@ void os::print_context(outputStream *st, const void *context) {
}
void os::print_register_info(outputStream *st, const void *context, int& continuation) {
- const int register_count = AMD64_ONLY(16) NOT_AMD64(8);
+ if (context == nullptr) {
+ return;
+ }
+ const ucontext_t *uc = (const ucontext_t*)context;
+ apx_state* apx = UseAPX ? get_apx_state(uc) : nullptr;
+
+ const int register_count = AMD64_ONLY(16 + (apx != nullptr ? 16 : 0)) NOT_AMD64(8);
int n = continuation;
assert(n >= 0 && n <= register_count, "Invalid continuation value");
- if (context == nullptr || n == register_count) {
+ if (n == register_count) {
return;
}
- const ucontext_t *uc = (const ucontext_t*)context;
while (n < register_count) {
// Update continuation with next index before printing location
continuation = n + 1;
+
+ if (n < 16) {
+ // Standard registers (RAX-R15)
# define CASE_PRINT_REG(n, str, id) case n: st->print(str); print_location(st, uc->uc_mcontext.gregs[REG_##id]);
- switch (n) {
+ switch (n) {
#ifdef AMD64
- CASE_PRINT_REG( 0, "RAX=", RAX); break;
- CASE_PRINT_REG( 1, "RBX=", RBX); break;
- CASE_PRINT_REG( 2, "RCX=", RCX); break;
- CASE_PRINT_REG( 3, "RDX=", RDX); break;
- CASE_PRINT_REG( 4, "RSP=", RSP); break;
- CASE_PRINT_REG( 5, "RBP=", RBP); break;
- CASE_PRINT_REG( 6, "RSI=", RSI); break;
- CASE_PRINT_REG( 7, "RDI=", RDI); break;
- CASE_PRINT_REG( 8, "R8 =", R8); break;
- CASE_PRINT_REG( 9, "R9 =", R9); break;
- CASE_PRINT_REG(10, "R10=", R10); break;
- CASE_PRINT_REG(11, "R11=", R11); break;
- CASE_PRINT_REG(12, "R12=", R12); break;
- CASE_PRINT_REG(13, "R13=", R13); break;
- CASE_PRINT_REG(14, "R14=", R14); break;
- CASE_PRINT_REG(15, "R15=", R15); break;
+ CASE_PRINT_REG( 0, "RAX=", RAX); break;
+ CASE_PRINT_REG( 1, "RBX=", RBX); break;
+ CASE_PRINT_REG( 2, "RCX=", RCX); break;
+ CASE_PRINT_REG( 3, "RDX=", RDX); break;
+ CASE_PRINT_REG( 4, "RSP=", RSP); break;
+ CASE_PRINT_REG( 5, "RBP=", RBP); break;
+ CASE_PRINT_REG( 6, "RSI=", RSI); break;
+ CASE_PRINT_REG( 7, "RDI=", RDI); break;
+ CASE_PRINT_REG( 8, "R8 =", R8); break;
+ CASE_PRINT_REG( 9, "R9 =", R9); break;
+ CASE_PRINT_REG(10, "R10=", R10); break;
+ CASE_PRINT_REG(11, "R11=", R11); break;
+ CASE_PRINT_REG(12, "R12=", R12); break;
+ CASE_PRINT_REG(13, "R13=", R13); break;
+ CASE_PRINT_REG(14, "R14=", R14); break;
+ CASE_PRINT_REG(15, "R15=", R15); break;
#else
CASE_PRINT_REG(0, "EAX=", EAX); break;
CASE_PRINT_REG(1, "EBX=", EBX); break;
@@ -624,8 +678,13 @@ void os::print_register_info(outputStream *st, const void *context, int& continu
CASE_PRINT_REG(6, "ESI=", ESI); break;
CASE_PRINT_REG(7, "EDI=", EDI); break;
#endif // AMD64
- }
+ }
# undef CASE_PRINT_REG
+ } else {
+ // APX extended general purpose registers (R16-R31)
+ st->print("R%d=", n);
+ print_location(st, apx->regs[n - 16]);
+ }
++n;
}
}
diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp
index b662c5a1b478..ed23f255173b 100644
--- a/src/hotspot/share/cds/aotClassLocation.cpp
+++ b/src/hotspot/share/cds/aotClassLocation.cpp
@@ -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
@@ -432,7 +432,8 @@ bool AOTClassLocation::check(const char* runtime_path, bool has_aot_linked_class
bool size_differs = _filesize != st.st_size;
bool time_differs = _check_time && (_timestamp != st.st_mtime);
if (size_differs || time_differs) {
- aot_log_warning(aot)("This file is not the one used while building the shared archive file: '%s'%s%s",
+ aot_log_warning(aot)("This file is not the one used while building the %s: '%s'%s%s",
+ CDSConfig::type_of_archive_being_loaded(),
runtime_path,
time_differs ? ", timestamp has changed" : "",
size_differs ? ", size has changed" : "");
@@ -454,6 +455,13 @@ void AOTClassLocationConfig::dumptime_init(JavaThread* current) {
java_lang_Throwable::print(current->pending_exception(), tty);
vm_exit_during_initialization("AOTClassLocationConfig::dumptime_init_helper() failed unexpectedly");
}
+
+ if (CDSConfig::is_dumping_final_static_archive()) {
+ // The _max_used_index is usually updated by ClassLoader::record_result(). However,
+ // when dumping the final archive, the classes are loaded from their images in
+ // the AOT config file, so we don't go through ClassLoader::record_result().
+ dumptime_update_max_used_index(runtime()->_max_used_index); // Same value as recorded in the training run.
+ }
}
void AOTClassLocationConfig::dumptime_init_helper(TRAPS) {
diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
index 6ab63418e09e..3ea0c76f8702 100644
--- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp
+++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
@@ -81,6 +81,7 @@ bool AOTConstantPoolResolver::is_resolution_deterministic(ConstantPool* cp, int
bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class) {
assert(!is_in_archivebuilder_buffer(cp_holder), "sanity");
assert(!is_in_archivebuilder_buffer(resolved_class), "sanity");
+ assert_at_safepoint(); // try_add_candidate() is called below and requires to be at safepoint.
if (resolved_class->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(resolved_class);
@@ -285,7 +286,15 @@ void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m
break;
case Bytecodes::_invokehandle:
- InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK);
+ if (CDSConfig::is_dumping_method_handles()) {
+ ResolvedMethodEntry* method_entry = cp->resolved_method_entry_at(raw_index);
+ int cp_index = method_entry->constant_pool_index();
+ Symbol* sig = cp->uncached_signature_ref_at(cp_index);
+ Klass* k;
+ if (check_methodtype_signature(cp(), sig, &k, true)) {
+ InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK);
+ }
+ }
break;
default:
@@ -339,7 +348,7 @@ void AOTConstantPoolResolver::preresolve_indy_cp_entries(JavaThread* current, In
// Check the MethodType signatures used by parameters to the indy BSMs. Make sure we don't
// use types that have been excluded, or else we might end up creating MethodTypes that cannot be stored
// in the AOT cache.
-bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret) {
+bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret, bool is_invokehandle) {
ResourceMark rm;
for (SignatureStream ss(sig); !ss.is_done(); ss.next()) {
if (ss.is_reference()) {
@@ -352,11 +361,18 @@ bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbo
if (SystemDictionaryShared::should_be_excluded(k)) {
if (log_is_enabled(Warning, aot, resolve)) {
ResourceMark rm;
- log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name());
+ log_warning(aot, resolve)("Cannot aot-resolve %s because %s is excluded",
+ is_invokehandle ? "invokehandle" : "Lambda proxy",
+ k->external_name());
}
return false;
}
+ // cp->pool_holder() must be able to resolve k in production run
+ precond(CDSConfig::is_dumping_aot_linked_classes());
+ precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data()));
+ precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data()));
+
if (ss.at_return_type() && return_type_ret != nullptr) {
*return_type_ret = k;
}
@@ -414,11 +430,44 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(Constant
return false;
}
+ // klass and sigature of the method (no need to check the method name)
Symbol* sig = cp->method_handle_signature_ref_at(mh_index);
+ Symbol* klass_name = cp->klass_name_at(cp->method_handle_klass_index_at(mh_index));
+
if (log_is_enabled(Debug, aot, resolve)) {
ResourceMark rm;
log_debug(aot, resolve)("Checking MethodType of MethodHandle for LambdaMetafactory BSM arg %d: %s", arg_i, sig->as_C_string());
}
+
+ {
+ Klass* k = find_loaded_class(Thread::current(), cp->pool_holder()->class_loader(), klass_name);
+ if (k == nullptr) {
+ // Dumping AOT cache: all classes should have been loaded by FinalImageRecipes::load_all_classes(). k must have
+ // been a class that was excluded when FinalImageRecipes recorded all classes at the end of the training run.
+ //
+ // Dumping static CDS archive: all classes in the classlist have already been loaded, before we resolve
+ // constants. k must have been a class that was excluded when the classlist was written
+ // at the end of the training run.
+ if (log_is_enabled(Warning, aot, resolve)) {
+ ResourceMark rm;
+ log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is not loaded", klass_name->as_C_string());
+ }
+ return false;
+ }
+ if (SystemDictionaryShared::should_be_excluded(k)) {
+ if (log_is_enabled(Warning, aot, resolve)) {
+ ResourceMark rm;
+ log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name());
+ }
+ return false;
+ }
+
+ // cp->pool_holder() must be able to resolve k in production run
+ precond(CDSConfig::is_dumping_aot_linked_classes());
+ precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data()));
+ precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data()));
+ }
+
return check_methodtype_signature(cp, sig);
}
diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.hpp b/src/hotspot/share/cds/aotConstantPoolResolver.hpp
index 89d9d5c24764..6ad762b4121d 100644
--- a/src/hotspot/share/cds/aotConstantPoolResolver.hpp
+++ b/src/hotspot/share/cds/aotConstantPoolResolver.hpp
@@ -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
@@ -74,7 +74,10 @@ class AOTConstantPoolResolver : AllStatic {
static void maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index,
GrowableArray* resolve_fmi_list, TRAPS);
- static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr);
+public:
+ static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr, bool is_invokehandle = false);
+
+private:
static bool check_lambda_metafactory_signature(ConstantPool* cp, Symbol* sig);
static bool check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i);
static bool check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i);
diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp
index ee1e334e84bf..6c6ab9012676 100644
--- a/src/hotspot/share/cds/archiveHeapWriter.cpp
+++ b/src/hotspot/share/cds/archiveHeapWriter.cpp
@@ -754,8 +754,15 @@ void ArchiveHeapWriter::compute_ptrmap(ArchiveHeapInfo* heap_info) {
Metadata** buffered_field_addr = requested_addr_to_buffered_addr(requested_field_addr);
Metadata* native_ptr = *buffered_field_addr;
guarantee(native_ptr != nullptr, "sanity");
- guarantee(ArchiveBuilder::current()->has_been_buffered((address)native_ptr),
- "Metadata %p should have been archived", native_ptr);
+
+ if (!ArchiveBuilder::current()->has_been_archived((address)native_ptr)) {
+ ResourceMark rm;
+ LogStreamHandle(Error, aot) log;
+ log.print("Marking native pointer for oop %p (type = %s, offset = %d)",
+ cast_from_oop(src_obj), src_obj->klass()->external_name(), field_offset);
+ src_obj->print_on(&log);
+ fatal("Metadata %p should have been archived", native_ptr);
+ }
address buffered_native_ptr = ArchiveBuilder::current()->get_buffered_addr((address)native_ptr);
address requested_native_ptr = ArchiveBuilder::current()->to_requested(buffered_native_ptr);
diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp
index 5556e4684445..a8217af5ed39 100644
--- a/src/hotspot/share/classfile/classLoader.cpp
+++ b/src/hotspot/share/classfile/classLoader.cpp
@@ -1387,6 +1387,10 @@ char* ClassLoader::lookup_vm_options() {
jio_snprintf(modules_path, JVM_MAXPATHLEN, "%s%slib%smodules", Arguments::get_java_home(), fileSep, fileSep);
JImage_file =(*JImageOpen)(modules_path, &error);
if (JImage_file == nullptr) {
+ if (Arguments::has_jimage()) {
+ // The modules file exists but is unreadable or corrupt
+ vm_exit_during_initialization(err_msg("Unable to load %s", modules_path));
+ }
return nullptr;
}
diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp
index 7f925388eb03..324b72f051da 100644
--- a/src/hotspot/share/code/dependencies.cpp
+++ b/src/hotspot/share/code/dependencies.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
@@ -1125,7 +1125,7 @@ class AbstractClassHierarchyWalker {
Klass* find_witness(InstanceKlass* context_type, KlassDepChange* changes = nullptr);
static void init();
- static void print_statistics();
+ NOT_PRODUCT(static void print_statistics();)
};
PerfCounter* AbstractClassHierarchyWalker::_perf_find_witness_anywhere_calls_count = nullptr;
@@ -2278,6 +2278,7 @@ bool KlassDepChange::involves_context(Klass* k) {
return is_contained;
}
+#ifndef PRODUCT
void Dependencies::print_statistics() {
AbstractClassHierarchyWalker::print_statistics();
}
@@ -2303,6 +2304,7 @@ void AbstractClassHierarchyWalker::print_statistics() {
}
}
}
+#endif
CallSiteDepChange::CallSiteDepChange(Handle call_site, Handle method_handle) :
_call_site(call_site),
diff --git a/src/hotspot/share/code/dependencies.hpp b/src/hotspot/share/code/dependencies.hpp
index d11c51a66dc6..582a08183f9c 100644
--- a/src/hotspot/share/code/dependencies.hpp
+++ b/src/hotspot/share/code/dependencies.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -649,7 +649,7 @@ class Dependencies: public ResourceObj {
};
friend class Dependencies::DepStream;
- static void print_statistics();
+ NOT_PRODUCT(static void print_statistics();)
};
diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp
index d842bcb2b6f8..49549b1142cc 100644
--- a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp
+++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp
@@ -1010,8 +1010,10 @@ void CompilationMemoryStatistic::print_error_report(outputStream* st) {
oom_stats->print_peak_state_on(st);
st->cr();
}
- st->print_cr("Compiler Memory Statistic, 10 most expensive compilations:");
- print_all_by_size(st, false, false, 0, 10);
+ if (Thread::current_or_null_safe() != nullptr) {
+ st->print_cr("Compiler Memory Statistic, 10 most expensive compilations:");
+ print_all_by_size(st, false, false, 0, 10);
+ }
}
void CompilationMemoryStatistic::print_final_report(outputStream* st) {
diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp
index f8711e8785a8..901c5ba9b555 100644
--- a/src/hotspot/share/compiler/compileBroker.cpp
+++ b/src/hotspot/share/compiler/compileBroker.cpp
@@ -1058,7 +1058,9 @@ void CompileBroker::possibly_add_compiler_threads(JavaThread* THREAD) {
if (new_c2_count <= old_c2_count && new_c1_count <= old_c1_count) return;
// Now, we do the more expensive operations.
- julong free_memory = os::free_memory();
+ physical_memory_size_type free_memory = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::free_memory(free_memory);
// If SegmentedCodeCache is off, both values refer to the single heap (with type CodeBlobType::All).
size_t available_cc_np = CodeCache::unallocated_capacity(CodeBlobType::MethodNonProfiled),
available_cc_p = CodeCache::unallocated_capacity(CodeBlobType::MethodProfiled);
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
index e96fbeba964a..f1f1c747af0e 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
@@ -2220,7 +2220,8 @@ void G1CollectedHeap::print_heap_regions() const {
}
void G1CollectedHeap::print_heap_on(outputStream* st) const {
- size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked();
+ size_t heap_used = (Thread::current_or_null_safe() != nullptr &&
+ Heap_lock->owned_by_self()) ? used() : used_unlocked();
st->print("%-20s", "garbage-first heap");
st->print(" total reserved %zuK, committed %zuK, used %zuK",
_hrm.reserved().byte_size()/K, capacity()/K, heap_used/K);
diff --git a/src/hotspot/share/gc/shared/gcInitLogger.cpp b/src/hotspot/share/gc/shared/gcInitLogger.cpp
index 91bebf726c12..ba6399458603 100644
--- a/src/hotspot/share/gc/shared/gcInitLogger.cpp
+++ b/src/hotspot/share/gc/shared/gcInitLogger.cpp
@@ -62,8 +62,8 @@ void GCInitLogger::print_cpu() {
}
void GCInitLogger::print_memory() {
- julong memory = os::physical_memory();
- log_info_p(gc, init)("Memory: " JULONG_FORMAT "%s",
+ physical_memory_size_type memory = os::physical_memory();
+ log_info_p(gc, init)("Memory: " PHYS_MEM_TYPE_FORMAT "%s",
byte_size_in_proper_unit(memory), proper_unit_for_byte_size(memory));
}
diff --git a/src/hotspot/share/gc/shared/gcLogPrecious.cpp b/src/hotspot/share/gc/shared/gcLogPrecious.cpp
index 43bd58db1aa0..d556eed1b690 100644
--- a/src/hotspot/share/gc/shared/gcLogPrecious.cpp
+++ b/src/hotspot/share/gc/shared/gcLogPrecious.cpp
@@ -25,6 +25,7 @@
#include "runtime/mutex.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
+#include "runtime/thread.hpp"
#include "utilities/ostream.hpp"
stringStream* GCLogPrecious::_lines = nullptr;
@@ -83,7 +84,8 @@ void GCLogPrecious::print_on_error(outputStream* st) {
return;
}
- if (!_lock->try_lock_without_rank_check()) {
+ if (Thread::current_or_null_safe() == nullptr ||
+ !_lock->try_lock_without_rank_check()) {
st->print_cr("\n");
return;
}
diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp
index 4b3cbf04b40e..9fdf0d63eccf 100644
--- a/src/hotspot/share/gc/shared/gc_globals.hpp
+++ b/src/hotspot/share/gc/shared/gc_globals.hpp
@@ -260,6 +260,7 @@
develop(uintx, ObjArrayMarkingStride, 2048, \
"Number of object array elements to push onto the marking stack " \
"before pushing a continuation entry") \
+ range(1, INT_MAX/2) \
\
product_pd(bool, NeverActAsServerClassMachine, \
"Never act like a server-class machine") \
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp
index 144773908caa..6498f0acdb67 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp
@@ -240,13 +240,14 @@ bool ShenandoahAdaptiveHeuristics::should_start_gc() {
log_debug(gc)("should_start_gc? available: %zu, soft_max_capacity: %zu"
", allocated: %zu", available, capacity, allocated);
+ // Track allocation rate even if we decide to start a cycle for other reasons.
+ double rate = _allocation_rate.sample(allocated);
+
if (_start_gc_is_pending) {
log_trigger("GC start is already pending");
return true;
}
- // Track allocation rate even if we decide to start a cycle for other reasons.
- double rate = _allocation_rate.sample(allocated);
_last_trigger = OTHER;
size_t min_threshold = min_free_threshold();
@@ -360,16 +361,32 @@ ShenandoahAllocationRate::ShenandoahAllocationRate() :
_rate_avg(int(ShenandoahAdaptiveSampleSizeSeconds * ShenandoahAdaptiveSampleFrequencyHz), ShenandoahAdaptiveDecayFactor) {
}
+double ShenandoahAllocationRate::force_sample(size_t allocated, size_t &unaccounted_bytes_allocated) {
+ const double MinSampleTime = 0.002; // Do not sample if time since last update is less than 2 ms
+ double now = os::elapsedTime();
+ double time_since_last_update = now -_last_sample_time;
+ if (time_since_last_update < MinSampleTime) {
+ unaccounted_bytes_allocated = allocated - _last_sample_value;
+ _last_sample_value = 0;
+ return 0.0;
+ } else {
+ double rate = instantaneous_rate(now, allocated);
+ _rate.add(rate);
+ _rate_avg.add(_rate.avg());
+ _last_sample_time = now;
+ _last_sample_value = allocated;
+ unaccounted_bytes_allocated = 0;
+ return rate;
+ }
+}
+
double ShenandoahAllocationRate::sample(size_t allocated) {
double now = os::elapsedTime();
double rate = 0.0;
if (now - _last_sample_time > _interval_sec) {
- if (allocated >= _last_sample_value) {
- rate = instantaneous_rate(now, allocated);
- _rate.add(rate);
- _rate_avg.add(_rate.avg());
- }
-
+ rate = instantaneous_rate(now, allocated);
+ _rate.add(rate);
+ _rate_avg.add(_rate.avg());
_last_sample_time = now;
_last_sample_value = allocated;
}
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp
index 68e540960c79..014a4d991311 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp
@@ -37,10 +37,12 @@ class ShenandoahAllocationRate : public CHeapObj {
explicit ShenandoahAllocationRate();
void allocation_counter_reset();
+ double force_sample(size_t allocated, size_t &unaccounted_bytes_allocated);
double sample(size_t allocated);
double upper_bound(double sds) const;
bool is_spiking(double rate, double threshold) const;
+
private:
double instantaneous_rate(double time, size_t allocated) const;
@@ -71,18 +73,18 @@ class ShenandoahAdaptiveHeuristics : public ShenandoahHeuristics {
virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset,
RegionData* data, size_t size,
- size_t actual_free);
+ size_t actual_free) override;
- void record_cycle_start();
- void record_success_concurrent();
- void record_success_degenerated();
- void record_success_full();
+ virtual void record_cycle_start() override;
+ virtual void record_success_concurrent() override;
+ virtual void record_success_degenerated() override;
+ virtual void record_success_full() override;
- virtual bool should_start_gc();
+ virtual bool should_start_gc() override;
- virtual const char* name() { return "Adaptive"; }
- virtual bool is_diagnostic() { return false; }
- virtual bool is_experimental() { return false; }
+ virtual const char* name() override { return "Adaptive"; }
+ virtual bool is_diagnostic() override { return false; }
+ virtual bool is_experimental() override { return false; }
private:
// These are used to adjust the margin of error and the spike threshold
@@ -150,6 +152,13 @@ class ShenandoahAdaptiveHeuristics : public ShenandoahHeuristics {
_last_trigger = trigger_type;
ShenandoahHeuristics::accept_trigger();
}
+
+public:
+ virtual size_t force_alloc_rate_sample(size_t bytes_allocated) override {
+ size_t unaccounted_bytes;
+ _allocation_rate.force_sample(bytes_allocated, unaccounted_bytes);
+ return unaccounted_bytes;
+ }
};
#endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHADAPTIVEHEURISTICS_HPP
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp
index 592bba677574..f8a77d95d51c 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp
@@ -38,6 +38,7 @@ ShenandoahCompactHeuristics::ShenandoahCompactHeuristics(ShenandoahSpaceInfo* sp
SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahImplicitGCInvokesConcurrent);
SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahUncommit);
SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahAlwaysClearSoftRefs);
+ SHENANDOAH_ERGO_ENABLE_FLAG(UseStringDeduplication);
SHENANDOAH_ERGO_OVERRIDE_DEFAULT(ShenandoahAllocationThreshold, 10);
SHENANDOAH_ERGO_OVERRIDE_DEFAULT(ShenandoahImmediateThreshold, 100);
SHENANDOAH_ERGO_OVERRIDE_DEFAULT(ShenandoahUncommitDelay, 1000);
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp
index dfae90402424..c7067b2e5abf 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp
@@ -26,7 +26,6 @@
#include "gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp"
#include "gc/shenandoah/shenandoahCollectionSet.hpp"
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
-#include "gc/shenandoah/shenandoahEvacInfo.hpp"
#include "gc/shenandoah/shenandoahGeneration.hpp"
#include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
@@ -185,59 +184,16 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio
heap->shenandoah_policy()->record_mixed_cycle();
}
- size_t cset_percent = (total_garbage == 0) ? 0 : (collection_set->garbage() * 100 / total_garbage);
- size_t collectable_garbage = collection_set->garbage() + immediate_garbage;
- size_t collectable_garbage_percent = (total_garbage == 0) ? 0 : (collectable_garbage * 100 / total_garbage);
+ collection_set->summarize(total_garbage, immediate_garbage, immediate_regions);
- log_info(gc, ergo)("Collectable Garbage: %zu%s (%zu%%), "
- "Immediate: %zu%s (%zu%%), %zu regions, "
- "CSet: %zu%s (%zu%%), %zu regions",
-
- byte_size_in_proper_unit(collectable_garbage),
- proper_unit_for_byte_size(collectable_garbage),
- collectable_garbage_percent,
-
- byte_size_in_proper_unit(immediate_garbage),
- proper_unit_for_byte_size(immediate_garbage),
- immediate_percent,
- immediate_regions,
-
- byte_size_in_proper_unit(collection_set->garbage()),
- proper_unit_for_byte_size(collection_set->garbage()),
- cset_percent,
- collection_set->count());
-
- if (collection_set->garbage() > 0) {
- size_t young_evac_bytes = collection_set->get_young_bytes_reserved_for_evacuation();
- size_t promote_evac_bytes = collection_set->get_young_bytes_to_be_promoted();
- size_t old_evac_bytes = collection_set->get_old_bytes_reserved_for_evacuation();
- size_t total_evac_bytes = young_evac_bytes + promote_evac_bytes + old_evac_bytes;
- log_info(gc, ergo)("Evacuation Targets: YOUNG: %zu%s, "
- "PROMOTE: %zu%s, "
- "OLD: %zu%s, "
- "TOTAL: %zu%s",
- byte_size_in_proper_unit(young_evac_bytes), proper_unit_for_byte_size(young_evac_bytes),
- byte_size_in_proper_unit(promote_evac_bytes), proper_unit_for_byte_size(promote_evac_bytes),
- byte_size_in_proper_unit(old_evac_bytes), proper_unit_for_byte_size(old_evac_bytes),
- byte_size_in_proper_unit(total_evac_bytes), proper_unit_for_byte_size(total_evac_bytes));
-
- ShenandoahEvacuationInformation evacInfo;
- evacInfo.set_collection_set_regions(collection_set->count());
- evacInfo.set_collection_set_used_before(collection_set->used());
- evacInfo.set_collection_set_used_after(collection_set->live());
- evacInfo.set_collected_old(old_evac_bytes);
- evacInfo.set_collected_promoted(promote_evac_bytes);
- evacInfo.set_collected_young(young_evac_bytes);
- evacInfo.set_regions_promoted_humongous(humongous_regions_promoted);
- evacInfo.set_regions_promoted_regular(regular_regions_promoted_in_place);
- evacInfo.set_regular_promoted_garbage(regular_regions_promoted_garbage);
- evacInfo.set_regular_promoted_free(regular_regions_promoted_free);
- evacInfo.set_regions_immediate(immediate_regions);
- evacInfo.set_immediate_size(immediate_garbage);
- evacInfo.set_free_regions(free_regions);
-
- ShenandoahTracer().report_evacuation_info(&evacInfo);
- }
+ ShenandoahTracer::report_evacuation_info(collection_set,
+ free_regions,
+ humongous_regions_promoted,
+ regular_regions_promoted_in_place,
+ regular_regions_promoted_garbage,
+ regular_regions_promoted_free,
+ immediate_regions,
+ immediate_garbage);
}
@@ -268,15 +224,3 @@ size_t ShenandoahGenerationalHeuristics::add_preselected_regions_to_collection_s
return cur_young_garbage;
}
-void ShenandoahGenerationalHeuristics::log_cset_composition(ShenandoahCollectionSet* cset) const {
- size_t collected_old = cset->get_old_bytes_reserved_for_evacuation();
- size_t collected_promoted = cset->get_young_bytes_to_be_promoted();
- size_t collected_young = cset->get_young_bytes_reserved_for_evacuation();
-
- log_info(gc, ergo)(
- "Chosen CSet evacuates young: %zu%s (of which at least: %zu%s are to be promoted), "
- "old: %zu%s",
- byte_size_in_proper_unit(collected_young), proper_unit_for_byte_size(collected_young),
- byte_size_in_proper_unit(collected_promoted), proper_unit_for_byte_size(collected_promoted),
- byte_size_in_proper_unit(collected_old), proper_unit_for_byte_size(collected_old));
-}
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp
index 6708c63f0425..31c016bb4b7f 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp
@@ -51,9 +51,8 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics {
size_t add_preselected_regions_to_collection_set(ShenandoahCollectionSet* cset,
const RegionData* data,
size_t size) const;
-
- void log_cset_composition(ShenandoahCollectionSet* cset) const;
};
#endif //SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGENERATIONALHEURISTICS_HPP
+
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp
index 331bd0405752..93f9b18ad9f5 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp
@@ -42,8 +42,6 @@ void ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(Shenandoa
QuickSort::sort(data, (int) size, compare_by_garbage);
choose_global_collection_set(cset, data, size, actual_free, 0 /* cur_young_garbage */);
-
- log_cset_composition(cset);
}
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp
index b151a75e6e7e..c8a0c3dc5183 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp
@@ -153,27 +153,7 @@ void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collec
choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free);
}
- size_t cset_percent = (total_garbage == 0) ? 0 : (collection_set->garbage() * 100 / total_garbage);
- size_t collectable_garbage = collection_set->garbage() + immediate_garbage;
- size_t collectable_garbage_percent = (total_garbage == 0) ? 0 : (collectable_garbage * 100 / total_garbage);
-
- log_info(gc, ergo)("Collectable Garbage: %zu%s (%zu%%), "
- "Immediate: %zu%s (%zu%%), %zu regions, "
- "CSet: %zu%s (%zu%%), %zu regions",
-
- byte_size_in_proper_unit(collectable_garbage),
- proper_unit_for_byte_size(collectable_garbage),
- collectable_garbage_percent,
-
- byte_size_in_proper_unit(immediate_garbage),
- proper_unit_for_byte_size(immediate_garbage),
- immediate_percent,
- immediate_regions,
-
- byte_size_in_proper_unit(collection_set->garbage()),
- proper_unit_for_byte_size(collection_set->garbage()),
- cset_percent,
- collection_set->count());
+ collection_set->summarize(total_garbage, immediate_garbage, immediate_regions);
}
void ShenandoahHeuristics::record_cycle_start() {
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp
index d036a62a32cf..3cd2cb1d171d 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp
@@ -36,7 +36,7 @@
do { \
if (FLAG_IS_DEFAULT(name) && (name)) { \
log_info(gc)("Heuristics ergonomically sets -XX:-" #name); \
- FLAG_SET_DEFAULT(name, false); \
+ FLAG_SET_ERGO(name, false); \
} \
} while (0)
@@ -44,7 +44,7 @@
do { \
if (FLAG_IS_DEFAULT(name) && !(name)) { \
log_info(gc)("Heuristics ergonomically sets -XX:+" #name); \
- FLAG_SET_DEFAULT(name, true); \
+ FLAG_SET_ERGO(name, true); \
} \
} while (0)
@@ -52,7 +52,7 @@
do { \
if (FLAG_IS_DEFAULT(name)) { \
log_info(gc)("Heuristics ergonomically sets -XX:" #name "=" #value); \
- FLAG_SET_DEFAULT(name, value); \
+ FLAG_SET_ERGO(name, value); \
} \
} while (0)
@@ -241,6 +241,11 @@ class ShenandoahHeuristics : public CHeapObj {
double elapsed_cycle_time() const;
+ virtual size_t force_alloc_rate_sample(size_t bytes_allocated) {
+ // do nothing
+ return 0;
+ }
+
// Format prefix and emit log message indicating a GC cycle hs been triggered
void log_trigger(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3);
};
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp
index 2d0bbfd5e4a3..e963bcc35bb4 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp
@@ -141,7 +141,7 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll
// If region r is evacuated to fragmented memory (to free memory within a partially used region), then we need
// to decrease the capacity of the fragmented memory by the scaled loss.
- size_t live_data_for_evacuation = r->get_live_data_bytes();
+ const size_t live_data_for_evacuation = r->get_live_data_bytes();
size_t lost_available = r->free();
if ((lost_available > 0) && (excess_fragmented_available > 0)) {
@@ -169,7 +169,9 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll
// We were not able to account for the lost free memory within fragmented memory, so we need to take this
// allocation out of unfragmented memory. Unfragmented memory does not need to account for loss of free.
if (live_data_for_evacuation > unfragmented_available) {
- // There is not room to evacuate this region or any that come after it in within the candidates array.
+ // There is no room to evacuate this region or any that come after it in within the candidates array.
+ log_debug(gc, cset)("Not enough unfragmented memory (%zu) to hold evacuees (%zu) from region: (%zu)",
+ unfragmented_available, live_data_for_evacuation, r->index());
break;
} else {
unfragmented_available -= live_data_for_evacuation;
@@ -187,7 +189,9 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll
evacuation_need = 0;
}
if (evacuation_need > unfragmented_available) {
- // There is not room to evacuate this region or any that come after it in within the candidates array.
+ // There is no room to evacuate this region or any that come after it in within the candidates array.
+ log_debug(gc, cset)("Not enough unfragmented memory (%zu) to hold evacuees (%zu) from region: (%zu)",
+ unfragmented_available, live_data_for_evacuation, r->index());
break;
} else {
unfragmented_available -= evacuation_need;
@@ -412,7 +416,7 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() {
size_t defrag_count = 0;
size_t total_uncollected_old_regions = _last_old_region - _last_old_collection_candidate;
- if (cand_idx > _last_old_collection_candidate) {
+ if ((ShenandoahGenerationalHumongousReserve > 0) && (cand_idx > _last_old_collection_candidate)) {
// Above, we have added into the set of mixed-evacuation candidates all old-gen regions for which the live memory
// that they contain is below a particular old-garbage threshold. Regions that were not selected for the collection
// set hold enough live memory that it is not considered efficient (by "garbage-first standards") to compact these
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp
index 2131f95b413d..7fb44c7b71ba 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp
@@ -41,6 +41,10 @@ class ShenandoahSpaceInfo {
virtual size_t soft_available() const = 0;
virtual size_t available() const = 0;
virtual size_t used() const = 0;
+
+ // Return an approximation of the bytes allocated since GC start. The value returned is monotonically non-decreasing
+ // 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;
};
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp
index d236be8c9e60..15d1058d7cd5 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp
@@ -55,8 +55,6 @@ void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(Shenandoah
size_t cur_young_garbage = add_preselected_regions_to_collection_set(cset, data, size);
choose_young_collection_set(cset, data, size, actual_free, cur_young_garbage);
-
- log_cset_composition(cset);
}
void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollectionSet* cset,
diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp
index 296a1979b01b..2dfa52b86bb9 100644
--- a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp
+++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp
@@ -29,6 +29,7 @@
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "logging/log.hpp"
#include "logging/logTag.hpp"
+#include "runtime/globals_extension.hpp"
#include "runtime/java.hpp"
void ShenandoahPassiveMode::initialize_flags() const {
@@ -41,7 +42,10 @@ void ShenandoahPassiveMode::initialize_flags() const {
// No need for evacuation reserve with Full GC, only for Degenerated GC.
if (!ShenandoahDegeneratedGC) {
- SHENANDOAH_ERGO_OVERRIDE_DEFAULT(ShenandoahEvacReserve, 0);
+ if (FLAG_IS_DEFAULT(ShenandoahEvacReserve)) {
+ log_info(gc)("Heuristics sets -XX:ShenandoahEvacReserve=0");
+ FLAG_SET_DEFAULT(ShenandoahEvacReserve, 0);
+ }
}
// Disable known barriers by default.
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp
index bd66f55bd8fa..86ff6f22c721 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp
@@ -43,7 +43,7 @@ ShenandoahAgeCensus::ShenandoahAgeCensus(uint max_workers)
ShenandoahGenerationalMinTenuringAge, ShenandoahGenerationalMaxTenuringAge));
}
- _global_age_table = NEW_C_HEAP_ARRAY(AgeTable*, MAX_SNAPSHOTS, mtGC);
+ _global_age_tables = NEW_C_HEAP_ARRAY(AgeTable*, MAX_SNAPSHOTS, mtGC);
CENSUS_NOISE(_global_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, MAX_SNAPSHOTS, mtGC);)
_tenuring_threshold = NEW_C_HEAP_ARRAY(uint, MAX_SNAPSHOTS, mtGC);
CENSUS_NOISE(_skipped = 0);
@@ -52,36 +52,40 @@ ShenandoahAgeCensus::ShenandoahAgeCensus(uint max_workers)
for (int i = 0; i < MAX_SNAPSHOTS; i++) {
// Note that we don't now get perfdata from age_table
- _global_age_table[i] = new AgeTable(false);
+ _global_age_tables[i] = new AgeTable(false);
CENSUS_NOISE(_global_noise[i].clear();)
// Sentinel value
_tenuring_threshold[i] = MAX_COHORTS;
}
- if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) {
- _local_age_table = NEW_C_HEAP_ARRAY(AgeTable*, _max_workers, mtGC);
+ if (ShenandoahGenerationalAdaptiveTenuring) {
+ _local_age_tables = NEW_C_HEAP_ARRAY(AgeTable*, _max_workers, mtGC);
CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);)
for (uint i = 0; i < _max_workers; i++) {
- _local_age_table[i] = new AgeTable(false);
+ _local_age_tables[i] = new AgeTable(false);
CENSUS_NOISE(_local_noise[i].clear();)
}
} else {
- _local_age_table = nullptr;
+ _local_age_tables = nullptr;
+ }
+ _epoch = MAX_SNAPSHOTS - 1; // see prepare_for_census_update()
+
+ if (!ShenandoahGenerationalAdaptiveTenuring) {
+ _tenuring_threshold[_epoch] = InitialTenuringThreshold;
}
- _epoch = MAX_SNAPSHOTS - 1; // see update_epoch()
}
ShenandoahAgeCensus::~ShenandoahAgeCensus() {
for (uint i = 0; i < MAX_SNAPSHOTS; i++) {
- delete _global_age_table[i];
+ delete _global_age_tables[i];
}
- FREE_C_HEAP_ARRAY(AgeTable*, _global_age_table);
+ FREE_C_HEAP_ARRAY(AgeTable*, _global_age_tables);
FREE_C_HEAP_ARRAY(uint, _tenuring_threshold);
CENSUS_NOISE(FREE_C_HEAP_ARRAY(ShenandoahNoiseStats, _global_noise));
- if (_local_age_table) {
+ if (_local_age_tables) {
for (uint i = 0; i < _max_workers; i++) {
- delete _local_age_table[i];
+ delete _local_age_tables[i];
}
- FREE_C_HEAP_ARRAY(AgeTable*, _local_age_table);
+ FREE_C_HEAP_ARRAY(AgeTable*, _local_age_tables);
CENSUS_NOISE(FREE_C_HEAP_ARRAY(ShenandoahNoiseStats, _local_noise));
}
}
@@ -142,37 +146,31 @@ void ShenandoahAgeCensus::prepare_for_census_update() {
if (++_epoch >= MAX_SNAPSHOTS) {
_epoch=0;
}
- _global_age_table[_epoch]->clear();
+ _global_age_tables[_epoch]->clear();
CENSUS_NOISE(_global_noise[_epoch].clear();)
}
// Update the census data from appropriate sources,
// and compute the new tenuring threshold.
-void ShenandoahAgeCensus::update_census(size_t age0_pop, AgeTable* pv1, AgeTable* pv2) {
+void ShenandoahAgeCensus::update_census(size_t age0_pop) {
prepare_for_census_update();
- assert(_global_age_table[_epoch]->is_clear(), "Dirty decks");
+ assert(ShenandoahGenerationalAdaptiveTenuring, "Only update census when adaptive tenuring is enabled");
+ assert(_global_age_tables[_epoch]->is_clear(), "Dirty decks");
CENSUS_NOISE(assert(_global_noise[_epoch].is_clear(), "Dirty decks");)
- if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) {
- assert(pv1 == nullptr && pv2 == nullptr, "Error, check caller");
- // Seed cohort 0 with population that may have been missed during
- // regular census.
- _global_age_table[_epoch]->add(0u, age0_pop);
-
- // Merge data from local age tables into the global age table for the epoch,
- // clearing the local tables.
- for (uint i = 0; i < _max_workers; i++) {
- // age stats
- _global_age_table[_epoch]->merge(_local_age_table[i]);
- _local_age_table[i]->clear(); // clear for next census
- // Merge noise stats
- CENSUS_NOISE(_global_noise[_epoch].merge(_local_noise[i]);)
- CENSUS_NOISE(_local_noise[i].clear();)
- }
- } else {
- // census during evac
- assert(pv1 != nullptr && pv2 != nullptr, "Error, check caller");
- _global_age_table[_epoch]->merge(pv1);
- _global_age_table[_epoch]->merge(pv2);
+
+ // Seed cohort 0 with population that may have been missed during
+ // regular census.
+ _global_age_tables[_epoch]->add(0u, age0_pop);
+
+ // Merge data from local age tables into the global age table for the epoch,
+ // clearing the local tables.
+ for (uint i = 0; i < _max_workers; i++) {
+ // age stats
+ _global_age_tables[_epoch]->merge(_local_age_tables[i]);
+ _local_age_tables[i]->clear(); // clear for next census
+ // Merge noise stats
+ CENSUS_NOISE(_global_noise[_epoch].merge(_local_noise[i]);)
+ CENSUS_NOISE(_local_noise[i].clear();)
}
update_tenuring_threshold();
@@ -188,7 +186,7 @@ void ShenandoahAgeCensus::update_census(size_t age0_pop, AgeTable* pv1, AgeTable
void ShenandoahAgeCensus::reset_global() {
assert(_epoch < MAX_SNAPSHOTS, "Out of bounds");
for (uint i = 0; i < MAX_SNAPSHOTS; i++) {
- _global_age_table[i]->clear();
+ _global_age_tables[i]->clear();
CENSUS_NOISE(_global_noise[i].clear();)
}
_epoch = MAX_SNAPSHOTS;
@@ -197,12 +195,12 @@ void ShenandoahAgeCensus::reset_global() {
// Reset the local age tables, clearing any partial census.
void ShenandoahAgeCensus::reset_local() {
- if (!ShenandoahGenerationalAdaptiveTenuring || ShenandoahGenerationalCensusAtEvac) {
- assert(_local_age_table == nullptr, "Error");
+ if (!ShenandoahGenerationalAdaptiveTenuring) {
+ assert(_local_age_tables == nullptr, "Error");
return;
}
for (uint i = 0; i < _max_workers; i++) {
- _local_age_table[i]->clear();
+ _local_age_tables[i]->clear();
CENSUS_NOISE(_local_noise[i].clear();)
}
}
@@ -212,7 +210,7 @@ void ShenandoahAgeCensus::reset_local() {
bool ShenandoahAgeCensus::is_clear_global() {
assert(_epoch < MAX_SNAPSHOTS, "Out of bounds");
for (uint i = 0; i < MAX_SNAPSHOTS; i++) {
- bool clear = _global_age_table[i]->is_clear();
+ bool clear = _global_age_tables[i]->is_clear();
CENSUS_NOISE(clear |= _global_noise[i].is_clear();)
if (!clear) {
return false;
@@ -223,12 +221,12 @@ bool ShenandoahAgeCensus::is_clear_global() {
// Is local census information clear?
bool ShenandoahAgeCensus::is_clear_local() {
- if (!ShenandoahGenerationalAdaptiveTenuring || ShenandoahGenerationalCensusAtEvac) {
- assert(_local_age_table == nullptr, "Error");
+ if (!ShenandoahGenerationalAdaptiveTenuring) {
+ assert(_local_age_tables == nullptr, "Error");
return true;
}
for (uint i = 0; i < _max_workers; i++) {
- bool clear = _local_age_table[i]->is_clear();
+ bool clear = _local_age_tables[i]->is_clear();
CENSUS_NOISE(clear |= _local_noise[i].is_clear();)
if (!clear) {
return false;
@@ -240,7 +238,7 @@ bool ShenandoahAgeCensus::is_clear_local() {
size_t ShenandoahAgeCensus::get_all_ages(uint snap) {
assert(snap < MAX_SNAPSHOTS, "Out of bounds");
size_t pop = 0;
- const AgeTable* pv = _global_age_table[snap];
+ const AgeTable* pv = _global_age_tables[snap];
for (uint i = 0; i < MAX_COHORTS; i++) {
pop += pv->sizes[i];
}
@@ -260,13 +258,11 @@ void ShenandoahAgeCensus::update_total() {
#endif // !PRODUCT
void ShenandoahAgeCensus::update_tenuring_threshold() {
- if (!ShenandoahGenerationalAdaptiveTenuring) {
- _tenuring_threshold[_epoch] = InitialTenuringThreshold;
- } else {
- uint tt = compute_tenuring_threshold();
- assert(tt <= MAX_COHORTS, "Out of bounds");
- _tenuring_threshold[_epoch] = tt;
- }
+ assert(ShenandoahGenerationalAdaptiveTenuring, "Only update when adaptive tenuring is enabled");
+ uint tt = compute_tenuring_threshold();
+ assert(tt <= MAX_COHORTS, "Out of bounds");
+ _tenuring_threshold[_epoch] = tt;
+
print();
log_info(gc, age)("New tenuring threshold %zu (min %zu, max %zu)",
(uintx) _tenuring_threshold[_epoch], ShenandoahGenerationalMinTenuringAge, ShenandoahGenerationalMaxTenuringAge);
@@ -296,8 +292,8 @@ uint ShenandoahAgeCensus::compute_tenuring_threshold() {
const uint prev_epoch = cur_epoch > 0 ? cur_epoch - 1 : markWord::max_age;
// Current and previous population vectors in ring
- const AgeTable* cur_pv = _global_age_table[cur_epoch];
- const AgeTable* prev_pv = _global_age_table[prev_epoch];
+ const AgeTable* cur_pv = _global_age_tables[cur_epoch];
+ const AgeTable* prev_pv = _global_age_tables[prev_epoch];
uint upper_bound = ShenandoahGenerationalMaxTenuringAge;
const uint prev_tt = previous_tenuring_threshold();
if (ShenandoahGenerationalCensusIgnoreOlderCohorts && prev_tt > 0) {
@@ -372,8 +368,8 @@ void ShenandoahAgeCensus::print() {
const uint cur_epoch = _epoch;
const uint prev_epoch = cur_epoch > 0 ? cur_epoch - 1: markWord::max_age;
- const AgeTable* cur_pv = _global_age_table[cur_epoch];
- const AgeTable* prev_pv = _global_age_table[prev_epoch];
+ const AgeTable* cur_pv = _global_age_tables[cur_epoch];
+ const AgeTable* prev_pv = _global_age_tables[prev_epoch];
const uint tt = tenuring_threshold();
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp
index 90d188e1fcae..39ea4ee9002a 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp
@@ -97,8 +97,8 @@ struct ShenandoahNoiseStats {
// once the per-worker data is consolidated into the appropriate population vector
// per minor collection. The _local_age_table is thus C x N, for N GC workers.
class ShenandoahAgeCensus: public CHeapObj {
- AgeTable** _global_age_table; // Global age table used for adapting tenuring threshold, one per snapshot
- AgeTable** _local_age_table; // Local scratch age tables to track object ages, one per worker
+ AgeTable** _global_age_tables; // Global age tables used for adapting tenuring threshold, one per snapshot
+ AgeTable** _local_age_tables; // Local scratch age tables to track object ages, one per worker
#ifdef SHENANDOAH_CENSUS_NOISE
ShenandoahNoiseStats* _global_noise; // Noise stats, one per snapshot
@@ -173,9 +173,9 @@ class ShenandoahAgeCensus: public CHeapObj {
~ShenandoahAgeCensus();
// Return the local age table (population vector) for worker_id.
- // Only used in the case of (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac)
+ // Only used in the case of ShenandoahGenerationalAdaptiveTenuring
AgeTable* get_local_age_table(uint worker_id) const {
- return _local_age_table[worker_id];
+ return _local_age_tables[worker_id];
}
// Return the most recently computed tenuring threshold.
@@ -204,17 +204,12 @@ class ShenandoahAgeCensus: public CHeapObj {
#endif // SHENANDOAH_CENSUS_NOISE
// Update the census data, and compute the new tenuring threshold.
- // This method should be called at the end of each marking (or optionally
- // evacuation) cycle to update the tenuring threshold to be used in
- // the next cycle.
+ // This method should be called at the end of each marking cycle to update
+ // the tenuring threshold to be used in the next cycle.
// age0_pop is the population of Cohort 0 that may have been missed in
// the regular census during the marking cycle, corresponding to objects
// allocated when the concurrent marking was in progress.
- // Optional parameters, pv1 and pv2 are population vectors that together
- // provide object census data (only) for the case when
- // ShenandoahGenerationalCensusAtEvac. In this case, the age0_pop
- // is 0, because the evacuated objects have all had their ages incremented.
- void update_census(size_t age0_pop, AgeTable* pv1 = nullptr, AgeTable* pv2 = nullptr);
+ void update_census(size_t age0_pop);
// Reset the epoch, clearing accumulated census history
// Note: this isn't currently used, but reserved for planned
@@ -231,7 +226,7 @@ class ShenandoahAgeCensus: public CHeapObj {
// Return the net size of objects encountered (counted or skipped) in census
// at most recent epoch.
- size_t get_total() { return _total; }
+ size_t get_total() const { return _total; }
#endif // !PRODUCT
// Print the age census information
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
index 65495882ef0b..8247b82e8618 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
@@ -37,6 +37,7 @@
#include "runtime/globals_extension.hpp"
#include "runtime/java.hpp"
#include "utilities/defaultStream.hpp"
+#include "utilities/powerOfTwo.hpp"
void ShenandoahArguments::initialize() {
#if !(defined AARCH64 || defined AMD64 || defined IA32 || defined PPC64 || defined RISCV64)
@@ -204,7 +205,7 @@ void ShenandoahArguments::initialize() {
}
size_t ShenandoahArguments::conservative_max_heap_alignment() {
- size_t align = ShenandoahMaxRegionSize;
+ size_t align = next_power_of_2(ShenandoahMaxRegionSize);
if (UseLargePages) {
align = MAX2(align, os::large_page_size());
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp
index 5d19a6a34e31..f6733d4a923d 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp
@@ -89,8 +89,19 @@ bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators, Basi
void ShenandoahBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop new_obj) {
#if COMPILER2_OR_JVMCI
- assert(!ReduceInitialCardMarks || !ShenandoahCardBarrier || ShenandoahGenerationalHeap::heap()->is_in_young(new_obj),
- "Allocating new object outside of young generation: " INTPTR_FORMAT, p2i(new_obj));
+ if (ReduceInitialCardMarks && ShenandoahCardBarrier && !ShenandoahHeap::heap()->is_in_young(new_obj)) {
+ log_debug(gc)("Newly allocated object (" PTR_FORMAT ") is not in the young generation", p2i(new_obj));
+ // This can happen when an object is newly allocated, but we come to a safepoint before returning
+ // the object. If the safepoint runs a degenerated cycle that is upgraded to a full GC, this object
+ // will have survived two GC cycles. If the tenuring age is very low (1), this object may be promoted.
+ // In this case, we have an allocated object, but it has received no stores yet. If card marking barriers
+ // have been elided, we could end up with an object in old holding pointers to young that won't be in
+ // the remembered set. The solution here is conservative, but this problem should be rare, and it will
+ // correct itself on subsequent cycles when the remembered set is updated.
+ ShenandoahGenerationalHeap::heap()->old_generation()->card_scan()->mark_range_as_dirty(
+ cast_from_oop(new_obj), new_obj->size()
+ );
+ }
#endif // COMPILER2_OR_JVMCI
assert(thread->deferred_card_mark().is_empty(), "We don't use this");
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
index b176446452a1..86244711844f 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
@@ -191,6 +191,20 @@ inline void ShenandoahBarrierSet::keep_alive_if_weak(DecoratorSet decorators, oo
template
inline void ShenandoahBarrierSet::write_ref_field_post(T* field) {
assert(ShenandoahCardBarrier, "Should have been checked by caller");
+ if (_heap->is_in_young(field)) {
+ // Young field stores do not require card mark.
+ return;
+ }
+ T heap_oop = RawAccess<>::oop_load(field);
+ if (CompressedOops::is_null(heap_oop)) {
+ // Null reference store do not require card mark.
+ return;
+ }
+ oop obj = CompressedOops::decode_not_null(heap_oop);
+ if (!_heap->is_in_young(obj)) {
+ // Not an old->young reference store.
+ return;
+ }
volatile CardTable::CardValue* byte = card_table()->byte_for(field);
*byte = CardTable::dirty_card_val();
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp
index 35faa40af771..2d808cef834b 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp
@@ -200,3 +200,38 @@ void ShenandoahCollectionSet::print_on(outputStream* out) const {
}
assert(regions == count(), "Must match");
}
+
+void ShenandoahCollectionSet::summarize(size_t total_garbage, size_t immediate_garbage, size_t immediate_regions) const {
+ const LogTarget(Info, gc, ergo) lt;
+ LogStream ls(lt);
+ if (lt.is_enabled()) {
+ const size_t cset_percent = (total_garbage == 0) ? 0 : (garbage() * 100 / total_garbage);
+ const size_t collectable_garbage = garbage() + immediate_garbage;
+ const size_t collectable_garbage_percent = (total_garbage == 0) ? 0 : (collectable_garbage * 100 / total_garbage);
+ const size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage);
+
+ ls.print_cr("Collectable Garbage: " PROPERFMT " (%zu%%), "
+ "Immediate: " PROPERFMT " (%zu%%), %zu regions, "
+ "CSet: " PROPERFMT " (%zu%%), %zu regions",
+ PROPERFMTARGS(collectable_garbage),
+ collectable_garbage_percent,
+
+ PROPERFMTARGS(immediate_garbage),
+ immediate_percent,
+ immediate_regions,
+
+ PROPERFMTARGS(garbage()),
+ cset_percent,
+ count());
+
+ if (garbage() > 0) {
+ const size_t young_evac_bytes = get_live_bytes_in_untenurable_regions();
+ const size_t promote_evac_bytes = get_live_bytes_in_tenurable_regions();
+ const size_t old_evac_bytes = get_live_bytes_in_old_regions();
+ const size_t total_evac_bytes = young_evac_bytes + promote_evac_bytes + old_evac_bytes;
+ ls.print_cr("Evacuation Targets: "
+ "YOUNG: " PROPERFMT ", " "PROMOTE: " PROPERFMT ", " "OLD: " PROPERFMT ", " "TOTAL: " PROPERFMT,
+ PROPERFMTARGS(young_evac_bytes), PROPERFMTARGS(promote_evac_bytes), PROPERFMTARGS(old_evac_bytes), PROPERFMTARGS(total_evac_bytes));
+ }
+ }
+}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp
index 4f9f6fc20522..a1b77baa2d3c 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.hpp
@@ -103,20 +103,29 @@ class ShenandoahCollectionSet : public CHeapObj {
inline bool is_in(oop obj) const;
inline bool is_in_loc(void* loc) const;
+ // Prints a detailed accounting of all regions in the collection set when gc+cset=debug
void print_on(outputStream* out) const;
- // It is not known how many of these bytes will be promoted.
- inline size_t get_young_bytes_reserved_for_evacuation();
- inline size_t get_old_bytes_reserved_for_evacuation();
+ // Prints a summary of the collection set when gc+ergo=info
+ void summarize(size_t total_garbage, size_t immediate_garbage, size_t immediate_regions) const;
- inline size_t get_young_bytes_to_be_promoted();
+ // Returns the amount of live bytes in young regions with an age below the tenuring threshold.
+ inline size_t get_live_bytes_in_untenurable_regions() const;
- size_t get_young_available_bytes_collected() { return _young_available_bytes_collected; }
+ // Returns the amount of live bytes in old regions in the collection set.
+ inline size_t get_live_bytes_in_old_regions() const;
- inline size_t get_old_garbage();
+ // Returns the amount of live bytes in young regions with an age at or above the tenuring threshold.
+ inline size_t get_live_bytes_in_tenurable_regions() const;
+
+ // Returns the amount of free bytes in young regions in the collection set.
+ size_t get_young_available_bytes_collected() const { return _young_available_bytes_collected; }
+
+ // Returns the amount of garbage in old regions in the collection set.
+ inline size_t get_old_garbage() const;
bool is_preselected(size_t region_idx) {
- assert(_preselected_regions != nullptr, "Missing etsablish after abandon");
+ assert(_preselected_regions != nullptr, "Missing establish after abandon");
return _preselected_regions[region_idx];
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp
index 791e9c73b28e..3ff5f2f81d70 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.inline.hpp
@@ -54,19 +54,19 @@ bool ShenandoahCollectionSet::is_in_loc(void* p) const {
return _biased_cset_map[index] == 1;
}
-size_t ShenandoahCollectionSet::get_old_bytes_reserved_for_evacuation() {
+size_t ShenandoahCollectionSet::get_live_bytes_in_old_regions() const {
return _old_bytes_to_evacuate;
}
-size_t ShenandoahCollectionSet::get_young_bytes_reserved_for_evacuation() {
+size_t ShenandoahCollectionSet::get_live_bytes_in_untenurable_regions() const {
return _young_bytes_to_evacuate - _young_bytes_to_promote;
}
-size_t ShenandoahCollectionSet::get_young_bytes_to_be_promoted() {
+size_t ShenandoahCollectionSet::get_live_bytes_in_tenurable_regions() const {
return _young_bytes_to_promote;
}
-size_t ShenandoahCollectionSet::get_old_garbage() {
+size_t ShenandoahCollectionSet::get_old_garbage() const {
return _old_garbage;
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
index 9cab6807bd06..bdd495a86853 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
@@ -202,27 +202,8 @@ void ShenandoahControlThread::run_service() {
heuristics->clear_metaspace_oom();
}
- // Commit worker statistics to cycle data
- heap->phase_timings()->flush_par_workers_to_cycle();
- if (ShenandoahPacing) {
- heap->pacer()->flush_stats_to_cycle();
- }
-
- // Print GC stats for current cycle
- {
- LogTarget(Info, gc, stats) lt;
- if (lt.is_enabled()) {
- ResourceMark rm;
- LogStream ls(lt);
- heap->phase_timings()->print_cycle_on(&ls);
- if (ShenandoahPacing) {
- heap->pacer()->print_cycle_on(&ls);
- }
- }
- }
-
- // Commit statistics to globals
- heap->phase_timings()->flush_cycle_to_global();
+ // Manage and print gc stats
+ heap->process_gc_stats();
// Print Metaspace change following GC (if logging is enabled).
MetaspaceUtils::print_metaspace_change(meta_sizes);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
index 8a0eba5cd9f2..0f84d95d02a8 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
@@ -115,8 +115,7 @@ void ShenandoahDegenGC::op_degenerated() {
}
#endif
- ShenandoahMetricsSnapshot metrics;
- metrics.snap_before();
+ ShenandoahMetricsSnapshot metrics(heap->free_set());
switch (_degen_point) {
// The cases below form the Duff's-like device: it describes the actual GC cycle,
@@ -308,10 +307,8 @@ void ShenandoahDegenGC::op_degenerated() {
Universe::verify();
}
- metrics.snap_after();
-
// Decide if this cycle made good progress, and, if not, should it upgrade to a full GC.
- const bool progress = metrics.is_good_progress(_generation);
+ const bool progress = metrics.is_good_progress();
ShenandoahCollectorPolicy* policy = heap->shenandoah_policy();
policy->record_degenerated(_generation->is_young(), _abbreviated, progress);
if (progress) {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacInfo.hpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacInfo.hpp
deleted file mode 100644
index 8069fd13afa6..000000000000
--- a/src/hotspot/share/gc/shenandoah/shenandoahEvacInfo.hpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.
- *
- */
-
-#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHEVACINFO_HPP
-#define SHARE_GC_SHENANDOAH_SHENANDOAHEVACINFO_HPP
-
-#include "memory/allocation.hpp"
-
-class ShenandoahEvacuationInformation : public StackObj {
- // Values for ShenandoahEvacuationInformation jfr event, sizes stored as bytes
- size_t _collection_set_regions;
- size_t _collection_set_used_before;
- size_t _collection_set_used_after;
- size_t _collected_old;
- size_t _collected_promoted;
- size_t _collected_young;
- 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;
-
-public:
- ShenandoahEvacuationInformation() :
- _collection_set_regions(0), _collection_set_used_before(0), _collection_set_used_after(0),
- _collected_old(0), _collected_promoted(0), _collected_young(0), _free_regions(0),
- _regions_promoted_humongous(0), _regions_promoted_regular(0), _regular_promoted_garbage(0),
- _regular_promoted_free(0), _regions_immediate(0), _immediate_size(0) { }
-
- void set_collection_set_regions(size_t collection_set_regions) {
- _collection_set_regions = collection_set_regions;
- }
-
- void set_collection_set_used_before(size_t used) {
- _collection_set_used_before = used;
- }
-
- void set_collection_set_used_after(size_t used) {
- _collection_set_used_after = used;
- }
-
- void set_collected_old(size_t collected) {
- _collected_old = collected;
- }
-
- void set_collected_promoted(size_t collected) {
- _collected_promoted = collected;
- }
-
- void set_collected_young(size_t collected) {
- _collected_young = collected;
- }
-
- void set_free_regions(size_t freed) {
- _free_regions = freed;
- }
-
- void set_regions_promoted_humongous(size_t humongous) {
- _regions_promoted_humongous = humongous;
- }
-
- void set_regions_promoted_regular(size_t regular) {
- _regions_promoted_regular = regular;
- }
-
- void set_regular_promoted_garbage(size_t garbage) {
- _regular_promoted_garbage = garbage;
- }
-
- void set_regular_promoted_free(size_t free) {
- _regular_promoted_free = free;
- }
-
- void set_regions_immediate(size_t immediate) {
- _regions_immediate = immediate;
- }
-
- void set_immediate_size(size_t size) {
- _immediate_size = size;
- }
-
- size_t collection_set_regions() { return _collection_set_regions; }
- size_t collection_set_used_before() { return _collection_set_used_before; }
- size_t collection_set_used_after() { return _collection_set_used_after; }
- size_t collected_old() { return _collected_old; }
- size_t collected_promoted() { return _collected_promoted; }
- size_t collected_young() { return _collected_young; }
- size_t regions_promoted_humongous() { return _regions_promoted_humongous; }
- size_t regions_promoted_regular() { return _regions_promoted_regular; }
- size_t regular_promoted_garbage() { return _regular_promoted_garbage; }
- size_t regular_promoted_free() { return _regular_promoted_free; }
- size_t free_regions() { return _free_regions; }
- size_t regions_immediate() { return _regions_immediate; }
- size_t immediate_size() { return _immediate_size; }
-};
-
-#endif // SHARE_GC_SHENANDOAH_SHENANDOAHEVACINFO_HPP
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp
index b1d474fa78da..72d0773eccdf 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp
@@ -23,76 +23,65 @@
*
*/
-#include "gc/shenandoah/shenandoahAgeCensus.hpp"
#include "gc/shenandoah/shenandoahEvacTracker.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahThreadLocalData.hpp"
#include "runtime/thread.hpp"
#include "runtime/threadSMR.inline.hpp"
-ShenandoahEvacuationStats::ShenandoahEvacuationStats()
- : _evacuations_completed(0), _bytes_completed(0),
- _evacuations_attempted(0), _bytes_attempted(0),
- _use_age_table(ShenandoahGenerationalCensusAtEvac || !ShenandoahGenerationalAdaptiveTenuring),
- _age_table(nullptr) {
- if (_use_age_table) {
- _age_table = new AgeTable(false);
+ShenandoahEvacuationStats::ShenandoahEvacuations* ShenandoahEvacuationStats::get_category(
+ ShenandoahAffiliation from,
+ ShenandoahAffiliation to) {
+ if (from == YOUNG_GENERATION) {
+ if (to == YOUNG_GENERATION) {
+ return &_young;
+ }
+ assert(to == OLD_GENERATION, "If not evacuating to young, must be promotion to old");
+ return &_promotion;
}
+ assert(from == OLD_GENERATION, "If not evacuating from young, then must be from old");
+ return &_old;
}
-AgeTable* ShenandoahEvacuationStats::age_table() const {
- assert(_use_age_table, "Don't call");
- return _age_table;
-}
+void ShenandoahEvacuationStats::begin_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) {
+ ShenandoahEvacuations* category = get_category(from, to);
+ category->_evacuations_attempted++;
+ category->_bytes_attempted += bytes;
-void ShenandoahEvacuationStats::begin_evacuation(size_t bytes) {
- ++_evacuations_attempted;
- _bytes_attempted += bytes;
}
-void ShenandoahEvacuationStats::end_evacuation(size_t bytes) {
- ++_evacuations_completed;
- _bytes_completed += bytes;
-}
-
-void ShenandoahEvacuationStats::record_age(size_t bytes, uint age) {
- assert(_use_age_table, "Don't call!");
- if (age <= markWord::max_age) { // Filter age sentinel.
- _age_table->add(age, bytes >> LogBytesPerWord);
- }
+void ShenandoahEvacuationStats::end_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) {
+ ShenandoahEvacuations* category = get_category(from, to);
+ category->_evacuations_completed++;
+ category->_bytes_completed += bytes;
}
void ShenandoahEvacuationStats::accumulate(const ShenandoahEvacuationStats* other) {
- _evacuations_completed += other->_evacuations_completed;
- _bytes_completed += other->_bytes_completed;
- _evacuations_attempted += other->_evacuations_attempted;
- _bytes_attempted += other->_bytes_attempted;
- if (_use_age_table) {
- _age_table->merge(other->age_table());
- }
+ _young.accumulate(other->_young);
+ _old.accumulate(other->_old);
+ _promotion.accumulate(other->_promotion);
}
void ShenandoahEvacuationStats::reset() {
- _evacuations_completed = _evacuations_attempted = 0;
- _bytes_completed = _bytes_attempted = 0;
- if (_use_age_table) {
- _age_table->clear();
- }
+ _young.reset();
+ _old.reset();
+ _promotion.reset();
}
-void ShenandoahEvacuationStats::print_on(outputStream* st) {
-#ifndef PRODUCT
+void ShenandoahEvacuationStats::ShenandoahEvacuations::print_on(outputStream* st) const {
size_t abandoned_size = _bytes_attempted - _bytes_completed;
size_t abandoned_count = _evacuations_attempted - _evacuations_completed;
- st->print_cr("Evacuated %zu%s across %zu objects, "
- "abandoned %zu%s across %zu objects.",
- byte_size_in_proper_unit(_bytes_completed), proper_unit_for_byte_size(_bytes_completed),
- _evacuations_completed,
- byte_size_in_proper_unit(abandoned_size), proper_unit_for_byte_size(abandoned_size),
- abandoned_count);
-#endif
- if (_use_age_table) {
- _age_table->print_on(st);
+ st->print_cr("Evacuated " PROPERFMT" across %zu objects, "
+ "abandoned " PROPERFMT " across %zu objects.",
+ PROPERFMTARGS(_bytes_completed), _evacuations_completed,
+ PROPERFMTARGS(abandoned_size), abandoned_count);
+}
+
+void ShenandoahEvacuationStats::print_on(outputStream* st) const {
+ st->print("Young: "); _young.print_on(st);
+ if (ShenandoahHeap::heap()->mode()->is_generational()) {
+ st->print("Promotion: "); _promotion.print_on(st);
+ st->print("Old: "); _old.print_on(st);
}
}
@@ -103,25 +92,13 @@ void ShenandoahEvacuationTracker::print_global_on(outputStream* st) {
void ShenandoahEvacuationTracker::print_evacuations_on(outputStream* st,
ShenandoahEvacuationStats* workers,
ShenandoahEvacuationStats* mutators) {
- st->print("Workers: ");
+ assert(ShenandoahEvacTracking, "Only when evac tracking is enabled");
+ st->print_cr("Workers: ");
workers->print_on(st);
st->cr();
- st->print("Mutators: ");
+ st->print_cr("Mutators: ");
mutators->print_on(st);
st->cr();
-
- ShenandoahHeap* heap = ShenandoahHeap::heap();
-
- AgeTable young_region_ages(false);
- for (uint i = 0; i < heap->num_regions(); ++i) {
- ShenandoahHeapRegion* r = heap->get_region(i);
- if (r->is_young()) {
- young_region_ages.add(r->age(), r->get_live_data_words());
- }
- }
- st->print("Young regions: ");
- young_region_ages.print_on(st);
- st->cr();
}
class ShenandoahStatAggregator : public ThreadClosure {
@@ -148,26 +125,13 @@ ShenandoahCycleStats ShenandoahEvacuationTracker::flush_cycle_to_global() {
_mutators_global.accumulate(&mutators);
_workers_global.accumulate(&workers);
- if (ShenandoahGenerationalCensusAtEvac || !ShenandoahGenerationalAdaptiveTenuring) {
- // Ingest mutator & worker collected population vectors into the heap's
- // global census data, and use it to compute an appropriate tenuring threshold
- // for use in the next cycle.
- // The first argument is used for any age 0 cohort population that we may otherwise have
- // missed during the census. This is non-zero only when census happens at marking.
- ShenandoahGenerationalHeap::heap()->age_census()->update_census(0, mutators.age_table(), workers.age_table());
- }
-
return {workers, mutators};
}
-void ShenandoahEvacuationTracker::begin_evacuation(Thread* thread, size_t bytes) {
- ShenandoahThreadLocalData::begin_evacuation(thread, bytes);
-}
-
-void ShenandoahEvacuationTracker::end_evacuation(Thread* thread, size_t bytes) {
- ShenandoahThreadLocalData::end_evacuation(thread, bytes);
+void ShenandoahEvacuationTracker::begin_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) {
+ ShenandoahThreadLocalData::begin_evacuation(thread, bytes, from, to);
}
-void ShenandoahEvacuationTracker::record_age(Thread* thread, size_t bytes, uint age) {
- ShenandoahThreadLocalData::record_age(thread, bytes, age);
+void ShenandoahEvacuationTracker::end_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) {
+ ShenandoahThreadLocalData::end_evacuation(thread, bytes, from, to);
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp
index 7d195656b111..6e1182680a55 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.hpp
@@ -26,28 +26,54 @@
#define SHARE_GC_SHENANDOAH_SHENANDOAHEVACTRACKER_HPP
#include "gc/shared/ageTable.hpp"
+#include "gc/shenandoah/shenandoahAffiliation.hpp"
#include "utilities/ostream.hpp"
class ShenandoahEvacuationStats : public CHeapObj {
private:
- size_t _evacuations_completed;
- size_t _bytes_completed;
- size_t _evacuations_attempted;
- size_t _bytes_attempted;
-
- bool _use_age_table;
- AgeTable* _age_table;
+ struct ShenandoahEvacuations {
+ size_t _evacuations_completed;
+ size_t _bytes_completed;
+ size_t _evacuations_attempted;
+ size_t _bytes_attempted;
+ ShenandoahEvacuations()
+ : _evacuations_completed(0)
+ , _bytes_completed(0)
+ , _evacuations_attempted(0)
+ , _bytes_attempted(0) {
+ }
+
+ void accumulate(const ShenandoahEvacuations& other) {
+ _evacuations_completed += other._evacuations_completed;
+ _bytes_completed += other._bytes_completed;
+ _evacuations_attempted += other._evacuations_attempted;
+ _bytes_attempted += other._bytes_attempted;
+ }
+
+ void reset() {
+ _evacuations_completed = 0;
+ _bytes_completed = 0;
+ _evacuations_attempted = 0;
+ _bytes_attempted = 0;
+ }
+
+ void print_on(outputStream* st) const;
+ };
+
+ ShenandoahEvacuations* get_category(ShenandoahAffiliation from, ShenandoahAffiliation to);
+
+ ShenandoahEvacuations _young;
+ ShenandoahEvacuations _old;
+ ShenandoahEvacuations _promotion;
public:
- ShenandoahEvacuationStats();
-
- AgeTable* age_table() const;
+ // Record that the current thread is attempting to copy this many bytes.
+ void begin_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to);
- void begin_evacuation(size_t bytes);
- void end_evacuation(size_t bytes);
- void record_age(size_t bytes, uint age);
+ // Record that the current thread has completed copying this many bytes.
+ void end_evacuation(size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to);
- void print_on(outputStream* st);
+ void print_on(outputStream* st) const;
void accumulate(const ShenandoahEvacuationStats* other);
void reset();
};
@@ -66,9 +92,12 @@ class ShenandoahEvacuationTracker : public CHeapObj {
public:
ShenandoahEvacuationTracker() = default;
- void begin_evacuation(Thread* thread, size_t bytes);
- void end_evacuation(Thread* thread, size_t bytes);
- void record_age(Thread* thread, size_t bytes, uint age);
+ // Record that the given thread has begun to evacuate an object of this size.
+ void begin_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to);
+
+ // Multiple threads may attempt to evacuate the same object, but only the successful thread will end the evacuation.
+ // Evacuations that were begun, but not ended are considered 'abandoned'.
+ void end_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to);
void print_global_on(outputStream* st);
void print_evacuations_on(outputStream* st,
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp
index 27ff45e67de1..557a3847bab6 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp
@@ -104,21 +104,18 @@ void ShenandoahFullGC::entry_full(GCCause::Cause cause) {
}
void ShenandoahFullGC::op_full(GCCause::Cause cause) {
- ShenandoahMetricsSnapshot metrics;
- metrics.snap_before();
+ ShenandoahHeap* const heap = ShenandoahHeap::heap();
+
+ ShenandoahMetricsSnapshot metrics(heap->free_set());
// Perform full GC
do_it(cause);
- ShenandoahHeap* const heap = ShenandoahHeap::heap();
-
if (heap->mode()->is_generational()) {
ShenandoahGenerationalFullGC::handle_completion(heap);
}
- metrics.snap_after();
-
- if (metrics.is_good_progress(heap->global_generation())) {
+ if (metrics.is_good_progress()) {
heap->notify_gc_progress();
} else {
// Nothing to do. Tell the allocation path that we have failed to make
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp
index 7b3839dc1980..7b425a8fd46b 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp
@@ -74,6 +74,9 @@ class ShenandoahResetBitmapClosure final : public ShenandoahHeapRegionClosure {
}
}
+ // Bitmap reset task is heavy-weight and benefits from much smaller tasks than the default.
+ size_t parallel_region_stride() override { return 8; }
+
bool is_thread_safe() override { return true; }
};
@@ -151,8 +154,8 @@ size_t ShenandoahGeneration::bytes_allocated_since_gc_start() const {
return Atomic::load(&_bytes_allocated_since_gc_start);
}
-void ShenandoahGeneration::reset_bytes_allocated_since_gc_start() {
- Atomic::store(&_bytes_allocated_since_gc_start, (size_t)0);
+void ShenandoahGeneration::reset_bytes_allocated_since_gc_start(size_t initial_bytes_allocated) {
+ Atomic::store(&_bytes_allocated_since_gc_start, initial_bytes_allocated);
}
void ShenandoahGeneration::increase_allocated(size_t bytes) {
@@ -382,11 +385,11 @@ void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap,
// available that results from a decrease in memory consumed by old evacuation is not necessarily available to be loaned
// to young-gen.
- size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes();
+ const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes();
ShenandoahOldGeneration* const old_generation = heap->old_generation();
ShenandoahYoungGeneration* const young_generation = heap->young_generation();
- size_t old_evacuated = collection_set->get_old_bytes_reserved_for_evacuation();
+ const size_t old_evacuated = collection_set->get_live_bytes_in_old_regions();
size_t old_evacuated_committed = (size_t) (ShenandoahOldEvacWaste * double(old_evacuated));
size_t old_evacuation_reserve = old_generation->get_evacuation_reserve();
@@ -399,14 +402,15 @@ void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap,
// Leave old_evac_reserve as previously configured
} else if (old_evacuated_committed < old_evacuation_reserve) {
// This happens if the old-gen collection consumes less than full budget.
+ log_debug(gc, cset)("Shrinking old evac reserve to match old_evac_commited: " PROPERFMT, PROPERFMTARGS(old_evacuated_committed));
old_evacuation_reserve = old_evacuated_committed;
old_generation->set_evacuation_reserve(old_evacuation_reserve);
}
- size_t young_advance_promoted = collection_set->get_young_bytes_to_be_promoted();
+ size_t young_advance_promoted = collection_set->get_live_bytes_in_tenurable_regions();
size_t young_advance_promoted_reserve_used = (size_t) (ShenandoahPromoEvacWaste * double(young_advance_promoted));
- size_t young_evacuated = collection_set->get_young_bytes_reserved_for_evacuation();
+ size_t young_evacuated = collection_set->get_live_bytes_in_untenurable_regions();
size_t young_evacuated_reserve_used = (size_t) (ShenandoahEvacWaste * double(young_evacuated));
size_t total_young_available = young_generation->available_with_reserve();
@@ -524,7 +528,7 @@ inline void assert_no_in_place_promotions() {
// 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 ShenandoahGeneration::select_aged_regions(size_t old_available) {
+size_t ShenandoahGeneration::select_aged_regions(const size_t old_promotion_reserve) {
// There should be no regions configured for subsequent in-place-promotions carried over from the previous cycle.
assert_no_in_place_promotions();
@@ -535,7 +539,8 @@ size_t ShenandoahGeneration::select_aged_regions(size_t old_available) {
const size_t old_garbage_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold) / 100;
- size_t old_consumed = 0;
+ const size_t pip_used_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahGenerationalMinPIPUsage) / 100;
+
size_t promo_potential = 0;
size_t candidates = 0;
@@ -557,10 +562,8 @@ size_t ShenandoahGeneration::select_aged_regions(size_t old_available) {
continue;
}
if (heap->is_tenurable(r)) {
- if ((r->garbage() < old_garbage_threshold)) {
- // This tenure-worthy region has too little garbage, so we do not want to expend the copying effort to
- // reclaim the garbage; instead this region may be eligible for promotion-in-place to the
- // old generation.
+ if ((r->garbage() < old_garbage_threshold) && (r->used() > pip_used_threshold)) {
+ // We prefer to promote this region in place because it has a small amount of garbage and a large usage.
HeapWord* tams = ctx->top_at_mark_start(r);
HeapWord* original_top = r->top();
if (!heap->is_concurrent_old_mark_in_progress() && tams == original_top) {
@@ -586,7 +589,7 @@ size_t ShenandoahGeneration::select_aged_regions(size_t old_available) {
// Else, we do not promote this region (either in place or by copy) because it has received new allocations.
// During evacuation, we exclude from promotion regions for which age > tenure threshold, garbage < garbage-threshold,
- // and get_top_before_promote() != tams
+ // used > pip_used_threshold, and get_top_before_promote() != tams
} else {
// Record this promotion-eligible candidate region. After sorting and selecting the best candidates below,
// we may still decide to exclude this promotion-eligible region from the current collection set. If this
@@ -620,17 +623,21 @@ size_t ShenandoahGeneration::select_aged_regions(size_t old_available) {
// Note that we keep going even if one region is excluded from selection.
// Subsequent regions may be selected if they have smaller live data.
}
+
+ log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential));
+
// 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;
- size_t region_live_data = sorted_regions[i]._live_data;
- size_t promotion_need = (size_t) (region_live_data * ShenandoahPromoEvacWaste);
- if (old_consumed + promotion_need <= old_available) {
+ 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;
candidate_regions_for_promotion_by_copy[region->index()] = true;
selected_regions++;
@@ -644,9 +651,9 @@ size_t ShenandoahGeneration::select_aged_regions(size_t old_available) {
// 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)("Preselected %zu regions containing %zu live bytes,"
- " consuming: %zu of budgeted: %zu",
- selected_regions, selected_live, old_consumed, old_available);
+ 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));
}
heap->old_generation()->set_pad_for_promote_in_place(promote_in_place_pad);
@@ -678,7 +685,7 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) {
}
// Tally the census counts and compute the adaptive tenuring threshold
- if (is_generational && ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) {
+ if (is_generational && ShenandoahGenerationalAdaptiveTenuring) {
// Objects above TAMS weren't included in the age census. Since they were all
// allocated in this cycle they belong in the age 0 cohort. We walk over all
// young regions and sum the volume of objects between TAMS and top.
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp
index 2b7aca342dad..c49deef561b0 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp
@@ -97,7 +97,7 @@ class ShenandoahGeneration : public CHeapObj, public ShenandoahSpaceInfo {
// regions, which are marked in the preselected_regions() indicator
// array of the heap's collection set, which should be initialized
// to false.
- size_t select_aged_regions(size_t old_available);
+ size_t select_aged_regions(size_t old_promotion_reserve);
size_t available(size_t capacity) const;
@@ -142,7 +142,7 @@ class ShenandoahGeneration : public CHeapObj, public ShenandoahSpaceInfo {
size_t soft_available() const override;
size_t bytes_allocated_since_gc_start() const override;
- void reset_bytes_allocated_since_gc_start();
+ void reset_bytes_allocated_since_gc_start(size_t initial_bytes_allocated);
void increase_allocated(size_t bytes);
// These methods change the capacity of the generation by adding or subtracting the given number of bytes from the current
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp
index 2555f73d018d..cd144cf05712 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp
@@ -210,6 +210,24 @@ ShenandoahGenerationalControlThread::GCMode ShenandoahGenerationalControlThread:
return request.generation->is_old() ? servicing_old : concurrent_normal;
}
+void ShenandoahGenerationalControlThread::maybe_print_young_region_ages() const {
+ LogTarget(Debug, gc, age) lt;
+ if (lt.is_enabled()) {
+ LogStream ls(lt);
+ AgeTable young_region_ages(false);
+ for (uint i = 0; i < _heap->num_regions(); ++i) {
+ const ShenandoahHeapRegion* r = _heap->get_region(i);
+ if (r->is_young()) {
+ young_region_ages.add(r->age(), r->get_live_data_words());
+ }
+ }
+
+ ls.print("Young regions: ");
+ young_region_ages.print_on(&ls);
+ ls.cr();
+ }
+}
+
void ShenandoahGenerationalControlThread::maybe_set_aging_cycle() {
if (_age_period-- == 0) {
_heap->set_aging_cycle(true);
@@ -304,7 +322,11 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest
_heap->global_generation()->heuristics()->clear_metaspace_oom();
}
- process_phase_timings();
+ // Manage and print gc stats
+ _heap->process_gc_stats();
+
+ // Print table for young region ages if log is enabled
+ maybe_print_young_region_ages();
// Print Metaspace change following GC (if logging is enabled).
MetaspaceUtils::print_metaspace_change(meta_sizes);
@@ -328,35 +350,6 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest
gc_mode_name(gc_mode()), GCCause::to_string(request.cause), request.generation->name(), GCCause::to_string(_heap->cancelled_cause()));
}
-void ShenandoahGenerationalControlThread::process_phase_timings() const {
- // Commit worker statistics to cycle data
- _heap->phase_timings()->flush_par_workers_to_cycle();
- if (ShenandoahPacing) {
- _heap->pacer()->flush_stats_to_cycle();
- }
-
- ShenandoahEvacuationTracker* evac_tracker = _heap->evac_tracker();
- ShenandoahCycleStats evac_stats = evac_tracker->flush_cycle_to_global();
-
- // Print GC stats for current cycle
- {
- LogTarget(Info, gc, stats) lt;
- if (lt.is_enabled()) {
- ResourceMark rm;
- LogStream ls(lt);
- _heap->phase_timings()->print_cycle_on(&ls);
- evac_tracker->print_evacuations_on(&ls, &evac_stats.workers,
- &evac_stats.mutators);
- if (ShenandoahPacing) {
- _heap->pacer()->print_cycle_on(&ls);
- }
- }
- }
-
- // Commit statistics to globals
- _heap->phase_timings()->flush_cycle_to_global();
-}
-
// Young and old concurrent cycles are initiated by the regulator. Implicit
// and explicit GC requests are handled by the controller thread and always
// run a global cycle (which is concurrent by default, but may be overridden
@@ -432,7 +425,7 @@ void ShenandoahGenerationalControlThread::service_concurrent_old_cycle(const She
set_gc_mode(bootstrapping_old);
young_generation->set_old_gen_task_queues(old_generation->task_queues());
service_concurrent_cycle(young_generation, request.cause, true);
- process_phase_timings();
+ _heap->process_gc_stats();
if (_heap->cancelled_gc()) {
// Young generation bootstrap cycle has failed. Concurrent mark for old generation
// is going to resume after degenerated bootstrap cycle completes.
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp
index 1586205742a7..6a4f5bde5789 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp
@@ -129,9 +129,6 @@ class ShenandoahGenerationalControlThread: public ShenandoahController {
// Returns true if the old generation marking was interrupted to allow a young cycle.
bool preempt_old_marking(ShenandoahGeneration* generation);
- // Flushes cycle timings to global timings and prints the phase timings for the last completed cycle.
- void process_phase_timings() const;
-
// Set the gc mode and post a notification if it has changed. The overloaded variant should be used
// when the _control_lock is already held.
void set_gc_mode(GCMode new_mode);
@@ -160,6 +157,9 @@ class ShenandoahGenerationalControlThread: public ShenandoahController {
GCMode prepare_for_allocation_failure_gc(ShenandoahGCRequest &request);
GCMode prepare_for_explicit_gc(ShenandoahGCRequest &request) const;
GCMode prepare_for_concurrent_gc(const ShenandoahGCRequest &request) const;
+
+ // Print table for young region ages if log is enabled
+ void maybe_print_young_region_ages() const;
};
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALCONTROLTHREAD_HPP
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp
index b538f7b1417e..3f0db4fb8eab 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp
@@ -150,7 +150,13 @@ void ShenandoahGenerationalEvacuationTask::maybe_promote_region(ShenandoahHeapRe
// more garbage than ShenandoahOldGarbageThreshold, we'll promote by evacuation. If there is room for evacuation
// in this cycle, the region will be in the collection set. If there is not room, the region will be promoted
// by evacuation in some future GC cycle.
- promote_humongous(r);
+
+ // We do not promote primitive arrays because there's no performance penalty keeping them in young. When/if they
+ // become garbage, reclaiming the memory from young is much quicker and more efficient than reclaiming them from old.
+ oop obj = cast_to_oop(r->bottom());
+ if (!obj->is_typeArray()) {
+ promote_humongous(r);
+ }
} else if (r->is_regular() && (r->get_top_before_promote() != nullptr)) {
// Likewise, we cannot put promote-in-place regions into the collection set because that would also trigger
// the LRB to copy on reference fetch.
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
index feb82dd05271..316ac61dc352 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
@@ -80,7 +80,6 @@ size_t ShenandoahGenerationalHeap::unsafe_max_tlab_alloc(Thread *thread) const {
ShenandoahGenerationalHeap::ShenandoahGenerationalHeap(ShenandoahCollectorPolicy* policy) :
ShenandoahHeap(policy),
_age_census(nullptr),
- _evac_tracker(new ShenandoahEvacuationTracker()),
_min_plab_size(calculate_min_plab()),
_max_plab_size(calculate_max_plab()),
_regulator_thread(nullptr),
@@ -100,18 +99,6 @@ void ShenandoahGenerationalHeap::print_init_logger() const {
logger.print_all();
}
-void ShenandoahGenerationalHeap::print_tracing_info() const {
- ShenandoahHeap::print_tracing_info();
-
- LogTarget(Info, gc, stats) lt;
- if (lt.is_enabled()) {
- LogStream ls(lt);
- ls.cr();
- ls.cr();
- evac_tracker()->print_global_on(&ls);
- }
-}
-
void ShenandoahGenerationalHeap::initialize_heuristics() {
// Initialize global generation and heuristics even in generational mode.
ShenandoahHeap::initialize_heuristics();
@@ -123,7 +110,6 @@ void ShenandoahGenerationalHeap::initialize_heuristics() {
_generation_sizer.heap_size_changed(max_capacity());
size_t initial_capacity_young = _generation_sizer.max_young_size();
size_t max_capacity_young = _generation_sizer.max_young_size();
- size_t initial_capacity_old = max_capacity() - max_capacity_young;
size_t max_capacity_old = max_capacity() - initial_capacity_young;
_young_generation = new ShenandoahYoungGeneration(max_workers(), max_capacity_young);
@@ -280,6 +266,7 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, Shena
// the requested object does not fit within the current plab but the plab still has an "abundance" of memory,
// where abundance is defined as >= ShenGenHeap::plab_min_size(). In the former case, we try shrinking the
// desired PLAB size to the minimum and retry PLAB allocation to avoid cascading of shared memory allocations.
+ // Shrinking the desired PLAB size may allow us to eke out a small PLAB while staying beneath evacuation reserve.
if (plab->words_remaining() < plab_min_size()) {
ShenandoahThreadLocalData::set_plab_size(thread, plab_min_size());
copy = allocate_from_plab(thread, size, is_promotion);
@@ -337,8 +324,11 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, Shena
return ShenandoahBarrierSet::resolve_forwarded(p);
}
+ if (ShenandoahEvacTracking) {
+ evac_tracker()->begin_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen);
+ }
+
// Copy the object:
- NOT_PRODUCT(evac_tracker()->begin_evacuation(thread, size * HeapWordSize));
Copy::aligned_disjoint_words(cast_from_oop(p), copy, size);
oop copy_val = cast_to_oop(copy);
@@ -359,8 +349,10 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, Shena
// safe to do this on the public copy (this is also done during concurrent mark).
ContinuationGCSupport::relativize_stack_chunk(copy_val);
- // Record that the evacuation succeeded
- NOT_PRODUCT(evac_tracker()->end_evacuation(thread, size * HeapWordSize));
+ if (ShenandoahEvacTracking) {
+ // Record that the evacuation succeeded
+ evac_tracker()->end_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen);
+ }
if (target_gen == OLD_GENERATION) {
old_generation()->handle_evacuation(copy, size, from_region->is_young());
@@ -368,11 +360,6 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, Shena
// When copying to the old generation above, we don't care
// about recording object age in the census stats.
assert(target_gen == YOUNG_GENERATION, "Error");
- // We record this census only when simulating pre-adaptive tenuring behavior, or
- // when we have been asked to record the census at evacuation rather than at mark
- if (ShenandoahGenerationalCensusAtEvac || !ShenandoahGenerationalAdaptiveTenuring) {
- evac_tracker()->record_age(thread, size * HeapWordSize, ShenandoahHeap::get_object_age(copy_val));
- }
}
shenandoah_assert_correct(nullptr, copy_val);
return copy_val;
@@ -449,9 +436,8 @@ inline HeapWord* ShenandoahGenerationalHeap::allocate_from_plab(Thread* thread,
// Establish a new PLAB and allocate size HeapWords within it.
HeapWord* ShenandoahGenerationalHeap::allocate_from_plab_slow(Thread* thread, size_t size, bool is_promotion) {
- // New object should fit the PLAB size
-
assert(mode()->is_generational(), "PLABs only relevant to generational GC");
+
const size_t plab_min_size = this->plab_min_size();
// PLABs are aligned to card boundaries to avoid synchronization with concurrent
// allocations in other PLABs.
@@ -464,23 +450,24 @@ HeapWord* ShenandoahGenerationalHeap::allocate_from_plab_slow(Thread* thread, si
}
// Expand aggressively, doubling at each refill in this epoch, ceiling at plab_max_size()
- size_t future_size = MIN2(cur_size * 2, plab_max_size());
+ const size_t future_size = MIN2(cur_size * 2, plab_max_size());
// Doubling, starting at a card-multiple, should give us a card-multiple. (Ceiling and floor
// are card multiples.)
assert(is_aligned(future_size, CardTable::card_size_in_words()), "Card multiple by construction, future_size: %zu"
- ", card_size: %zu, cur_size: %zu, max: %zu",
- future_size, (size_t) CardTable::card_size_in_words(), cur_size, plab_max_size());
+ ", card_size: %u, cur_size: %zu, max: %zu",
+ future_size, CardTable::card_size_in_words(), cur_size, plab_max_size());
// Record new heuristic value even if we take any shortcut. This captures
// the case when moderately-sized objects always take a shortcut. At some point,
// heuristics should catch up with them. Note that the requested cur_size may
// not be honored, but we remember that this is the preferred size.
- log_debug(gc, free)("Set new PLAB size: %zu", future_size);
+ log_debug(gc, plab)("Set next PLAB refill size: %zu bytes", future_size * HeapWordSize);
ShenandoahThreadLocalData::set_plab_size(thread, future_size);
+
if (cur_size < size) {
// The PLAB to be allocated is still not large enough to hold the object. Fall back to shared allocation.
// This avoids retiring perfectly good PLABs in order to represent a single large object allocation.
- log_debug(gc, free)("Current PLAB size (%zu) is too small for %zu", cur_size, size);
+ log_debug(gc, plab)("Current PLAB size (%zu) is too small for %zu", cur_size * HeapWordSize, size * HeapWordSize);
return nullptr;
}
@@ -566,6 +553,7 @@ void ShenandoahGenerationalHeap::retire_plab(PLAB* plab, Thread* thread) {
ShenandoahThreadLocalData::reset_plab_promoted(thread);
ShenandoahThreadLocalData::set_plab_actual_size(thread, 0);
if (not_promoted > 0) {
+ log_debug(gc, plab)("Retire PLAB, unexpend unpromoted: %zu", not_promoted * HeapWordSize);
old_generation()->unexpend_promoted(not_promoted);
}
const size_t original_waste = plab->waste();
@@ -577,8 +565,8 @@ void ShenandoahGenerationalHeap::retire_plab(PLAB* plab, Thread* thread) {
if (top != nullptr && plab->waste() > original_waste && 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)("retire_plab() is registering remnant of size %zu at " PTR_FORMAT,
- plab->waste() - original_waste, p2i(top));
+ 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.
old_generation()->card_scan()->register_object_without_lock(top);
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
index fb356873356d..adf9c73a2322 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
@@ -53,7 +53,6 @@ class ShenandoahGenerationalHeap : public ShenandoahHeap {
}
void print_init_logger() const override;
- void print_tracing_info() const override;
size_t unsafe_max_tlab_alloc(Thread *thread) const override;
@@ -64,8 +63,6 @@ class ShenandoahGenerationalHeap : public ShenandoahHeap {
ShenandoahSharedFlag _is_aging_cycle;
// Age census used for adapting tenuring threshold
ShenandoahAgeCensus* _age_census;
- // Used primarily to look for failed evacuation attempts.
- ShenandoahEvacuationTracker* _evac_tracker;
public:
void set_aging_cycle(bool cond) {
@@ -83,9 +80,6 @@ class ShenandoahGenerationalHeap : public ShenandoahHeap {
inline bool is_tenurable(const ShenandoahHeapRegion* r) const;
- ShenandoahEvacuationTracker* evac_tracker() const {
- return _evac_tracker;
- }
// Ages regions that haven't been used for allocations in the current cycle.
// Resets ages for regions that have been used for allocations.
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
index a56ada222d7e..9be46eb20d5d 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
@@ -574,7 +574,8 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) :
_bitmap_region_special(false),
_aux_bitmap_region_special(false),
_liveness_cache(nullptr),
- _collection_set(nullptr)
+ _collection_set(nullptr),
+ _evac_tracker(new ShenandoahEvacuationTracker())
{
// Initialize GC mode early, many subsequent initialization procedures depend on it
initialize_mode();
@@ -1287,7 +1288,7 @@ void ShenandoahHeap::evacuate_collection_set(bool concurrent) {
void ShenandoahHeap::concurrent_prepare_for_update_refs() {
{
- // Java threads take this lock while they are being attached and added to the list of thread.
+ // Java threads take this lock while they are being attached and added to the list of threads.
// If another thread holds this lock before we update the gc state, it will receive a stale
// gc state, but they will have been added to the list of java threads and so will be corrected
// by the following handshake.
@@ -1394,6 +1395,10 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg
return ShenandoahBarrierSet::resolve_forwarded(p);
}
+ if (ShenandoahEvacTracking) {
+ evac_tracker()->begin_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen);
+ }
+
// Copy the object:
Copy::aligned_disjoint_words(cast_from_oop(p), copy, size);
@@ -1404,6 +1409,9 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg
// Successfully evacuated. Our copy is now the public one!
ContinuationGCSupport::relativize_stack_chunk(copy_val);
shenandoah_assert_correct(nullptr, copy_val);
+ if (ShenandoahEvacTracking) {
+ evac_tracker()->end_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen);
+ }
return copy_val;
} else {
// Failed to evacuate. We need to deal with the object that is left behind. Since this
@@ -1457,6 +1465,31 @@ void ShenandoahHeap::print_heap_regions_on(outputStream* st) const {
}
}
+void ShenandoahHeap::process_gc_stats() const {
+ // Commit worker statistics to cycle data
+ phase_timings()->flush_par_workers_to_cycle();
+
+ if (ShenandoahPacing) {
+ pacer()->flush_stats_to_cycle();
+ }
+
+ // Print GC stats for current cycle
+ LogTarget(Info, gc, stats) lt;
+ if (lt.is_enabled()) {
+ ResourceMark rm;
+ LogStream ls(lt);
+ phase_timings()->print_cycle_on(&ls);
+ if (ShenandoahEvacTracking) {
+ ShenandoahCycleStats evac_stats = evac_tracker()->flush_cycle_to_global();
+ evac_tracker()->print_evacuations_on(&ls, &evac_stats.workers,
+ &evac_stats.mutators);
+ }
+ }
+
+ // Commit statistics to globals
+ phase_timings()->flush_cycle_to_global();
+}
+
size_t ShenandoahHeap::trash_humongous_region_at(ShenandoahHeapRegion* start) const {
assert(start->is_humongous_start(), "reclaim regions starting with the first one");
assert(!start->has_live(), "liveness must be zero");
@@ -1571,8 +1604,8 @@ void ShenandoahHeap::collect_as_vm_thread(GCCause::Cause cause) {
// cycle. We _could_ cancel the concurrent cycle and then try to run a cycle directly
// on the VM thread, but this would confuse the control thread mightily and doesn't
// seem worth the trouble. Instead, we will have the caller thread run (and wait for) a
- // concurrent cycle in the prologue of the heap inspect/dump operation. This is how
- // other concurrent collectors in the JVM handle this scenario as well.
+ // concurrent cycle in the prologue of the heap inspect/dump operation (see VM_HeapDumper::doit_prologue).
+ // This is how other concurrent collectors in the JVM handle this scenario as well.
assert(Thread::current()->is_VM_thread(), "Should be the VM thread");
guarantee(cause == GCCause::_heap_dump || cause == GCCause::_heap_inspection, "Invalid cause");
}
@@ -1582,7 +1615,10 @@ void ShenandoahHeap::collect(GCCause::Cause cause) {
}
void ShenandoahHeap::do_full_collection(bool clear_all_soft_refs) {
- //assert(false, "Shouldn't need to do full collections");
+ // This method is only called by `CollectedHeap::collect_as_vm_thread`, which we have
+ // overridden to do nothing. See the comment there for an explanation of how heap inspections
+ // work for Shenandoah.
+ ShouldNotReachHere();
}
HeapWord* ShenandoahHeap::block_start(const void* addr) const {
@@ -1633,6 +1669,12 @@ void ShenandoahHeap::print_tracing_info() const {
ResourceMark rm;
LogStream ls(lt);
+ if (ShenandoahEvacTracking) {
+ evac_tracker()->print_global_on(&ls);
+ ls.cr();
+ ls.cr();
+ }
+
phase_timings()->print_global_on(&ls);
ls.cr();
@@ -2003,7 +2045,7 @@ void ShenandoahHeap::parallel_heap_region_iterate(ShenandoahHeapRegionClosure* b
assert(blk->is_thread_safe(), "Only thread-safe closures here");
const uint active_workers = workers()->active_workers();
const size_t n_regions = num_regions();
- size_t stride = ShenandoahParallelRegionStride;
+ size_t stride = blk->parallel_region_stride();
if (stride == 0 && active_workers > 1) {
// Automatically derive the stride to balance the work between threads
// evenly. Do not try to split work if below the reasonable threshold.
@@ -2322,12 +2364,27 @@ address ShenandoahHeap::in_cset_fast_test_addr() {
}
void ShenandoahHeap::reset_bytes_allocated_since_gc_start() {
+ // It is important to force_alloc_rate_sample() before the associated generation's bytes_allocated has been reset.
+ // Note that there is no lock to prevent additional alloations between sampling bytes_allocated_since_gc_start() and
+ // reset_bytes_allocated_since_gc_start(). If additional allocations happen, they will be ignored in the average
+ // allocation rate computations. This effect is considered to be be negligible.
+
+ // unaccounted_bytes is the bytes not accounted for by our forced sample. If the sample interval is too short,
+ // the "forced sample" will not happen, and any recently allocated bytes are "unaccounted for". We pretend these
+ // bytes are allocated after the start of subsequent gc.
+ size_t unaccounted_bytes;
if (mode()->is_generational()) {
- young_generation()->reset_bytes_allocated_since_gc_start();
- old_generation()->reset_bytes_allocated_since_gc_start();
+ size_t bytes_allocated = young_generation()->bytes_allocated_since_gc_start();
+ unaccounted_bytes = young_generation()->heuristics()->force_alloc_rate_sample(bytes_allocated);
+ young_generation()->reset_bytes_allocated_since_gc_start(unaccounted_bytes);
+ unaccounted_bytes = 0;
+ old_generation()->reset_bytes_allocated_since_gc_start(unaccounted_bytes);
+ } else {
+ size_t bytes_allocated = global_generation()->bytes_allocated_since_gc_start();
+ // Single-gen Shenandoah uses global heuristics.
+ unaccounted_bytes = heuristics()->force_alloc_rate_sample(bytes_allocated);
}
-
- global_generation()->reset_bytes_allocated_since_gc_start();
+ global_generation()->reset_bytes_allocated_since_gc_start(unaccounted_bytes);
}
void ShenandoahHeap::set_degenerated_gc_in_progress(bool in_progress) {
@@ -2739,18 +2796,6 @@ bool ShenandoahRegionIterator::has_next() const {
return _index < _heap->num_regions();
}
-char ShenandoahHeap::gc_state() const {
- return _gc_state.raw_value();
-}
-
-bool ShenandoahHeap::is_gc_state(GCState state) const {
- // If the global gc state has been changed, but hasn't yet been propagated to all threads, then
- // the global gc state is the correct value. Once the gc state has been synchronized with all threads,
- // _gc_state_changed will be toggled to false and we need to use the thread local state.
- return _gc_state_changed ? _gc_state.is_set(state) : ShenandoahThreadLocalData::is_gc_state(state);
-}
-
-
ShenandoahLiveData* ShenandoahHeap::get_liveness_cache(uint worker_id) {
#ifdef ASSERT
assert(_liveness_cache != nullptr, "sanity");
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
index 509ba1db9c2d..f8d609d61527 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
@@ -115,6 +115,7 @@ class ShenandoahRegionIterator : public StackObj {
class ShenandoahHeapRegionClosure : public StackObj {
public:
virtual void heap_region_do(ShenandoahHeapRegion* r) = 0;
+ virtual size_t parallel_region_stride() { return ShenandoahParallelRegionStride; }
virtual bool is_thread_safe() { return false; }
};
@@ -203,10 +204,13 @@ class ShenandoahHeap : public CollectedHeap {
void initialize_serviceability() override;
void print_heap_on(outputStream* st) const override;
- void print_gc_on(outputStream *st) const override;
+ void print_gc_on(outputStream* st) const override;
void print_tracing_info() const override;
void print_heap_regions_on(outputStream* st) const;
+ // Flushes cycle timings to global timings and prints the phase timings for the last completed cycle.
+ void process_gc_stats() const;
+
void stop() override;
void prepare_for_verify() override;
@@ -375,7 +379,7 @@ class ShenandoahHeap : public CollectedHeap {
public:
// This returns the raw value of the singular, global gc state.
- char gc_state() const;
+ inline char gc_state() const;
// Compares the given state against either the global gc state, or the thread local state.
// The global gc state may change on a safepoint and is the correct value to use until
@@ -383,7 +387,7 @@ class ShenandoahHeap : public CollectedHeap {
// compare against the thread local state). The thread local gc state may also be changed
// by a handshake operation, in which case, this function continues using the updated thread
// local value.
- bool is_gc_state(GCState state) const;
+ inline bool is_gc_state(GCState state) const;
// This copies the global gc state into a thread local variable for all threads.
// The thread local gc state is primarily intended to support quick access at barriers.
@@ -557,6 +561,10 @@ class ShenandoahHeap : public CollectedHeap {
ShenandoahEvacOOMHandler* oom_evac_handler() { return &_oom_evac_handler; }
+ ShenandoahEvacuationTracker* evac_tracker() const {
+ return _evac_tracker;
+ }
+
void on_cycle_start(GCCause::Cause cause, ShenandoahGeneration* generation);
void on_cycle_end(ShenandoahGeneration* generation);
@@ -789,6 +797,10 @@ class ShenandoahHeap : public CollectedHeap {
oop try_evacuate_object(oop src, Thread* thread, ShenandoahHeapRegion* from_region, ShenandoahAffiliation target_gen);
+protected:
+ // Used primarily to look for failed evacuation attempts.
+ ShenandoahEvacuationTracker* _evac_tracker;
+
public:
static address in_cset_fast_test_addr();
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
index cf9d808f7ce8..a4c5c91966fc 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
@@ -451,6 +451,17 @@ inline bool ShenandoahHeap::in_collection_set_loc(void* p) const {
return collection_set()->is_in_loc(p);
}
+inline char ShenandoahHeap::gc_state() const {
+ return _gc_state.raw_value();
+}
+
+inline bool ShenandoahHeap::is_gc_state(GCState state) const {
+ // If the global gc state has been changed, but hasn't yet been propagated to all threads, then
+ // the global gc state is the correct value. Once the gc state has been synchronized with all threads,
+ // _gc_state_changed will be toggled to false and we need to use the thread local state.
+ return _gc_state_changed ? _gc_state.is_set(state) : ShenandoahThreadLocalData::is_gc_state(state);
+}
+
inline bool ShenandoahHeap::is_idle() const {
return _gc_state_changed ? _gc_state.is_clear() : ShenandoahThreadLocalData::gc_state(Thread::current()) == 0;
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.hpp
index 0daf268628c1..3f3b57c9bb2d 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionClosures.hpp
@@ -44,6 +44,10 @@ class ShenandoahIncludeRegionClosure : public ShenandoahHeapRegionClosure {
}
}
+ size_t parallel_region_stride() override {
+ return _closure->parallel_region_stride();
+ }
+
bool is_thread_safe() override {
return _closure->is_thread_safe();
}
@@ -64,6 +68,10 @@ class ShenandoahExcludeRegionClosure : public ShenandoahHeapRegionClosure {
}
}
+ size_t parallel_region_stride() override {
+ return _closure->parallel_region_stride();
+ }
+
bool is_thread_safe() override {
return _closure->is_thread_safe();
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp
index 2dc0813e5135..c588a03fdf90 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp
@@ -118,7 +118,7 @@ inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop ob
// Age census for objects in the young generation
if (GENERATION == YOUNG || (GENERATION == GLOBAL && region->is_young())) {
assert(heap->mode()->is_generational(), "Only if generational");
- if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) {
+ if (ShenandoahGenerationalAdaptiveTenuring) {
assert(region->is_young(), "Only for young objects");
uint age = ShenandoahHeap::get_object_age(obj);
ShenandoahAgeCensus* const census = ShenandoahGenerationalHeap::heap()->age_census();
@@ -226,8 +226,6 @@ inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl,
assert(obj->is_objArray(), "expect object array");
objArrayOop array = objArrayOop(obj);
- assert (ObjArrayMarkingStride > 0, "sanity");
-
// Split out tasks, as suggested in ShenandoahMarkTask docs. Avoid pushing tasks that
// are known to start beyond the array.
while ((1 << pow) > (int)ObjArrayMarkingStride && (chunk*2 < ShenandoahMarkTask::chunk_size())) {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp
index 21b526f99954..73442ff0520a 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp
@@ -136,14 +136,12 @@ inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_next_bit_impl(idx_t
// Get the word containing l_index, and shift out low bits.
idx_t index = to_words_align_down(l_index);
bm_word_t cword = (map(index) ^ flip) >> bit_in_word(l_index);
- if ((cword & 1) != 0) {
- // The first bit is similarly often interesting. When it matters
- // (density or features of the calling algorithm make it likely
- // the first bit is set), going straight to the next clause compares
- // poorly with doing this check first; count_trailing_zeros can be
- // relatively expensive, plus there is the additional range check.
- // But when the first bit isn't set, the cost of having tested for
- // it is relatively small compared to the rest of the search.
+ if ((cword & 0x03) != 0) {
+ // The first bits (representing weak mark or strong mark) are similarly often interesting. When it matters
+ // (density or features of the calling algorithm make it likely the first bits are set), going straight to
+ // the next clause compares poorly with doing this check first; count_trailing_zeros can be relatively expensive,
+ // plus there is the additional range check. But when the first bits are not set, the cost of having tested for
+ // them is relatively small compared to the rest of the search.
return l_index;
} else if (cword != 0) {
// Flipped and shifted first word is non-zero.
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp
index 0babeaffd3e0..40eee8c342ba 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp
@@ -74,8 +74,8 @@ void ShenandoahMarkingContext::initialize_top_at_mark_start(ShenandoahHeapRegion
_top_at_mark_starts_base[idx] = bottom;
_top_bitmaps[idx] = bottom;
- log_debug(gc)("SMC:initialize_top_at_mark_start for Region %zu, TAMS: " PTR_FORMAT ", TopOfBitMap: " PTR_FORMAT,
- r->index(), p2i(bottom), p2i(r->end()));
+ log_debug(gc, mark)("SMC:initialize_top_at_mark_start for Region %zu, TAMS: " PTR_FORMAT ", TopOfBitMap: " PTR_FORMAT,
+ r->index(), p2i(bottom), p2i(r->end()));
}
HeapWord* ShenandoahMarkingContext::top_bitmap(ShenandoahHeapRegion* r) {
@@ -86,8 +86,8 @@ void ShenandoahMarkingContext::clear_bitmap(ShenandoahHeapRegion* r) {
HeapWord* bottom = r->bottom();
HeapWord* top_bitmap = _top_bitmaps[r->index()];
- log_debug(gc)("SMC:clear_bitmap for %s Region %zu, top_bitmap: " PTR_FORMAT,
- r->affiliation_name(), r->index(), p2i(top_bitmap));
+ log_debug(gc, mark)("SMC:clear_bitmap for %s Region %zu, top_bitmap: " PTR_FORMAT,
+ r->affiliation_name(), r->index(), p2i(top_bitmap));
if (top_bitmap > bottom) {
_mark_bit_map.clear_range_large(MemRegion(bottom, top_bitmap));
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp
index fe98413a8ccb..637dbf47c3f4 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp
@@ -108,8 +108,8 @@ inline void ShenandoahMarkingContext::capture_top_at_mark_start(ShenandoahHeapRe
"Region %zu, bitmap should be clear while adjusting TAMS: " PTR_FORMAT " -> " PTR_FORMAT,
idx, p2i(old_tams), p2i(new_tams));
- log_debug(gc)("Capturing TAMS for %s Region %zu, was: " PTR_FORMAT ", now: " PTR_FORMAT,
- r->affiliation_name(), idx, p2i(old_tams), p2i(new_tams));
+ log_debug(gc, mark)("Capturing TAMS for %s Region %zu, was: " PTR_FORMAT ", now: " PTR_FORMAT,
+ r->affiliation_name(), idx, p2i(old_tams), p2i(new_tams));
_top_at_mark_starts_base[idx] = new_tams;
_top_bitmaps[idx] = new_tams;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp
index dc666a34c595..d774a8dba427 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp
@@ -28,69 +28,45 @@
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
#include "gc/shenandoah/shenandoahMetrics.hpp"
-ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot() {
- _heap = ShenandoahHeap::heap();
+ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot(ShenandoahFreeSet* free_set)
+ : _free_set(free_set)
+ , _used_before(free_set->used())
+ , _if_before(free_set->internal_fragmentation())
+ , _ef_before(free_set->external_fragmentation()) {
}
-void ShenandoahMetricsSnapshot::snap_before() {
- _used_before = _heap->used();
- _if_before = _heap->free_set()->internal_fragmentation();
- _ef_before = _heap->free_set()->external_fragmentation();
-}
-void ShenandoahMetricsSnapshot::snap_after() {
- _used_after = _heap->used();
- _if_after = _heap->free_set()->internal_fragmentation();
- _ef_after = _heap->free_set()->external_fragmentation();
-}
-
-// For degenerated GC, generation is Young in generational mode, Global in non-generational mode.
-// For full GC, generation is always Global.
-//
-// Note that the size of the chosen collection set is proportional to the relevant generation's collection set.
-// Note also that the generation size may change following selection of the collection set, as a side effect
-// of evacuation. Evacuation may promote objects, causing old to grow and young to shrink. Or this may be a
-// mixed evacuation. When old regions are evacuated, this typically allows young to expand. In all of these
-// various scenarios, the purpose of asking is_good_progress() is to determine if there is enough memory available
-// within young generation to justify making an attempt to perform a concurrent collection. For this reason, we'll
-// use the current size of the generation (which may not be different than when the collection set was chosen) to
-// assess how much free memory we require in order to consider the most recent GC to have had good progress.
-
-bool ShenandoahMetricsSnapshot::is_good_progress(ShenandoahGeneration* generation) {
+bool ShenandoahMetricsSnapshot::is_good_progress() const {
// Under the critical threshold?
- ShenandoahFreeSet* free_set = _heap->free_set();
- size_t free_actual = free_set->available();
+ const size_t free_actual = _free_set->available();
assert(free_actual != ShenandoahFreeSet::FreeSetUnderConstruction, "Avoid this race");
- // ShenandoahCriticalFreeThreshold is expressed as a percentage. We multiple this percentage by 1/100th
- // of the generation capacity to determine whether the available memory within the generation exceeds the
- // critical threshold.
- size_t free_expected = (ShenandoahHeap::heap()->soft_max_capacity() / 100) * ShenandoahCriticalFreeThreshold;
-
- bool prog_free = free_actual >= free_expected;
- log_info(gc, ergo)("%s progress for free space: %zu%s, need %zu%s",
- prog_free ? "Good" : "Bad",
- byte_size_in_proper_unit(free_actual), proper_unit_for_byte_size(free_actual),
- byte_size_in_proper_unit(free_expected), proper_unit_for_byte_size(free_expected));
+ // ShenandoahCriticalFreeThreshold is expressed as a percentage. We multiply this percentage by 1/100th
+ // of the soft max capacity to determine whether the available memory within the mutator partition of the
+ // freeset exceeds the critical threshold.
+ const size_t free_expected = (ShenandoahHeap::heap()->soft_max_capacity() / 100) * ShenandoahCriticalFreeThreshold;
+ const bool prog_free = free_actual >= free_expected;
+ log_info(gc, ergo)("%s progress for free space: " PROPERFMT ", need " PROPERFMT,
+ prog_free ? "Good" : "Bad", PROPERFMTARGS(free_actual), PROPERFMTARGS(free_expected));
if (!prog_free) {
return false;
}
// Freed up enough?
- size_t progress_actual = (_used_before > _used_after) ? _used_before - _used_after : 0;
- size_t progress_expected = ShenandoahHeapRegion::region_size_bytes();
- bool prog_used = progress_actual >= progress_expected;
- log_info(gc, ergo)("%s progress for used space: %zu%s, need %zu%s",
- prog_used ? "Good" : "Bad",
- byte_size_in_proper_unit(progress_actual), proper_unit_for_byte_size(progress_actual),
- byte_size_in_proper_unit(progress_expected), proper_unit_for_byte_size(progress_expected));
+ const size_t used_after = _free_set->used();
+ const size_t progress_actual = (_used_before > used_after) ? _used_before - used_after : 0;
+ const size_t progress_expected = ShenandoahHeapRegion::region_size_bytes();
+ const bool prog_used = progress_actual >= progress_expected;
+ log_info(gc, ergo)("%s progress for used space: " PROPERFMT ", need " PROPERFMT,
+ prog_used ? "Good" : "Bad", PROPERFMTARGS(progress_actual), PROPERFMTARGS(progress_expected));
if (prog_used) {
return true;
}
// Internal fragmentation is down?
- double if_actual = _if_before - _if_after;
- double if_expected = 0.01; // 1% should be enough
- bool prog_if = if_actual >= if_expected;
+ const double if_after = _free_set->internal_fragmentation();
+ const double if_actual = _if_before - if_after;
+ const double if_expected = 0.01; // 1% should be enough
+ const bool prog_if = if_actual >= if_expected;
log_info(gc, ergo)("%s progress for internal fragmentation: %.1f%%, need %.1f%%",
prog_if ? "Good" : "Bad",
if_actual * 100, if_expected * 100);
@@ -99,9 +75,10 @@ bool ShenandoahMetricsSnapshot::is_good_progress(ShenandoahGeneration* generatio
}
// External fragmentation is down?
- double ef_actual = _ef_before - _ef_after;
- double ef_expected = 0.01; // 1% should be enough
- bool prog_ef = ef_actual >= ef_expected;
+ const double ef_after = _free_set->external_fragmentation();
+ const double ef_actual = _ef_before - ef_after;
+ const double ef_expected = 0.01; // 1% should be enough
+ const bool prog_ef = ef_actual >= ef_expected;
log_info(gc, ergo)("%s progress for external fragmentation: %.1f%%, need %.1f%%",
prog_ef ? "Good" : "Bad",
ef_actual * 100, ef_expected * 100);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.hpp
index 20d8ebfd5957..c554a0653867 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.hpp
@@ -25,22 +25,20 @@
#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMETRICS_HPP
#define SHARE_GC_SHENANDOAH_SHENANDOAHMETRICS_HPP
-#include "gc/shenandoah/shenandoahHeap.hpp"
+#include "gc/shenandoah/shenandoahFreeSet.hpp"
class ShenandoahMetricsSnapshot : public StackObj {
private:
- ShenandoahHeap* _heap;
- size_t _used_before, _used_after;
- double _if_before, _if_after;
- double _ef_before, _ef_after;
+ ShenandoahFreeSet* _free_set;
+ size_t _used_before;
+ double _if_before;
+ double _ef_before;
public:
- ShenandoahMetricsSnapshot();
+ explicit ShenandoahMetricsSnapshot(ShenandoahFreeSet* free_set);
- void snap_before();
- void snap_after();
-
- bool is_good_progress(ShenandoahGeneration *generation);
+ // Decide if the GC made "good" progress (i.e., reduced fragmentation, freed up sufficient memory).
+ bool is_good_progress() const;
};
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHMETRICS_HPP
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp
index 1724fc2849f7..707c2690b581 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp
@@ -69,12 +69,6 @@ void ShenandoahOldGC::op_final_mark() {
heap->set_unload_classes(false);
heap->prepare_concurrent_roots();
- // Believe verification following old-gen concurrent mark needs to be different than verification following
- // young-gen concurrent mark, so am commenting this out for now:
- // if (ShenandoahVerify) {
- // heap->verifier()->verify_after_concmark();
- // }
-
if (VerifyAfterGC) {
Universe::verify();
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp
index 5cccd395d381..0d5a13fa5471 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp
@@ -300,6 +300,8 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc
if (can_promote(actual_size)) {
// Assume the entirety of this PLAB will be used for promotion. This prevents promotion from overreach.
// When we retire this plab, we'll unexpend what we don't really use.
+ log_debug(gc, plab)("Thread can promote using PLAB of %zu bytes. Expended: %zu, available: %zu",
+ actual_size, get_promoted_expended(), get_promoted_reserve());
expend_promoted(actual_size);
ShenandoahThreadLocalData::enable_plab_promotions(thread);
ShenandoahThreadLocalData::set_plab_actual_size(thread, actual_size);
@@ -307,9 +309,12 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc
// Disable promotions in this thread because entirety of this PLAB must be available to hold old-gen evacuations.
ShenandoahThreadLocalData::disable_plab_promotions(thread);
ShenandoahThreadLocalData::set_plab_actual_size(thread, 0);
+ log_debug(gc, plab)("Thread cannot promote using PLAB of %zu bytes. Expended: %zu, available: %zu, mixed evacuations? %s",
+ actual_size, get_promoted_expended(), get_promoted_reserve(), BOOL_TO_STR(ShenandoahHeap::heap()->collection_set()->has_old_regions()));
}
} else if (req.is_promotion()) {
// Shared promotion.
+ log_debug(gc, plab)("Expend shared promotion of %zu bytes", actual_size);
expend_promoted(actual_size);
}
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp
index ea94c2926e86..1643b34e853a 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp
@@ -323,7 +323,6 @@ HeapWord* ShenandoahCardCluster::first_object_start(const size_t card_index, con
if (ctx->is_marked(p)) {
oop obj = cast_to_oop(p);
assert(oopDesc::is_oop(obj), "Should be an object");
- assert(Klass::is_valid(obj->klass()), "Not a valid klass ptr");
assert(p + obj->size() > left, "This object should span start of card");
assert(p < right, "Result must precede right");
return p;
@@ -350,15 +349,15 @@ HeapWord* ShenandoahCardCluster::first_object_start(const size_t card_index, con
// Recall that we already dealt with the co-initial object case above
assert(p < left, "obj should start before left");
- // While it is safe to ask an object its size in the loop that
- // follows, the (ifdef'd out) loop should never be needed.
+ // While it is safe to ask an object its size in the block that
+ // follows, the (ifdef'd out) block should never be needed.
// 1. we ask this question only for regions in the old generation, and those
// that are not humongous regions
// 2. there is no direct allocation ever by mutators in old generation
// regions walked by this code. Only GC will ever allocate in old regions,
// and then too only during promotion/evacuation phases. Thus there is no danger
// of races between reading from and writing to the object start array,
- // or of asking partially initialized objects their size (in the loop below).
+ // or of asking partially initialized objects their size (in the ifdef below).
// Furthermore, humongous regions (and their dirty cards) are never processed
// by this code.
// 3. only GC asks this question during phases when it is not concurrently
@@ -370,15 +369,6 @@ HeapWord* ShenandoahCardCluster::first_object_start(const size_t card_index, con
#ifdef ASSERT
oop obj = cast_to_oop(p);
assert(oopDesc::is_oop(obj), "Should be an object");
- while (p + obj->size() < left) {
- p += obj->size();
- obj = cast_to_oop(p);
- assert(oopDesc::is_oop(obj), "Should be an object");
- assert(Klass::is_valid(obj->klass()), "Not a valid klass ptr");
- // Check assumptions in previous block comment if this assert fires
- fatal("Should never need forward walk in block start");
- }
- assert(p <= left, "p should start at or before left end of card");
assert(p + obj->size() > left, "obj should end after left end of card");
#endif // ASSERT
return p;
@@ -777,9 +767,9 @@ void ShenandoahScanRememberedTask::do_work(uint worker_id) {
struct ShenandoahRegionChunk assignment;
while (_work_list->next(&assignment)) {
ShenandoahHeapRegion* region = assignment._r;
- log_debug(gc)("ShenandoahScanRememberedTask::do_work(%u), processing slice of region "
- "%zu at offset %zu, size: %zu",
- worker_id, region->index(), assignment._chunk_offset, assignment._chunk_size);
+ log_debug(gc, remset)("ShenandoahScanRememberedTask::do_work(%u), processing slice of region "
+ "%zu at offset %zu, size: %zu",
+ worker_id, region->index(), assignment._chunk_offset, assignment._chunk_size);
if (region->is_old()) {
size_t cluster_size =
CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp
index 82022420a2a8..998e786eff28 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp
@@ -361,9 +361,9 @@ ShenandoahScanRemembered::process_region_slice(ShenandoahHeapRegion *region, siz
}
}
- log_debug(gc)("Remembered set scan processing Region %zu, from " PTR_FORMAT " to " PTR_FORMAT ", using %s table",
- region->index(), p2i(start_of_range), p2i(end_of_range),
- use_write_table? "read/write (updating)": "read (marking)");
+ log_debug(gc, remset)("Remembered set scan processing Region %zu, from " PTR_FORMAT " to " PTR_FORMAT ", using %s table",
+ region->index(), p2i(start_of_range), p2i(end_of_range),
+ use_write_table? "read/write (updating)": "read (marking)");
// Note that end_of_range may point to the middle of a cluster because we limit scanning to
// region->top() or region->get_update_watermark(). We avoid processing past end_of_range.
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp
index 1d1c93599464..1c86531f21e5 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp
@@ -29,11 +29,7 @@
#include "memory/allocation.hpp"
#include "runtime/atomic.hpp"
-typedef jbyte ShenandoahSharedValue;
-
-// Needed for cooperation with generated code.
-STATIC_ASSERT(sizeof(ShenandoahSharedValue) == 1);
-
+typedef int32_t ShenandoahSharedValue;
typedef struct ShenandoahSharedFlag {
enum {
UNSET = 0,
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp
index c444a0ba86a6..dd500462d0ff 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp
@@ -44,10 +44,7 @@ ShenandoahThreadLocalData::ShenandoahThreadLocalData() :
_plab_promoted(0),
_plab_allows_promotion(true),
_plab_retries_enabled(true),
- _evacuation_stats(nullptr) {
- if (ShenandoahHeap::heap()->mode()->is_generational()) {
- _evacuation_stats = new ShenandoahEvacuationStats();
- }
+ _evacuation_stats(new ShenandoahEvacuationStats()) {
}
ShenandoahThreadLocalData::~ShenandoahThreadLocalData() {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp
index c1cebdf1ddef..8a3132bdf171 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp
@@ -30,6 +30,7 @@
#include "gc/shared/gcThreadLocalData.hpp"
#include "gc/shared/plab.hpp"
#include "gc/shenandoah/mode/shenandoahMode.hpp"
+#include "gc/shenandoah/shenandoahAffiliation.hpp"
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
#include "gc/shenandoah/shenandoahCardTable.hpp"
#include "gc/shenandoah/shenandoahCodeRoots.hpp"
@@ -159,20 +160,15 @@ class ShenandoahThreadLocalData {
data(thread)->_gclab_size = v;
}
- static void begin_evacuation(Thread* thread, size_t bytes) {
- data(thread)->_evacuation_stats->begin_evacuation(bytes);
+ static void begin_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) {
+ data(thread)->_evacuation_stats->begin_evacuation(bytes, from, to);
}
- static void end_evacuation(Thread* thread, size_t bytes) {
- data(thread)->_evacuation_stats->end_evacuation(bytes);
- }
-
- static void record_age(Thread* thread, size_t bytes, uint age) {
- data(thread)->_evacuation_stats->record_age(bytes, age);
+ static void end_evacuation(Thread* thread, size_t bytes, ShenandoahAffiliation from, ShenandoahAffiliation to) {
+ data(thread)->_evacuation_stats->end_evacuation(bytes, from, to);
}
static ShenandoahEvacuationStats* evacuation_stats(Thread* thread) {
- shenandoah_assert_generational();
return data(thread)->_evacuation_stats;
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp
index dd153718c9f1..bbb44348355b 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.cpp
@@ -22,31 +22,31 @@
*
*/
-#include "gc/shenandoah/shenandoahEvacInfo.hpp"
+#include "gc/shenandoah/shenandoahCollectionSet.inline.hpp"
#include "gc/shenandoah/shenandoahTrace.hpp"
#include "jfr/jfrEvents.hpp"
-void ShenandoahTracer::report_evacuation_info(ShenandoahEvacuationInformation* info) {
- send_evacuation_info_event(info);
-}
+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) {
-void ShenandoahTracer::send_evacuation_info_event(ShenandoahEvacuationInformation* info) {
EventShenandoahEvacuationInformation e;
if (e.should_commit()) {
e.set_gcId(GCId::current());
- e.set_cSetRegions(info->collection_set_regions());
- e.set_cSetUsedBefore(info->collection_set_used_before());
- e.set_cSetUsedAfter(info->collection_set_used_after());
- e.set_collectedOld(info->collected_old());
- e.set_collectedPromoted(info->collected_promoted());
- e.set_collectedYoung(info->collected_young());
- e.set_regionsPromotedHumongous(info->regions_promoted_humongous());
- e.set_regionsPromotedRegular(info->regions_promoted_regular());
- e.set_regularPromotedGarbage(info->regular_promoted_garbage());
- e.set_regularPromotedFree(info->regular_promoted_free());
- e.set_freeRegions(info->free_regions());
- e.set_regionsImmediate(info->regions_immediate());
- e.set_immediateBytes(info->immediate_size());
+ e.set_cSetRegions(cset->count());
+ e.set_cSetUsedBefore(cset->used());
+ e.set_cSetUsedAfter(cset->live());
+ 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_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 a5351f4ef281..116968103dea 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahTrace.hpp
@@ -28,15 +28,17 @@
#include "gc/shared/gcTrace.hpp"
#include "memory/allocation.hpp"
-class ShenandoahEvacuationInformation;
+class ShenandoahCollectionSet;
class ShenandoahTracer : public GCTracer, public CHeapObj {
public:
ShenandoahTracer() : GCTracer(Shenandoah) {}
- void report_evacuation_info(ShenandoahEvacuationInformation* info);
-private:
- void send_evacuation_info_event(ShenandoahEvacuationInformation* info);
+ // Sends a JFR event (if enabled) 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);
};
#endif
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp
index cdf784852076..82358aafd3ce 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp
@@ -115,15 +115,15 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
void do_oop_work(T* p) {
T o = RawAccess<>::oop_load(p);
if (!CompressedOops::is_null(o)) {
- oop obj = CompressedOops::decode_not_null(o);
+ // Basic verification should happen before we touch anything else.
+ // For performance reasons, only fully verify non-marked field values.
+ // We are here when the host object for *p is already marked.
+ oop obj = CompressedOops::decode_raw_not_null(o);
+ verify_oop_at_basic(p, obj);
+
if (is_instance_ref_klass(ShenandoahForwarding::klass(obj))) {
obj = ShenandoahForwarding::get_forwardee(obj);
}
- // Single threaded verification can use faster non-atomic stack and bitmap
- // methods.
- //
- // For performance reasons, only fully verify non-marked field values.
- // We are here when the host object for *p is already marked.
if (in_generation(obj) && _map->par_mark(obj)) {
verify_oop_at(p, obj);
_stack->push(ShenandoahVerifierTask(obj));
@@ -140,7 +140,7 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
return _generation->contains(region);
}
- void verify_oop(oop obj) {
+ void verify_oop(oop obj, bool basic = false) {
// Perform consistency checks with gradually decreasing safety level. This guarantees
// that failure report would not try to touch something that was not yet verified to be
// safe to process.
@@ -177,10 +177,14 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
}
}
+ check(ShenandoahAsserts::_safe_unknown, obj, obj_reg->is_active(),
+ "Object should be in active region");
+
// ------------ obj is safe at this point --------------
- check(ShenandoahAsserts::_safe_oop, obj, obj_reg->is_active(),
- "Object should be in active region");
+ if (basic) {
+ return;
+ }
switch (_options._verify_liveness) {
case ShenandoahVerifier::_verify_liveness_disable:
@@ -333,6 +337,18 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
_interior_loc = nullptr;
}
+ /**
+ * Verify object with known interior reference, with only basic verification.
+ * @param p interior reference where the object is referenced from; can be off-heap
+ * @param obj verified object
+ */
+ template
+ void verify_oop_at_basic(T* p, oop obj) {
+ _interior_loc = p;
+ verify_oop(obj, /* basic = */ true);
+ _interior_loc = nullptr;
+ }
+
/**
* Verify object without known interior reference.
* Useful when picking up the object at known offset in heap,
@@ -1203,7 +1219,9 @@ class ShenandoahVerifyNoForwarded : public BasicOopIterateClosure {
void do_oop_work(T* p) {
T o = RawAccess<>::oop_load(p);
if (!CompressedOops::is_null(o)) {
- oop obj = CompressedOops::decode_not_null(o);
+ oop obj = CompressedOops::decode_raw_not_null(o);
+ ShenandoahAsserts::assert_correct(p, obj, __FILE__, __LINE__);
+
oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
if (obj != fwd) {
ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr,
@@ -1223,7 +1241,9 @@ class ShenandoahVerifyInToSpaceClosure : public BasicOopIterateClosure {
void do_oop_work(T* p) {
T o = RawAccess<>::oop_load(p);
if (!CompressedOops::is_null(o)) {
- oop obj = CompressedOops::decode_not_null(o);
+ oop obj = CompressedOops::decode_raw_not_null(o);
+ ShenandoahAsserts::assert_correct(p, obj, __FILE__, __LINE__);
+
ShenandoahHeap* heap = ShenandoahHeap::heap();
if (!heap->marking_context()->is_marked_or_old(obj)) {
@@ -1277,7 +1297,9 @@ class ShenandoahVerifyRemSetClosure : public BasicOopIterateClosure {
inline void work(T* p) {
T o = RawAccess<>::oop_load(p);
if (!CompressedOops::is_null(o)) {
- oop obj = CompressedOops::decode_not_null(o);
+ oop obj = CompressedOops::decode_raw_not_null(o);
+ ShenandoahAsserts::assert_correct(p, obj, __FILE__, __LINE__);
+
if (_heap->is_in_young(obj) && !_scanner->is_card_dirty((HeapWord*) p)) {
ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr,
_message, "clean card, it should be dirty.", __FILE__, __LINE__);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp
index 6811c042e2ab..82378d43cf1d 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp
@@ -34,6 +34,20 @@
range, \
constraint) \
\
+ product(uintx, ShenandoahGenerationalMinPIPUsage, 30, EXPERIMENTAL, \
+ "(Generational mode only) What percent of a heap region " \
+ "should be used before we consider promoting a region in " \
+ "place? Regions with less than this amount of used will " \
+ "promoted by evacuation. A benefit of promoting in place " \
+ "is that less work is required by the GC at the time the " \
+ "region is promoted. A disadvantage of promoting in place " \
+ "is that this introduces fragmentation of old-gen memory, " \
+ "with old-gen regions scattered throughout the heap. Regions " \
+ "that have been promoted in place may need to be evacuated at " \
+ "a later time in order to compact old-gen memory to enable " \
+ "future humongous allocations.") \
+ range(0,100) \
+ \
product(uintx, ShenandoahGenerationalHumongousReserve, 0, EXPERIMENTAL, \
"(Generational mode only) What percent of the heap should be " \
"reserved for humongous objects if possible. Old-generation " \
@@ -70,10 +84,6 @@
"many consecutive young-gen collections have been " \
"completed following the preceding old-gen collection.") \
\
- product(bool, ShenandoahGenerationalCensusAtEvac, false, EXPERIMENTAL, \
- "(Generational mode only) Object age census at evacuation, " \
- "rather than during marking.") \
- \
product(bool, ShenandoahGenerationalAdaptiveTenuring, true, EXPERIMENTAL, \
"(Generational mode only) Dynamically adapt tenuring age.") \
\
@@ -169,7 +179,7 @@
"collector accepts. In percents of heap region size.") \
range(0,100) \
\
- product(uintx, ShenandoahOldGarbageThreshold, 15, EXPERIMENTAL, \
+ product(uintx, ShenandoahOldGarbageThreshold, 25, EXPERIMENTAL, \
"How much garbage an old region has to contain before it would " \
"be taken for collection.") \
range(0,100) \
@@ -377,13 +387,13 @@
\
product(uintx, ShenandoahOldEvacRatioPercent, 75, EXPERIMENTAL, \
"The maximum proportion of evacuation from old-gen memory, " \
- "expressed as a percentage. The default value 75 denotes that no" \
- "more than 75% of the collection set evacuation workload may be " \
- "towards evacuation of old-gen heap regions. This limits both the"\
- "promotion of aged regions and the compaction of existing old " \
- "regions. A value of 75 denotes that the total evacuation work" \
- "may increase to up to four times the young gen evacuation work." \
- "A larger value allows quicker promotion and allows" \
+ "expressed as a percentage. The default value 75 denotes that " \
+ "no more than 75% of the collection set evacuation workload may " \
+ "be towards evacuation of old-gen heap regions. This limits both "\
+ "the promotion of aged regions and the compaction of existing " \
+ "old regions. A value of 75 denotes that the total evacuation " \
+ "work may increase to up to four times the young gen evacuation " \
+ "work. A larger value allows quicker promotion and allows " \
"a smaller number of mixed evacuations to process " \
"the entire list of old-gen collection candidates at the cost " \
"of an increased disruption of the normal cadence of young-gen " \
@@ -391,13 +401,20 @@
"focus entirely on old-gen memory, allowing no young-gen " \
"regions to be collected, likely resulting in subsequent " \
"allocation failures because the allocation pool is not " \
- "replenished. A value of 0 allows a mixed evacuation to" \
+ "replenished. A value of 0 allows a mixed evacuation to " \
"focus entirely on young-gen memory, allowing no old-gen " \
"regions to be collected, likely resulting in subsequent " \
"promotion failures and triggering of stop-the-world full GC " \
"events.") \
range(0,100) \
\
+ product(bool, ShenandoahEvacTracking, false, DIAGNOSTIC, \
+ "Collect additional metrics about evacuations. Enabling this " \
+ "tracks how many objects and how many bytes were evacuated, and " \
+ "how many were abandoned. The information will be categorized " \
+ "by thread type (worker or mutator) and evacuation type (young, " \
+ "old, or promotion.") \
+ \
product(uintx, ShenandoahMinYoungPercentage, 20, EXPERIMENTAL, \
"The minimum percentage of the heap to use for the young " \
"generation. Heuristics will not adjust the young generation " \
diff --git a/src/hotspot/share/gc/z/zLargePages.cpp b/src/hotspot/share/gc/z/zLargePages.cpp
index 56c94a75713c..c259448563bd 100644
--- a/src/hotspot/share/gc/z/zLargePages.cpp
+++ b/src/hotspot/share/gc/z/zLargePages.cpp
@@ -31,7 +31,8 @@ bool ZLargePages::_os_enforced_transparent_mode;
void ZLargePages::initialize() {
pd_initialize();
- log_info_p(gc, init)("Memory: " JULONG_FORMAT "M", os::physical_memory() / M);
+ const size_t memory = static_cast(os::physical_memory());
+ log_info_p(gc, init)("Memory: " PROPERFMT, PROPERFMTARGS(memory));
log_info_p(gc, init)("Large Page Support: %s", to_string());
}
diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h
index 73f60765a702..a01bad14ab70 100644
--- a/src/hotspot/share/include/jvm.h
+++ b/src/hotspot/share/include/jvm.h
@@ -87,6 +87,9 @@ JVM_InternString(JNIEnv *env, jstring str);
/*
* java.lang.System
*/
+JNIEXPORT jboolean JNICALL
+JVM_AOTEndRecording(JNIEnv *env);
+
JNIEXPORT jlong JNICALL
JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored);
diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp
index 0b4ab5320648..9b4ead7b17b9 100644
--- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp
@@ -411,9 +411,9 @@ JVM_ENTRY_NO_ENV(jlong, jfr_host_total_memory(JNIEnv* env, jclass jvm))
#ifdef LINUX
// We want the host memory, not the container limit.
// os::physical_memory() would return the container limit.
- return os::Linux::physical_memory();
+ return static_cast(os::Linux::physical_memory());
#else
- return os::physical_memory();
+ return static_cast(os::physical_memory());
#endif
JVM_END
@@ -422,7 +422,10 @@ JVM_ENTRY_NO_ENV(jlong, jfr_host_total_swap_memory(JNIEnv* env, jclass jvm))
// We want the host swap memory, not the container value.
return os::Linux::host_swap();
#else
- return os::total_swap_space();
+ physical_memory_size_type total_swap_space = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::total_swap_space(total_swap_space);
+ return static_cast(total_swap_space);
#endif
JVM_END
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp
index 83eee96091e0..8b5819e92c40 100644
--- a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp
+++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp
@@ -34,6 +34,7 @@
#include "memory/resourceArea.hpp"
#include "oops/access.inline.hpp"
#include "oops/oop.inline.hpp"
+#include "runtime/os.hpp"
#include "utilities/align.hpp"
UnifiedOopRef DFSClosure::_reference_stack[max_dfs_depth];
@@ -67,9 +68,27 @@ void DFSClosure::find_leaks_from_root_set(EdgeStore* edge_store,
rs.process();
}
+static address calculate_headroom_limit() {
+ static constexpr size_t required_headroom = K * 64;
+ const Thread* const t = Thread::current_or_null();
+ return t->stack_end() + required_headroom;
+}
+
DFSClosure::DFSClosure(EdgeStore* edge_store, JFRBitSet* mark_bits, const Edge* start_edge)
:_edge_store(edge_store), _mark_bits(mark_bits), _start_edge(start_edge),
- _max_depth(max_dfs_depth), _depth(0), _ignore_root_set(false) {
+ _max_depth(max_dfs_depth), _depth(0), _ignore_root_set(false),
+ _headroom_limit(calculate_headroom_limit()) {
+}
+
+bool DFSClosure::have_headroom() const {
+ const address sp = (address) os::current_stack_pointer();
+#ifdef ASSERT
+ const Thread* const t = Thread::current_or_null();
+ assert(t->is_VM_thread(), "invariant");
+ assert(t->is_in_full_stack(_headroom_limit), "invariant");
+ assert(t->is_in_full_stack(sp), "invariant");
+#endif
+ return sp > _headroom_limit;
}
void DFSClosure::closure_impl(UnifiedOopRef reference, const oop pointee) {
@@ -97,7 +116,7 @@ void DFSClosure::closure_impl(UnifiedOopRef reference, const oop pointee) {
}
}
assert(_max_depth >= 1, "invariant");
- if (_depth < _max_depth - 1) {
+ if (_depth < _max_depth - 1 && have_headroom()) {
_depth++;
pointee->oop_iterate(this);
assert(_depth > 0, "invariant");
diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp
index be0cd2a5d7eb..a22b5137380f 100644
--- a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp
+++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp
@@ -46,12 +46,15 @@ class DFSClosure : public BasicOopIterateClosure {
size_t _max_depth;
size_t _depth;
bool _ignore_root_set;
+ const address _headroom_limit;
DFSClosure(EdgeStore* edge_store, JFRBitSet* mark_bits, const Edge* start_edge);
void add_chain();
void closure_impl(UnifiedOopRef reference, const oop pointee);
+ bool have_headroom() const;
+
public:
virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS_EXCEPT_REFERENT; }
diff --git a/src/hotspot/share/jfr/leakprofiler/leakProfiler.cpp b/src/hotspot/share/jfr/leakprofiler/leakProfiler.cpp
index a5b4babb2b19..11be1c87ff41 100644
--- a/src/hotspot/share/jfr/leakprofiler/leakProfiler.cpp
+++ b/src/hotspot/share/jfr/leakprofiler/leakProfiler.cpp
@@ -34,9 +34,15 @@
#include "runtime/vmThread.hpp"
bool LeakProfiler::is_supported() {
- if (UseShenandoahGC) {
+ if (UseShenandoahGC || UseZGC) {
// Leak Profiler uses mark words in the ways that might interfere
// with concurrent GC uses of them. This affects Shenandoah.
+ //
+ // Generational ZGC only does weak reference processing in the old generation.
+ // All objects that would usually die, because we are sampling stuff
+ // that immediately becomes garbage, will be artificially kept alive
+ // until an old-generation collection. This incurs a significant
+ // performance hit by causing allocation stalls.
return false;
}
return true;
@@ -58,7 +64,8 @@ bool LeakProfiler::start(int sample_count) {
// Exit cleanly if not supported
if (!is_supported()) {
- log_trace(jfr, system)("Object sampling is not supported");
+ log_info(jfr, system)("jdk.OldObjectSample event is currently not supported for %s.",
+ UseShenandoahGC ? "ShenandoahGC" : "ZGC");
return false;
}
diff --git a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp
index 00d41a10bf0e..e7f12f7428f0 100644
--- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp
+++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp
@@ -528,17 +528,26 @@ TRACE_REQUEST_FUNC(ThreadAllocationStatistics) {
* the total memory reported is the amount of memory configured for the guest OS by the hypervisor.
*/
TRACE_REQUEST_FUNC(PhysicalMemory) {
- u8 totalPhysicalMemory = os::physical_memory();
+ physical_memory_size_type totalPhysicalMemory = os::physical_memory();
EventPhysicalMemory event;
event.set_totalSize(totalPhysicalMemory);
- event.set_usedSize(totalPhysicalMemory - os::available_memory());
+ physical_memory_size_type avail_mem = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::available_memory(avail_mem);
+ event.set_usedSize(totalPhysicalMemory - avail_mem);
event.commit();
}
TRACE_REQUEST_FUNC(SwapSpace) {
EventSwapSpace event;
- event.set_totalSize(os::total_swap_space());
- event.set_freeSize(os::free_swap_space());
+ physical_memory_size_type total_swap_space = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::total_swap_space(total_swap_space);
+ event.set_totalSize(static_cast(total_swap_space));
+ physical_memory_size_type free_swap_space = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::free_swap_space(free_swap_space);
+ event.set_freeSize(static_cast(free_swap_space));
event.commit();
}
diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp
index a855639e74b8..b0f629bf572d 100644
--- a/src/hotspot/share/oops/cpCache.cpp
+++ b/src/hotspot/share/oops/cpCache.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
@@ -550,6 +550,8 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv
return false;
}
+ int cp_index = method_entry->constant_pool_index();
+
if (!method_entry->is_resolved(Bytecodes::_invokevirtual)) {
if (method_entry->method() == nullptr) {
return false;
@@ -557,9 +559,27 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv
if (method_entry->method()->is_continuation_native_intrinsic()) {
return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call).
}
+ if (method_entry->is_resolved(Bytecodes::_invokehandle)) {
+ if (!CDSConfig::is_dumping_method_handles()) {
+ return false;
+ }
+
+ Symbol* sig = constant_pool()->uncached_signature_ref_at(cp_index);
+ Klass* k;
+ if (!AOTConstantPoolResolver::check_methodtype_signature(constant_pool(), sig, &k, true)) {
+ // invokehandles that were resolved in the training run should have been filtered in
+ // AOTConstantPoolResolver::maybe_resolve_fmi_ref so we shouldn't come to here.
+ //
+ // If we come here it's because the AOT assembly phase has executed an invokehandle
+ // that uses an excluded type like jdk.jfr.Event. This should not happen because the
+ // AOT assembly phase should execute only a very limited set of Java code.
+ ResourceMark rm;
+ fatal("AOT assembly phase must not resolve any invokehandles whose signatures include an excluded type");
+ }
+ return true;
+ }
}
- int cp_index = method_entry->constant_pool_index();
assert(src_cp->tag_at(cp_index).is_method() || src_cp->tag_at(cp_index).is_interface_method(), "sanity");
if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) {
@@ -570,13 +590,6 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv
method_entry->is_resolved(Bytecodes::_invokevirtual) ||
method_entry->is_resolved(Bytecodes::_invokespecial)) {
return true;
- } else if (method_entry->is_resolved(Bytecodes::_invokehandle)) {
- if (CDSConfig::is_dumping_method_handles()) {
- // invokehandle depends on archived MethodType and LambdaForms.
- return true;
- } else {
- return false;
- }
} else {
return false;
}
diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp
index 85b6bd21aece..c02aefc79437 100644
--- a/src/hotspot/share/opto/arraycopynode.cpp
+++ b/src/hotspot/share/opto/arraycopynode.cpp
@@ -212,6 +212,15 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c
}
}
+ const TypeInstPtr* dest_type = phase->type(base_dest)->is_instptr();
+ if (dest_type->instance_klass() != ik) {
+ // At parse time, the exact type of the object to clone was not known. That inexact type was captured by the CheckCastPP
+ // of the newly allocated cloned object (in dest). The exact type is now known (in src), but the type for the cloned object
+ // (dest) was not updated. When copying the fields below, Store nodes may write to offsets for fields that don't exist in
+ // the inexact class. The stores would then be assigned an incorrect slice.
+ return NodeSentinel;
+ }
+
assert(ik->nof_nonstatic_fields() <= ArrayCopyLoadStoreMaxElem, "too many fields");
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp
index e0ad2f00ab57..fa78fa73e37c 100644
--- a/src/hotspot/share/opto/callGenerator.cpp
+++ b/src/hotspot/share/opto/callGenerator.cpp
@@ -424,7 +424,6 @@ bool LateInlineMHCallGenerator::do_late_inline_check(Compile* C, JVMState* jvms)
}
assert(!cg->is_late_inline() || cg->is_mh_late_inline() || AlwaysIncrementalInline || StressIncrementalInlining, "we're doing late inlining");
_inline_cg = cg;
- C->dec_number_of_mh_late_inlines();
return true;
} else {
// Method handle call which has a constant appendix argument should be either inlined or replaced with a direct call
@@ -436,7 +435,7 @@ bool LateInlineMHCallGenerator::do_late_inline_check(Compile* C, JVMState* jvms)
CallGenerator* CallGenerator::for_mh_late_inline(ciMethod* caller, ciMethod* callee, bool input_not_const) {
assert(IncrementalInlineMH, "required");
- Compile::current()->inc_number_of_mh_late_inlines();
+ Compile::current()->mark_has_mh_late_inlines();
CallGenerator* cg = new LateInlineMHCallGenerator(caller, callee, input_not_const);
return cg;
}
diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp
index f0fa73f11ce7..6f13ce0f809a 100644
--- a/src/hotspot/share/opto/callnode.cpp
+++ b/src/hotspot/share/opto/callnode.cpp
@@ -1145,7 +1145,6 @@ Node* CallStaticJavaNode::Ideal(PhaseGVN* phase, bool can_reshape) {
assert(callee->has_member_arg(), "wrong type of call?");
if (in(TypeFunc::Parms + callee->arg_size() - 1)->Opcode() == Op_ConP) {
register_for_late_inline();
- phase->C->inc_number_of_mh_late_inlines();
}
}
} else {
diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp
index 2a95cb3b1f24..213fbda4e896 100644
--- a/src/hotspot/share/opto/callnode.hpp
+++ b/src/hotspot/share/opto/callnode.hpp
@@ -729,9 +729,10 @@ class CallNode : public SafePointNode {
// for some macro nodes whose expansion does not have a safepoint on the fast path.
virtual bool guaranteed_safepoint() { return true; }
// For macro nodes, the JVMState gets modified during expansion. If calls
- // use MachConstantBase, it gets modified during matching. So when cloning
- // the node the JVMState must be deep cloned. Default is to shallow clone.
- virtual bool needs_deep_clone_jvms(Compile* C) { return C->needs_deep_clone_jvms(); }
+ // use MachConstantBase, it gets modified during matching. If the call is
+ // late inlined, it also needs the full JVMState. So when cloning the
+ // node the JVMState must be deep cloned. Default is to shallow clone.
+ virtual bool needs_deep_clone_jvms(Compile* C) { return _generator != nullptr || C->needs_deep_clone_jvms(); }
// Returns true if the call may modify n
virtual bool may_modify(const TypeOopPtr* t_oop, PhaseValues* phase);
diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp
index 92f6c938dbac..cdcfe3a6b351 100644
--- a/src/hotspot/share/opto/cfgnode.cpp
+++ b/src/hotspot/share/opto/cfgnode.cpp
@@ -2580,6 +2580,10 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
for( uint i=1; i _vector_reboxing_late_inlines; // same but for vector reboxing operations
int _late_inlines_pos; // Where in the queue should the next late inlining candidate go (emulate depth first inlining)
- uint _number_of_mh_late_inlines; // number of method handle late inlining still pending
+ bool _has_mh_late_inlines; // Can there still be a method handle late inlining pending?
+ // false: there can't be one
+ // true: we've enqueued one at some point so there may still be one
// "MemLimit" directive was specified and the memory limit was hit during compilation
bool _oom;
@@ -1087,9 +1089,8 @@ class Compile : public Phase {
}
}
- void inc_number_of_mh_late_inlines() { _number_of_mh_late_inlines++; }
- void dec_number_of_mh_late_inlines() { assert(_number_of_mh_late_inlines > 0, "_number_of_mh_late_inlines < 0 !"); _number_of_mh_late_inlines--; }
- bool has_mh_late_inlines() const { return _number_of_mh_late_inlines > 0; }
+ void mark_has_mh_late_inlines() { _has_mh_late_inlines = true; }
+ bool has_mh_late_inlines() const { return _has_mh_late_inlines; }
bool inline_incrementally_one();
void inline_incrementally_cleanup(PhaseIterGVN& igvn);
diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp
index 3dd43f12b2f5..d7253b065995 100644
--- a/src/hotspot/share/opto/escape.cpp
+++ b/src/hotspot/share/opto/escape.cpp
@@ -3129,6 +3129,14 @@ void ConnectionGraph::find_scalar_replaceable_allocs(GrowableArrayis_LocalVar()) {
+ Node* phi = use->ideal_node();
+ if (phi->Opcode() == Op_Phi && reducible_merges.member(phi) && !can_reduce_phi(phi->as_Phi())) {
+ set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA "is merged in a non-reducible phi"));
+ reducible_merges.yank(phi);
+ found_nsr_alloc = true;
+ break;
+ }
}
}
}
diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp
index 99fedcdf8802..acf1dbabf198 100644
--- a/src/hotspot/share/opto/macro.cpp
+++ b/src/hotspot/share/opto/macro.cpp
@@ -2312,12 +2312,7 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) {
// No need for a null check on unlock
// Make the merge point
- Node *region;
- Node *mem_phi;
-
- region = new RegionNode(3);
- // create a Phi for the memory state
- mem_phi = new PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM);
+ Node* region = new RegionNode(3);
FastUnlockNode *funlock = new FastUnlockNode( ctrl, obj, box );
funlock = transform_later( funlock )->as_FastUnlock();
@@ -2346,12 +2341,15 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) {
transform_later(region);
_igvn.replace_node(_callprojs.fallthrough_proj, region);
- Node *memproj = transform_later(new ProjNode(call, TypeFunc::Memory) );
- mem_phi->init_req(1, memproj );
- mem_phi->init_req(2, mem);
- transform_later(mem_phi);
-
- _igvn.replace_node(_callprojs.fallthrough_memproj, mem_phi);
+ if (_callprojs.fallthrough_memproj != nullptr) {
+ // create a Phi for the memory state
+ Node* mem_phi = new PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM);
+ Node* memproj = transform_later(new ProjNode(call, TypeFunc::Memory));
+ mem_phi->init_req(1, memproj);
+ mem_phi->init_req(2, mem);
+ transform_later(mem_phi);
+ _igvn.replace_node(_callprojs.fallthrough_memproj, mem_phi);
+ }
}
void PhaseMacroExpand::expand_subtypecheck_node(SubTypeCheckNode *check) {
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 41f33cda11ba..93ae77b0fccc 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -228,6 +228,19 @@ extern void trace_class_resolution(Klass* to_class) {
// java.lang.System //////////////////////////////////////////////////////////////////////
+JVM_ENTRY(jboolean, JVM_AOTEndRecording(JNIEnv *env))
+#if INCLUDE_CDS
+ if (CDSConfig::is_dumping_preimage_static_archive()) {
+ if (!MetaspaceShared::preimage_static_archive_dumped()) {
+ MetaspaceShared::preload_and_dump(THREAD);
+ return JNI_TRUE;
+ }
+ }
+ return JNI_FALSE;
+#else
+ return JNI_FALSE;
+#endif // INCLUDE_CDS
+JVM_END
JVM_LEAF(jlong, JVM_CurrentTimeMillis(JNIEnv *env, jclass ignored))
return os::javaTimeMillis();
diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp
index 13822f73f772..e0a2c54137d3 100644
--- a/src/hotspot/share/prims/jvmtiExport.cpp
+++ b/src/hotspot/share/prims/jvmtiExport.cpp
@@ -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
@@ -71,6 +71,7 @@
#include "runtime/threadSMR.hpp"
#include "runtime/vframe.inline.hpp"
#include "runtime/vm_version.hpp"
+#include "utilities/events.hpp"
#include "utilities/macros.hpp"
#ifdef JVMTI_TRACE
@@ -626,22 +627,27 @@ JvmtiExport::decode_version_values(jint version, int * major, int * minor,
}
void JvmtiExport::enter_primordial_phase() {
+ Events::log(Thread::current_or_null(), "JVMTI - enter primordial phase");
JvmtiEnvBase::set_phase(JVMTI_PHASE_PRIMORDIAL);
}
void JvmtiExport::enter_early_start_phase() {
+ Events::log(Thread::current_or_null(), "JVMTI - enter early start phase");
set_early_vmstart_recorded(true);
}
void JvmtiExport::enter_start_phase() {
+ Events::log(Thread::current_or_null(), "JVMTI - enter start phase");
JvmtiEnvBase::set_phase(JVMTI_PHASE_START);
}
void JvmtiExport::enter_onload_phase() {
+ Events::log(Thread::current_or_null(), "JVMTI - enter onload phase");
JvmtiEnvBase::set_phase(JVMTI_PHASE_ONLOAD);
}
void JvmtiExport::enter_live_phase() {
+ Events::log(Thread::current_or_null(), "JVMTI - enter live phase");
JvmtiEnvBase::set_phase(JVMTI_PHASE_LIVE);
}
@@ -779,6 +785,7 @@ void JvmtiExport::post_vm_death() {
}
}
+ Events::log(Thread::current_or_null(), "JVMTI - enter dead phase");
JvmtiEnvBase::set_phase(JVMTI_PHASE_DEAD);
JvmtiEventController::vm_death();
}
@@ -1726,6 +1733,23 @@ void JvmtiExport::post_vthread_unmount(jobject vthread) {
}
}
+bool JvmtiExport::has_frame_pops(JavaThread* thread) {
+ if (!can_post_frame_pop()) {
+ return false;
+ }
+ JvmtiThreadState *state = get_jvmti_thread_state(thread);
+ if (state == nullptr) {
+ return false;
+ }
+ JvmtiEnvThreadStateIterator it(state);
+ for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) {
+ if (ets->has_frame_pops()) {
+ return true;
+ }
+ }
+ return false;
+}
+
void JvmtiExport::continuation_yield_cleanup(JavaThread* thread, jint continuation_frame_count) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp
index e47cd3d6363d..2f03b130288e 100644
--- a/src/hotspot/share/prims/jvmtiExport.hpp
+++ b/src/hotspot/share/prims/jvmtiExport.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -373,6 +373,7 @@ class JvmtiExport : public AllStatic {
JVMTI_ONLY(return _should_post_class_file_load_hook);
NOT_JVMTI(return false;)
}
+ static bool has_frame_pops(JavaThread* thread) NOT_JVMTI_RETURN_(false);
static bool is_early_phase() NOT_JVMTI_RETURN_(false);
static bool has_early_class_hook_env() NOT_JVMTI_RETURN_(false);
static bool has_early_vmstart_env() NOT_JVMTI_RETURN_(false);
diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
index 4257d7e997b9..396c6db150d8 100644
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
@@ -1356,10 +1356,12 @@ jvmtiError VM_RedefineClasses::load_new_class_versions() {
// constant pools
HandleMark hm(current);
InstanceKlass* the_class = get_ik(_class_defs[i].klass);
-
+ physical_memory_size_type avail_mem = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::available_memory(avail_mem);
log_debug(redefine, class, load)
- ("loading name=%s kind=%d (avail_mem=" UINT64_FORMAT "K)",
- the_class->external_name(), _class_load_kind, os::available_memory() >> 10);
+ ("loading name=%s kind=%d (avail_mem=" PHYS_MEM_TYPE_FORMAT "K)",
+ the_class->external_name(), _class_load_kind, avail_mem >> 10);
ClassFileStream st((u1*)_class_defs[i].class_bytes,
_class_defs[i].class_byte_count,
@@ -1523,9 +1525,10 @@ jvmtiError VM_RedefineClasses::load_new_class_versions() {
return JVMTI_ERROR_INTERNAL;
}
}
-
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::available_memory(avail_mem);
log_debug(redefine, class, load)
- ("loaded name=%s (avail_mem=" UINT64_FORMAT "K)", the_class->external_name(), os::available_memory() >> 10);
+ ("loaded name=%s (avail_mem=" PHYS_MEM_TYPE_FORMAT "K)", the_class->external_name(), avail_mem >> 10);
}
return JVMTI_ERROR_NONE;
@@ -4422,9 +4425,12 @@ void VM_RedefineClasses::redefine_single_class(Thread* current, jclass the_jclas
ResourceMark rm(current);
// increment the classRedefinedCount field in the_class and in any
// direct and indirect subclasses of the_class
+ physical_memory_size_type avail_mem = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::available_memory(avail_mem);
log_info(redefine, class, load)
- ("redefined name=%s, count=%d (avail_mem=" UINT64_FORMAT "K)",
- the_class->external_name(), java_lang_Class::classRedefinedCount(the_class->java_mirror()), os::available_memory() >> 10);
+ ("redefined name=%s, count=%d (avail_mem=" PHYS_MEM_TYPE_FORMAT "K)",
+ the_class->external_name(), java_lang_Class::classRedefinedCount(the_class->java_mirror()), avail_mem >> 10);
Events::log_redefinition(current, "redefined class name=%s, count=%d",
the_class->external_name(),
java_lang_Class::classRedefinedCount(the_class->java_mirror()));
diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
index f9f37d84ddef..9999b59decee 100644
--- a/src/hotspot/share/prims/whitebox.cpp
+++ b/src/hotspot/share/prims/whitebox.cpp
@@ -2544,13 +2544,16 @@ WB_END
// Physical memory of the host machine (including containers)
WB_ENTRY(jlong, WB_HostPhysicalMemory(JNIEnv* env, jobject o))
- LINUX_ONLY(return os::Linux::physical_memory();)
- return os::physical_memory();
+ LINUX_ONLY(return static_cast(os::Linux::physical_memory());)
+ return static_cast(os::physical_memory());
WB_END
// Available memory of the host machine (container-aware)
WB_ENTRY(jlong, WB_HostAvailableMemory(JNIEnv* env, jobject o))
- return os::available_memory();
+ physical_memory_size_type avail_mem = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::available_memory(avail_mem);
+ return static_cast(avail_mem);
WB_END
// Physical swap of the host machine (including containers), Linux only.
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index f1f39e0f8f8c..6d1d93381670 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -1452,6 +1452,7 @@ void Arguments::set_conservative_max_heap_alignment() {
os::vm_allocation_granularity(),
os::max_page_size(),
GCArguments::compute_heap_alignment());
+ assert(is_power_of_2(_conservative_max_heap_alignment), "Expected to be a power-of-2");
}
jint Arguments::set_ergonomics_flags() {
@@ -1503,13 +1504,13 @@ void Arguments::set_heap_size() {
!FLAG_IS_DEFAULT(MaxRAM));
if (override_coop_limit) {
if (FLAG_IS_DEFAULT(MaxRAM)) {
- phys_mem = os::physical_memory();
+ phys_mem = static_cast(os::physical_memory());
FLAG_SET_ERGO(MaxRAM, (uint64_t)phys_mem);
} else {
phys_mem = (julong)MaxRAM;
}
} else {
- phys_mem = FLAG_IS_DEFAULT(MaxRAM) ? MIN2(os::physical_memory(), (julong)MaxRAM)
+ phys_mem = FLAG_IS_DEFAULT(MaxRAM) ? MIN2(static_cast(os::physical_memory()), (julong)MaxRAM)
: (julong)MaxRAM;
}
@@ -1631,7 +1632,8 @@ jint Arguments::set_aggressive_heap_flags() {
// Thus, we need to make sure we're using a julong for intermediate
// calculations.
julong initHeapSize;
- julong total_memory = os::physical_memory();
+ physical_memory_size_type phys_mem = os::physical_memory();
+ julong total_memory = static_cast(phys_mem);
if (total_memory < (julong) 256 * M) {
jio_fprintf(defaultStream::error_stream(),
diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
index a928b0443eef..821b32f21ed2 100644
--- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp
+++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
@@ -1623,15 +1623,14 @@ static int num_java_frames(ContinuationWrapper& cont) {
}
static void invalidate_jvmti_stack(JavaThread* thread) {
- if (thread->is_interp_only_mode()) {
- JvmtiThreadState *state = thread->jvmti_thread_state();
- if (state != nullptr)
- state->invalidate_cur_stack_depth();
+ JvmtiThreadState *state = thread->jvmti_thread_state();
+ if (state != nullptr) {
+ state->invalidate_cur_stack_depth();
}
}
static void jvmti_yield_cleanup(JavaThread* thread, ContinuationWrapper& cont) {
- if (JvmtiExport::can_post_frame_pop()) {
+ if (JvmtiExport::has_frame_pops(thread)) {
int num_frames = num_java_frames(cont);
ContinuationWrapper::SafepointOp so(Thread::current(), cont);
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index 1e1f8e90be62..390ca92984d5 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -1183,12 +1183,13 @@ void os::print_summary_info(outputStream* st, char* buf, size_t buflen) {
#endif // PRODUCT
get_summary_cpu_info(buf, buflen);
st->print("%s, ", buf);
- size_t mem = physical_memory()/G;
+ physical_memory_size_type phys_mem = physical_memory();
+ physical_memory_size_type mem = phys_mem/G;
if (mem == 0) { // for low memory systems
- mem = physical_memory()/M;
- st->print("%d cores, %zuM, ", processor_count(), mem);
+ mem = phys_mem/M;
+ st->print("%d cores, " PHYS_MEM_TYPE_FORMAT "M, ", processor_count(), mem);
} else {
- st->print("%d cores, %zuG, ", processor_count(), mem);
+ st->print("%d cores, " PHYS_MEM_TYPE_FORMAT "G, ", processor_count(), mem);
}
get_summary_os_info(buf, buflen);
st->print_raw(buf);
@@ -1933,17 +1934,17 @@ bool os::is_server_class_machine() {
return true;
}
// Then actually look at the machine
- bool result = false;
- const unsigned int server_processors = 2;
- const julong server_memory = 2UL * G;
+ bool result = false;
+ const unsigned int server_processors = 2;
+ const physical_memory_size_type server_memory = 2UL * G;
// We seem not to get our full complement of memory.
// We allow some part (1/8?) of the memory to be "missing",
// based on the sizes of DIMMs, and maybe graphics cards.
- const julong missing_memory = 256UL * M;
-
+ const physical_memory_size_type missing_memory = 256UL * M;
+ physical_memory_size_type phys_mem = os::physical_memory();
/* Is this a server class machine? */
if ((os::active_processor_count() >= (int)server_processors) &&
- (os::physical_memory() >= (server_memory - missing_memory))) {
+ (phys_mem >= server_memory - missing_memory)) {
const unsigned int logical_processors =
VM_Version::logical_processors_per_package();
if (logical_processors > 1) {
@@ -2202,16 +2203,24 @@ static void assert_nonempty_range(const char* addr, size_t bytes) {
p2i(addr), p2i(addr) + bytes);
}
-julong os::used_memory() {
+bool os::used_memory(physical_memory_size_type& value) {
#ifdef LINUX
if (OSContainer::is_containerized()) {
jlong mem_usage = OSContainer::memory_usage_in_bytes();
if (mem_usage > 0) {
- return mem_usage;
+ value = static_cast(mem_usage);
+ return true;
+ } else {
+ return false;
}
}
#endif
- return os::physical_memory() - os::available_memory();
+ physical_memory_size_type avail_mem = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::available_memory(avail_mem);
+ physical_memory_size_type phys_mem = os::physical_memory();
+ value = phys_mem - avail_mem;
+ return true;
}
diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
index bb07abad6b14..42551edd0ba0 100644
--- a/src/hotspot/share/runtime/os.hpp
+++ b/src/hotspot/share/runtime/os.hpp
@@ -337,14 +337,14 @@ class os: AllStatic {
// For example, on Linux, "available" memory (`MemAvailable` in `/proc/meminfo`) is greater
// than "free" memory (`MemFree` in `/proc/meminfo`) because Linux can free memory
// aggressively (e.g. clear caches) so that it becomes available.
- static julong available_memory();
- static julong used_memory();
- static julong free_memory();
+ [[nodiscard]] static bool available_memory(physical_memory_size_type& value);
+ [[nodiscard]] static bool used_memory(physical_memory_size_type& value);
+ [[nodiscard]] static bool free_memory(physical_memory_size_type& value);
- static jlong total_swap_space();
- static jlong free_swap_space();
+ [[nodiscard]] static bool total_swap_space(physical_memory_size_type& value);
+ [[nodiscard]] static bool free_swap_space(physical_memory_size_type& value);
- static julong physical_memory();
+ static physical_memory_size_type physical_memory();
static bool has_allocatable_memory_limit(size_t* limit);
static bool is_server_class_machine();
static size_t rss();
diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp
index 503b78333517..6c1591e57cec 100644
--- a/src/hotspot/share/runtime/synchronizer.cpp
+++ b/src/hotspot/share/runtime/synchronizer.cpp
@@ -2048,7 +2048,11 @@ void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out,
}
const markWord mark = obj->mark();
- if (!mark.has_monitor()) {
+ // Note: When using ObjectMonitorTable we may observe an intermediate state,
+ // where the monitor is globally visible, but no thread has yet transitioned
+ // the markWord. To avoid reporting a false positive during this transition, we
+ // skip the `!mark.has_monitor()` test if we are using the ObjectMonitorTable.
+ if (!UseObjectMonitorTable && !mark.has_monitor()) {
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
"object does not think it has a monitor: obj="
INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n),
diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp
index 631a7ed8d797..1eb6195543a1 100644
--- a/src/hotspot/share/runtime/threadSMR.cpp
+++ b/src/hotspot/share/runtime/threadSMR.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -726,7 +726,8 @@ JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const {
}
}
}
- } else if (!thread->is_exiting()) {
+ } else if (includes(thread) && !thread->is_exiting()) {
+ // The thread is protected by this list and has not yet exited
return thread;
}
return nullptr;
@@ -865,7 +866,7 @@ void ThreadsSMRSupport::add_thread(JavaThread *thread){
ThreadsList *old_list = xchg_java_thread_list(new_list);
free_list(old_list);
- if (ThreadIdTable::is_initialized()) {
+ if (ThreadIdTable::is_initialized_acquire()) {
jlong tid = SharedRuntime::get_java_tid(thread);
ThreadIdTable::add_thread(tid, thread);
}
diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
index 35670e1e9371..0abcdf9f854c 100644
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -1139,7 +1139,7 @@ void Threads::remove(JavaThread* p, bool is_daemon) {
ConditionalMutexLocker throttle_ml(ThreadsLockThrottle_lock, UseThreadsLockThrottleLock);
MonitorLocker ml(Threads_lock);
- if (ThreadIdTable::is_initialized()) {
+ if (ThreadIdTable::is_initialized_acquire()) {
// This cleanup must be done before the current thread's GC barrier
// is detached since we need to touch the threadObj oop.
jlong tid = SharedRuntime::get_java_tid(p);
diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp
index b32b18c0852a..7fddaa179ae8 100644
--- a/src/hotspot/share/services/heapDumper.cpp
+++ b/src/hotspot/share/services/heapDumper.cpp
@@ -2612,7 +2612,10 @@ int HeapDumper::dump(const char* path, outputStream* out, int compression, bool
// (DumpWriter buffer, DumperClassCacheTable, GZipCompressor buffers).
// For the OOM handling we may already be limited in memory.
// Lets ensure we have at least 20MB per thread.
- julong max_threads = os::free_memory() / (20 * M);
+ physical_memory_size_type free_memory = 0;
+ // Return value ignored - defaulting to 0 on failure.
+ (void)os::free_memory(free_memory);
+ julong max_threads = free_memory / (20 * M);
if (num_dump_threads > max_threads) {
num_dump_threads = MAX2(1, (uint)max_threads);
}
diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp
index 070d4daa3fb5..9d4ed5ff94a5 100644
--- a/src/hotspot/share/services/management.cpp
+++ b/src/hotspot/share/services/management.cpp
@@ -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
@@ -975,7 +975,7 @@ static jlong get_long_attribute(jmmLongAttribute att) {
return ClassLoadingService::class_method_data_size();
case JMM_OS_MEM_TOTAL_PHYSICAL_BYTES:
- return os::physical_memory();
+ return static_cast(os::physical_memory());
default:
return -1;
@@ -1127,6 +1127,7 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo
// create dummy snapshot
dump_result.add_thread_snapshot();
} else {
+ assert(dump_result.t_list()->includes(jt), "Must be protected");
dump_result.add_thread_snapshot(jt);
}
}
diff --git a/src/hotspot/share/services/threadIdTable.cpp b/src/hotspot/share/services/threadIdTable.cpp
index e7fd97911483..54041dfbb83c 100644
--- a/src/hotspot/share/services/threadIdTable.cpp
+++ b/src/hotspot/share/services/threadIdTable.cpp
@@ -1,6 +1,6 @@
/*
-* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,7 @@
*/
#include "classfile/javaClasses.inline.hpp"
-#include "runtime/atomic.hpp"
+#include "runtime/handles.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/javaThread.inline.hpp"
#include "runtime/threadSMR.hpp"
@@ -82,24 +82,25 @@ class ThreadIdTableConfig : public AllStatic {
// Lazily creates the table and populates it with the given
// thread list
void ThreadIdTable::lazy_initialize(const ThreadsList *threads) {
- if (!_is_initialized) {
+ if (!Atomic::load_acquire(&_is_initialized)) {
{
// There is no obvious benefit in allowing the thread table
// to be concurrently populated during initialization.
MutexLocker ml(ThreadIdTableCreate_lock);
- if (_is_initialized) {
+ if (Atomic::load(&_is_initialized)) {
return;
}
create_table(threads->length());
- _is_initialized = true;
+ Atomic::release_store(&_is_initialized, true);
}
+
for (uint i = 0; i < threads->length(); i++) {
JavaThread* thread = threads->thread_at(i);
- oop tobj = thread->threadObj();
+ Handle tobj = Handle(JavaThread::current(), thread->threadObj());
if (tobj != nullptr) {
- jlong java_tid = java_lang_Thread::thread_id(tobj);
MutexLocker ml(Threads_lock);
if (!thread->is_exiting()) {
+ jlong java_tid = java_lang_Thread::thread_id(tobj());
// Must be inside the lock to ensure that we don't add a thread to the table
// that has just passed the removal point in Threads::remove().
add_thread(java_tid, thread);
@@ -212,7 +213,7 @@ class ThreadGet : public StackObj {
};
void ThreadIdTable::do_concurrent_work(JavaThread* jt) {
- assert(_is_initialized, "Thread table is not initialized");
+ assert(Atomic::load(&_is_initialized), "Thread table is not initialized");
_has_work = false;
double load_factor = get_load_factor();
log_debug(thread, table)("Concurrent work, load factor: %g", load_factor);
@@ -222,7 +223,8 @@ void ThreadIdTable::do_concurrent_work(JavaThread* jt) {
}
JavaThread* ThreadIdTable::add_thread(jlong tid, JavaThread* java_thread) {
- assert(_is_initialized, "Thread table is not initialized");
+ assert(Threads_lock->owned_by_self(), "Must hold Threads_lock");
+ assert(Atomic::load(&_is_initialized), "Thread table is not initialized");
Thread* thread = Thread::current();
ThreadIdTableLookup lookup(tid);
ThreadGet tg;
@@ -241,7 +243,7 @@ JavaThread* ThreadIdTable::add_thread(jlong tid, JavaThread* java_thread) {
}
JavaThread* ThreadIdTable::find_thread_by_tid(jlong tid) {
- assert(_is_initialized, "Thread table is not initialized");
+ assert(Atomic::load(&_is_initialized), "Thread table is not initialized");
Thread* thread = Thread::current();
ThreadIdTableLookup lookup(tid);
ThreadGet tg;
@@ -250,7 +252,8 @@ JavaThread* ThreadIdTable::find_thread_by_tid(jlong tid) {
}
bool ThreadIdTable::remove_thread(jlong tid) {
- assert(_is_initialized, "Thread table is not initialized");
+ assert(Threads_lock->owned_by_self(), "Must hold Threads_lock");
+ assert(Atomic::load(&_is_initialized), "Thread table is not initialized");
Thread* thread = Thread::current();
ThreadIdTableLookup lookup(tid);
return _local_table->remove(thread, lookup);
diff --git a/src/hotspot/share/services/threadIdTable.hpp b/src/hotspot/share/services/threadIdTable.hpp
index 12772aed88c0..a292d04452a2 100644
--- a/src/hotspot/share/services/threadIdTable.hpp
+++ b/src/hotspot/share/services/threadIdTable.hpp
@@ -1,6 +1,6 @@
/*
-* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
#define SHARE_SERVICES_THREADIDTABLE_HPP
#include "memory/allStatic.hpp"
+#include "runtime/atomic.hpp"
class JavaThread;
class ThreadsList;
@@ -41,7 +42,9 @@ class ThreadIdTable : public AllStatic {
public:
// Initialization
static void lazy_initialize(const ThreadsList* threads);
- static bool is_initialized() { return _is_initialized; }
+ static bool is_initialized_acquire() {
+ return Atomic::load_acquire(&_is_initialized);
+ }
// Lookup and list management
static JavaThread* find_thread_by_tid(jlong tid);
diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp
index 57c5f330bfcb..e7a934787f49 100644
--- a/src/hotspot/share/services/threadService.cpp
+++ b/src/hotspot/share/services/threadService.cpp
@@ -1161,9 +1161,11 @@ class GetThreadSnapshotClosure: public HandshakeClosure {
Type _type;
// park blocker or an object the thread waiting on/trying to lock
OopHandle _obj;
+ // thread that owns park blocker object when park blocker is AbstractOwnableSynchronizer
+ OopHandle _owner;
- Blocker(Type type, OopHandle obj): _type(type), _obj(obj) {}
- Blocker(): _type(NOTHING), _obj(nullptr) {}
+ Blocker(Type type, OopHandle obj): _type(type), _obj(obj), _owner() {}
+ Blocker(): _type(NOTHING), _obj(), _owner() {}
bool is_empty() const {
return _type == NOTHING;
@@ -1198,6 +1200,7 @@ class GetThreadSnapshotClosure: public HandshakeClosure {
delete _locks;
}
_blocker._obj.release(oop_storage());
+ _blocker._owner.release(oop_storage());
}
private:
@@ -1299,6 +1302,13 @@ class GetThreadSnapshotClosure: public HandshakeClosure {
oop park_blocker = java_lang_Thread::park_blocker(_thread_h());
if (park_blocker != nullptr) {
_blocker = Blocker(Blocker::PARK_BLOCKER, OopHandle(oop_storage(), park_blocker));
+ if (park_blocker->is_a(vmClasses::java_util_concurrent_locks_AbstractOwnableSynchronizer_klass())) {
+ // could be stale (unlikely in practice), but it's good enough to see deadlocks
+ oop ownerObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(park_blocker);
+ if (ownerObj != nullptr) {
+ _blocker._owner = OopHandle(oop_storage(), ownerObj);
+ }
+ }
}
ResourceMark rm(current);
@@ -1380,6 +1390,7 @@ class jdk_internal_vm_ThreadSnapshot: AllStatic {
static int _locks_offset;
static int _blockerTypeOrdinal_offset;
static int _blockerObject_offset;
+ static int _parkBlockerOwner_offset;
static void compute_offsets(InstanceKlass* klass, TRAPS) {
JavaClasses::compute_offset(_name_offset, klass, "name", vmSymbols::string_signature(), false);
@@ -1389,6 +1400,7 @@ class jdk_internal_vm_ThreadSnapshot: AllStatic {
JavaClasses::compute_offset(_locks_offset, klass, "locks", vmSymbols::jdk_internal_vm_ThreadLock_array(), false);
JavaClasses::compute_offset(_blockerTypeOrdinal_offset, klass, "blockerTypeOrdinal", vmSymbols::int_signature(), false);
JavaClasses::compute_offset(_blockerObject_offset, klass, "blockerObject", vmSymbols::object_signature(), false);
+ JavaClasses::compute_offset(_parkBlockerOwner_offset, klass, "parkBlockerOwner", vmSymbols::thread_signature(), false);
}
public:
static void init(InstanceKlass* klass, TRAPS) {
@@ -1419,9 +1431,10 @@ class jdk_internal_vm_ThreadSnapshot: AllStatic {
static void set_locks(oop snapshot, oop locks) {
snapshot->obj_field_put(_locks_offset, locks);
}
- static void set_blocker(oop snapshot, int type_ordinal, oop lock) {
+ static void set_blocker(oop snapshot, int type_ordinal, oop lock, oop owner) {
snapshot->int_field_put(_blockerTypeOrdinal_offset, type_ordinal);
snapshot->obj_field_put(_blockerObject_offset, lock);
+ snapshot->obj_field_put(_parkBlockerOwner_offset, owner);
}
};
@@ -1433,6 +1446,7 @@ int jdk_internal_vm_ThreadSnapshot::_stackTrace_offset;
int jdk_internal_vm_ThreadSnapshot::_locks_offset;
int jdk_internal_vm_ThreadSnapshot::_blockerTypeOrdinal_offset;
int jdk_internal_vm_ThreadSnapshot::_blockerObject_offset;
+int jdk_internal_vm_ThreadSnapshot::_parkBlockerOwner_offset;
oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) {
ThreadsListHandle tlh(THREAD);
@@ -1548,7 +1562,8 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) {
jdk_internal_vm_ThreadSnapshot::set_stack_trace(snapshot(), trace());
jdk_internal_vm_ThreadSnapshot::set_locks(snapshot(), locks());
if (!cl._blocker.is_empty()) {
- jdk_internal_vm_ThreadSnapshot::set_blocker(snapshot(), cl._blocker._type, cl._blocker._obj.resolve());
+ jdk_internal_vm_ThreadSnapshot::set_blocker(snapshot(),
+ cl._blocker._type, cl._blocker._obj.resolve(), cl._blocker._owner.resolve());
}
return snapshot();
}
diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp
index 4ec61babec3d..bc6944175879 100644
--- a/src/hotspot/share/utilities/globalDefinitions.hpp
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp
@@ -134,6 +134,7 @@ class oopDesc;
#define UINT64_FORMAT_X_0 "0x%016" PRIx64
#define UINT64_FORMAT_W(width) "%" #width PRIu64
#define UINT64_FORMAT_0 "%016" PRIx64
+#define PHYS_MEM_TYPE_FORMAT "%" PRIu64
// Format jlong, if necessary
#ifndef JLONG_FORMAT
@@ -413,6 +414,11 @@ const uintx max_uintx = (uintx)-1;
typedef unsigned int uint; NEEDS_CLEANUP
+// This typedef is to address the issue of running a 32-bit VM. In this case the amount
+// of physical memory may not fit in size_t, so we have to have a larger type. Once 32-bit
+// is deprecated, one can use size_t.
+typedef uint64_t physical_memory_size_type;
+
//----------------------------------------------------------------------------------------------------
// Java type definitions
diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp
index c47274bde18b..a3121a3de7f6 100644
--- a/src/hotspot/share/utilities/vmError.cpp
+++ b/src/hotspot/share/utilities/vmError.cpp
@@ -671,6 +671,7 @@ void VMError::report(outputStream* st, bool _verbose) {
BEGIN
if (MemTracker::enabled() &&
NmtVirtualMemory_lock != nullptr &&
+ _thread != nullptr &&
NmtVirtualMemory_lock->owned_by_self()) {
// Manually unlock to avoid reentrancy due to mallocs in detailed mode.
NmtVirtualMemory_lock->unlock();
@@ -1298,7 +1299,7 @@ void VMError::report(outputStream* st, bool _verbose) {
os::print_signal_handlers(st, buf, sizeof(buf));
st->cr();
- STEP_IF("Native Memory Tracking", _verbose)
+ STEP_IF("Native Memory Tracking", _verbose && _thread != nullptr)
MemTracker::error_report(st);
st->cr();
diff --git a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c
index efbd0ca56847..54d640b03a44 100644
--- a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c
+++ b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -63,7 +63,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferFrom0(JNIEnv *env, jobject this,
if (n < 0) {
if (errno == EAGAIN)
return IOS_UNAVAILABLE;
- if (errno == ENOSYS)
+ if (errno == ENOSYS || errno == EOPNOTSUPP)
return IOS_UNSUPPORTED_CASE;
if ((errno == EBADF || errno == EINVAL || errno == EXDEV) &&
((ssize_t)count >= 0))
@@ -103,6 +103,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferTo0(JNIEnv *env, jobject this,
case EINVAL:
case ENOSYS:
case EXDEV:
+ case EOPNOTSUPP:
// ignore and try sendfile()
break;
default:
diff --git a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
index c90e99dda07b..4677411b0bad 100644
--- a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
+++ b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -193,6 +193,7 @@ Java_sun_nio_fs_LinuxNativeDispatcher_directCopy0
case EINVAL:
case ENOSYS:
case EXDEV:
+ case EOPNOTSUPP:
// ignore and try sendfile()
break;
default:
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java
index 1280ccdad74d..b463f3599952 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java
@@ -498,7 +498,7 @@ protected Object checkPrivateKey(byte[] sk) throws InvalidKeyException {
/*
Main internal algorithms from Section 6 of specification
*/
- protected ML_KEM_KeyPair generateKemKeyPair(byte[] kem_d, byte[] kem_z) {
+ protected ML_KEM_KeyPair generateKemKeyPair(byte[] kem_d_z) {
MessageDigest mlKemH;
try {
mlKemH = MessageDigest.getInstance(HASH_H_NAME);
@@ -508,7 +508,8 @@ protected ML_KEM_KeyPair generateKemKeyPair(byte[] kem_d, byte[] kem_z) {
}
//Generate K-PKE keys
- var kPkeKeyPair = generateK_PkeKeyPair(kem_d);
+ //The 1st 32-byte `d` is used in K-PKE key pair generation
+ var kPkeKeyPair = generateK_PkeKeyPair(kem_d_z);
//encaps key = kPke encryption key
byte[] encapsKey = kPkeKeyPair.publicKey.keyBytes;
@@ -527,7 +528,8 @@ protected ML_KEM_KeyPair generateKemKeyPair(byte[] kem_d, byte[] kem_z) {
// This should never happen.
throw new RuntimeException(e);
}
- System.arraycopy(kem_z, 0, decapsKey,
+ // The 2nd 32-byte `z` is copied into decapsKey
+ System.arraycopy(kem_d_z, 32, decapsKey,
kPkePrivateKey.length + encapsKey.length + 32, 32);
return new ML_KEM_KeyPair(
@@ -535,6 +537,12 @@ protected ML_KEM_KeyPair generateKemKeyPair(byte[] kem_d, byte[] kem_z) {
new ML_KEM_DecapsulationKey(decapsKey));
}
+ public byte[] privKeyToPubKey(byte[] decapsKey) {
+ int pkLen = (mlKem_k * ML_KEM_N * 12) / 8 + 32 /* rho */;
+ int skLen = (mlKem_k * ML_KEM_N * 12) / 8;
+ return Arrays.copyOfRange(decapsKey, skLen, skLen + pkLen);
+ }
+
protected ML_KEM_EncapsulateResult encapsulate(
ML_KEM_EncapsulationKey encapsulationKey, byte[] randomMessage) {
MessageDigest mlKemH;
@@ -648,10 +656,12 @@ private K_PKE_KeyPair generateK_PkeKeyPair(byte[] seed) {
throw new RuntimeException(e);
}
- mlKemG.update(seed);
+ // Note: only the 1st 32-byte in the seed is used
+ mlKemG.update(seed, 0, 32);
mlKemG.update((byte)mlKem_k);
var rhoSigma = mlKemG.digest();
+ mlKemG.reset();
var rho = Arrays.copyOfRange(rhoSigma, 0, 32);
var sigma = Arrays.copyOfRange(rhoSigma, 32, 64);
Arrays.fill(rhoSigma, (byte)0);
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java
index 2ce5b3324e76..117f26e69810 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,9 +26,12 @@
package com.sun.crypto.provider;
import sun.security.jca.JCAUtil;
+import sun.security.pkcs.NamedPKCS8Key;
import sun.security.provider.NamedKEM;
import sun.security.provider.NamedKeyFactory;
import sun.security.provider.NamedKeyPairGenerator;
+import sun.security.util.KeyChoices;
+import sun.security.x509.NamedX509Key;
import java.security.*;
import java.util.Arrays;
@@ -37,6 +40,20 @@
public final class ML_KEM_Impls {
+ private static final int SEED_LEN = 64;
+
+ public static byte[] seedToExpanded(String pname, byte[] seed) {
+ return new ML_KEM(pname).generateKemKeyPair(seed)
+ .decapsulationKey()
+ .keyBytes();
+ }
+
+ public static NamedX509Key privKeyToPubKey(NamedPKCS8Key npk) {
+ return new NamedX509Key(npk.getAlgorithm(),
+ npk.getParams().getName(),
+ new ML_KEM(npk.getParams().getName()).privKeyToPubKey(npk.getExpanded()));
+ }
+
public sealed static class KPG
extends NamedKeyPairGenerator permits KPG2, KPG3, KPG5 {
@@ -50,25 +67,27 @@ protected KPG(String pname) {
}
@Override
- protected byte[][] implGenerateKeyPair(String name, SecureRandom random) {
- byte[] seed = new byte[32];
+ protected byte[][] implGenerateKeyPair(String pname, SecureRandom random) {
+ byte[] seed = new byte[SEED_LEN];
var r = random != null ? random : JCAUtil.getDefSecureRandom();
r.nextBytes(seed);
- byte[] z = new byte[32];
- r.nextBytes(z);
- ML_KEM mlKem = new ML_KEM(name);
+ ML_KEM mlKem = new ML_KEM(pname);
ML_KEM.ML_KEM_KeyPair kp;
+ kp = mlKem.generateKemKeyPair(seed);
+ var expanded = kp.decapsulationKey().keyBytes();
+
try {
- kp = mlKem.generateKemKeyPair(seed, z);
+ return new byte[][]{
+ kp.encapsulationKey().keyBytes(),
+ KeyChoices.writeToChoice(
+ KeyChoices.getPreferred("mlkem"),
+ seed, expanded),
+ expanded
+ };
} finally {
- Arrays.fill(seed, (byte)0);
- Arrays.fill(z, (byte)0);
+ Arrays.fill(seed, (byte) 0);
}
- return new byte[][] {
- kp.encapsulationKey().keyBytes(),
- kp.decapsulationKey().keyBytes()
- };
}
}
@@ -94,8 +113,39 @@ public sealed static class KF extends NamedKeyFactory permits KF2, KF3, KF5 {
public KF() {
super("ML-KEM", "ML-KEM-512", "ML-KEM-768", "ML-KEM-1024");
}
- public KF(String name) {
- super("ML-KEM", name);
+ public KF(String pname) {
+ super("ML-KEM", pname);
+ }
+
+ @Override
+ protected byte[] implExpand(String pname, byte[] input)
+ throws InvalidKeyException {
+ return KeyChoices.choiceToExpanded(pname, SEED_LEN, input,
+ ML_KEM_Impls::seedToExpanded);
+ }
+
+ @Override
+ protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+ var nk = toNamedKey(key);
+ if (nk instanceof NamedPKCS8Key npk) {
+ var type = KeyChoices.getPreferred("mlkem");
+ if (KeyChoices.typeOfChoice(npk.getRawBytes()) != type) {
+ var encoding = KeyChoices.choiceToChoice(
+ type,
+ npk.getParams().getName(),
+ SEED_LEN, npk.getRawBytes(),
+ ML_KEM_Impls::seedToExpanded);
+ nk = NamedPKCS8Key.internalCreate(
+ npk.getAlgorithm(),
+ npk.getParams().getName(),
+ encoding,
+ npk.getExpanded().clone());
+ if (npk != key) { // npk is neither input or output
+ npk.destroy();
+ }
+ }
+ }
+ return nk;
}
}
@@ -121,15 +171,15 @@ public sealed static class K extends NamedKEM permits K2, K3, K5 {
private static final int SEED_SIZE = 32;
@Override
- protected byte[][] implEncapsulate(String name, byte[] encapsulationKey,
+ protected byte[][] implEncapsulate(String pname, byte[] encapsulationKey,
Object ek, SecureRandom secureRandom) {
byte[] randomBytes = new byte[SEED_SIZE];
var r = secureRandom != null ? secureRandom : JCAUtil.getDefSecureRandom();
r.nextBytes(randomBytes);
- ML_KEM mlKem = new ML_KEM(name);
- ML_KEM.ML_KEM_EncapsulateResult mlKemEncapsulateResult = null;
+ ML_KEM mlKem = new ML_KEM(pname);
+ ML_KEM.ML_KEM_EncapsulateResult mlKemEncapsulateResult;
try {
mlKemEncapsulateResult = mlKem.encapsulate(
new ML_KEM.ML_KEM_EncapsulationKey(
@@ -145,49 +195,49 @@ protected byte[][] implEncapsulate(String name, byte[] encapsulationKey,
}
@Override
- protected byte[] implDecapsulate(String name, byte[] decapsulationKey,
+ protected byte[] implDecapsulate(String pname, byte[] decapsulationKey,
Object dk, byte[] cipherText)
throws DecapsulateException {
- ML_KEM mlKem = new ML_KEM(name);
+ ML_KEM mlKem = new ML_KEM(pname);
var kpkeCipherText = new ML_KEM.K_PKE_CipherText(cipherText);
return mlKem.decapsulate(new ML_KEM.ML_KEM_DecapsulationKey(
decapsulationKey), kpkeCipherText);
}
@Override
- protected int implSecretSize(String name) {
+ protected int implSecretSize(String pname) {
return ML_KEM.SECRET_SIZE;
}
@Override
- protected int implEncapsulationSize(String name) {
- ML_KEM mlKem = new ML_KEM(name);
+ protected int implEncapsulationSize(String pname) {
+ ML_KEM mlKem = new ML_KEM(pname);
return mlKem.getEncapsulationSize();
}
@Override
- protected Object implCheckPublicKey(String name, byte[] pk)
+ protected Object implCheckPublicKey(String pname, byte[] pk)
throws InvalidKeyException {
- ML_KEM mlKem = new ML_KEM(name);
+ ML_KEM mlKem = new ML_KEM(pname);
return mlKem.checkPublicKey(pk);
}
@Override
- protected Object implCheckPrivateKey(String name, byte[] sk)
+ protected Object implCheckPrivateKey(String pname, byte[] sk)
throws InvalidKeyException {
- ML_KEM mlKem = new ML_KEM(name);
+ ML_KEM mlKem = new ML_KEM(pname);
return mlKem.checkPrivateKey(sk);
}
public K() {
- super("ML-KEM", "ML-KEM-512", "ML-KEM-768", "ML-KEM-1024");
+ super("ML-KEM", new KF());
}
- public K(String name) {
- super("ML-KEM", name);
+ public K(String pname) {
+ super("ML-KEM", new KF(pname));
}
}
diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java
index b0b134e69a35..c4b01f9f76fe 100644
--- a/src/java.base/share/classes/java/lang/VirtualThread.java
+++ b/src/java.base/share/classes/java/lang/VirtualThread.java
@@ -603,8 +603,11 @@ private void afterYield() {
// Object.wait
if (s == WAITING || s == TIMED_WAITING) {
int newState;
+ boolean blocked;
if (s == WAITING) {
setState(newState = WAIT);
+ // may have been notified while in transition
+ blocked = notified && compareAndSetState(WAIT, BLOCKED);
} else {
// For timed-wait, a timeout task is scheduled to execute. The timeout
// task will change the thread state to UNBLOCKED and submit the thread
@@ -619,22 +622,22 @@ private void afterYield() {
byte seqNo = ++timedWaitSeqNo;
timeoutTask = schedule(() -> waitTimeoutExpired(seqNo), timeout, MILLISECONDS);
setState(newState = TIMED_WAIT);
+ // May have been notified while in transition. This must be done while
+ // holding the monitor to avoid changing the state of a new timed wait call.
+ blocked = notified && compareAndSetState(TIMED_WAIT, BLOCKED);
}
}
- // may have been notified while in transition to wait state
- if (notified && compareAndSetState(newState, BLOCKED)) {
- // may have even been unblocked already
+ if (blocked) {
+ // may have been unblocked already
if (blockPermit && compareAndSetState(BLOCKED, UNBLOCKED)) {
- submitRunContinuation();
+ lazySubmitRunContinuation();
+ }
+ } else {
+ // may have been interrupted while in transition to wait state
+ if (interrupted && compareAndSetState(newState, UNBLOCKED)) {
+ lazySubmitRunContinuation();
}
- return;
- }
-
- // may have been interrupted while in transition to wait state
- if (interrupted && compareAndSetState(newState, UNBLOCKED)) {
- submitRunContinuation();
- return;
}
return;
}
diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java
index f477110a46c3..561758f09379 100644
--- a/src/java.base/share/classes/java/security/KeyStore.java
+++ b/src/java.base/share/classes/java/security/KeyStore.java
@@ -1799,6 +1799,7 @@ private static final KeyStore getInstance(File file, char[] password,
}
KeyStore keystore = null;
+ String matched = null;
try (DataInputStream dataStream =
new DataInputStream(
@@ -1822,8 +1823,10 @@ private static final KeyStore getInstance(File file, char[] password,
if (CryptoAlgorithmConstraints.permits(
"KEYSTORE", ksAlgo)) {
keystore = new KeyStore(impl, p, ksAlgo);
- break;
+ } else {
+ matched = ksAlgo;
}
+ break;
}
} catch (NoSuchAlgorithmException e) {
// ignore
@@ -1853,9 +1856,14 @@ private static final KeyStore getInstance(File file, char[] password,
return keystore;
}
}
-
- throw new KeyStoreException("Unrecognized keystore format. "
- + "Please load it with a specified type");
+ if (matched == null) {
+ throw new KeyStoreException("Unrecognized keystore format. "
+ + "Please load it with a specified type");
+ } else {
+ throw new KeyStoreException("Keystore format " +
+ matched +
+ " disabled by jdk.crypto.disabledAlgorithms property");
+ }
}
/**
diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java
index 0fd90ef6f73a..c9994ec29306 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java
@@ -245,7 +245,12 @@ VMStorage nextStorage(int type, boolean is32Bit) {
// Regular struct, no HFA.
VMStorage[] structAlloc(MemoryLayout layout) {
// Allocate enough gp slots (regs and stack) such that the struct fits in them.
- int numChunks = (int) Utils.alignUp(layout.byteSize(), MAX_COPY_SIZE) / MAX_COPY_SIZE;
+ final int numChunks;
+ try {
+ numChunks = Math.toIntExact(Utils.alignUp(layout.byteSize(), MAX_COPY_SIZE) / MAX_COPY_SIZE);
+ } catch (ArithmeticException ae) {
+ throw new IllegalArgumentException("Layout too large: " + layout, ae);
+ }
VMStorage[] result = new VMStorage[numChunks];
for (int i = 0; i < numChunks; i++) {
result[i] = nextStorage(StorageType.INTEGER, false);
diff --git a/src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java b/src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java
index a26003a3afbe..276c379a5646 100644
--- a/src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java
+++ b/src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java
@@ -205,7 +205,10 @@ private static boolean dumpThread(Thread thread, TextWriter writer) {
// park blocker
Object parkBlocker = snapshot.parkBlocker();
if (parkBlocker != null) {
- writer.println(" - parking to wait for " + decorateObject(parkBlocker));
+ String suffix = (snapshot.parkBlockerOwner() instanceof Thread owner)
+ ? ", owner #" + owner.threadId()
+ : "";
+ writer.println(" - parking to wait for " + decorateObject(parkBlocker) + suffix);
}
// blocked on monitor enter or Object.wait
@@ -335,6 +338,9 @@ private static boolean dumpThread(Thread thread, JsonWriter jsonWriter) {
// parkBlocker is an object to allow for exclusiveOwnerThread in the future
jsonWriter.startObject("parkBlocker");
jsonWriter.writeProperty("object", Objects.toIdentityString(parkBlocker));
+ if (snapshot.parkBlockerOwner() instanceof Thread owner) {
+ jsonWriter.writeProperty("owner", owner.threadId());
+ }
jsonWriter.endObject();
}
diff --git a/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java b/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java
index 4fcbaf24d2e6..357d38008d15 100644
--- a/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java
+++ b/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java
@@ -44,6 +44,8 @@ class ThreadSnapshot {
// an object the thread is blocked/waiting on, converted to ThreadBlocker by ThreadSnapshot.of()
private int blockerTypeOrdinal;
private Object blockerObject;
+ // the owner of the blockerObject when the object is park blocker and is AbstractOwnableSynchronizer
+ private Thread parkBlockerOwner;
// set by ThreadSnapshot.of()
private ThreadBlocker blocker;
@@ -70,8 +72,11 @@ static ThreadSnapshot of(Thread thread) {
snapshot.locks = EMPTY_LOCKS;
}
if (snapshot.blockerObject != null) {
- snapshot.blocker = new ThreadBlocker(snapshot.blockerTypeOrdinal, snapshot.blockerObject);
+ snapshot.blocker = new ThreadBlocker(snapshot.blockerTypeOrdinal,
+ snapshot.blockerObject,
+ snapshot.parkBlockerOwner);
snapshot.blockerObject = null; // release
+ snapshot.parkBlockerOwner = null;
}
return snapshot;
}
@@ -104,6 +109,13 @@ Object parkBlocker() {
return getBlocker(BlockerLockType.PARK_BLOCKER);
}
+ /**
+ * Returns the owner of the parkBlocker if the parkBlocker is an AbstractOwnableSynchronizer.
+ */
+ Thread parkBlockerOwner() {
+ return (blocker != null && blocker.type == BlockerLockType.PARK_BLOCKER) ? blocker.owner : null;
+ }
+
/**
* Returns the object that the thread is blocked on.
* @throws IllegalStateException if not in the blocked state
@@ -211,11 +223,11 @@ Object lockObject() {
}
}
- private record ThreadBlocker(BlockerLockType type, Object obj) {
+ private record ThreadBlocker(BlockerLockType type, Object obj, Thread owner) {
private static final BlockerLockType[] lockTypeValues = BlockerLockType.values(); // cache
- ThreadBlocker(int typeOrdinal, Object obj) {
- this(lockTypeValues[typeOrdinal], obj);
+ ThreadBlocker(int typeOrdinal, Object obj, Thread owner) {
+ this(lockTypeValues[typeOrdinal], obj, owner);
}
}
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
index 857c2f6ad6d1..0ac1f75b86a0 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.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
@@ -1652,11 +1652,7 @@ private InputStream getInputStream0() throws IOException {
if (method.equals("HEAD") || cl == 0 ||
respCode == HTTP_NOT_MODIFIED ||
respCode == HTTP_NO_CONTENT) {
-
- http.finished();
- http = null;
- inputStream = new EmptyInputStream();
- connected = false;
+ noResponseBody();
}
if (respCode == 200 || respCode == 203 || respCode == 206 ||
@@ -1738,6 +1734,24 @@ private InputStream getInputStream0() throws IOException {
}
}
+ /**
+ * This method is called when a response with no response
+ * body is received, and arrange for the http client to
+ * be returned to the pool (or released) immediately when
+ * possible.
+ * @apiNote Used by {@link sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection}
+ * to preserve the TLS information after receiving an empty body.
+ * @implSpec
+ * Subclasses that override this method should call the super class
+ * implementation.
+ */
+ protected void noResponseBody() {
+ http.finished();
+ http = null;
+ inputStream = new EmptyInputStream();
+ connected = false;
+ }
+
/*
* Creates a chained exception that has the same type as
* original exception and with the same message. Right now,
diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java
index 7bf8280a7ada..1415658e34d6 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2024, 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
@@ -51,6 +51,7 @@
public abstract class AbstractDelegateHttpsURLConnection extends
HttpURLConnection {
+ private SSLSession savedSession = null;
protected AbstractDelegateHttpsURLConnection(URL url,
sun.net.www.protocol.http.Handler handler) throws IOException {
this(url, null, handler);
@@ -92,6 +93,7 @@ public void setNewClient (URL url)
public void setNewClient (URL url, boolean useCache)
throws IOException {
int readTimeout = getReadTimeout();
+ savedSession = null;
http = HttpsClient.New (getSSLSocketFactory(),
url,
getHostnameVerifier(),
@@ -184,6 +186,7 @@ public void connect() throws IOException {
if (!http.isCachedConnection() && http.needsTunneling()) {
doTunneling();
}
+ savedSession = null;
((HttpsClient)http).afterConnect();
}
@@ -204,6 +207,19 @@ protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout,
useCache, connectTimeout, this);
}
+ @Override
+ protected void noResponseBody() {
+ savedSession = ((HttpsClient)http).getSSLSession();
+ super.noResponseBody();
+ }
+
+ private SSLSession session() {
+ if (http instanceof HttpsClient https) {
+ return https.getSSLSession();
+ }
+ return savedSession;
+ }
+
/**
* Returns the cipher suite in use on this connection.
*/
@@ -211,11 +227,12 @@ public String getCipherSuite () {
if (cachedResponse != null) {
return ((SecureCacheResponse)cachedResponse).getCipherSuite();
}
- if (http == null) {
+
+ var session = session();
+ if (session == null) {
throw new IllegalStateException("connection not yet open");
- } else {
- return ((HttpsClient)http).getCipherSuite ();
}
+ return session.getCipherSuite();
}
/**
@@ -231,11 +248,12 @@ public java.security.cert.Certificate[] getLocalCertificates() {
return l.toArray(new java.security.cert.Certificate[0]);
}
}
- if (http == null) {
+
+ var session = session();
+ if (session == null) {
throw new IllegalStateException("connection not yet open");
- } else {
- return (((HttpsClient)http).getLocalCertificates ());
}
+ return session.getLocalCertificates();
}
/**
@@ -256,11 +274,11 @@ public java.security.cert.Certificate[] getServerCertificates()
}
}
- if (http == null) {
+ var session = session();
+ if (session == null) {
throw new IllegalStateException("connection not yet open");
- } else {
- return (((HttpsClient)http).getServerCertificates ());
}
+ return session.getPeerCertificates();
}
/**
@@ -274,11 +292,11 @@ Principal getPeerPrincipal()
return ((SecureCacheResponse)cachedResponse).getPeerPrincipal();
}
- if (http == null) {
+ var session = session();
+ if (session == null) {
throw new IllegalStateException("connection not yet open");
- } else {
- return (((HttpsClient)http).getPeerPrincipal());
}
+ return getPeerPrincipal(session);
}
/**
@@ -291,11 +309,11 @@ Principal getLocalPrincipal()
return ((SecureCacheResponse)cachedResponse).getLocalPrincipal();
}
- if (http == null) {
+ var session = session();
+ if (session == null) {
throw new IllegalStateException("connection not yet open");
- } else {
- return (((HttpsClient)http).getLocalPrincipal());
}
+ return getLocalPrincipal(session);
}
SSLSession getSSLSession() {
@@ -307,11 +325,12 @@ SSLSession getSSLSession() {
}
}
- if (http == null) {
+ var session = session();
+ if (session == null) {
throw new IllegalStateException("connection not yet open");
}
- return ((HttpsClient)http).getSSLSession();
+ return session;
}
/*
@@ -354,7 +373,7 @@ protected HttpCallerInfo getHttpCallerInfo(URL url, String proxy, int port,
}
HttpsClient https = (HttpsClient)http;
try {
- Certificate[] certs = https.getServerCertificates();
+ Certificate[] certs = https.getSSLSession().getPeerCertificates();
if (certs[0] instanceof X509Certificate x509Cert) {
return new HttpCallerInfo(url, proxy, port, x509Cert, authenticator);
}
@@ -372,7 +391,7 @@ protected HttpCallerInfo getHttpCallerInfo(URL url, Authenticator authenticator)
}
HttpsClient https = (HttpsClient)http;
try {
- Certificate[] certs = https.getServerCertificates();
+ Certificate[] certs = https.getSSLSession().getPeerCertificates();
if (certs[0] instanceof X509Certificate x509Cert) {
return new HttpCallerInfo(url, x509Cert, authenticator);
}
@@ -381,4 +400,58 @@ protected HttpCallerInfo getHttpCallerInfo(URL url, Authenticator authenticator)
}
return super.getHttpCallerInfo(url, authenticator);
}
+
+ @Override
+ public void disconnect() {
+ super.disconnect();
+ savedSession = null;
+ }
+
+ /**
+ * Returns the principal with which the server authenticated
+ * itself, or throw a SSLPeerUnverifiedException if the
+ * server did not authenticate.
+ * @param session The {@linkplain #getSSLSession() SSL session}
+ */
+ private static Principal getPeerPrincipal(SSLSession session)
+ throws SSLPeerUnverifiedException
+ {
+ Principal principal;
+ try {
+ principal = session.getPeerPrincipal();
+ } catch (AbstractMethodError e) {
+ // if the provider does not support it, fallback to peer certs.
+ // return the X500Principal of the end-entity cert.
+ java.security.cert.Certificate[] certs =
+ session.getPeerCertificates();
+ principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
+ }
+ return principal;
+ }
+
+ /**
+ * Returns the principal the client sent to the
+ * server, or null if the client did not authenticate.
+ * @param session The {@linkplain #getSSLSession() SSL session}
+ */
+ private static Principal getLocalPrincipal(SSLSession session)
+ {
+ Principal principal;
+ try {
+ principal = session.getLocalPrincipal();
+ } catch (AbstractMethodError e) {
+ principal = null;
+ // if the provider does not support it, fallback to local certs.
+ // return the X500Principal of the end-entity cert.
+ java.security.cert.Certificate[] certs =
+ session.getLocalCertificates();
+ if (certs != null) {
+ principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
+ }
+ }
+ return principal;
+ }
+
+
+
}
diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java
index 2f011f5805b4..f5804cd83bd8 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2024, 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
@@ -44,6 +44,7 @@
import java.util.StringTokenizer;
import javax.net.ssl.*;
+import sun.net.util.IPAddressUtil;
import sun.net.www.http.HttpClient;
import sun.net.www.protocol.http.AuthCacheImpl;
import sun.net.www.protocol.http.HttpURLConnection;
@@ -471,7 +472,13 @@ public void afterConnect() throws IOException, UnknownHostException {
SSLParameters parameters = s.getSSLParameters();
parameters.setEndpointIdentificationAlgorithm("HTTPS");
// host has been set previously for SSLSocketImpl
- if (!(s instanceof SSLSocketImpl)) {
+ if (!(s instanceof SSLSocketImpl) &&
+ !IPAddressUtil.isIPv4LiteralAddress(host) &&
+ !(host.charAt(0) == '[' && host.charAt(host.length() - 1) == ']' &&
+ IPAddressUtil.isIPv6LiteralAddress(host.substring(1, host.length() - 1))
+ )) {
+ // Fully qualified DNS hostname of the server, as per section 3, RFC 6066
+ // Literal IPv4 and IPv6 addresses are not permitted in "HostName".
parameters.setServerNames(List.of(new SNIHostName(host)));
}
s.setSSLParameters(parameters);
@@ -592,75 +599,6 @@ public void closeIdleConnection() {
}
}
- /**
- * Returns the cipher suite in use on this connection.
- */
- String getCipherSuite() {
- return session.getCipherSuite();
- }
-
- /**
- * Returns the certificate chain the client sent to the
- * server, or null if the client did not authenticate.
- */
- public java.security.cert.Certificate [] getLocalCertificates() {
- return session.getLocalCertificates();
- }
-
- /**
- * Returns the certificate chain with which the server
- * authenticated itself, or throw a SSLPeerUnverifiedException
- * if the server did not authenticate.
- */
- java.security.cert.Certificate [] getServerCertificates()
- throws SSLPeerUnverifiedException
- {
- return session.getPeerCertificates();
- }
-
- /**
- * Returns the principal with which the server authenticated
- * itself, or throw a SSLPeerUnverifiedException if the
- * server did not authenticate.
- */
- Principal getPeerPrincipal()
- throws SSLPeerUnverifiedException
- {
- Principal principal;
- try {
- principal = session.getPeerPrincipal();
- } catch (AbstractMethodError e) {
- // if the provider does not support it, fallback to peer certs.
- // return the X500Principal of the end-entity cert.
- java.security.cert.Certificate[] certs =
- session.getPeerCertificates();
- principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
- }
- return principal;
- }
-
- /**
- * Returns the principal the client sent to the
- * server, or null if the client did not authenticate.
- */
- Principal getLocalPrincipal()
- {
- Principal principal;
- try {
- principal = session.getLocalPrincipal();
- } catch (AbstractMethodError e) {
- principal = null;
- // if the provider does not support it, fallback to local certs.
- // return the X500Principal of the end-entity cert.
- java.security.cert.Certificate[] certs =
- session.getLocalCertificates();
- if (certs != null) {
- principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
- }
- }
- return principal;
- }
-
/**
* Returns the {@code SSLSession} in use on this connection.
*/
diff --git a/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java
index a748433da875..e1beb8b6b9bc 100644
--- a/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java
+++ b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java
@@ -25,11 +25,8 @@
package sun.security.pkcs;
-import sun.security.util.DerInputStream;
-import sun.security.util.DerValue;
import sun.security.x509.AlgorithmId;
-import javax.security.auth.DestroyFailedException;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
@@ -39,6 +36,7 @@
import java.security.ProviderException;
import java.security.spec.NamedParameterSpec;
import java.util.Arrays;
+import java.util.Objects;
/// Represents a private key from an algorithm family that is specialized
/// with a named parameter set.
@@ -50,6 +48,28 @@
/// identifier in the PKCS #8 encoding of the key is always a single OID derived
/// from the parameter set name.
///
+/// Besides the existing [PKCS8Key#privKeyMaterial] field, this class optionally
+/// supports an expanded format stored in [#expanded]. While `privKeyMaterial`
+/// always represents the format used for encoding, `expanded` is always used
+/// in computations. The expanded format must be self-sufficient for
+/// cryptographic computations without requiring the encoding format.
+///
+/// 1. If only `privKeyMaterial` is present, it's also the expanded format.
+/// 2. If both `privKeyMaterial` and `expanded` are available, `privKeyMaterial`
+/// is the encoding format, and `expanded` is the expanded format.
+///
+/// If the two formats are the same, only `privKeyMaterial` is included, and
+/// `expanded` must be `null`. Some implementations might be tempted to put the
+/// same value into `privKeyMaterial` and `expanded`. However, problems can
+/// arise if they happen to be the same object. To avoid ambiguity, always set
+/// `expanded` to `null`.
+///
+/// If the `expanded` field is required by the algorithm, it is either
+/// [calculated from the PKCS #8 encoding][#NamedPKCS8Key(String, byte\[\], Expander)],
+/// or [provided directly][#internalCreate(String, String, byte\[\], byte\[\])].
+/// In the latter case, the caller must ensure the consistency of the `encoded`
+/// and `expanded` arguments. For example, seed and expanded key must match.
+///
/// @see sun.security.provider.NamedKeyPairGenerator
public final class NamedPKCS8Key extends PKCS8Key {
@Serial
@@ -57,42 +77,64 @@ public final class NamedPKCS8Key extends PKCS8Key {
private final String fname;
private final transient NamedParameterSpec paramSpec;
- private final byte[] rawBytes;
+ private final transient byte[] expanded;
private transient boolean destroyed = false;
- /// Ctor from family name, parameter set name, raw key bytes.
- /// Key bytes won't be cloned, caller must relinquish ownership
- public NamedPKCS8Key(String fname, String pname, byte[] rawBytes) {
+ /// Creates a `NamedPKCS8Key` from raw components.
+ ///
+ /// @param fname family name
+ /// @param pname parameter set name
+ /// @param encoded raw key bytes, not null
+ /// @param expanded expanded key format, can be `null`.
+ private NamedPKCS8Key(String fname, String pname, byte[] encoded, byte[] expanded) {
this.fname = fname;
this.paramSpec = new NamedParameterSpec(pname);
+ this.expanded = expanded;
+ this.privKeyMaterial = Objects.requireNonNull(encoded);
try {
this.algid = AlgorithmId.get(pname);
} catch (NoSuchAlgorithmException e) {
throw new ProviderException(e);
}
- this.rawBytes = rawBytes;
+ }
- DerValue val = new DerValue(DerValue.tag_OctetString, rawBytes);
- try {
- this.privKeyMaterial = val.toByteArray();
- } finally {
- val.clear();
- }
+ /// Creates a `NamedPKCS8Key` from raw components.
+ ///
+ /// `encoded` and `expanded` won't be cloned, caller must relinquish
+ /// ownership. This caller must ensure `encoded` and `expanded` match
+ /// each other and `encoded` is valid and internally-consistent.
+ ///
+ /// @param fname family name
+ /// @param pname parameter set name
+ /// @param encoded raw key bytes, not null
+ /// @param expanded expanded key format, can be `null`.
+ public static NamedPKCS8Key internalCreate(String fname, String pname,
+ byte[] encoded, byte[] expanded) {
+ return new NamedPKCS8Key(fname, pname, encoded, expanded);
}
- /// Ctor from family name, and PKCS #8 bytes
- public NamedPKCS8Key(String fname, byte[] encoded) throws InvalidKeyException {
+ /// Creates a `NamedPKCS8Key` from family name and PKCS #8 encoding.
+ ///
+ /// @param fname family name
+ /// @param encoded PKCS #8 encoding. It is copied so caller can modify
+ /// it after the method call.
+ /// @param expander a function that is able to calculate the expanded
+ /// format from the encoding format inside `encoded`. If it recognizes
+ /// the input already in expanded format, it must return `null`.
+ /// This argument must be `null` if the algorithm's expanded format
+ /// is always the same as its encoding format. Whatever the case, the
+ /// ownership of the result is fully granted to this object.
+ public NamedPKCS8Key(String fname, byte[] encoded, Expander expander)
+ throws InvalidKeyException {
super(encoded);
this.fname = fname;
- try {
- paramSpec = new NamedParameterSpec(algid.getName());
- if (algid.getEncodedParams() != null) {
- throw new InvalidKeyException("algorithm identifier has params");
- }
- rawBytes = new DerInputStream(privKeyMaterial).getOctetString();
- } catch (IOException e) {
- throw new InvalidKeyException("Cannot parse input", e);
+ this.expanded = expander == null
+ ? null
+ : expander.expand(algid.getName(), this.privKeyMaterial);
+ paramSpec = new NamedParameterSpec(algid.getName());
+ if (algid.getEncodedParams() != null) {
+ throw new InvalidKeyException("algorithm identifier has params");
}
}
@@ -104,9 +146,15 @@ public String toString() {
}
/// Returns the reference to the internal key. Caller must not modify
- /// the content or keep a reference.
+ /// the content or pass the reference to untrusted application code.
public byte[] getRawBytes() {
- return rawBytes;
+ return privKeyMaterial;
+ }
+
+ /// Returns the reference to the key in expanded format. Caller must not
+ /// modify the content or pass the reference to untrusted application code.
+ public byte[] getExpanded() {
+ return expanded == null ? privKeyMaterial : expanded;
}
@Override
@@ -127,9 +175,11 @@ private void readObject(ObjectInputStream stream)
}
@Override
- public void destroy() throws DestroyFailedException {
- Arrays.fill(rawBytes, (byte)0);
+ public void destroy() {
Arrays.fill(privKeyMaterial, (byte)0);
+ if (expanded != null) {
+ Arrays.fill(expanded, (byte)0);
+ }
if (encodedKey != null) {
Arrays.fill(encodedKey, (byte)0);
}
@@ -140,4 +190,17 @@ public void destroy() throws DestroyFailedException {
public boolean isDestroyed() {
return destroyed;
}
+
+ /// Expands from encoding format to expanded format.
+ @FunctionalInterface
+ public interface Expander {
+ /// The expand method
+ ///
+ /// @param pname parameter set name
+ /// @param input input encoding
+ /// @return the expanded key, `null` if `input` is already in expanded
+ /// @throws InvalidKeyException if `input` is invalid, for example,
+ /// wrong encoding, or internal inconsistency
+ byte[] expand(String pname, byte[] input) throws InvalidKeyException;
+ }
}
diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA.java b/src/java.base/share/classes/sun/security/provider/ML_DSA.java
index af64ef399a8d..a55f7d258b16 100644
--- a/src/java.base/share/classes/sun/security/provider/ML_DSA.java
+++ b/src/java.base/share/classes/sun/security/provider/ML_DSA.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -568,6 +568,54 @@ public ML_DSA_KeyPair generateKeyPairInternal(byte[] randomBytes) {
return new ML_DSA_KeyPair(sk, pk);
}
+ private static int[][] deepClone(int[][] array) {
+ int[][] clone = new int[array.length][];
+ for (int i = 0; i < array.length; i++) {
+ clone[i] = array[i].clone();
+ }
+ return clone;
+ }
+
+ // This is similar to the generateKeyPairInternal method. Instead of
+ // generating from a seed, it uses stored fields inside the private key
+ // to calculate the public key. It performs several checks during the
+ // calculation to make sure the private key is a valid one. Otherwise,
+ // an IllegalArgumentException is thrown.
+ public ML_DSA_PublicKey privKeyToPubKey(ML_DSA_PrivateKey sk) {
+ // Sample A
+ int[][][] keygenA = generateA(sk.rho); //A is in NTT domain
+
+ // Compute t and tr
+ // make a copy of sk.s1 and modify it. Although we can also
+ // take it out of NTT domain later, it was modified for a while.
+ var s1 = deepClone(sk.s1);
+ mlDsaVectorNtt(s1); //s1 now in NTT domain
+ int[][] As1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N);
+ matrixVectorPointwiseMultiply(As1, keygenA, s1);
+
+ mlDsaVectorInverseNtt(As1);
+ int[][] t = vectorAddPos(As1, sk.s2);
+ int[][] t0 = integerMatrixAlloc(mlDsa_k, ML_DSA_N);
+ int[][] t1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N);
+ power2Round(t, t0, t1);
+ if (!Arrays.deepEquals(t0, sk.t0)) {
+ throw new IllegalArgumentException("t0 does not patch");
+ }
+
+ var crHash = new SHAKE256(TR_LEN);
+
+ ML_DSA_PublicKey pk = new ML_DSA_PublicKey(sk.rho, t1);
+ byte[] publicKeyBytes = pkEncode(pk);
+ crHash.update(publicKeyBytes);
+ byte[] tr = crHash.digest();
+ if (!Arrays.equals(tr, sk.tr)) {
+ throw new IllegalArgumentException("tr does not patch");
+ }
+
+ //Encode PK
+ return new ML_DSA_PublicKey(sk.rho, t1);
+ }
+
public ML_DSA_Signature signInternal(byte[] message, byte[] rnd, byte[] skBytes) {
//Decode private key and initialize hash function
ML_DSA_PrivateKey sk = skDecode(skBytes);
diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java b/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java
index dffe7c5cdb18..730e253f407f 100644
--- a/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java
+++ b/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,12 +26,35 @@
package sun.security.provider;
import sun.security.jca.JCAUtil;
+import sun.security.pkcs.NamedPKCS8Key;
+import sun.security.util.KeyChoices;
+import sun.security.x509.NamedX509Key;
+
import java.security.*;
import java.security.SecureRandom;
import java.util.Arrays;
public class ML_DSA_Impls {
+ private static final int SEED_LEN = 32;
+
+ public static byte[] seedToExpanded(String pname, byte[] seed) {
+ var impl = new ML_DSA(name2int(pname));
+ var sk = impl.generateKeyPairInternal(seed).privateKey();
+ try {
+ return impl.skEncode(sk);
+ } finally {
+ sk.destroy();
+ }
+ }
+
+ public static NamedX509Key privKeyToPubKey(NamedPKCS8Key npk) {
+ var dsa = new ML_DSA(name2int(npk.getParams().getName()));
+ return new NamedX509Key(npk.getAlgorithm(),
+ npk.getParams().getName(),
+ dsa.pkEncode(dsa.privKeyToPubKey(dsa.skDecode(npk.getExpanded()))));
+ }
+
public enum Version {
DRAFT, FINAL
}
@@ -43,16 +66,16 @@ public enum Version {
// --add-exports java.base/sun.security.provider=ALL-UNNAMED
public static Version version = Version.FINAL;
- static int name2int(String name) {
- if (name.endsWith("44")) {
+ static int name2int(String pname) {
+ if (pname.endsWith("44")) {
return 2;
- } else if (name.endsWith("65")) {
+ } else if (pname.endsWith("65")) {
return 3;
- } else if (name.endsWith("87")) {
+ } else if (pname.endsWith("87")) {
return 5;
} else {
// should not happen
- throw new ProviderException("Unknown name " + name);
+ throw new ProviderException("Unknown name " + pname);
}
}
@@ -69,20 +92,26 @@ public KPG(String pname) {
}
@Override
- protected byte[][] implGenerateKeyPair(String name, SecureRandom sr) {
- byte[] seed = new byte[32];
- var r = sr != null ? sr : JCAUtil.getDefSecureRandom();
+ protected byte[][] implGenerateKeyPair(String pname, SecureRandom random) {
+ byte[] seed = new byte[SEED_LEN];
+ var r = random != null ? random : JCAUtil.getDefSecureRandom();
r.nextBytes(seed);
- ML_DSA mlDsa = new ML_DSA(name2int(name));
+
+ ML_DSA mlDsa = new ML_DSA(name2int(pname));
ML_DSA.ML_DSA_KeyPair kp = mlDsa.generateKeyPairInternal(seed);
+ var expanded = mlDsa.skEncode(kp.privateKey());
+
try {
return new byte[][]{
mlDsa.pkEncode(kp.publicKey()),
- mlDsa.skEncode(kp.privateKey())
+ KeyChoices.writeToChoice(
+ KeyChoices.getPreferred("mldsa"),
+ seed, expanded),
+ expanded
};
} finally {
kp.privateKey().destroy();
- Arrays.fill(seed, (byte)0);
+ Arrays.fill(seed, (byte) 0);
}
}
}
@@ -109,8 +138,39 @@ public sealed static class KF extends NamedKeyFactory permits KF2, KF3, KF5 {
public KF() {
super("ML-DSA", "ML-DSA-44", "ML-DSA-65", "ML-DSA-87");
}
- public KF(String name) {
- super("ML-DSA", name);
+ public KF(String pname) {
+ super("ML-DSA", pname);
+ }
+
+ @Override
+ protected byte[] implExpand(String pname, byte[] input)
+ throws InvalidKeyException {
+ return KeyChoices.choiceToExpanded(pname, SEED_LEN, input,
+ ML_DSA_Impls::seedToExpanded);
+ }
+
+ @Override
+ protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+ var nk = toNamedKey(key);
+ if (nk instanceof NamedPKCS8Key npk) {
+ var type = KeyChoices.getPreferred("mldsa");
+ if (KeyChoices.typeOfChoice(npk.getRawBytes()) != type) {
+ var encoding = KeyChoices.choiceToChoice(
+ type,
+ npk.getParams().getName(),
+ SEED_LEN, npk.getRawBytes(),
+ ML_DSA_Impls::seedToExpanded);
+ nk = NamedPKCS8Key.internalCreate(
+ npk.getAlgorithm(),
+ npk.getParams().getName(),
+ encoding,
+ npk.getExpanded().clone());
+ if (npk != key) { // npk is neither input or output
+ npk.destroy();
+ }
+ }
+ }
+ return nk;
}
}
@@ -134,16 +194,16 @@ public KF5() {
public sealed static class SIG extends NamedSignature permits SIG2, SIG3, SIG5 {
public SIG() {
- super("ML-DSA", "ML-DSA-44", "ML-DSA-65", "ML-DSA-87");
+ super("ML-DSA", new KF());
}
- public SIG(String name) {
- super("ML-DSA", name);
+ public SIG(String pname) {
+ super("ML-DSA", new KF(pname));
}
@Override
- protected byte[] implSign(String name, byte[] skBytes,
+ protected byte[] implSign(String pname, byte[] skBytes,
Object sk2, byte[] msg, SecureRandom sr) {
- var size = name2int(name);
+ var size = name2int(pname);
var r = sr != null ? sr : JCAUtil.getDefSecureRandom();
byte[] rnd = new byte[32];
r.nextBytes(rnd);
@@ -160,10 +220,10 @@ protected byte[] implSign(String name, byte[] skBytes,
}
@Override
- protected boolean implVerify(String name, byte[] pkBytes,
+ protected boolean implVerify(String pname, byte[] pkBytes,
Object pk2, byte[] msg, byte[] sigBytes)
throws SignatureException {
- var size = name2int(name);
+ var size = name2int(pname);
var mlDsa = new ML_DSA(size);
if (version == Version.FINAL) {
// FIPS 204 Algorithm 3 ML-DSA.Verify prepend {0, len(ctx)}
@@ -176,18 +236,18 @@ protected boolean implVerify(String name, byte[] pkBytes,
}
@Override
- protected Object implCheckPublicKey(String name, byte[] pk)
+ protected Object implCheckPublicKey(String pname, byte[] pk)
throws InvalidKeyException {
- ML_DSA mlDsa = new ML_DSA(name2int(name));
+ ML_DSA mlDsa = new ML_DSA(name2int(pname));
return mlDsa.checkPublicKey(pk);
}
@Override
- protected Object implCheckPrivateKey(String name, byte[] sk)
+ protected Object implCheckPrivateKey(String pname, byte[] sk)
throws InvalidKeyException {
- ML_DSA mlDsa = new ML_DSA(name2int(name));
+ ML_DSA mlDsa = new ML_DSA(name2int(pname));
return mlDsa.checkPrivateKey(sk);
}
}
diff --git a/src/java.base/share/classes/sun/security/provider/NamedKEM.java b/src/java.base/share/classes/sun/security/provider/NamedKEM.java
index 2731b3460af3..60449396d4d8 100644
--- a/src/java.base/share/classes/sun/security/provider/NamedKEM.java
+++ b/src/java.base/share/classes/sun/security/provider/NamedKEM.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,7 +42,6 @@
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.NamedParameterSpec;
import java.util.Arrays;
-import java.util.Objects;
/// A base class for all `KEM` implementations that can be
/// configured with a named parameter set. See [NamedKeyPairGenerator]
@@ -50,21 +49,19 @@
public abstract class NamedKEM implements KEMSpi {
private final String fname; // family name
- private final String[] pnames; // allowed parameter set name (at least one)
+ private final NamedKeyFactory fac;
/// Creates a new `NamedKEM` object.
///
/// @param fname the family name
- /// @param pnames the standard parameter set names, at least one is needed.
- protected NamedKEM(String fname, String... pnames) {
+ /// @param fac the `KeyFactory` used to translate foreign keys and
+ /// perform key validation
+ protected NamedKEM(String fname, NamedKeyFactory fac) {
if (fname == null) {
throw new AssertionError("fname cannot be null");
}
- if (pnames == null || pnames.length == 0) {
- throw new AssertionError("pnames cannot be null or empty");
- }
this.fname = fname;
- this.pnames = pnames;
+ this.fac = fac;
}
@Override
@@ -76,8 +73,7 @@ public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey,
"The " + fname + " algorithm does not take any parameters");
}
// translate also check the key
- var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames)
- .engineTranslateKey(publicKey);
+ var nk = (NamedX509Key) fac.toNamedKey(publicKey);
var pk = nk.getRawBytes();
return getKeyConsumerImpl(this, nk.getParams(), pk,
implCheckPublicKey(nk.getParams().getName(), pk), secureRandom);
@@ -92,16 +88,15 @@ public DecapsulatorSpi engineNewDecapsulator(
"The " + fname + " algorithm does not take any parameters");
}
// translate also check the key
- var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames)
- .engineTranslateKey(privateKey);
- var sk = nk.getRawBytes();
+ var nk = (NamedPKCS8Key) fac.toNamedKey(privateKey);
+ var sk = nk.getExpanded();
return getKeyConsumerImpl(this, nk.getParams(), sk,
implCheckPrivateKey(nk.getParams().getName(), sk), null);
}
// We don't have a flag on whether key is public key or private key.
// The correct method should always be called.
- private record KeyConsumerImpl(NamedKEM kem, String name, int sslen,
+ private record KeyConsumerImpl(NamedKEM kem, String pname, int sslen,
int clen, byte[] key, Object k2, SecureRandom sr)
implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi {
@Override
@@ -110,7 +105,7 @@ public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to,
if (encapsulation.length != clen) {
throw new DecapsulateException("Invalid key encapsulation message length");
}
- var ss = kem.implDecapsulate(name, key, k2, encapsulation);
+ var ss = kem.implDecapsulate(pname, key, k2, encapsulation);
try {
return new SecretKeySpec(ss,
from, to - from, algorithm);
@@ -121,7 +116,7 @@ public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to,
@Override
public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) {
- var enc = kem.implEncapsulate(name, key, k2, sr);
+ var enc = kem.implEncapsulate(pname, key, k2, sr);
try {
return new KEM.Encapsulated(
new SecretKeySpec(enc[1],
@@ -146,46 +141,46 @@ public int engineEncapsulationSize() {
private static KeyConsumerImpl getKeyConsumerImpl(NamedKEM kem,
NamedParameterSpec nps, byte[] key, Object k2, SecureRandom sr) {
- String name = nps.getName();
- return new KeyConsumerImpl(kem, name, kem.implSecretSize(name), kem.implEncapsulationSize(name),
+ String pname = nps.getName();
+ return new KeyConsumerImpl(kem, pname, kem.implSecretSize(pname), kem.implEncapsulationSize(pname),
key, k2, sr);
}
/// User-defined encap function.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @param pk public key in raw bytes
/// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey].
/// @param sr SecureRandom object, `null` if not initialized
/// @return the key encapsulation message and the shared key (in this order)
/// @throws ProviderException if there is an internal error
- protected abstract byte[][] implEncapsulate(String name, byte[] pk, Object pk2, SecureRandom sr);
+ protected abstract byte[][] implEncapsulate(String pname, byte[] pk, Object pk2, SecureRandom sr);
/// User-defined decap function.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @param sk private key in raw bytes
/// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey].
/// @param encap the key encapsulation message
/// @return the shared key
/// @throws ProviderException if there is an internal error
/// @throws DecapsulateException if there is another error
- protected abstract byte[] implDecapsulate(String name, byte[] sk, Object sk2, byte[] encap)
+ protected abstract byte[] implDecapsulate(String pname, byte[] sk, Object sk2, byte[] encap)
throws DecapsulateException;
/// User-defined function returning shared secret key length.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @return shared secret key length
/// @throws ProviderException if there is an internal error
- protected abstract int implSecretSize(String name);
+ protected abstract int implSecretSize(String pname);
/// User-defined function returning key encapsulation message length.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @return key encapsulation message length
/// @throws ProviderException if there is an internal error
- protected abstract int implEncapsulationSize(String name);
+ protected abstract int implEncapsulationSize(String pname);
/// User-defined function to validate a public key.
///
@@ -196,11 +191,11 @@ protected abstract byte[] implDecapsulate(String name, byte[] sk, Object sk2, by
///
/// The default implementation returns `null`.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @param pk public key in raw bytes
/// @return a parsed key, `null` if none.
/// @throws InvalidKeyException if the key is invalid
- protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException {
+ protected Object implCheckPublicKey(String pname, byte[] pk) throws InvalidKeyException {
return null;
}
@@ -213,11 +208,11 @@ protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyExc
///
/// The default implementation returns `null`.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @param sk private key in raw bytes
/// @return a parsed key, `null` if none.
/// @throws InvalidKeyException if the key is invalid
- protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException {
+ protected Object implCheckPrivateKey(String pname, byte[] sk) throws InvalidKeyException {
return null;
}
}
diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java
index 727358dd0749..9099f1446ff9 100644
--- a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java
+++ b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,7 +42,6 @@
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
-import java.util.Objects;
/// A base class for all `KeyFactory` implementations that can be
/// configured with a named parameter set. See [NamedKeyPairGenerator]
@@ -58,7 +57,7 @@
///
/// When reading from a RAW format, it needs enough info to derive the
/// parameter set name.
-public class NamedKeyFactory extends KeyFactorySpi {
+public abstract class NamedKeyFactory extends KeyFactorySpi {
private final String fname; // family name
private final String[] pnames; // allowed parameter set name (at least one)
@@ -78,92 +77,110 @@ protected NamedKeyFactory(String fname, String... pnames) {
this.pnames = pnames;
}
- private String checkName(String name) throws InvalidKeyException {
- for (var pname : pnames) {
- if (pname.equalsIgnoreCase(name)) {
+ private String checkName(String pname) throws InvalidKeyException {
+ for (var n : pnames) {
+ if (n.equalsIgnoreCase(pname)) {
// return the stored standard name
- return pname;
+ return n;
}
}
- throw new InvalidKeyException("Unsupported parameter set name: " + name);
+ throw new InvalidKeyException("Unsupported parameter set name: " + pname);
}
@Override
protected PublicKey engineGeneratePublic(KeySpec keySpec)
throws InvalidKeySpecException {
- if (keySpec instanceof X509EncodedKeySpec xspec) {
- try {
- return fromX509(xspec.getEncoded());
- } catch (InvalidKeyException e) {
- throw new InvalidKeySpecException(e);
+ return switch (keySpec) {
+ case X509EncodedKeySpec xspec -> {
+ try {
+ yield fromX509(xspec.getEncoded());
+ } catch (InvalidKeyException e) {
+ throw new InvalidKeySpecException(e);
+ }
}
- } else if (keySpec instanceof RawKeySpec rks) {
- if (pnames.length == 1) {
- return new NamedX509Key(fname, pnames[0], rks.getKeyArr());
- } else {
- throw new InvalidKeySpecException("Parameter set name unavailable");
+ case RawKeySpec rks -> {
+ if (pnames.length == 1) {
+ yield new NamedX509Key(fname, pnames[0], rks.getKeyArr());
+ } else {
+ throw new InvalidKeySpecException("Parameter set name unavailable");
+ }
}
- } else if (keySpec instanceof EncodedKeySpec espec
- && espec.getFormat().equalsIgnoreCase("RAW")) {
- if (pnames.length == 1) {
- return new NamedX509Key(fname, pnames[0], espec.getEncoded());
- } else {
- throw new InvalidKeySpecException("Parameter set name unavailable");
+ case EncodedKeySpec espec when espec.getFormat().equalsIgnoreCase("RAW") -> {
+ if (pnames.length == 1) {
+ yield new NamedX509Key(fname, pnames[0], espec.getEncoded());
+ } else {
+ throw new InvalidKeySpecException("Parameter set name unavailable");
+ }
}
- } else {
- throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec);
- }
+ case null -> throw new InvalidKeySpecException(
+ "keySpec must not be null");
+ default ->
+ throw new InvalidKeySpecException(keySpec.getClass().getName() +
+ " not supported.");
+ };
}
@Override
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
throws InvalidKeySpecException {
- if (keySpec instanceof PKCS8EncodedKeySpec pspec) {
- var bytes = pspec.getEncoded();
- try {
- return fromPKCS8(bytes);
- } catch (InvalidKeyException e) {
- throw new InvalidKeySpecException(e);
- } finally {
- Arrays.fill(bytes, (byte) 0);
- }
- } else if (keySpec instanceof RawKeySpec rks) {
- if (pnames.length == 1) {
- var bytes = rks.getKeyArr();
+ return switch (keySpec) {
+ case PKCS8EncodedKeySpec pspec -> {
+ var bytes = pspec.getEncoded();
try {
- return new NamedPKCS8Key(fname, pnames[0], bytes);
+ yield fromPKCS8(bytes);
+ } catch (InvalidKeyException e) {
+ throw new InvalidKeySpecException(e);
} finally {
Arrays.fill(bytes, (byte) 0);
}
- } else {
- throw new InvalidKeySpecException("Parameter set name unavailable");
}
- } else if (keySpec instanceof EncodedKeySpec espec
- && espec.getFormat().equalsIgnoreCase("RAW")) {
- if (pnames.length == 1) {
- var bytes = espec.getEncoded();
- try {
- return new NamedPKCS8Key(fname, pnames[0], bytes);
- } finally {
- Arrays.fill(bytes, (byte) 0);
+ case RawKeySpec rks -> {
+ if (pnames.length == 1) {
+ var raw = rks.getKeyArr();
+ try {
+ yield fromRaw(pnames[0], raw);
+ } catch (InvalidKeyException e) {
+ throw new InvalidKeySpecException("Invalid key input", e);
+ }
+ } else {
+ throw new InvalidKeySpecException("Parameter set name unavailable");
}
- } else {
- throw new InvalidKeySpecException("Parameter set name unavailable");
}
- } else {
- throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec);
- }
+ case EncodedKeySpec espec when espec.getFormat().equalsIgnoreCase("RAW") -> {
+ if (pnames.length == 1) {
+ var raw = espec.getEncoded();
+ try {
+ yield fromRaw(pnames[0], raw);
+ } catch (InvalidKeyException e) {
+ throw new InvalidKeySpecException("Invalid key input", e);
+ }
+ } else {
+ throw new InvalidKeySpecException("Parameter set name unavailable");
+ }
+ }
+ case null -> throw new InvalidKeySpecException(
+ "keySpec must not be null");
+ default ->
+ throw new InvalidKeySpecException(keySpec.getClass().getName() +
+ " not supported.");
+ };
+ }
+
+ private PrivateKey fromRaw(String pname, byte[] raw)
+ throws InvalidKeyException {
+ return NamedPKCS8Key.internalCreate(
+ fname, pname, raw, implExpand(pname, raw));
}
private PrivateKey fromPKCS8(byte[] bytes)
- throws InvalidKeyException, InvalidKeySpecException {
- var k = new NamedPKCS8Key(fname, bytes);
+ throws InvalidKeyException {
+ var k = new NamedPKCS8Key(fname, bytes, this::implExpand);
checkName(k.getParams().getName());
return k;
}
private PublicKey fromX509(byte[] bytes)
- throws InvalidKeyException, InvalidKeySpecException {
+ throws InvalidKeyException {
var k = new NamedX509Key(fname, bytes);
checkName(k.getParams().getName());
return k;
@@ -184,7 +201,7 @@ public String getFormat() {
protected T engineGetKeySpec(Key key, Class keySpec)
throws InvalidKeySpecException {
try {
- key = engineTranslateKey(key);
+ key = toNamedKey(key);
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
}
@@ -225,6 +242,12 @@ protected T engineGetKeySpec(Key key, Class keySpec)
@Override
protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+ // The base toNamedKey only makes sure key is translated into a NamedKey.
+ // the key material is still the same as the input.
+ return toNamedKey(key);
+ }
+
+ protected Key toNamedKey(Key key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Key must not be null");
}
@@ -242,27 +265,28 @@ protected Key engineTranslateKey(Key key) throws InvalidKeyException {
} else if (format.equalsIgnoreCase("RAW")) {
var kAlg = key.getAlgorithm();
if (key instanceof AsymmetricKey pk) {
- String name;
+ String pname;
// Three cases that we can find the parameter set name from a RAW key:
// 1. getParams() returns one
// 2. getAlgorithm() returns param set name (some provider does this)
// 3. getAlgorithm() returns family name but this KF is for param set name
if (pk.getParams() instanceof NamedParameterSpec nps) {
- name = checkName(nps.getName());
+ pname = checkName(nps.getName());
} else {
if (kAlg.equalsIgnoreCase(fname)) {
if (pnames.length == 1) {
- name = pnames[0];
+ pname = pnames[0];
} else {
throw new InvalidKeyException("No parameter set info");
}
} else {
- name = checkName(kAlg);
+ pname = checkName(kAlg);
}
}
+ var raw = key.getEncoded();
return key instanceof PrivateKey
- ? new NamedPKCS8Key(fname, name, key.getEncoded())
- : new NamedX509Key(fname, name, key.getEncoded());
+ ? fromRaw(pname, raw)
+ : new NamedX509Key(fname, pname, raw);
} else {
throw new InvalidKeyException("Unsupported key type: " + key.getClass());
}
@@ -270,19 +294,26 @@ protected Key engineTranslateKey(Key key) throws InvalidKeyException {
var bytes = key.getEncoded();
try {
return fromPKCS8(bytes);
- } catch (InvalidKeySpecException e) {
- throw new InvalidKeyException("Invalid PKCS#8 key", e);
} finally {
Arrays.fill(bytes, (byte) 0);
}
} else if (format.equalsIgnoreCase("X.509") && key instanceof PublicKey) {
- try {
- return fromX509(key.getEncoded());
- } catch (InvalidKeySpecException e) {
- throw new InvalidKeyException("Invalid X.509 key", e);
- }
+ return fromX509(key.getEncoded());
} else {
throw new InvalidKeyException("Unsupported key format: " + key.getFormat());
}
}
+
+ /// User-defined function to generate the expanded format of
+ /// a [NamedPKCS8Key] from its encoding format.
+ ///
+ /// This method is called when the key factory is constructing a private
+ /// key. The ownership of the result is fully granted to the caller.
+ ///
+ /// @param pname the parameter set name
+ /// @param input the encoding, could be any format
+ /// @return the expanded key, not null
+ /// @throws InvalidKeyException if `input` is invalid
+ protected abstract byte[] implExpand(String pname, byte[] input)
+ throws InvalidKeyException;
}
diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java b/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java
index 5be2b2b2a08b..6b55924b0fe2 100644
--- a/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java
+++ b/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,6 @@
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.NamedParameterSpec;
-import java.util.Objects;
/// A base class for all `KeyPairGenerator` implementations that can be
/// configured with a named parameter set.
@@ -52,15 +51,21 @@
/// with `getAlgorithm` returning the family name, and `getParams` returning
/// the parameter set name as a [NamedParameterSpec] object.
///
-/// An implementation must include a zero-argument public constructor that
-/// calls `super(fname, pnames)`, where `fname` is the family name of the
-/// algorithm and `pnames` are its supported parameter set names. `pnames`
-/// must contain at least one element. For an implementation of
-/// `NamedKeyPairGenerator`, the first element becomes its default parameter
-/// set, i.e. the parameter set to be used in key pair generation unless
+/// A `NamedKeyPairGenerator` or `NamedKeyFactory` implementation must include
+/// a zero-argument public constructor that calls `super(fname, pnames)`, where
+/// `fname` is the family name of the algorithm and `pnames` are its supported
+/// parameter set names. `pnames` must contain at least one element. For an
+/// implementation of `NamedKeyPairGenerator`, the first element becomes its
+/// default parameter set, i.e. the parameter set used by generated keys unless
/// [#initialize(AlgorithmParameterSpec, java.security.SecureRandom)]
/// is called on a different parameter set.
///
+/// A `NamedKEM` or `NamedSignature` implementation must include a zero-argument
+/// public constructor that calls `super(fname, factory)`, where `fname` is the
+/// family name of the algorithm and `factory` is the `NamedKeyFactory` object
+/// that is used to translate foreign keys. `factory` only recognizes
+/// parameter sets supported by this implementation.
+///
/// An implementation must implement all abstract methods. For all these
/// methods, the implementation must relinquish any "ownership" of any input
/// and output array argument. Precisely, the implementation must not retain
@@ -69,8 +74,8 @@
/// array argument and must not retain any reference to an input array argument
/// after the call.
///
-/// Also, an implementation must not keep any extra copy of a private key.
-/// For key generation, the only copy is the one returned in the
+/// Also, an implementation must not keep any extra copy of a private key in
+/// any format. For key generation, the only copy is the one returned in the
/// [#implGenerateKeyPair] call. For all other methods, it must not make
/// a copy of the input private key. A `KEM` implementation also must not
/// keep a copy of the shared secret key, no matter if it's an encapsulator
@@ -84,6 +89,34 @@
/// (For example, `implSign`) later. An implementation must not retain
/// a reference of the parsed key.
///
+/// The private key, represented as a byte array when used in `NamedKEM` or
+/// `NamedSignature`, is referred to as its expanded format. For some
+/// algorithms, this format may differ from the
+/// [key material][NamedPKCS8Key#getRawBytes()] inside a PKCS #8 file. For example,
+/// [FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf)
+/// Table 2 defines the ML-DSA-65 private key as a 4032-byte array, which is
+/// used in the ML-DSA.Sign function in Algorithm 2, representing the
+/// expanded format. However, in
+/// [RFC 9881](https://datatracker.ietf.org/doc/html/rfc9881#name-private-key-format),
+/// a private key can be encoded into a CHOICE of three formats, none in the
+/// same as the FIPS 204 format. The choices are defined in
+/// [sun.security.util.KeyChoices]. A `NamedKeyPairGenerator` implementation
+/// should return both the expanded key and a preferred encoding in its
+/// [#implGenerateKeyPair] method.
+///
+/// A `NamedKeyFactory` must override the `implExpand` method to derive
+/// the expanded format from an encoding format, or return `null` if there
+/// is no difference.
+///
+/// Implementations may support multiple encoding formats.
+///
+/// A `NamedKeyFactory` must not modify the encoding when generating a key
+/// from a `KeySpec` object, ensuring that when re-encoded, the key retains
+/// its original encoding format.
+///
+/// A `NamedKeyFactory` can choose a different encoding format when
+/// `translateKey` is called.
+///
/// When constructing a [NamedX509Key] or [NamedPKCS8Key] object from raw key
/// bytes, the key bytes are directly referenced within the object, so the
/// caller must not modify them afterward. Similarly, the key's `getRawBytes`
@@ -105,9 +138,9 @@
public abstract class NamedKeyPairGenerator extends KeyPairGeneratorSpi {
private final String fname; // family name
- private final String[] pnames; // allowed parameter set name (at least one)
+ private final String[] pnames; // allowed parameter set names (at least one)
- protected String name; // init as
+ protected String pname; // parameter set name, if can be determined
private SecureRandom secureRandom;
/// Creates a new `NamedKeyPairGenerator` object.
@@ -126,22 +159,22 @@ protected NamedKeyPairGenerator(String fname, String... pnames) {
this.pnames = pnames;
}
- private String checkName(String name) throws InvalidAlgorithmParameterException {
- for (var pname : pnames) {
- if (pname.equalsIgnoreCase(name)) {
- // return the stored standard name
- return pname;
+ private String checkName(String pname) throws InvalidAlgorithmParameterException {
+ for (var n : pnames) {
+ if (n.equalsIgnoreCase(pname)) {
+ // return the stored standard pname
+ return n;
}
}
throw new InvalidAlgorithmParameterException(
- "Unsupported parameter set name: " + name);
+ "Unsupported parameter set name: " + pname);
}
@Override
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
throws InvalidAlgorithmParameterException {
if (params instanceof NamedParameterSpec spec) {
- name = checkName(spec.getName());
+ pname = checkName(spec.getName());
} else {
throw new InvalidAlgorithmParameterException(
"Unsupported AlgorithmParameterSpec: " + params);
@@ -161,17 +194,21 @@ public void initialize(int keysize, SecureRandom random) {
@Override
public KeyPair generateKeyPair() {
- String pname = name != null ? name : pnames[0];
- var keys = implGenerateKeyPair(pname, secureRandom);
- return new KeyPair(new NamedX509Key(fname, pname, keys[0]),
- new NamedPKCS8Key(fname, pname, keys[1]));
+ String tmpName = pname != null ? pname : pnames[0];
+ var keys = implGenerateKeyPair(tmpName, secureRandom);
+ return new KeyPair(new NamedX509Key(fname, tmpName, keys[0]),
+ NamedPKCS8Key.internalCreate(fname, tmpName, keys[1],
+ keys.length == 2 ? null : keys[2]));
}
/// User-defined key pair generator.
///
/// @param pname parameter set name
/// @param sr `SecureRandom` object, `null` if not initialized
- /// @return public key and private key (in this order) in raw bytes
+ /// @return the public key, the private key in its encoding format, and
+ /// the private key in its expanded format (in this order) in
+ /// raw bytes. If the expanded format of the private key is the
+ /// same as its encoding format, the 3rd element must be omitted.
/// @throws ProviderException if there is an internal error
protected abstract byte[][] implGenerateKeyPair(String pname, SecureRandom sr);
}
diff --git a/src/java.base/share/classes/sun/security/provider/NamedSignature.java b/src/java.base/share/classes/sun/security/provider/NamedSignature.java
index 921a39cfc926..07d20828c3c1 100644
--- a/src/java.base/share/classes/sun/security/provider/NamedSignature.java
+++ b/src/java.base/share/classes/sun/security/provider/NamedSignature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,7 +40,6 @@
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.security.spec.AlgorithmParameterSpec;
-import java.util.Objects;
/// A base class for all `Signature` implementations that can be
/// configured with a named parameter set. See [NamedKeyPairGenerator]
@@ -50,12 +49,12 @@
public abstract class NamedSignature extends SignatureSpi {
private final String fname; // family name
- private final String[] pnames; // allowed parameter set name (at least one)
+ private final NamedKeyFactory fac;
private final ByteArrayOutputStream bout = new ByteArrayOutputStream();
// init with...
- private String name;
+ private String pname;
private byte[] secKey;
private byte[] pubKey;
@@ -65,26 +64,23 @@ public abstract class NamedSignature extends SignatureSpi {
/// Creates a new `NamedSignature` object.
///
/// @param fname the family name
- /// @param pnames the standard parameter set names, at least one is needed.
- protected NamedSignature(String fname, String... pnames) {
+ /// @param fac the `KeyFactory` used to translate foreign keys and
+ /// perform key validation
+ protected NamedSignature(String fname, NamedKeyFactory fac) {
if (fname == null) {
throw new AssertionError("fname cannot be null");
}
- if (pnames == null || pnames.length == 0) {
- throw new AssertionError("pnames cannot be null or empty");
- }
this.fname = fname;
- this.pnames = pnames;
+ this.fac = fac;
}
@Override
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
// translate also check the key
- var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames)
- .engineTranslateKey(publicKey);
- name = nk.getParams().getName();
+ var nk = (NamedX509Key) fac.toNamedKey(publicKey);
+ pname = nk.getParams().getName();
pubKey = nk.getRawBytes();
- pk2 = implCheckPublicKey(name, pubKey);
+ pk2 = implCheckPublicKey(pname, pubKey);
secKey = null;
bout.reset();
}
@@ -92,11 +88,10 @@ protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException
@Override
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
// translate also check the key
- var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames)
- .engineTranslateKey(privateKey);
- name = nk.getParams().getName();
- secKey = nk.getRawBytes();
- sk2 = implCheckPrivateKey(name, secKey);
+ var nk = (NamedPKCS8Key) fac.toNamedKey(privateKey);
+ pname = nk.getParams().getName();
+ secKey = nk.getExpanded();
+ sk2 = implCheckPrivateKey(pname, secKey);
pubKey = null;
bout.reset();
}
@@ -116,7 +111,7 @@ protected byte[] engineSign() throws SignatureException {
if (secKey != null) {
var msg = bout.toByteArray();
bout.reset();
- return implSign(name, secKey, sk2, msg, appRandom);
+ return implSign(pname, secKey, sk2, msg, appRandom);
} else {
throw new SignatureException("No private key");
}
@@ -127,21 +122,21 @@ protected boolean engineVerify(byte[] sig) throws SignatureException {
if (pubKey != null) {
var msg = bout.toByteArray();
bout.reset();
- return implVerify(name, pubKey, pk2, msg, sig);
+ return implVerify(pname, pubKey, pk2, msg, sig);
} else {
throw new SignatureException("No public key");
}
}
@Override
- @SuppressWarnings("deprecation")
+ @Deprecated
protected void engineSetParameter(String param, Object value)
throws InvalidParameterException {
throw new InvalidParameterException("setParameter() not supported");
}
@Override
- @SuppressWarnings("deprecation")
+ @Deprecated
protected Object engineGetParameter(String param) throws InvalidParameterException {
throw new InvalidParameterException("getParameter() not supported");
}
@@ -162,7 +157,7 @@ protected AlgorithmParameters engineGetParameters() {
/// User-defined sign function.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @param sk private key in raw bytes
/// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey].
/// @param msg the message
@@ -170,12 +165,12 @@ protected AlgorithmParameters engineGetParameters() {
/// @return the signature
/// @throws ProviderException if there is an internal error
/// @throws SignatureException if there is another error
- protected abstract byte[] implSign(String name, byte[] sk, Object sk2,
+ protected abstract byte[] implSign(String pname, byte[] sk, Object sk2,
byte[] msg, SecureRandom sr) throws SignatureException;
/// User-defined verify function.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @param pk public key in raw bytes
/// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey].
/// @param msg the message
@@ -183,7 +178,7 @@ protected abstract byte[] implSign(String name, byte[] sk, Object sk2,
/// @return true if verified
/// @throws ProviderException if there is an internal error
/// @throws SignatureException if there is another error
- protected abstract boolean implVerify(String name, byte[] pk, Object pk2,
+ protected abstract boolean implVerify(String pname, byte[] pk, Object pk2,
byte[] msg, byte[] sig) throws SignatureException;
/// User-defined function to validate a public key.
@@ -195,11 +190,11 @@ protected abstract boolean implVerify(String name, byte[] pk, Object pk2,
///
/// The default implementation returns `null`.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @param pk public key in raw bytes
/// @return a parsed key, `null` if none.
/// @throws InvalidKeyException if the key is invalid
- protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException {
+ protected Object implCheckPublicKey(String pname, byte[] pk) throws InvalidKeyException {
return null;
}
@@ -212,11 +207,11 @@ protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyExc
///
/// The default implementation returns `null`.
///
- /// @param name parameter name
+ /// @param pname parameter name
/// @param sk private key in raw bytes
/// @return a parsed key, `null` if none.
/// @throws InvalidKeyException if the key is invalid
- protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException {
+ protected Object implCheckPrivateKey(String pname, byte[] sk) throws InvalidKeyException {
return null;
}
}
diff --git a/src/java.base/share/classes/sun/security/util/KeyChoices.java b/src/java.base/share/classes/sun/security/util/KeyChoices.java
new file mode 100644
index 000000000000..00c4463d0987
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/util/KeyChoices.java
@@ -0,0 +1,289 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.util;
+
+import java.security.*;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.function.BiFunction;
+
+/**
+ * The content of an ML-KEM or ML-DSA private key is defined as a CHOICE
+ * among three different representations. For example:
+ *
+ * This class supports reading, writing, and converting between them.
+ *
+ * Current code follows RFC 9935 and RFC 9881.
+ */
+public final class KeyChoices {
+
+ public enum Type { SEED, EXPANDED_KEY, BOTH }
+
+ private record Choice(Type type, byte[] seed, byte[] expanded) {}
+
+ /**
+ * Gets the preferred choice type for an algorithm, defined as an
+ * overridable security property "jdk..pkcs8.encoding".
+ *
+ * @param name "mlkem" or "mldsa".
+ * @throws IllegalArgumentException if property is invalid value
+ * @return the type
+ */
+ public static Type getPreferred(String name) {
+ var prop = SecurityProperties.getOverridableProperty(
+ "jdk." + name + ".pkcs8.encoding");
+ if (prop == null) {
+ return Type.SEED;
+ }
+ return switch (prop.toLowerCase(Locale.ROOT)) {
+ case "seed" -> Type.SEED;
+ case "expandedkey" -> Type.EXPANDED_KEY;
+ case "both" -> Type.BOTH;
+ default -> throw new IllegalArgumentException("Unknown format: " + prop);
+ };
+ }
+
+ /**
+ * Writes one of the ML-KEM or ML-DSA private key formats.
+ *
+ * This method does not check the length of the inputs or whether
+ * they match each other. The caller must make sure `seed` and/or
+ * `expanded` are provided if `type` requires any of them.
+ *
+ * @param type preferred output choice type
+ * @param seed the seed, could be null
+ * @param expanded the expanded key, could be null
+ * @return one of the choices
+ */
+ public static byte[] writeToChoice(Type type, byte[] seed, byte[] expanded) {
+ byte[] skOctets;
+ // Ensures using one-byte len in DER
+ assert seed == null || seed.length < 128;
+ // Ensures using two-byte len in DER
+ assert expanded == null || expanded.length > 256 && expanded.length < 60000;
+
+ return switch (type) {
+ case SEED -> {
+ assert seed != null;
+ skOctets = new byte[seed.length + 2];
+ skOctets[0] = (byte)0x80;
+ skOctets[1] = (byte) seed.length;
+ System.arraycopy(seed, 0, skOctets, 2, seed.length);
+ yield skOctets;
+ }
+ case EXPANDED_KEY -> {
+ assert expanded != null;
+ skOctets = new byte[expanded.length + 4];
+ skOctets[0] = 0x04;
+ writeShortLength(skOctets, 1, expanded.length);
+ System.arraycopy(expanded, 0, skOctets, 4, expanded.length);
+ yield skOctets;
+ }
+ case BOTH -> {
+ assert seed != null;
+ assert expanded != null;
+ skOctets = new byte[10 + seed.length + expanded.length];
+ skOctets[0] = 0x30;
+ writeShortLength(skOctets, 1, 6 + seed.length + expanded.length);
+ skOctets[4] = 0x04;
+ skOctets[5] = (byte)seed.length;
+ System.arraycopy(seed, 0, skOctets, 6, seed.length);
+ skOctets[6 + seed.length] = 0x04;
+ writeShortLength(skOctets, 7 + seed.length, expanded.length);
+ System.arraycopy(expanded, 0, skOctets, 10 + seed.length, expanded.length);
+ yield skOctets;
+ }
+ };
+ }
+
+ /**
+ * Gets the type of input.
+ *
+ * @param input input bytes
+ * @return the type
+ * @throws InvalidKeyException if input is invalid
+ */
+ public static Type typeOfChoice(byte[] input) throws InvalidKeyException {
+ if (input.length < 1) {
+ throw new InvalidKeyException("Empty key");
+ }
+ return switch (input[0]) {
+ case (byte) 0x80 -> Type.SEED;
+ case 0x04 -> Type.EXPANDED_KEY;
+ case 0x30 -> Type.BOTH;
+ default -> throw new InvalidKeyException("Wrong tag: " + input[0]);
+ };
+ }
+
+ /**
+ * Splits one of the ML-KEM or ML-DSA private key formats into
+ * seed and expandedKey, if exists.
+ *
+ * @param seedLen correct seed length
+ * @param input input bytes
+ * @return a {@code Choice} object. Byte arrays inside are newly allocated
+ * @throws InvalidKeyException if input is invalid
+ */
+ private static Choice readFromChoice(int seedLen, byte[] input)
+ throws InvalidKeyException {
+ if (input.length < seedLen + 2) {
+ throw new InvalidKeyException("Too short");
+ }
+ return switch (input[0]) {
+ case (byte) 0x80 -> {
+ // 80 SEED_LEN
+ if (input[1] != seedLen && input.length != seedLen + 2) {
+ throw new InvalidKeyException("Invalid seed");
+ }
+ yield new Choice(Type.SEED,
+ Arrays.copyOfRange(input, 2, seedLen + 2), null);
+ }
+ case 0x04 -> {
+ // 04 82 nn nn
+ if (readShortLength(input, 1) != input.length - 4) {
+ throw new InvalidKeyException("Invalid expandedKey");
+ }
+ yield new Choice(Type.EXPANDED_KEY,
+ null, Arrays.copyOfRange(input, 4, input.length));
+ }
+ case 0x30 -> {
+ // 30 82 mm mm 04 SEED_LEN 04 82 nn nn
+ if (input.length < 6 + seedLen + 4) {
+ throw new InvalidKeyException("Too short");
+ }
+ if (readShortLength(input, 1) != input.length - 4
+ || input[4] != 0x04
+ || input[5] != (byte)seedLen
+ || input[seedLen + 6] != 0x04
+ || readShortLength(input, seedLen + 7)
+ != input.length - 10 - seedLen) {
+ throw new InvalidKeyException("Invalid both");
+ }
+ yield new Choice(Type.BOTH,
+ Arrays.copyOfRange(input, 6, 6 + seedLen),
+ Arrays.copyOfRange(input, seedLen + 10, input.length));
+ }
+ default -> throw new InvalidKeyException("Wrong tag: " + input[0]);
+ };
+ }
+
+ /**
+ * Reads from any encoding and write to the specified type.
+ *
+ * @param type preferred output choice type
+ * @param pname parameter set name
+ * @param seedLen seed length
+ * @param input the input encoding
+ * @param expander function to calculate expanded from seed, could be null
+ * if there is already expanded in input
+ * @return the preferred encoding
+ * @throws InvalidKeyException if input is invalid or does not have enough
+ * information to generate the output
+ */
+ public static byte[] choiceToChoice(Type type, String pname,
+ int seedLen, byte[] input,
+ BiFunction expander)
+ throws InvalidKeyException {
+ var choice = readFromChoice(seedLen, input);
+ try {
+ if (type != Type.EXPANDED_KEY && choice.type == Type.EXPANDED_KEY) {
+ throw new InvalidKeyException(
+ "key contains not enough info to translate");
+ }
+ var expanded = (choice.expanded == null && type != Type.SEED)
+ ? expander.apply(pname, choice.seed)
+ : choice.expanded;
+ return writeToChoice(type, choice.seed, expanded);
+ } finally {
+ if (choice.seed != null) {
+ Arrays.fill(choice.seed, (byte) 0);
+ }
+ if (choice.expanded != null) {
+ Arrays.fill(choice.expanded, (byte) 0);
+ }
+ }
+ }
+
+ /**
+ * Reads from any choice of encoding and return the expanded format.
+ *
+ * @param pname parameter set name
+ * @param seedLen seed length
+ * @param input input encoding
+ * @param expander function to calculate expanded from seed, could be null
+ * if there is already expanded in input
+ * @return the expanded key
+ * @throws InvalidKeyException if input is invalid
+ */
+ public static byte[] choiceToExpanded(String pname,
+ int seedLen, byte[] input,
+ BiFunction expander)
+ throws InvalidKeyException {
+ var choice = readFromChoice(seedLen, input);
+ if (choice.type == Type.BOTH) {
+ var calculated = expander.apply(pname, choice.seed);
+ if (!Arrays.equals(choice.expanded, calculated)) {
+ throw new InvalidKeyException("seed and expandedKey do not match");
+ }
+ Arrays.fill(calculated, (byte)0);
+ }
+ try {
+ if (choice.expanded != null) {
+ return choice.expanded;
+ }
+ return expander.apply(pname, choice.seed);
+ } finally {
+ if (choice.seed != null) {
+ Arrays.fill(choice.seed, (byte)0);
+ }
+ }
+ }
+
+ // Reads a 2 bytes length from DER encoding
+ private static int readShortLength(byte[] input, int from)
+ throws InvalidKeyException {
+ if (input[from] != (byte)0x82) {
+ throw new InvalidKeyException("Unexpected length");
+ }
+ return ((input[from + 1] & 0xff) << 8) + (input[from + 2] & 0xff);
+ }
+
+ // Writes a 2 bytes length to DER encoding
+ private static void writeShortLength(byte[] input, int from, int value) {
+ input[from] = (byte)0x82;
+ input[from + 1] = (byte) (value >> 8);
+ input[from + 2] = (byte) (value);
+ }
+}
diff --git a/src/java.base/share/classes/sun/security/x509/NamedX509Key.java b/src/java.base/share/classes/sun/security/x509/NamedX509Key.java
index dc36bd3b9b30..0c3fe2bf1212 100644
--- a/src/java.base/share/classes/sun/security/x509/NamedX509Key.java
+++ b/src/java.base/share/classes/sun/security/x509/NamedX509Key.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -71,7 +71,8 @@ public NamedX509Key(String fname, String pname, byte[] rawBytes) {
setKey(new BitArray(rawBytes.length * 8, rawBytes));
}
- /// Ctor from family name, and X.509 bytes
+ /// Ctor from family name, and X.509 bytes. Input byte array
+ /// is copied. Caller can modify it after the method call.
public NamedX509Key(String fname, byte[] encoded) throws InvalidKeyException {
this.fname = fname;
decode(encoded);
diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security
index 22020218d138..7f367b9a991b 100644
--- a/src/java.base/share/conf/security/java.security
+++ b/src/java.base/share/conf/security/java.security
@@ -1677,3 +1677,28 @@ jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA256AndAES_128
# com.sun.security.allowedAIALocations=http://some.company.com/cacert \
# ldap://ldap.company.com/dc=company,dc=com?caCertificate;binary
com.sun.security.allowedAIALocations=
+
+#
+# PKCS #8 encoding format for newly created ML-KEM and ML-DSA private keys
+#
+# RFC 9935 and RFC 9881 define three possible formats for a private key:
+# a seed (64 bytes for ML-KEM, 32 bytes for ML-DSA), an expanded private key,
+# or a sequence containing both.
+#
+# The following security properties determine the encoding format used when a
+# new keypair is generated with a KeyPairGenerator, and the output of the
+# translateKey method on an existing key using a ML-KEM or ML-DSA KeyFactory.
+#
+# Valid values for these properties are "seed", "expandedKey", and "both"
+# (case-insensitive). The default is "seed".
+#
+# If a system property of the same name is also specified, it supersedes the
+# security property value defined here.
+#
+# Note: These properties are currently used by the SunJCE (for ML-KEM) and SUN
+# (for ML-DSA) providers in the JDK Reference implementation. They are not
+# guaranteed to be supported by other implementations or third-party security
+# providers.
+#
+#jdk.mlkem.pkcs8.encoding = seed
+#jdk.mldsa.pkcs8.encoding = seed
diff --git a/src/java.base/share/data/lsrdata/language-subtag-registry.txt b/src/java.base/share/data/lsrdata/language-subtag-registry.txt
index 64c40f28162c..82618f9b40eb 100644
--- a/src/java.base/share/data/lsrdata/language-subtag-registry.txt
+++ b/src/java.base/share/data/lsrdata/language-subtag-registry.txt
@@ -1,4 +1,4 @@
-File-Date: 2025-05-15
+File-Date: 2025-08-25
%%
Type: language
Subtag: aa
@@ -3102,6 +3102,7 @@ Added: 2009-07-29
Type: language
Subtag: asb
Description: Assiniboine
+Description: Nakoda Assiniboine
Added: 2009-07-29
%%
Type: language
@@ -3269,6 +3270,7 @@ Added: 2009-07-29
Type: language
Subtag: atj
Description: Atikamekw
+Description: Nehirowimowin
Added: 2009-07-29
%%
Type: language
@@ -7981,6 +7983,7 @@ Added: 2009-07-29
Type: language
Subtag: clc
Description: Chilcotin
+Description: Tsilhqot’in
Added: 2009-07-29
%%
Type: language
@@ -8021,6 +8024,7 @@ Added: 2009-07-29
%%
Type: language
Subtag: clm
+Description: Klallam
Description: Clallam
Added: 2009-07-29
%%
@@ -13509,7 +13513,7 @@ Added: 2009-07-29
%%
Type: language
Subtag: haa
-Description: Han
+Description: Hän
Added: 2009-07-29
%%
Type: language
@@ -19022,6 +19026,7 @@ Added: 2009-07-29
%%
Type: language
Subtag: kwk
+Description: Kwak'wala
Description: Kwakiutl
Added: 2009-07-29
%%
@@ -22262,7 +22267,7 @@ Added: 2009-07-29
%%
Type: language
Subtag: mhn
-Description: Mócheno
+Description: Mòcheno
Added: 2009-07-29
%%
Type: language
@@ -31655,6 +31660,7 @@ Added: 2009-07-29
Type: language
Subtag: sec
Description: Sechelt
+Description: She shashishalhem
Added: 2009-07-29
%%
Type: language
@@ -32003,6 +32009,7 @@ Added: 2009-07-29
Type: language
Subtag: shs
Description: Shuswap
+Description: Secwepemctsín
Added: 2009-07-29
%%
Type: language
@@ -33014,6 +33021,7 @@ Added: 2009-07-29
Type: language
Subtag: squ
Description: Squamish
+Description: Sḵwx̱wú7mesh sníchim
Added: 2009-07-29
%%
Type: language
@@ -34664,6 +34672,8 @@ Added: 2009-07-29
Type: language
Subtag: thp
Description: Thompson
+Description: Nłeʔkepmxcín
+Description: Thompson River Salish
Added: 2009-07-29
%%
Type: language
@@ -34684,6 +34694,7 @@ Added: 2009-07-29
Type: language
Subtag: tht
Description: Tahltan
+Description: Tāłtān
Added: 2009-07-29
%%
Type: language
@@ -42419,7 +42430,7 @@ Added: 2009-07-29
%%
Type: language
Subtag: zmp
-Description: Mpuono
+Description: Mbuun
Added: 2009-07-29
%%
Type: language
@@ -47639,6 +47650,12 @@ Comments: Denotes conventions established by the Academia Brasileira de
Letras in 1943 and generally used in Brazil until 2009
%%
Type: variant
+Subtag: akhmimic
+Description: Akhmimic dialect of Coptic
+Added: 2025-07-14
+Prefix: cop
+%%
+Type: variant
Subtag: akuapem
Description: Akuapem Twi
Added: 2017-06-05
@@ -47814,6 +47831,12 @@ Comments: Black American Sign Language (BASL) or Black Sign Variation
(BSV) is a dialect of American Sign Language (ASL)
%%
Type: variant
+Subtag: bohairic
+Description: Bohairic dialect of Coptic
+Added: 2025-07-14
+Prefix: cop
+%%
+Type: variant
Subtag: bohoric
Description: Slovene in Bohorič alphabet
Added: 2012-06-27
@@ -47898,6 +47921,12 @@ Comments: Represents the standard written form of Ladin in Fascia which
unified the three subvarieties Cazet, Brach and Moenat
%%
Type: variant
+Subtag: fayyumic
+Description: Fayyumic dialect of Coptic
+Added: 2025-07-14
+Prefix: cop
+%%
+Type: variant
Subtag: fodom
Description: Fodom standard of Ladin
Added: 2024-03-04
@@ -48167,6 +48196,12 @@ Comments: Russian orthography as established by the 1917/1918
orthographic reforms
%%
Type: variant
+Subtag: lycopol
+Description: Lycopolitan alias Subakhmimic dialect of Coptic
+Added: 2025-07-14
+Prefix: cop
+%%
+Type: variant
Subtag: mdcegyp
Description: Ancient Egyptian hieroglyphs encoded in Manuel de Codage
Added: 2025-02-06
@@ -48180,6 +48215,12 @@ Added: 2025-02-06
Prefix: egy
%%
Type: variant
+Subtag: mesokem
+Description: Mesokemic alias Oxyrhynchite dialect of Coptic
+Added: 2025-07-14
+Prefix: cop
+%%
+Type: variant
Subtag: metelko
Description: Slovene in Metelko alphabet
Added: 2012-06-27
@@ -48367,6 +48408,12 @@ Prefix: rm
Comments: Supraregional Romansh written standard
%%
Type: variant
+Subtag: sahidic
+Description: Sahidic dialect of Coptic
+Added: 2025-07-14
+Prefix: cop
+%%
+Type: variant
Subtag: saigon
Description: The Sài Gòn variant of Vietnamese
Added: 2025-03-10
@@ -48555,6 +48602,12 @@ Comments: The subtag represents the old orthography of the Latvian
language used during c. 1600s–1920s.
%%
Type: variant
+Subtag: viennese
+Description: The Viennese dialect of German
+Added: 2025-06-22
+Prefix: de
+%%
+Type: variant
Subtag: vivaraup
Description: Vivaro-Alpine
Added: 2018-04-22
diff --git a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c
index 98aa95cb986d..73864a2ed3e9 100644
--- a/src/java.base/share/native/libjli/java.c
+++ b/src/java.base/share/native/libjli/java.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1563,6 +1563,7 @@ InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn)
r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
JLI_MemFree(options);
+ options = NULL;
return r == JNI_OK;
}
@@ -2261,6 +2262,7 @@ FreeKnownVMs()
knownVMs[i].name = NULL;
}
JLI_MemFree(knownVMs);
+ knownVMs = NULL;
}
/*
@@ -2334,8 +2336,9 @@ ShowSplashScreen()
(void)UnsetEnv(SPLASH_JAR_ENV_ENTRY);
JLI_MemFree(splash_jar_entry);
+ splash_jar_entry = NULL;
JLI_MemFree(splash_file_entry);
-
+ splash_file_entry = NULL;
}
static const char* GetFullVersion()
diff --git a/src/java.base/windows/native/libjava/canonicalize_md.c b/src/java.base/windows/native/libjava/canonicalize_md.c
index ecfdf63d0916..7e567c7fbb41 100644
--- a/src/java.base/windows/native/libjava/canonicalize_md.c
+++ b/src/java.base/windows/native/libjava/canonicalize_md.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -41,7 +41,7 @@
/* We should also include jdk_util.h here, for the prototype of JDK_Canonicalize.
This isn't possible though because canonicalize_md.c is as well used in
different contexts within Oracle.
- */
+*/
#include "io_util_md.h"
/* Copy bytes to dst, not going past dend; return dst + number of bytes copied,
@@ -139,7 +139,8 @@ lastErrorReportable()
|| (errval == ERROR_ACCESS_DENIED)
|| (errval == ERROR_NETWORK_UNREACHABLE)
|| (errval == ERROR_NETWORK_ACCESS_DENIED)
- || (errval == ERROR_NO_MORE_FILES)) {
+ || (errval == ERROR_NO_MORE_FILES)
+ || (errval == ERROR_NETNAME_DELETED)) {
return 0;
}
return 1;
@@ -183,7 +184,7 @@ wcanonicalize(WCHAR *orig_path, WCHAR *result, int size)
/* Copy prefix, assuming path is absolute */
c = src[0];
if (((c <= L'z' && c >= L'a') || (c <= L'Z' && c >= L'A'))
- && (src[1] == L':') && (src[2] == L'\\')) {
+ && (src[1] == L':') && (src[2] == L'\\')) {
/* Drive specifier */
*src = towupper(*src); /* Canonicalize drive letter */
if (!(dst = wcp(dst, dend, L'\0', src, src + 2))) {
@@ -244,9 +245,9 @@ wcanonicalize(WCHAR *orig_path, WCHAR *result, int size)
continue;
} else {
if (!lastErrorReportable()) {
- if (!(dst = wcp(dst, dend, L'\0', src, src + wcslen(src)))){
- goto err;
- }
+ if (!(dst = wcp(dst, dend, L'\0', src, src + wcslen(src)))){
+ goto err;
+ }
break;
} else {
goto err;
@@ -255,7 +256,7 @@ wcanonicalize(WCHAR *orig_path, WCHAR *result, int size)
}
if (dst >= dend) {
- errno = ENAMETOOLONG;
+ errno = ENAMETOOLONG;
goto err;
}
*dst = L'\0';
@@ -366,7 +367,7 @@ JDK_Canonicalize(const char *orig, char *out, int len) {
// Change return value to success.
ret = 0;
-finish:
+ finish:
free(wresult);
free(wpath);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java
index 1ca94eb3f51c..ac37526e25a4 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java
@@ -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
@@ -322,20 +322,9 @@ public void print(PrintRequestAttributeSet attributes) throws PrinterException {
validateDestination(destinationAttr);
}
- /* Get the range of pages we are to print. If the
- * last page to print is unknown, then we print to
- * the end of the document. Note that firstPage
- * and lastPage are 0 based page indices.
- */
-
+ // Note that firstPage is 0 based page index.
int firstPage = getFirstPage();
- int lastPage = getLastPage();
- if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES) {
- int totalPages = mDocument.getNumberOfPages();
- if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) {
- lastPage = mDocument.getNumberOfPages() - 1;
- }
- }
+ int totalPages = mDocument.getNumberOfPages();
try {
synchronized (this) {
@@ -360,7 +349,7 @@ public void print(PrintRequestAttributeSet attributes) throws PrinterException {
try {
// Fire off the print rendering loop on the AppKit thread, and don't have
// it wait and block this thread.
- if (printLoop(false, firstPage, lastPage)) {
+ if (printLoop(false, firstPage, totalPages)) {
// Start a secondary loop on EDT until printing operation is finished or cancelled
printingLoop.enter();
}
@@ -374,7 +363,7 @@ public void print(PrintRequestAttributeSet attributes) throws PrinterException {
onEventThread = false;
try {
- printLoop(true, firstPage, lastPage);
+ printLoop(true, firstPage, totalPages);
} catch (Exception e) {
e.printStackTrace();
}
@@ -384,7 +373,6 @@ public void print(PrintRequestAttributeSet attributes) throws PrinterException {
}
if (++loopi < prMembers.length) {
firstPage = prMembers[loopi][0]-1;
- lastPage = prMembers[loopi][1] -1;
}
} while (loopi < prMembers.length);
} finally {
@@ -634,7 +622,7 @@ private long getNSPrintInfo() {
}
}
- private native boolean printLoop(boolean waitUntilDone, int firstPage, int lastPage) throws PrinterException;
+ private native boolean printLoop(boolean waitUntilDone, int firstPage, int totalPages) throws PrinterException;
private PageFormat getPageFormat(int pageIndex) {
// This is called from the native side.
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m
index e1841c9398c6..faacef5adea0 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m
@@ -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
@@ -70,6 +70,7 @@
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout
// Asynchronous call to openURL
+ dispatch_retain(semaphore);
[[NSWorkspace sharedWorkspace] openURLs:urls
withApplicationAtURL:appURI
configuration:configuration
@@ -78,9 +79,11 @@
status = (OSStatus) error.code;
}
dispatch_semaphore_signal(semaphore);
+ dispatch_release(semaphore);
}];
dispatch_semaphore_wait(semaphore, timeout);
+ dispatch_release(semaphore);
JNI_COCOA_EXIT(env);
return status;
@@ -120,6 +123,7 @@
if (appURI == nil
|| [[urlToOpen absoluteString] containsString:[appURI absoluteString]]
|| [[defaultTerminalApp absoluteString] containsString:[appURI absoluteString]]) {
+ [urlToOpen release];
return -1;
}
// Additionally set forPrinting=TRUE for print
@@ -129,6 +133,7 @@
} else if (action == sun_lwawt_macosx_CDesktopPeer_EDIT) {
if (appURI == nil
|| [[urlToOpen absoluteString] containsString:[appURI absoluteString]]) {
+ [urlToOpen release];
return -1;
}
// for EDIT: if (defaultApp = TerminalApp) then set appURI = DefaultTextEditor
@@ -144,6 +149,7 @@
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout
// Asynchronous call - openURLs:withApplicationAtURL
+ dispatch_retain(semaphore);
[[NSWorkspace sharedWorkspace] openURLs:urls
withApplicationAtURL:appURI
configuration:configuration
@@ -152,10 +158,13 @@
status = (OSStatus) error.code;
}
dispatch_semaphore_signal(semaphore);
+ dispatch_release(semaphore);
}];
dispatch_semaphore_wait(semaphore, timeout);
+ dispatch_release(semaphore);
+ [urlToOpen release];
JNI_COCOA_EXIT(env);
return status;
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m
index 9333aa8676bd..92d58f806ae1 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2024, 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
@@ -656,7 +656,7 @@ static void javaPrinterJobToNSPrintInfo(JNIEnv* env, jobject srcPrinterJob, jobj
* Signature: ()V
*/
JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPrinterJob_printLoop
- (JNIEnv *env, jobject jthis, jboolean blocks, jint firstPage, jint lastPage)
+ (JNIEnv *env, jobject jthis, jboolean blocks, jint firstPage, jint totalPages)
{
AWT_ASSERT_NOT_APPKIT_THREAD;
@@ -672,14 +672,14 @@ static void javaPrinterJobToNSPrintInfo(JNIEnv* env, jobject srcPrinterJob, jobj
JNI_COCOA_ENTER(env);
// Get the first page's PageFormat for setting things up (This introduces
// and is a facet of the same problem in Radar 2818593/2708932).
- jobject page = (*env)->CallObjectMethod(env, jthis, jm_getPageFormat, 0); // AWT_THREADING Safe (!appKit)
+ jobject page = (*env)->CallObjectMethod(env, jthis, jm_getPageFormat, firstPage); // AWT_THREADING Safe (!appKit)
CHECK_EXCEPTION();
if (page != NULL) {
jobject pageFormatArea = (*env)->CallObjectMethod(env, jthis, jm_getPageFormatArea, page); // AWT_THREADING Safe (!appKit)
CHECK_EXCEPTION();
PrinterView* printerView = [[PrinterView alloc] initWithFrame:JavaToNSRect(env, pageFormatArea) withEnv:env withPrinterJob:jthis];
- [printerView setFirstPage:firstPage lastPage:lastPage];
+ [printerView setTotalPages:totalPages];
GET_NSPRINTINFO_METHOD_RETURN(NO)
NSPrintInfo* printInfo = (NSPrintInfo*)jlong_to_ptr((*env)->CallLongMethod(env, jthis, sjm_getNSPrintInfo)); // AWT_THREADING Safe (known object)
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h
index 43472bee9208..95a8055cdb0c 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2012, 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
@@ -32,12 +32,12 @@
jobject fCurPainter;
jobject fCurPeekGraphics;
- jint fFirstPage, fLastPage;
+ jint fTotalPages;
}
- (id)initWithFrame:(NSRect)aRect withEnv:(JNIEnv*)env withPrinterJob:(jobject)printerJob;
-- (void)setFirstPage:(jint)firstPage lastPage:(jint)lastPage;
+- (void)setTotalPages:(jint)totalPages;
- (void)releaseReferences:(JNIEnv*)env;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m
index f39ca25a08f7..8a80df6ee0bf 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2024, 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
@@ -72,9 +72,8 @@ - (void)releaseReferences:(JNIEnv*)env
}
}
-- (void)setFirstPage:(jint)firstPage lastPage:(jint)lastPage {
- fFirstPage = firstPage;
- fLastPage = lastPage;
+- (void)setTotalPages:(jint)totalPages {
+ fTotalPages = totalPages;
}
- (void)drawRect:(NSRect)aRect
@@ -139,15 +138,15 @@ - (BOOL)knowsPageRange:(NSRangePointer)aRange
return NO;
}
- aRange->location = fFirstPage + 1;
+ aRange->location = 1;
- if (fLastPage == java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES)
+ if (fTotalPages == java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES)
{
aRange->length = NSIntegerMax;
}
else
{
- aRange->length = (fLastPage + 1) - fFirstPage;
+ aRange->length = fTotalPages;
}
return YES;
diff --git a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp
index 441a71f5c50d..bae16cb0a9c0 100644
--- a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp
+++ b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2020, 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
@@ -162,8 +162,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre
sampleRate, // sample rate
DAUDIO_PCM, // only accept PCM
bits == 8 ? FALSE : TRUE, // signed
- bits == 8 ? FALSE // little-endian for 8bit
- : UTIL_IsBigEndianPlatform());
+ FALSE); // all supported macOS versions run on LE
}
}
// add default format
@@ -175,7 +174,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre
defSampleRate, // sample rate
DAUDIO_PCM, // PCM
TRUE, // signed
- UTIL_IsBigEndianPlatform()); // native endianness
+ FALSE); // native endianness; all supported macOS versions run on LE
}
TRACE0("<
MIT License
-Copyright (C) 1998-2025 Marti Maria Saguer
+Copyright (C) 1998-2026 Marti Maria Saguer
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/libjsound/Utilities.c b/src/java.desktop/share/native/libjsound/Utilities.c
index 6e92813d53cc..50ca18c0259b 100644
--- a/src/java.desktop/share/native/libjsound/Utilities.c
+++ b/src/java.desktop/share/native/libjsound/Utilities.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2007, 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
@@ -26,15 +26,6 @@
#include "Utilities.h"
-
-int UTIL_IsBigEndianPlatform() {
-#ifdef _LITTLE_ENDIAN
- return 0;
-#else
- return 1;
-#endif
-}
-
void ThrowJavaMessageException(JNIEnv *e, const char *exClass, const char *msg) {
jclass newExcCls;
diff --git a/src/java.desktop/share/native/libjsound/Utilities.h b/src/java.desktop/share/native/libjsound/Utilities.h
index fbecab1e005c..7cb0dcbdd4c9 100644
--- a/src/java.desktop/share/native/libjsound/Utilities.h
+++ b/src/java.desktop/share/native/libjsound/Utilities.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2015, 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
@@ -28,10 +28,6 @@
#include "SoundDefs.h"
#include "Configure.h" // put flags for debug msgs etc. here
-// return 1 if this platform is big endian, or 0 for little endian
-int UTIL_IsBigEndianPlatform();
-
-
// ERROR PRINTS
#ifdef USE_ERROR
#define ERROR0(string) { fprintf(stdout, (string)); fflush(stdout); }
diff --git a/src/java.desktop/share/native/liblcms/cmsalpha.c b/src/java.desktop/share/native/liblcms/cmsalpha.c
index 2e50b65be24c..bcedbde938e8 100644
--- a/src/java.desktop/share/native/liblcms/cmsalpha.c
+++ b/src/java.desktop/share/native/liblcms/cmsalpha.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -406,7 +406,7 @@ int FormatterPos(cmsUInt32Number frm)
static
cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out)
{
-static cmsFormatterAlphaFn FormattersAlpha[6][6] = {
+static const cmsFormatterAlphaFn FormattersAlpha[6][6] = {
/* from 8 */ { copy8, from8to16, from8to16SE, from8toHLF, from8toFLT, from8toDBL },
/* from 16*/ { from16to8, copy16, from16to16, from16toHLF, from16toFLT, from16toDBL },
diff --git a/src/java.desktop/share/native/liblcms/cmscam02.c b/src/java.desktop/share/native/liblcms/cmscam02.c
index 45ef4eef970c..168ef597032a 100644
--- a/src/java.desktop/share/native/liblcms/cmscam02.c
+++ b/src/java.desktop/share/native/liblcms/cmscam02.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -285,27 +285,32 @@ CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)
(clr.J / 100.0),
(1.0 / (pMod->c * pMod->z)));
- p1 = e / t;
p2 = (clr.A / pMod->Nbb) + 0.305;
- p3 = 21.0 / 20.0;
- hr = clr.h * d2r;
-
- if (fabs(sin(hr)) >= fabs(cos(hr))) {
- p4 = p1 / sin(hr);
- clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
- (p4 + (2.0 + p3) * (220.0 / 1403.0) *
- (cos(hr) / sin(hr)) - (27.0 / 1403.0) +
- p3 * (6300.0 / 1403.0));
- clr.a = clr.b * (cos(hr) / sin(hr));
+ if ( t <= 0.0 ) { // special case from spec notes, avoid divide by zero
+ clr.a = clr.b = 0.0;
}
else {
- p5 = p1 / cos(hr);
- clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
- (p5 + (2.0 + p3) * (220.0 / 1403.0) -
- ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) *
- (sin(hr) / cos(hr)));
- clr.b = clr.a * (sin(hr) / cos(hr));
+ hr = clr.h * d2r;
+ p1 = e / t;
+ p3 = 21.0 / 20.0;
+
+ if (fabs(sin(hr)) >= fabs(cos(hr))) {
+ p4 = p1 / sin(hr);
+ clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
+ (p4 + (2.0 + p3) * (220.0 / 1403.0) *
+ (cos(hr) / sin(hr)) - (27.0 / 1403.0) +
+ p3 * (6300.0 / 1403.0));
+ clr.a = clr.b * (cos(hr) / sin(hr));
+ }
+ else {
+ p5 = p1 / cos(hr);
+ clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
+ (p5 + (2.0 + p3) * (220.0 / 1403.0) -
+ ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) *
+ (sin(hr) / cos(hr)));
+ clr.b = clr.a * (sin(hr) / cos(hr));
+ }
}
clr.RGBpa[0] = ((460.0 / 1403.0) * p2) +
diff --git a/src/java.desktop/share/native/liblcms/cmscgats.c b/src/java.desktop/share/native/liblcms/cmscgats.c
index 3e62d064c3f6..e8a75c7355fd 100644
--- a/src/java.desktop/share/native/liblcms/cmscgats.c
+++ b/src/java.desktop/share/native/liblcms/cmscgats.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -295,7 +295,7 @@ typedef struct {
WRITEMODE as; // How is supposed to be written
} PROPERTY;
-static PROPERTY PredefinedProperties[] = {
+static const PROPERTY PredefinedProperties[] = {
{"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS
{"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS
@@ -458,7 +458,7 @@ cmsBool StringAppend(string* s, char c)
new_ptr = (char*) AllocChunk(s->it8, s->max);
if (new_ptr == NULL) return FALSE;
- if (new_ptr != NULL && s->begin != NULL)
+ if (s->begin != NULL)
memcpy(new_ptr, s->begin, s->len);
s->begin = new_ptr;
@@ -899,6 +899,11 @@ void InSymbol(cmsIT8* it8)
sign = -1;
NextCh(it8);
}
+ else
+ if (it8->ch == '+') {
+ sign = +1;
+ NextCh(it8);
+ }
it8->inum = 0;
it8->sy = SINUM;
@@ -3206,7 +3211,7 @@ cmsBool ParseCube(cmsIT8* cube, cmsStage** Shaper, cmsStage** CLUT, char title[]
int nodes = lut_size * lut_size * lut_size;
- cmsFloat32Number* lut_table = _cmsMalloc(cube->ContextID, nodes * 3 * sizeof(cmsFloat32Number));
+ cmsFloat32Number* lut_table = (cmsFloat32Number*) _cmsMalloc(cube->ContextID, nodes * 3 * sizeof(cmsFloat32Number));
if (lut_table == NULL) return FALSE;
for (i = 0; i < nodes; i++) {
diff --git a/src/java.desktop/share/native/liblcms/cmscnvrt.c b/src/java.desktop/share/native/liblcms/cmscnvrt.c
index 9f8619cb9dac..c66dbcbebad8 100644
--- a/src/java.desktop/share/native/liblcms/cmscnvrt.c
+++ b/src/java.desktop/share/native/liblcms/cmscnvrt.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmserr.c b/src/java.desktop/share/native/liblcms/cmserr.c
index d421c550d32d..877beb9ca6a3 100644
--- a/src/java.desktop/share/native/liblcms/cmserr.c
+++ b/src/java.desktop/share/native/liblcms/cmserr.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmsgamma.c b/src/java.desktop/share/native/liblcms/cmsgamma.c
index 773858b0c1f0..bace6ab02e2a 100644
--- a/src/java.desktop/share/native/liblcms/cmsgamma.c
+++ b/src/java.desktop/share/native/liblcms/cmsgamma.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -1187,6 +1187,7 @@ cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[]
cmsFloat32Number *c, *d, *e;
cmsBool st;
+ if (m < 4 || lambda < MATRIX_DET_TOLERANCE) return FALSE;
c = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number));
d = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number));
diff --git a/src/java.desktop/share/native/liblcms/cmsgmt.c b/src/java.desktop/share/native/liblcms/cmsgmt.c
index 03ac70202a50..1b023dcc299f 100644
--- a/src/java.desktop/share/native/liblcms/cmsgmt.c
+++ b/src/java.desktop/share/native/liblcms/cmsgmt.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmshalf.c b/src/java.desktop/share/native/liblcms/cmshalf.c
index 7e5f7a3c7e03..e1fb1d554883 100644
--- a/src/java.desktop/share/native/liblcms/cmshalf.c
+++ b/src/java.desktop/share/native/liblcms/cmshalf.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmsintrp.c b/src/java.desktop/share/native/liblcms/cmsintrp.c
index 43c47429c3cd..23e59a229a9f 100644
--- a/src/java.desktop/share/native/liblcms/cmsintrp.c
+++ b/src/java.desktop/share/native/liblcms/cmsintrp.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -984,9 +984,9 @@ void Eval4Inputs(CMSREGISTER const cmsUInt16Number Input[],
c1 = c2 = c3 = 0;
}
- Rest = c1 * rx + c2 * ry + c3 * rz;
+ Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
- Tmp1[OutChan] = (cmsUInt16Number)(c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
+ Tmp1[OutChan] = (cmsUInt16Number)c0 + ((Rest + (Rest >> 16)) >> 16);
}
@@ -1048,9 +1048,9 @@ void Eval4Inputs(CMSREGISTER const cmsUInt16Number Input[],
c1 = c2 = c3 = 0;
}
- Rest = c1 * rx + c2 * ry + c3 * rz;
+ Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
- Tmp2[OutChan] = (cmsUInt16Number) (c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
+ Tmp2[OutChan] = (cmsUInt16Number) c0 + ((Rest + (Rest >> 16)) >> 16);
}
diff --git a/src/java.desktop/share/native/liblcms/cmsio0.c b/src/java.desktop/share/native/liblcms/cmsio0.c
index 5258b7939d2b..5a4f09af5bcf 100644
--- a/src/java.desktop/share/native/liblcms/cmsio0.c
+++ b/src/java.desktop/share/native/liblcms/cmsio0.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -685,6 +685,7 @@ void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i)
// Free previous version
if (Icc ->TagSaveAsRaw[i]) {
_cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);
+ Icc->TagSaveAsRaw[i] = FALSE;
}
else {
cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i];
@@ -1605,6 +1606,8 @@ void freeOneTag(_cmsICCPROFILE* Icc, cmsUInt32Number i)
else
_cmsFree(Icc->ContextID, Icc->TagPtrs[i]);
}
+
+ Icc->TagPtrs[i] = NULL;
}
// Closes a profile freeing any involved resources
@@ -1847,8 +1850,11 @@ cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const v
if (!_cmsNewTag(Icc, sig, &i)) goto Error;
- // This is not raw
- Icc ->TagSaveAsRaw[i] = FALSE;
+ // This cannot be RAW
+ if (Icc->TagSaveAsRaw[i]) {
+ cmsSignalError(Icc->ContextID, cmsERROR_ALREADY_DEFINED, "Tag '%x' was already saved as RAW", sig);
+ goto Error;
+ }
// This is not a link
Icc ->TagLinked[i] = (cmsTagSignature) 0;
diff --git a/src/java.desktop/share/native/liblcms/cmsio1.c b/src/java.desktop/share/native/liblcms/cmsio1.c
index 48772c7cbde9..463f1192c2a4 100644
--- a/src/java.desktop/share/native/liblcms/cmsio1.c
+++ b/src/java.desktop/share/native/liblcms/cmsio1.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -1012,7 +1012,13 @@ const cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info)
switch (Info) {
case cmsInfoDescription:
- sig = cmsSigProfileDescriptionTag;
+ /**
+ * Add for MacOS, which uses propiertary tags for description
+ */
+ if (cmsIsTag(hProfile, cmsSigProfileDescriptionMLTag))
+ sig = cmsSigProfileDescriptionMLTag;
+ else
+ sig = cmsSigProfileDescriptionTag;
break;
case cmsInfoManufacturer:
diff --git a/src/java.desktop/share/native/liblcms/cmslut.c b/src/java.desktop/share/native/liblcms/cmslut.c
index 3cf4e8cac5a6..28220eae6673 100644
--- a/src/java.desktop/share/native/liblcms/cmslut.c
+++ b/src/java.desktop/share/native/liblcms/cmslut.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmsmd5.c b/src/java.desktop/share/native/liblcms/cmsmd5.c
index d9b9a4e52608..f18300ebace2 100644
--- a/src/java.desktop/share/native/liblcms/cmsmd5.c
+++ b/src/java.desktop/share/native/liblcms/cmsmd5.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmsmtrx.c b/src/java.desktop/share/native/liblcms/cmsmtrx.c
index 841da662a107..1db000752e3a 100644
--- a/src/java.desktop/share/native/liblcms/cmsmtrx.c
+++ b/src/java.desktop/share/native/liblcms/cmsmtrx.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmsnamed.c b/src/java.desktop/share/native/liblcms/cmsnamed.c
index 451bfe9f34d5..acdaabc3ec26 100644
--- a/src/java.desktop/share/native/liblcms/cmsnamed.c
+++ b/src/java.desktop/share/native/liblcms/cmsnamed.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -303,7 +303,7 @@ cmsUInt32Number encodeUTF8(char* out, const wchar_t* in, cmsUInt32Number max_wch
cmsUInt32Number size = 0;
cmsUInt32Number len_w = 0;
- while (*in && len_w < max_wchars)
+ while (len_w < max_wchars && *in)
{
if (*in >= 0xd800 && *in <= 0xdbff)
codepoint = ((*in - 0xd800) << 10) + 0x10000;
@@ -1071,17 +1071,17 @@ cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq)
if (pseq == NULL)
return NULL;
- NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ));
+ NewSeq = (cmsSEQ*)_cmsMallocZero(pseq->ContextID, sizeof(cmsSEQ));
if (NewSeq == NULL) return NULL;
+ NewSeq->ContextID = pseq->ContextID;
- NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC));
- if (NewSeq ->seq == NULL) goto Error;
+ NewSeq->seq = (cmsPSEQDESC*)_cmsCalloc(pseq->ContextID, pseq->n, sizeof(cmsPSEQDESC));
+ if (NewSeq->seq == NULL) goto Error;
- NewSeq -> ContextID = pseq ->ContextID;
- NewSeq -> n = pseq ->n;
+ NewSeq->n = pseq->n;
- for (i=0; i < pseq->n; i++) {
+ for (i = 0; i < pseq->n; i++) {
memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number));
diff --git a/src/java.desktop/share/native/liblcms/cmsopt.c b/src/java.desktop/share/native/liblcms/cmsopt.c
index 767008e68c58..9e71426a3327 100644
--- a/src/java.desktop/share/native/liblcms/cmsopt.c
+++ b/src/java.desktop/share/native/liblcms/cmsopt.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -698,11 +698,16 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
if (ColorSpace == (cmsColorSpaceSignature)0 ||
OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE;
- nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
-
// For empty LUTs, 2 points are enough
if (cmsPipelineStageCount(*Lut) == 0)
nGridPoints = 2;
+ else
+ {
+ nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
+
+ // Lab16 as input cannot be optimized by a CLUT due to centering issues, thanks to Mike Chaney for discovering this.
+ if (!(*dwFlags & cmsFLAGS_FORCE_CLUT) && (ColorSpace == cmsSigLabData) && (T_BYTES(*InputFormat) == 2)) return FALSE;
+ }
Src = *Lut;
@@ -813,6 +818,11 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
Dest ->OutputChannels,
DataSetOut);
+ if (p16 == NULL) {
+ cmsPipelineFree(Dest);
+ return FALSE;
+ }
+
_cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup);
}
diff --git a/src/java.desktop/share/native/liblcms/cmspack.c b/src/java.desktop/share/native/liblcms/cmspack.c
index d430e73051de..b740567af3b6 100644
--- a/src/java.desktop/share/native/liblcms/cmspack.c
+++ b/src/java.desktop/share/native/liblcms/cmspack.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmspcs.c b/src/java.desktop/share/native/liblcms/cmspcs.c
index 5f1b1f0d8e6d..8c33057721e9 100644
--- a/src/java.desktop/share/native/liblcms/cmspcs.c
+++ b/src/java.desktop/share/native/liblcms/cmspcs.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmsplugin.c b/src/java.desktop/share/native/liblcms/cmsplugin.c
index aaad39f52b04..a943c9f4dd96 100644
--- a/src/java.desktop/share/native/liblcms/cmsplugin.c
+++ b/src/java.desktop/share/native/liblcms/cmsplugin.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -522,7 +522,7 @@ cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
va_start(args, frm);
len = vsnprintf((char*) Buffer, 2047, frm, args);
- if (len < 0) {
+ if (len < 0 || len >= 2047) {
va_end(args);
return FALSE; // Truncated, which is a fatal error for us
}
diff --git a/src/java.desktop/share/native/liblcms/cmsps2.c b/src/java.desktop/share/native/liblcms/cmsps2.c
index 476817e9c1a2..80f7c8084ae7 100644
--- a/src/java.desktop/share/native/liblcms/cmsps2.c
+++ b/src/java.desktop/share/native/liblcms/cmsps2.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmssamp.c b/src/java.desktop/share/native/liblcms/cmssamp.c
index ca5c4a9d6931..c54a0d4ea723 100644
--- a/src/java.desktop/share/native/liblcms/cmssamp.c
+++ b/src/java.desktop/share/native/liblcms/cmssamp.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -152,9 +152,12 @@ cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput,
// Convert black to Lab
cmsDoTransform(xform, Black, &Lab, 1);
- // Force it to be neutral, check for inconsistencies
- Lab.a = Lab.b = 0;
- if (Lab.L > 50 || Lab.L < 0) Lab.L = 0;
+ if (Lab.L > 95)
+ Lab.L = 0; // for synthetical negative profiles
+ else if (Lab.L < 0)
+ Lab.L = 0;
+ else if (Lab.L > 50)
+ Lab.L = 50;
// Free the resources
cmsDeleteTransform(xform);
@@ -352,7 +355,7 @@ cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[]
if (fabs(a) < 1.0E-10) {
if (fabs(b) < 1.0E-10) return 0;
- return cmsmin(0, cmsmax(50, -c/b ));
+ return cmsmax(0, cmsmin(50, -c/b ));
}
else {
diff --git a/src/java.desktop/share/native/liblcms/cmssm.c b/src/java.desktop/share/native/liblcms/cmssm.c
index e2a810a26695..b79cd85488bf 100644
--- a/src/java.desktop/share/native/liblcms/cmssm.c
+++ b/src/java.desktop/share/native/liblcms/cmssm.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmstypes.c b/src/java.desktop/share/native/liblcms/cmstypes.c
index 22514f882268..eab74940cd09 100644
--- a/src/java.desktop/share/native/liblcms/cmstypes.c
+++ b/src/java.desktop/share/native/liblcms/cmstypes.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -4786,7 +4786,6 @@ cmsBool ReadMPEElem(struct _cms_typehandler_struct* self,
return TRUE;
- cmsUNUSED_PARAMETER(SizeOfTag);
cmsUNUSED_PARAMETER(n);
}
@@ -4894,9 +4893,11 @@ cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, v
goto Error;
}
+ Before = io ->Tell(io);
+
if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error;
if (!_cmsWriteUInt32Number(io, 0)) goto Error;
- Before = io ->Tell(io);
+
if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error;
if (!_cmsWriteAlignment(io)) goto Error;
@@ -5645,9 +5646,7 @@ void* Type_VideoSignal_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER*
{
cmsVideoSignalType* cicp = NULL;
- if (SizeOfTag != 8) return NULL;
-
- if (!_cmsReadUInt32Number(io, NULL)) return NULL;
+ if (SizeOfTag != 4) return NULL;
cicp = (cmsVideoSignalType*)_cmsCalloc(self->ContextID, 1, sizeof(cmsVideoSignalType));
if (cicp == NULL) return NULL;
@@ -5671,7 +5670,6 @@ cmsBool Type_VideoSignal_Write(struct _cms_typehandler_struct* self, cmsIOHANDLE
{
cmsVideoSignalType* cicp = (cmsVideoSignalType*)Ptr;
- if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
if (!_cmsWriteUInt8Number(io, cicp->ColourPrimaries)) return FALSE;
if (!_cmsWriteUInt8Number(io, cicp->TransferCharacteristics)) return FALSE;
if (!_cmsWriteUInt8Number(io, cicp->MatrixCoefficients)) return FALSE;
@@ -5744,11 +5742,11 @@ void Type_MHC2_Free(struct _cms_typehandler_struct* self, void* Ptr)
void* Type_MHC2_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
{
- cmsMHC2Type* mhc2 = _cmsDupMem(self->ContextID, Ptr, sizeof(cmsMHC2Type));
+ cmsMHC2Type* mhc2 = (cmsMHC2Type*)_cmsDupMem(self->ContextID, Ptr, sizeof(cmsMHC2Type));
- mhc2->RedCurve = _cmsDupMem(self->ContextID, mhc2->RedCurve, mhc2->CurveEntries*sizeof(cmsFloat64Number));
- mhc2->GreenCurve = _cmsDupMem(self->ContextID, mhc2->GreenCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
- mhc2->BlueCurve = _cmsDupMem(self->ContextID, mhc2->BlueCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
+ mhc2->RedCurve = (cmsFloat64Number*) _cmsDupMem(self->ContextID, mhc2->RedCurve, mhc2->CurveEntries*sizeof(cmsFloat64Number));
+ mhc2->GreenCurve = (cmsFloat64Number*) _cmsDupMem(self->ContextID, mhc2->GreenCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
+ mhc2->BlueCurve = (cmsFloat64Number*) _cmsDupMem(self->ContextID, mhc2->BlueCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
if (mhc2->RedCurve == NULL ||
mhc2->GreenCurve == NULL ||
@@ -5786,7 +5784,6 @@ cmsBool Type_MHC2_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
cmsUInt32Number MatrixOffset;
cmsUInt32Number OffsetRedTable, OffsetGreenTable, OffsetBlueTable;
- if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
if (!_cmsWriteUInt32Number(io, mhc2->CurveEntries)) return FALSE;
if (!_cmsWrite15Fixed16Number(io, mhc2->MinLuminance)) return FALSE;
@@ -5811,10 +5808,20 @@ cmsBool Type_MHC2_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
}
OffsetRedTable = io->Tell(io) - BaseOffset;
+
+ if(!_cmsWriteUInt32Number(io, cmsSigS15Fixed16ArrayType)) return FALSE;
+ if(!_cmsWriteUInt32Number(io, 0)) return FALSE;
+
if (!WriteDoubles(io, mhc2->CurveEntries, mhc2->RedCurve)) return FALSE;
+
OffsetGreenTable = io->Tell(io) - BaseOffset;
+ if (!_cmsWriteUInt32Number(io, cmsSigS15Fixed16ArrayType)) return FALSE;
+ if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
if (!WriteDoubles(io, mhc2->CurveEntries, mhc2->GreenCurve)) return FALSE;
+
OffsetBlueTable = io->Tell(io) - BaseOffset;
+ if (!_cmsWriteUInt32Number(io, cmsSigS15Fixed16ArrayType)) return FALSE;
+ if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
if (!WriteDoubles(io, mhc2->CurveEntries, mhc2->BlueCurve)) return FALSE;
if (!io->Seek(io, TablesOffsetPos)) return FALSE;
@@ -5858,8 +5865,6 @@ void* Type_MHC2_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cms
cmsUInt32Number MatrixOffset;
cmsUInt32Number OffsetRedTable, OffsetGreenTable, OffsetBlueTable;
- if (!_cmsReadUInt32Number(io, NULL)) return NULL;
-
mhc2 = (cmsMHC2Type*)_cmsCalloc(self->ContextID, 1, sizeof(cmsMHC2Type));
if (mhc2 == NULL) return NULL;
@@ -5890,9 +5895,10 @@ void* Type_MHC2_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cms
if (!ReadDoublesAt(io, BaseOffset + MatrixOffset, 3*4, &mhc2->XYZ2XYZmatrix[0][0])) goto Error;
}
- if (!ReadDoublesAt(io, BaseOffset + OffsetRedTable, mhc2->CurveEntries, mhc2->RedCurve)) goto Error;
- if (!ReadDoublesAt(io, BaseOffset + OffsetGreenTable, mhc2->CurveEntries, mhc2->GreenCurve)) goto Error;
- if (!ReadDoublesAt(io, BaseOffset + OffsetBlueTable, mhc2->CurveEntries, mhc2->BlueCurve)) goto Error;
+ // Skip sf32 tag and filler (8bytes)
+ if (!ReadDoublesAt(io, BaseOffset + OffsetRedTable + 8, mhc2->CurveEntries, mhc2->RedCurve)) goto Error;
+ if (!ReadDoublesAt(io, BaseOffset + OffsetGreenTable + 8, mhc2->CurveEntries, mhc2->GreenCurve)) goto Error;
+ if (!ReadDoublesAt(io, BaseOffset + OffsetBlueTable + 8, mhc2->CurveEntries, mhc2->BlueCurve)) goto Error;
// Success
*nItems = 1;
diff --git a/src/java.desktop/share/native/liblcms/cmsvirt.c b/src/java.desktop/share/native/liblcms/cmsvirt.c
index 1ef86dae0544..0dfc6e947a55 100644
--- a/src/java.desktop/share/native/liblcms/cmsvirt.c
+++ b/src/java.desktop/share/native/liblcms/cmsvirt.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -400,7 +400,7 @@ int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUI
SumCMY = (cmsFloat64Number) In[0] + In[1] + In[2];
SumCMYK = SumCMY + In[3];
- if (SumCMYK > InkLimit) {
+ if (SumCMYK > InkLimit && SumCMY > 0) {
Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
if (Ratio < 0)
@@ -513,16 +513,20 @@ cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIE
cmsSetColorSpace(hProfile, cmsSigLabData);
cmsSetPCS(hProfile, cmsSigLabData);
- if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL;
+ if (!SetTextTags(hProfile, L"Lab identity built-in"))
+ goto Error;
// An identity LUT is all we need
LUT = cmsPipelineAlloc(ContextID, 3, 3);
- if (LUT == NULL) goto Error;
+ if (LUT == NULL)
+ goto Error;
if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
goto Error;
- if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
+ if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT))
+ goto Error;
+
cmsPipelineFree(LUT);
return hProfile;
@@ -550,8 +554,14 @@ cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIE
{
cmsHPROFILE hProfile;
cmsPipeline* LUT = NULL;
+ cmsCIEXYZ xyz;
- hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
+ if (WhitePoint == NULL)
+ xyz = *cmsD50_XYZ();
+ else
+ cmsxyY2XYZ(&xyz, WhitePoint);
+
+ hProfile = cmsCreateRGBProfileTHR(ContextID, NULL, NULL, NULL);
if (hProfile == NULL) return NULL;
cmsSetProfileVersion(hProfile, 4.4);
@@ -560,6 +570,7 @@ cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIE
cmsSetColorSpace(hProfile, cmsSigLabData);
cmsSetPCS(hProfile, cmsSigLabData);
+ if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xyz)) goto Error;
if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error;
// An empty LUTs is all we need
@@ -929,25 +940,24 @@ cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
- if (CLUT == NULL) goto Error;
-
-
- if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
+ if (CLUT == NULL)
+ goto Error;
- // Shouldn't reach here
+ if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0))
goto Error;
- }
- if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
+ if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT))
goto Error;
- }
// Create tags
- if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
+ if (!SetTextTags(hICC, L"BCHS built-in"))
+ goto Error;
- cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
+ if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*)cmsD50_XYZ()))
+ goto Error;
- cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
+ if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*)Pipeline))
+ goto Error;
// Pipeline is already on virtual profile
cmsPipelineFree(Pipeline);
diff --git a/src/java.desktop/share/native/liblcms/cmswtpnt.c b/src/java.desktop/share/native/liblcms/cmswtpnt.c
index ebba2cd6a978..f6337765c0c7 100644
--- a/src/java.desktop/share/native/liblcms/cmswtpnt.c
+++ b/src/java.desktop/share/native/liblcms/cmswtpnt.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/cmsxform.c b/src/java.desktop/share/native/liblcms/cmsxform.c
index 1eb3eecbf182..b5dd302b973a 100644
--- a/src/java.desktop/share/native/liblcms/cmsxform.c
+++ b/src/java.desktop/share/native/liblcms/cmsxform.c
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -1101,6 +1101,8 @@ cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwForm
int Space1 = (int) T_COLORSPACE(dwFormat);
int Space2 = _cmsLCMScolorSpace(Check);
+ if (dwFormat == 0) return TRUE; // Bypass used by linkicc
+
if (Space1 == PT_ANY) return (T_CHANNELS(dwFormat) == cmsChannelsOf(Check));
if (Space1 == Space2) return TRUE;
@@ -1183,6 +1185,11 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
}
+ if ((dwFlags & cmsFLAGS_GAMUTCHECK) && (nGamutPCSposition <= 0 || nGamutPCSposition >= nProfiles - 1)) {
+ cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong gamut PCS position '%d'", nGamutPCSposition);
+ return NULL;
+ }
+
// On floating point transforms, inhibit cache
if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
dwFlags |= cmsFLAGS_NOCACHE;
diff --git a/src/java.desktop/share/native/liblcms/lcms2.h b/src/java.desktop/share/native/liblcms/lcms2.h
index 5ba096613088..17a523847211 100644
--- a/src/java.desktop/share/native/liblcms/lcms2.h
+++ b/src/java.desktop/share/native/liblcms/lcms2.h
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2025 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
@@ -52,7 +52,7 @@
//
//---------------------------------------------------------------------------------
//
-// Version 2.17
+// Version 2.18
//
#ifndef _lcms2_H
@@ -116,7 +116,7 @@ extern "C" {
#endif
// Version/release
-#define LCMS_VERSION 2170
+#define LCMS_VERSION 2180
// I will give the chance of redefining basic types for compilers that are not fully C99 compliant
#ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
diff --git a/src/java.desktop/share/native/liblcms/lcms2_internal.h b/src/java.desktop/share/native/liblcms/lcms2_internal.h
index d14c0dd823ea..6bfe67e53501 100644
--- a/src/java.desktop/share/native/liblcms/lcms2_internal.h
+++ b/src/java.desktop/share/native/liblcms/lcms2_internal.h
@@ -30,7 +30,7 @@
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/share/native/liblcms/lcms2_plugin.h b/src/java.desktop/share/native/liblcms/lcms2_plugin.h
index bdfc76f6bf5d..85de9bc56d5f 100644
--- a/src/java.desktop/share/native/liblcms/lcms2_plugin.h
+++ b/src/java.desktop/share/native/liblcms/lcms2_plugin.h
@@ -30,7 +30,7 @@
//---------------------------------------------------------------------------------
//
// Little Color Management System
-// Copyright (c) 1998-2024 Marti Maria Saguer
+// Copyright (c) 1998-2026 Marti Maria Saguer
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
diff --git a/src/java.desktop/unix/native/common/awt/utility/rect.h b/src/java.desktop/unix/native/common/awt/utility/rect.h
index ceea38f4349a..91b5a17ec58c 100644
--- a/src/java.desktop/unix/native/common/awt/utility/rect.h
+++ b/src/java.desktop/unix/native/common/awt/utility/rect.h
@@ -28,7 +28,7 @@
#ifndef _AWT_RECT_H
#define _AWT_RECT_H
-#ifndef MACOSX
+#if !defined(HEADLESS) && !defined(MACOSX)
#include
typedef XRectangle RECT_T;
#else
@@ -39,7 +39,7 @@ typedef struct {
int width;
int height;
} RECT_T;
-#endif /* !MACOSX */
+#endif /* !HEADLESS && !MACOSX */
#define RECT_EQ_X(r1,r2) ((r1).x==(r2).x && (r1).width==(r2).width)
diff --git a/src/java.desktop/unix/native/common/java2d/opengl/GLXSurfaceData.c b/src/java.desktop/unix/native/common/java2d/opengl/GLXSurfaceData.c
index c48b38fa1f3e..2ed575c10c7c 100644
--- a/src/java.desktop/unix/native/common/java2d/opengl/GLXSurfaceData.c
+++ b/src/java.desktop/unix/native/common/java2d/opengl/GLXSurfaceData.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2019, 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
@@ -40,6 +40,8 @@
#ifndef HEADLESS
+#include
+
extern LockFunc OGLSD_Lock;
extern GetRasInfoFunc OGLSD_GetRasInfo;
extern UnlockFunc OGLSD_Unlock;
@@ -50,6 +52,74 @@ extern void
jboolean surfaceCreationFailed = JNI_FALSE;
+/**
+ * Per-Window GLXWindow entry with reference counting.
+ * Stored in an XContext keyed by the X Window XID.
+ */
+typedef struct {
+ GLXWindow glxWindow;
+ int refCount;
+} GLXWindowRef;
+
+static XContext glxWindowContext;
+
+/**
+ * Gets or creates a shared GLXWindow for the given X Window.
+ * All callers are synchronized by the AWT lock.
+ */
+static GLXWindow acquireGLXWindow(Window window, GLXFBConfig fbconfig)
+{
+ if (glxWindowContext == 0) {
+ glxWindowContext = XUniqueContext();
+ }
+
+ XPointer data;
+ if (XFindContext(awt_display, window, glxWindowContext, &data) == 0) {
+ GLXWindowRef *ref = (GLXWindowRef *)data;
+ ref->refCount++;
+ return ref->glxWindow;
+ }
+
+ GLXWindow glxWin = j2d_glXCreateWindow(awt_display, fbconfig, window, NULL);
+ if (glxWin == 0) {
+ return 0;
+ }
+
+ GLXWindowRef *ref = malloc(sizeof(*ref));
+ if (ref == NULL) {
+ j2d_glXDestroyWindow(awt_display, glxWin);
+ return 0;
+ }
+ ref->glxWindow = glxWin;
+ ref->refCount = 1;
+ if (XSaveContext(awt_display, window, glxWindowContext, (XPointer)ref) != 0)
+ {
+ j2d_glXDestroyWindow(awt_display, glxWin);
+ free(ref);
+ return 0;
+ }
+ return glxWin;
+}
+
+/**
+ * Decrements the reference count for the GLXWindow associated with the given
+ * X Window. Destroys it when the count reaches zero.
+ * All callers are synchronized by the AWT lock.
+ */
+static void releaseGLXWindow(Window window)
+{
+ XPointer data;
+ if (XFindContext(awt_display, window, glxWindowContext, &data) != 0) {
+ return;
+ }
+ GLXWindowRef *ref = (GLXWindowRef *)data;
+ if (--ref->refCount <= 0) {
+ j2d_glXDestroyWindow(awt_display, ref->glxWindow);
+ XDeleteContext(awt_display, window, glxWindowContext);
+ free(ref);
+ }
+}
+
#endif /* !HEADLESS */
JNIEXPORT void JNICALL
@@ -74,7 +144,7 @@ Java_sun_java2d_opengl_GLXSurfaceData_initOps(JNIEnv *env, jobject glxsd,
// later the graphicsConfig will be used for deallocation of oglsdo
oglsdo->graphicsConfig = gc;
- GLXSDOps *glxsdo = (GLXSDOps *)malloc(sizeof(GLXSDOps));
+ GLXSDOps *glxsdo = (GLXSDOps *)calloc(1, sizeof(GLXSDOps));
if (glxsdo == NULL) {
JNU_ThrowOutOfMemoryError(env, "creating native GLX ops");
@@ -125,8 +195,13 @@ Java_sun_java2d_opengl_GLXSurfaceData_initOps(JNIEnv *env, jobject glxsd,
void
OGLSD_DestroyOGLSurface(JNIEnv *env, OGLSDOps *oglsdo)
{
+ GLXSDOps *glxsdo = (GLXSDOps *)oglsdo->privOps;
J2dTraceLn(J2D_TRACE_INFO, "OGLSD_DestroyOGLSurface");
- // X Window is free'd later by AWT code...
+ if (glxsdo != NULL && glxsdo->drawable != 0) {
+ releaseGLXWindow(glxsdo->window);
+ glxsdo->drawable = 0;
+ oglsdo->drawableType = OGLSD_UNDEFINED;
+ }
}
/**
@@ -296,6 +371,13 @@ OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo)
return JNI_FALSE;
}
+ glxsdo->drawable = acquireGLXWindow(window,
+ glxsdo->configData->glxInfo->fbconfig);
+ if (glxsdo->drawable == 0) {
+ J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: GLXWindow is 0");
+ return JNI_FALSE;
+ }
+
XGetWindowAttributes(awt_display, window, &attr);
oglsdo->width = attr.width;
oglsdo->height = attr.height;
@@ -304,7 +386,6 @@ OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo)
oglsdo->isOpaque = JNI_TRUE;
oglsdo->xOffset = 0;
oglsdo->yOffset = 0;
- glxsdo->drawable = window;
glxsdo->xdrawable = window;
J2dTraceLn2(J2D_TRACE_VERBOSE, " created window: w=%d h=%d",
@@ -333,7 +414,16 @@ OGLSD_SwapBuffers(JNIEnv *env, jlong window)
return;
}
- j2d_glXSwapBuffers(awt_display, (Window)window);
+ XPointer data;
+ if (XFindContext(awt_display, (Window)window, glxWindowContext, &data) != 0)
+ {
+ J2dRlsTraceLn(J2D_TRACE_ERROR,
+ "OGLSD_SwapBuffers: GLXWindow not found");
+ return;
+ }
+
+ GLXWindowRef *ref = (GLXWindowRef *)data;
+ j2d_glXSwapBuffers(awt_display, ref->glxWindow);
}
// needed by Mac OS X port, no-op on other platforms
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java
index 31a490bebccc..572e9e8c1178 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2014, 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
@@ -115,7 +115,7 @@ public static Border getInternalFrameBorder() {
}
@SuppressWarnings("serial") // Superclass is not serializable across versions
- public static final class ProgressBarBorder extends AbstractBorder implements UIResource {
+ public static class ProgressBarBorder extends AbstractBorder implements UIResource {
protected Color shadow;
protected Color highlight;
@@ -148,7 +148,7 @@ public Insets getBorderInsets(Component c, Insets insets) {
* @since 1.4
*/
@SuppressWarnings("serial") // Superclass is not serializable across versions
- public static final class ToolBarBorder extends AbstractBorder implements UIResource, SwingConstants {
+ public static class ToolBarBorder extends AbstractBorder implements UIResource, SwingConstants {
protected Color shadow;
protected Color highlight;
@@ -308,7 +308,7 @@ public void paintBorder(Component c, Graphics g, int x, int y, int width, int he
* @since 1.4
*/
@SuppressWarnings("serial") // Superclass is not serializable across versions
- public static final class InternalFrameLineBorder extends LineBorder implements
+ public static class InternalFrameLineBorder extends LineBorder implements
UIResource {
protected Color activeColor;
protected Color inactiveColor;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java
index 33dea2b3b082..6f280ba8b674 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java
@@ -58,7 +58,7 @@
*
* @author Jeff Dinkins
*/
-public final class WindowsButtonUI extends BasicButtonUI
+public class WindowsButtonUI extends BasicButtonUI
{
protected int dashedRectGapX;
protected int dashedRectGapY;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java
index 02054575d77d..0e6e6b8c3d3b 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.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
@@ -42,7 +42,7 @@
/**
* Windows check box menu item.
*/
-public final class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI {
+public class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI {
final WindowsMenuItemUIAccessor accessor =
new WindowsMenuItemUIAccessor() {
@@ -75,19 +75,20 @@ protected void paintBackground(Graphics g, JMenuItem menuItem,
super.paintBackground(g, menuItem, bgColor);
}
- /**
- * Paint MenuItem.
- */
+ @Override
protected void paintMenuItem(Graphics g, JComponent c,
Icon checkIcon, Icon arrowIcon,
Color background, Color foreground,
int defaultTextIconGap) {
if (WindowsMenuItemUI.isVistaPainting()) {
- WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon,
- arrowIcon, background, foreground,
- disabledForeground, acceleratorSelectionForeground,
- acceleratorForeground, defaultTextIconGap,
- menuItem, getPropertyPrefix());
+ WindowsMenuItemUI.paintMenuItem(accessor, g, c,
+ checkIcon, arrowIcon,
+ background, foreground,
+ disabledForeground,
+ acceleratorSelectionForeground,
+ acceleratorForeground,
+ defaultTextIconGap,
+ menuItem, getPropertyPrefix());
return;
}
super.paintMenuItem(g, c, checkIcon, arrowIcon, background,
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java
index 3ead1228b0e6..4fec3b16081a 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java
@@ -37,7 +37,7 @@
*
* @author Jeff Dinkins
*/
-public final class WindowsCheckBoxUI extends WindowsRadioButtonUI
+public class WindowsCheckBoxUI extends WindowsRadioButtonUI
{
// NOTE: WindowsCheckBoxUI inherits from WindowsRadioButtonUI instead
// of BasicCheckBoxUI because we want to pick up all the
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java
index 59eace01a4c8..802b5f668885 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2020, 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
@@ -31,7 +31,7 @@
* @since 1.5
*/
@SuppressWarnings("serial") // Superclass is not serializable across versions
-public final class WindowsClassicLookAndFeel extends WindowsLookAndFeel {
+public class WindowsClassicLookAndFeel extends WindowsLookAndFeel {
@Override
public String getName() {
return "Windows Classic";
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java
index f37ce17d8763..8717fd715ea6 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -75,7 +75,7 @@
* @author Tom Santos
* @author Igor Kushnirskiy
*/
-public final class WindowsComboBoxUI extends BasicComboBoxUI {
+public class WindowsComboBoxUI extends BasicComboBoxUI {
private static final MouseListener rolloverListener =
new MouseAdapter() {
@@ -532,7 +532,7 @@ WindowsComboBoxUI getWindowsComboBoxUI() {
}
@SuppressWarnings("serial") // Same-version serialization only
- protected final class WinComboPopUp extends BasicComboPopup {
+ protected class WinComboPopUp extends BasicComboPopup {
private Skin listBoxBorder = null;
private XPStyle xp;
@@ -550,7 +550,7 @@ protected KeyListener createKeyListener() {
return new InvocationKeyHandler();
}
- protected final class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler {
+ protected class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler {
protected InvocationKeyHandler() {
WinComboPopUp.this.super();
}
@@ -570,7 +570,7 @@ protected void paintComponent(Graphics g) {
/**
* Subclassed to highlight selected item in an editable combo box.
*/
- public static final class WindowsComboBoxEditor
+ public static class WindowsComboBoxEditor
extends BasicComboBoxEditor.UIResource {
/**
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java
index 8da4bd7921b8..2cebb050396b 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -36,7 +36,7 @@
/**
* Windows icon for a minimized window on the desktop.
*/
-public final class WindowsDesktopIconUI extends BasicDesktopIconUI {
+public class WindowsDesktopIconUI extends BasicDesktopIconUI {
private int width;
public static ComponentUI createUI(JComponent c) {
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java
index 355f70b46071..b12b95b52a51 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2014, 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
@@ -52,7 +52,7 @@
* @author Thomas Ball
*/
@SuppressWarnings("serial") // JDK-implementation class
-public final class WindowsDesktopManager extends DefaultDesktopManager
+public class WindowsDesktopManager extends DefaultDesktopManager
implements java.io.Serializable, javax.swing.plaf.UIResource {
/* The frame which is currently selected/activated.
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java
index 49ab809dddd1..4a3f0ec38b1d 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -34,7 +34,7 @@
*
* @author David Kloba
*/
-public final class WindowsDesktopPaneUI extends BasicDesktopPaneUI
+public class WindowsDesktopPaneUI extends BasicDesktopPaneUI
{
public static ComponentUI createUI(JComponent c) {
return new WindowsDesktopPaneUI();
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java
index abccb6b9a481..ea21b41c6199 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -33,7 +33,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsEditorPaneUI extends BasicEditorPaneUI
+public class WindowsEditorPaneUI extends BasicEditorPaneUI
{
/**
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java
index 08c01760be9f..86c40ea70d63 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.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
@@ -101,7 +101,7 @@
*
* @author Jeff Dinkins
*/
-public final class WindowsFileChooserUI extends BasicFileChooserUI {
+public class WindowsFileChooserUI extends BasicFileChooserUI {
// The following are private because the implementation of the
// Windows FileChooser L&F is not complete yet.
@@ -1122,7 +1122,7 @@ protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) {
* Data model for a type-face selection combo-box.
*/
@SuppressWarnings("serial") // Superclass is not serializable across versions
- protected final class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
+ protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
Vector directories = new Vector();
int[] depths = null;
File selectedDirectory = null;
@@ -1252,7 +1252,7 @@ protected FilterComboBoxRenderer createFilterComboBoxRenderer() {
* Render different type sizes and styles.
*/
@SuppressWarnings("serial") // Superclass is not serializable across versions
- public final class FilterComboBoxRenderer extends DefaultListCellRenderer {
+ public class FilterComboBoxRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(JList> list,
Object value, int index, boolean isSelected,
@@ -1279,7 +1279,7 @@ protected FilterComboBoxModel createFilterComboBoxModel() {
* Data model for a type-face selection combo-box.
*/
@SuppressWarnings("serial") // Superclass is not serializable across versions
- protected final class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel,
+ protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel,
PropertyChangeListener {
protected FileFilter[] filters;
protected FilterComboBoxModel() {
@@ -1362,7 +1362,7 @@ public void valueChanged(ListSelectionEvent e) {
/**
* Acts when DirectoryComboBox has changed the selected item.
*/
- protected final class DirectoryComboBoxAction implements ActionListener {
+ protected class DirectoryComboBoxAction implements ActionListener {
@@ -1387,7 +1387,7 @@ public FileView getFileView(JFileChooser fc) {
// ***********************
// * FileView operations *
// ***********************
- protected final class WindowsFileView extends BasicFileView {
+ protected class WindowsFileView extends BasicFileView {
/* FileView type descriptions */
@Override
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java
index 915a361a3a1d..91c2cbcd61d9 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java
@@ -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
@@ -905,10 +905,46 @@ public void paintIcon(Component c, Graphics g, int x, int y) {
XPStyle xp = XPStyle.getXP();
if (xp != null) {
Skin skin = xp.getSkin(c, part);
- if (icon == null || icon.getIconHeight() <= 16) {
- skin.paintSkin(g, x + OFFSET, y + OFFSET, state);
+ if (WindowsGraphicsUtils.isLeftToRight(c)) {
+ if (icon == null || icon.getIconHeight() <= 16) {
+ skin.paintSkin(g, x + OFFSET, y + OFFSET, state);
+ } else {
+ skin.paintSkin(g, x + OFFSET, y + icon.getIconHeight() / 2, state);
+ }
} else {
- skin.paintSkin(g, x + OFFSET, y + icon.getIconHeight() / 2, state);
+ if (icon == null) {
+ skin.paintSkin(g, x + 4 * OFFSET, y + OFFSET, state);
+ } else {
+ int ycoord = (icon.getIconHeight() <= 16)
+ ? y + OFFSET
+ : (y + icon.getIconHeight() / 2);
+ if (icon.getIconWidth() <= 8) {
+ skin.paintSkin(g, x + OFFSET, ycoord, state);
+ } else if (icon.getIconWidth() <= 16) {
+ if (menuItem.getText().isEmpty()) {
+ skin.paintSkin(g,
+ (menuItem.getAccelerator() != null)
+ ? (x + 2 * OFFSET) : (x + 3 * OFFSET),
+ ycoord, state);
+ } else {
+ skin.paintSkin(g,
+ (type == JRadioButtonMenuItem.class)
+ ? (x + 4 * OFFSET) : (x + 3 * OFFSET),
+ ycoord, state);
+ }
+ } else {
+ if (menuItem.getText().isEmpty()
+ || menuItem.getAccelerator() != null) {
+ skin.paintSkin(g,
+ (type == JRadioButtonMenuItem.class)
+ ? (x + 3 * OFFSET) : (x + 4 * OFFSET),
+ ycoord, state);
+ } else {
+ skin.paintSkin(g, x + 7 * OFFSET,
+ ycoord, state);
+ }
+ }
+ }
}
}
}
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java
index ba4bde12122d..029e139fe8f3 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.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
@@ -418,7 +418,7 @@ protected LayoutManager createLayout() {
return new WindowsTitlePaneLayout();
}
- public final class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout {
+ public class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout {
private Insets captionMargin = null;
private Insets contentMargin = null;
private XPStyle xp = XPStyle.getXP();
@@ -506,7 +506,7 @@ public void layoutContainer(Container c) {
}
} // end WindowsTitlePaneLayout
- public final class WindowsPropertyChangeHandler extends PropertyChangeHandler {
+ public class WindowsPropertyChangeHandler extends PropertyChangeHandler {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String prop = evt.getPropertyName();
@@ -530,7 +530,7 @@ public void propertyChange(PropertyChangeEvent evt) {
*
* Note: We assume here that icons are square.
*/
- public static final class ScalableIconUIResource implements Icon, UIResource {
+ public static class ScalableIconUIResource implements Icon, UIResource {
// We can use an arbitrary size here because we scale to it in paintIcon()
private static final int SIZE = 16;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java
index 5c331533af94..6e76ac6a5b44 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -45,7 +45,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsInternalFrameUI extends BasicInternalFrameUI
+public class WindowsInternalFrameUI extends BasicInternalFrameUI
{
XPStyle xp = XPStyle.getXP();
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java
index d10f3f47c3c3..a9e14fb09429 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java
@@ -41,7 +41,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsLabelUI extends BasicLabelUI {
+public class WindowsLabelUI extends BasicLabelUI {
private static final Object WINDOWS_LABEL_UI_KEY = new Object();
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java
index f663d8d1e908..ac26dcbf425f 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -57,7 +57,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsMenuBarUI extends BasicMenuBarUI
+public class WindowsMenuBarUI extends BasicMenuBarUI
{
/* to be accessed on the EDT only */
private WindowListener windowListener = null;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java
index 117d3b5fd08d..ceab93f1f18f 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.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
@@ -29,16 +29,11 @@
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
-import java.awt.Insets;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
-import java.util.Enumeration;
-import javax.swing.AbstractButton;
-import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
-import javax.swing.DefaultButtonModel;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JMenu;
@@ -63,7 +58,7 @@
*
* @author Igor Kushnirskiy
*/
-public final class WindowsMenuItemUI extends BasicMenuItemUI {
+public class WindowsMenuItemUI extends BasicMenuItemUI {
/**
* The instance of {@code PropertyChangeListener}.
*/
@@ -132,27 +127,6 @@ public void propertyChange(PropertyChangeEvent e) {
menuItem.addPropertyChangeListener(changeListener);
}
- protected void installDefaults() {
- super.installDefaults();
- String prefix = getPropertyPrefix();
-
- if (acceleratorSelectionForeground == null ||
- acceleratorSelectionForeground instanceof UIResource) {
- acceleratorSelectionForeground =
- UIManager.getColor(prefix + ".acceleratorSelectionForeground");
- }
- if (acceleratorForeground == null ||
- acceleratorForeground instanceof UIResource) {
- acceleratorForeground =
- UIManager.getColor(prefix + ".acceleratorForeground");
- }
- if (disabledForeground == null ||
- disabledForeground instanceof UIResource) {
- disabledForeground =
- UIManager.getColor(prefix + ".disabledForeground");
- }
- }
-
/**
* {@inheritDoc}
*/
@@ -165,15 +139,19 @@ protected void uninstallListeners() {
changeListener = null;
}
+ @Override
protected void paintMenuItem(Graphics g, JComponent c,
Icon checkIcon, Icon arrowIcon,
Color background, Color foreground,
int defaultTextIconGap) {
if (WindowsMenuItemUI.isVistaPainting()) {
- WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon,
- arrowIcon, background, foreground,
- disabledForeground, acceleratorSelectionForeground,
- acceleratorForeground, defaultTextIconGap, menuItem,
+ WindowsMenuItemUI.paintMenuItem(accessor, g, c,
+ checkIcon, arrowIcon,
+ background, foreground,
+ disabledForeground,
+ acceleratorSelectionForeground,
+ acceleratorForeground,
+ defaultTextIconGap, menuItem,
getPropertyPrefix());
return;
}
@@ -182,12 +160,16 @@ protected void paintMenuItem(Graphics g, JComponent c,
}
static void paintMenuItem(WindowsMenuItemUIAccessor accessor, Graphics g,
- JComponent c, Icon checkIcon, Icon arrowIcon,
+ JComponent c,
+ Icon checkIcon, Icon arrowIcon,
Color background, Color foreground,
Color disabledForeground,
Color acceleratorSelectionForeground,
Color acceleratorForeground,
- int defaultTextIconGap, JMenuItem menuItem, String prefix) {
+ int defaultTextIconGap, JMenuItem menuItem,
+ String prefix) {
+ assert c == menuItem : "menuItem passed as 'c' must be the same";
+
// Save original graphics font and color
Font holdf = g.getFont();
Color holdc = g.getColor();
@@ -255,6 +237,15 @@ static void paintMenuItem(WindowsMenuItemUIAccessor accessor, Graphics g,
SwingUtilities3.paintAccText(g, lh, lr, disabledForeground,
acceleratorSelectionForeground,
acceleratorForeground);
+ if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) {
+ Rectangle rect = lr.getArrowRect();
+ if (menuItem.getComponentOrientation().isLeftToRight()) {
+ rect.x += lh.getAfterCheckIconGap();
+ } else {
+ rect.x -= lh.getAfterCheckIconGap();
+ }
+ lr.setArrowRect(rect);
+ }
SwingUtilities3.paintArrowIcon(g, lh, lr, foreground);
// Restore original graphics font and color
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java
index 1476c6fc152b..464c80a6733f 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.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
@@ -50,7 +50,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsMenuUI extends BasicMenuUI {
+public class WindowsMenuUI extends BasicMenuUI {
protected Integer menuBarHeight;
protected boolean hotTrackingOn;
@@ -130,18 +130,20 @@ protected void installDefaults() {
hotTrackingOn = (obj instanceof Boolean) ? (Boolean)obj : true;
}
- /**
- * Paint MenuItem.
- */
+ @Override
protected void paintMenuItem(Graphics g, JComponent c,
- Icon checkIcon, Icon arrowIcon,
- Color background, Color foreground,
- int defaultTextIconGap) {
+ Icon checkIcon, Icon arrowIcon,
+ Color background, Color foreground,
+ int defaultTextIconGap) {
+ assert c == menuItem : "menuItem passed as 'c' must be the same";
if (WindowsMenuItemUI.isVistaPainting()) {
- WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon, arrowIcon,
+ WindowsMenuItemUI.paintMenuItem(accessor, g, c,
+ checkIcon, arrowIcon,
background, foreground,
- disabledForeground, acceleratorSelectionForeground,
- acceleratorForeground, defaultTextIconGap, menuItem,
+ disabledForeground,
+ acceleratorSelectionForeground,
+ acceleratorForeground,
+ defaultTextIconGap, menuItem,
getPropertyPrefix());
return;
}
@@ -280,7 +282,7 @@ protected MouseInputListener createMouseInputListener(JComponent c) {
* true when the mouse enters the menu and false when it exits.
* @since 1.4
*/
- protected final class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler {
+ protected class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler {
@Override
public void mouseEntered(MouseEvent evt) {
super.mouseEntered(evt);
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java
index 5ced1659adfe..3bed1856a557 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -30,5 +30,5 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsOptionPaneUI extends BasicOptionPaneUI {
+public class WindowsOptionPaneUI extends BasicOptionPaneUI {
}
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java
index 1f64c18e61f6..0c30b291648f 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -33,7 +33,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsPasswordFieldUI extends BasicPasswordFieldUI {
+public class WindowsPasswordFieldUI extends BasicPasswordFieldUI {
/**
* Creates a UI for a JPasswordField
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java
index 9d67278526a9..f236c6b14fc6 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,7 +42,7 @@
* @author Igor Kushnirskiy
*/
-public final class WindowsPopupMenuSeparatorUI extends BasicPopupMenuSeparatorUI {
+public class WindowsPopupMenuSeparatorUI extends BasicPopupMenuSeparatorUI {
public static ComponentUI createUI(JComponent c) {
return new WindowsPopupMenuSeparatorUI();
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java
index 4df236115fb2..1c85cfebd940 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -57,7 +57,7 @@
*
* @author Igor Kushnirskiy
*/
-public final class WindowsPopupMenuUI extends BasicPopupMenuUI {
+public class WindowsPopupMenuUI extends BasicPopupMenuUI {
static MnemonicListener mnemonicListener = null;
static final Object GUTTER_OFFSET_KEY =
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java
index 9cc7d277ff18..5440b98cd1b7 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.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
@@ -51,7 +51,7 @@
*
* @author Michael C. Albers
*/
-public final class WindowsProgressBarUI extends BasicProgressBarUI
+public class WindowsProgressBarUI extends BasicProgressBarUI
{
private Rectangle previousFullBox;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java
index 628a4be16378..a0acca095e17 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.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
@@ -42,7 +42,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI {
+public class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI {
final WindowsMenuItemUIAccessor accessor =
new WindowsMenuItemUIAccessor() {
@@ -75,19 +75,20 @@ protected void paintBackground(Graphics g, JMenuItem menuItem,
super.paintBackground(g, menuItem, bgColor);
}
- /**
- * Paint MenuItem.
- */
+ @Override
protected void paintMenuItem(Graphics g, JComponent c,
Icon checkIcon, Icon arrowIcon,
Color background, Color foreground,
int defaultTextIconGap) {
if (WindowsMenuItemUI.isVistaPainting()) {
- WindowsMenuItemUI.paintMenuItem(accessor, g, c, checkIcon,
- arrowIcon, background, foreground,
- disabledForeground, acceleratorSelectionForeground,
- acceleratorForeground, defaultTextIconGap,
- menuItem, getPropertyPrefix());
+ WindowsMenuItemUI.paintMenuItem(accessor, g, c,
+ checkIcon, arrowIcon,
+ background, foreground,
+ disabledForeground,
+ acceleratorSelectionForeground,
+ acceleratorForeground,
+ defaultTextIconGap,
+ menuItem, getPropertyPrefix());
return;
}
super.paintMenuItem(g, c, checkIcon, arrowIcon, background,
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java
index 19bfad0a1da8..d41fd9421e4d 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -70,7 +70,7 @@
* @author Mark Davidson
* @since 1.4
*/
-public final class WindowsRootPaneUI extends BasicRootPaneUI {
+public class WindowsRootPaneUI extends BasicRootPaneUI {
private static final WindowsRootPaneUI windowsRootPaneUI = new WindowsRootPaneUI();
static final AltProcessor altProcessor = new AltProcessor();
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java
index c8d62e52834a..2755f3543f1c 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -55,7 +55,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsScrollBarUI extends BasicScrollBarUI {
+public class WindowsScrollBarUI extends BasicScrollBarUI {
private Grid thumbGrid;
private Grid highlightGrid;
private Dimension horizontalThumbSize;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java
index 393f7595402a..56b8eb1004e8 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -30,5 +30,5 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsScrollPaneUI extends BasicScrollPaneUI
+public class WindowsScrollPaneUI extends BasicScrollPaneUI
{}
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java
index 9ce5db3b4ef8..12eaa33872c9 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 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
@@ -30,4 +30,4 @@
/**
* Windows Separator.
*/
-public final class WindowsSeparatorUI extends BasicSeparatorUI { }
+public class WindowsSeparatorUI extends BasicSeparatorUI { }
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java
index 88dc91d572b8..cfc509babf44 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -44,7 +44,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsSliderUI extends BasicSliderUI
+public class WindowsSliderUI extends BasicSliderUI
{
private boolean rollover = false;
private boolean pressed = false;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java
index bad66ce3a047..8934bf9ff218 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, 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
@@ -37,7 +37,7 @@
import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
-public final class WindowsSpinnerUI extends BasicSpinnerUI {
+public class WindowsSpinnerUI extends BasicSpinnerUI {
public static ComponentUI createUI(JComponent c) {
return new WindowsSpinnerUI();
}
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java
index 95062ef586ff..26cd1bd8c2df 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2020, 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
@@ -39,7 +39,7 @@
* @author Jeff Dinkins
*/
@SuppressWarnings("serial") // Superclass is not serializable across versions
-public final class WindowsSplitPaneDivider extends BasicSplitPaneDivider
+public class WindowsSplitPaneDivider extends BasicSplitPaneDivider
{
/**
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java
index 8c50e3911648..b67ab22f48f2 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -33,7 +33,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsSplitPaneUI extends BasicSplitPaneUI
+public class WindowsSplitPaneUI extends BasicSplitPaneUI
{
public WindowsSplitPaneUI() {
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java
index 86e2ee1fc523..da8e8b9d3857 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -48,7 +48,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsTabbedPaneUI extends BasicTabbedPaneUI {
+public class WindowsTabbedPaneUI extends BasicTabbedPaneUI {
/**
* Keys to use for forward focus traversal when the JComponent is
* managing focus.
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java
index b670bd294b0e..1db0050f1627 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -50,7 +50,7 @@
import static com.sun.java.swing.plaf.windows.TMSchema.State;
import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
-public final class WindowsTableHeaderUI extends BasicTableHeaderUI {
+public class WindowsTableHeaderUI extends BasicTableHeaderUI {
private TableCellRenderer originalHeaderRenderer;
public static ComponentUI createUI(JComponent h) {
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java
index e695d9f67080..7c9abb12e050 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -33,7 +33,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsTextAreaUI extends BasicTextAreaUI {
+public class WindowsTextAreaUI extends BasicTextAreaUI {
/**
* Creates the object to use for a caret. By default an
* instance of WindowsCaret is created. This method
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java
index 508d260895c6..9920ed371d88 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -62,7 +62,7 @@
*
* @author Timothy Prinzing
*/
-public final class WindowsTextFieldUI extends BasicTextFieldUI
+public class WindowsTextFieldUI extends BasicTextFieldUI
{
/**
* Creates a UI for a JTextField.
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java
index 7858a1195e80..d1418205385c 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -33,7 +33,7 @@
/**
* Windows rendition of the component.
*/
-public final class WindowsTextPaneUI extends BasicTextPaneUI
+public class WindowsTextPaneUI extends BasicTextPaneUI
{
/**
* Creates a UI for a JTextPane.
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java
index 0f468fdf6b10..c813f2016fa6 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java
@@ -45,7 +45,7 @@
*
* @author Jeff Dinkins
*/
-public final class WindowsToggleButtonUI extends BasicToggleButtonUI
+public class WindowsToggleButtonUI extends BasicToggleButtonUI
{
protected int dashedRectGapX;
protected int dashedRectGapY;
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java
index 5350de9ae5c6..1707ce5a80ca 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2005, 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
@@ -40,7 +40,7 @@
*
* @author Mark Davidson
*/
-public final class WindowsToolBarSeparatorUI extends BasicToolBarSeparatorUI {
+public class WindowsToolBarSeparatorUI extends BasicToolBarSeparatorUI {
public static ComponentUI createUI( JComponent c ) {
return new WindowsToolBarSeparatorUI();
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java
index 68e077fa3503..4e2cf42bf5dd 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2006, 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
@@ -45,7 +45,7 @@
import static com.sun.java.swing.plaf.windows.TMSchema.Part;
-public final class WindowsToolBarUI extends BasicToolBarUI {
+public class WindowsToolBarUI extends BasicToolBarUI {
public static ComponentUI createUI(JComponent c) {
return new WindowsToolBarUI();
diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java
index 6ab2352641f3..26edfb978bd7 100644
--- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java
+++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, 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
@@ -167,7 +167,7 @@ public int getIconHeight() {
* The plus sign button icon
*/
@SuppressWarnings("serial") // Superclass is not serializable across versions
- public static final class CollapsedIcon extends ExpandedIcon {
+ public static class CollapsedIcon extends ExpandedIcon {
public static Icon createCollapsedIcon() {
return new CollapsedIcon();
}
@@ -185,7 +185,7 @@ public void paintIcon(Component c, Graphics g, int x, int y) {
}
@SuppressWarnings("serial") // Superclass is not serializable across versions
- public final class WindowsTreeCellRenderer extends DefaultTreeCellRenderer {
+ public class WindowsTreeCellRenderer extends DefaultTreeCellRenderer {
/**
* Configures the renderer based on the passed in components.
diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java
index e50cfcff33b1..4ee48a5c725b 100644
--- a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java
+++ b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1741,6 +1741,12 @@ private void setRangeCopiesAttribute(int from, int to, boolean isRangeSet,
if (isRangeSet) {
attributes.add(new PageRanges(from, to));
setPageRange(from, to);
+ } else {
+ // Sets default values for PageRange attribute and setPageRange
+ attributes.add(new PageRanges(1,
+ Integer.MAX_VALUE));
+ setPageRange(Pageable.UNKNOWN_NUMBER_OF_PAGES,
+ Pageable.UNKNOWN_NUMBER_OF_PAGES);
}
defaultCopies = false;
attributes.add(new Copies(copies));
diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp
index ebb43b2f0789..ba69fa75f734 100644
--- a/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Desktop.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
@@ -92,6 +92,8 @@ JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_ShellExecute
if (wcscmp(verb_c, L"open") == 0) {
BOOL isExecutable = SaferiIsExecutableFileType(fileOrUri_c, FALSE);
if (isExecutable) {
+ JNU_ReleaseStringPlatformChars(env, fileOrUri_j, fileOrUri_c);
+ JNU_ReleaseStringPlatformChars(env, verb_j, verb_c);
return env->NewStringUTF("Unsupported URI content");
}
}
diff --git a/src/java.management/share/classes/sun/management/VMManagement.java b/src/java.management/share/classes/sun/management/VMManagement.java
index f4445f0225af..6548ae346d92 100644
--- a/src/java.management/share/classes/sun/management/VMManagement.java
+++ b/src/java.management/share/classes/sun/management/VMManagement.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -48,6 +48,9 @@ public interface VMManagement {
public boolean isGcNotificationSupported();
public boolean isRemoteDiagnosticCommandsSupported();
+ // AOT Subsystem
+ public boolean endAOTRecording();
+
// Class Loading Subsystem
public long getTotalClassCount();
public int getLoadedClassCount();
diff --git a/src/java.management/share/classes/sun/management/VMManagementImpl.java b/src/java.management/share/classes/sun/management/VMManagementImpl.java
index 041f09547d2b..e91d7955369d 100644
--- a/src/java.management/share/classes/sun/management/VMManagementImpl.java
+++ b/src/java.management/share/classes/sun/management/VMManagementImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -117,6 +117,9 @@ public boolean isRemoteDiagnosticCommandsSupported() {
public native boolean isThreadCpuTimeEnabled();
public native boolean isThreadAllocatedMemoryEnabled();
+ // AOT Subsystem
+ public native boolean endAOTRecording();
+
// Class Loading Subsystem
public int getLoadedClassCount() {
long count = getTotalClassCount() - getUnloadedClassCount();
diff --git a/src/java.management/share/native/libmanagement/VMManagementImpl.c b/src/java.management/share/native/libmanagement/VMManagementImpl.c
index f1a566676dce..dc8ad3c5c19b 100644
--- a/src/java.management/share/native/libmanagement/VMManagementImpl.c
+++ b/src/java.management/share/native/libmanagement/VMManagementImpl.c
@@ -101,6 +101,13 @@ Java_sun_management_VMManagementImpl_getVmArguments0
return JVM_GetVmArguments(env);
}
+JNIEXPORT jboolean JNICALL
+Java_sun_management_VMManagementImpl_endAOTRecording
+ (JNIEnv *env, jobject dummy)
+{
+ return JVM_AOTEndRecording(env);
+}
+
JNIEXPORT jlong JNICALL
Java_sun_management_VMManagementImpl_getTotalClassCount
(JNIEnv *env, jobject dummy)
diff --git a/src/java.net.http/share/classes/module-info.java b/src/java.net.http/share/classes/module-info.java
index 7d7c3fb351ad..14cbb85291d7 100644
--- a/src/java.net.http/share/classes/module-info.java
+++ b/src/java.net.http/share/classes/module-info.java
@@ -156,7 +156,8 @@
*
*
{@systemProperty jdk.httpclient.auth.retrylimit} (default: 3)
* The number of attempts the Basic authentication filter will attempt to retry a failed
- * authentication.
+ * authentication. The number of authentication attempts is always one greater than the
+ * retry limit, as the initial request does not count toward the retries.
*
*
{@systemProperty jdk.httpclient.sendBufferSize} (default: operating system
* default) The HTTP client {@linkplain java.nio.channels.SocketChannel socket}
diff --git a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp
index 747db0001580..fab17b66f56f 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, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -366,7 +366,11 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_PRNG_generateSeed
} else {
- if (length > 0) {
+ /*
+ * seed could be NULL here when SecureRandom.generateSeed() is
+ * called with a length of zero.
+ */
+ if ((length > 0) || (seed == NULL)) {
seed = env->NewByteArray(length);
if (seed == NULL) {
__leave;
@@ -1369,7 +1373,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;
}
}
@@ -1381,7 +1385,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.httpserver/share/classes/com/sun/net/httpserver/Headers.java b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java
index 279203d1d5ba..ef89088674f5 100644
--- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java
+++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -110,29 +110,69 @@ public Headers(Map> headers) {
}
/**
- * Normalize the key by converting to following form.
- * First {@code char} upper case, rest lower case.
- * key is presumed to be {@code ASCII}.
+ * {@return the normalized header name of the following form: the first
+ * character in upper-case, the rest in lower-case}
+ * The input header name is assumed to be encoded in ASCII.
+ *
+ * @implSpec
+ * This method is performance-sensitive; update with care.
+ *
+ * @param key an ASCII-encoded header name
+ * @throws NullPointerException on null {@code key}
+ * @throws IllegalArgumentException if {@code key} contains {@code \r} or {@code \n}
*/
- private String normalize(String key) {
+ private static String normalize(String key) {
+
+ // Fast path for the empty key
Objects.requireNonNull(key);
- int len = key.length();
- if (len == 0) {
+ int l = key.length();
+ if (l == 0) {
return key;
}
- char[] b = key.toCharArray();
- if (b[0] >= 'a' && b[0] <= 'z') {
- b[0] = (char)(b[0] - ('a' - 'A'));
- } else if (b[0] == '\r' || b[0] == '\n')
- throw new IllegalArgumentException("illegal character in key");
-
- for (int i=1; i= 'A' && b[i] <= 'Z') {
- b[i] = (char) (b[i] + ('a' - 'A'));
- } else if (b[i] == '\r' || b[i] == '\n')
- throw new IllegalArgumentException("illegal character in key");
+
+ // Find the first non-normalized `char`
+ int i = 0;
+ char c = key.charAt(i);
+ if (!(c == '\r' || c == '\n' || (c >= 'a' && c <= 'z'))) {
+ i++;
+ for (; i < l; i++) {
+ c = key.charAt(i);
+ if (c == '\r' || c == '\n' || (c >= 'A' && c <= 'Z')) {
+ break;
+ }
+ }
+ }
+
+ // Fast path for the already normalized key
+ if (i == l) {
+ return key;
}
- return new String(b);
+
+ // Upper-case the first `char`
+ char[] cs = key.toCharArray();
+ int o = 'a' - 'A';
+ if (i == 0) {
+ if (c == '\r' || c == '\n') {
+ throw new IllegalArgumentException("illegal character in key at index " + i);
+ }
+ if (c >= 'a' && c <= 'z') {
+ cs[0] = (char) (c - o);
+ }
+ i++;
+ }
+
+ // Lower-case the secondary `char`s
+ for (; i < l; i++) {
+ c = cs[i];
+ if (c >= 'A' && c <= 'Z') {
+ cs[i] = (char) (c + o);
+ } else if (c == '\r' || c == '\n') {
+ throw new IllegalArgumentException("illegal character in key at index " + i);
+ }
+ }
+
+ return new String(cs);
+
}
@Override
diff --git a/src/jdk.jcmd/share/man/jcmd.md b/src/jdk.jcmd/share/man/jcmd.md
index 4e67e7a45023..a25161444273 100644
--- a/src/jdk.jcmd/share/man/jcmd.md
+++ b/src/jdk.jcmd/share/man/jcmd.md
@@ -134,6 +134,16 @@ The following commands are available:
- `-all`: (Optional) Show help for all commands (BOOLEAN, false) .
+`AOT.end_recording`
+: Ends an in-progress AOT training and records the results to the file(s) specified by `-XX:AOTConfiguration` and/or `-XX:AOTCacheOutput`.
+
+ Impact: Low
+
+ **Note:**
+
+ The JVM must be started in AOT training mode using command-line arguments such as `-XX:AOTMode=record` or `-XX:AOTCacheOutput=`.
+ The results of the AOT training can be an AOT configuration file, an AOT cache file, or both.
+
`Compiler.CodeHeap_Analytics` \[*function*\] \[*granularity*\]
: Print CodeHeap analytics
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java
index e7dd234ca8d0..c973dadb3b7f 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java
@@ -132,9 +132,12 @@ private Parser createPrimitiveParser(Type type, boolean event) throws IOExceptio
case "short" -> new ShortParser();
case "byte" -> new ByteParser();
case "java.lang.String" -> {
- ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type);
- ConstantLookup lookup = new ConstantLookup(pool, type);
- constantLookups.put(type.getId(), lookup);
+ ConstantLookup lookup = constantLookups.get(type.getId());
+ if (lookup == null) {
+ ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type);
+ lookup = new ConstantLookup(pool, type);
+ constantLookups.put(type.getId(), lookup);
+ }
yield new StringParser(lookup, event);
}
default -> throw new IOException("Unknown primitive type " + type.getName());
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java
index dbafca4ed3ce..e06d361b2039 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java
@@ -58,7 +58,13 @@ public Instrumentation(ClassLoader classLoader, String internalClassName, byte[]
}
public void addMethod(long methodId, String name, String signature, int modification) {
- modificationMap.put(name + signature, new Method(methodId, Modification.valueOf(modification), className + "::" + name));
+ Method method = new Method(
+ methodId,
+ Modification.valueOf(modification),
+ name.equals(""),
+ className + "::" + name
+ );
+ modificationMap.put(name + signature, method);
}
public List getMethods() {
@@ -71,7 +77,7 @@ public byte[] generateBytecode() {
ClassModel classModel = classFile.parse(bytecode);
byte[] generated = classFile.build(classModel.thisClass().asSymbol(), classBuilder -> {
for (var ce : classModel) {
- if (modifyClassElement(classBuilder, ce)) {
+ if (modifyClassElement(classModel, classBuilder, ce)) {
modified[0] = true;
} else {
classBuilder.with(ce);
@@ -93,7 +99,7 @@ private ClassHierarchyResolver resolver() {
}
}
- private boolean modifyClassElement(ClassBuilder classBuilder, ClassElement ce) {
+ private boolean modifyClassElement(ClassModel classModel, ClassBuilder classBuilder, ClassElement ce) {
if (ce instanceof MethodModel mm) {
String method = mm.methodName().stringValue();
String signature = mm.methodType().stringValue();
@@ -102,15 +108,15 @@ private boolean modifyClassElement(ClassBuilder classBuilder, ClassElement ce) {
if (tm != null) {
Modification m = tm.modification();
if (m.tracing() || m.timing()) {
- return modifyMethod(classBuilder, mm, tm);
+ return modifyMethod(classModel, classBuilder, mm, tm);
}
}
}
return false;
}
- private boolean modifyMethod(ClassBuilder classBuilder, MethodModel m, Method method) {
- var code = m.code();
+ private boolean modifyMethod(ClassModel classModel, ClassBuilder classBuilder, MethodModel methodModel, Method method) {
+ var code = methodModel.code();
if (code.isPresent()) {
if (classLoader == null && ExcludeList.containsMethod(method.name())) {
String msg = "Risk of recursion, skipping bytecode generation of " + method.name();
@@ -118,9 +124,9 @@ private boolean modifyMethod(ClassBuilder classBuilder, MethodModel m, Method me
return false;
}
MethodTransform s = MethodTransform.ofStateful(
- () -> MethodTransform.transformingCode(new Transform(method))
+ () -> MethodTransform.transformingCode(new Transform(classModel, code.get(), method))
);
- classBuilder.transformMethod(m, s);
+ classBuilder.transformMethod(methodModel, s);
return true;
}
return false;
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java
index d685083153d6..d85e458e9d58 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java
@@ -31,7 +31,7 @@
/**
* Class that holds information about an instrumented method.
*/
-record Method(long methodId, Modification modification, String name) {
+record Method(long methodId, Modification modification, boolean constructor, String name) {
@Override
public String toString() {
return name + (modification.timing() ? " +timing" : " -timing") + (modification.tracing() ? " +tracing" : " -tracing") + " (Method ID: " + String.format("0x%08X)", methodId);
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java
index cd65a119cee4..377eede79256 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java
@@ -24,13 +24,19 @@
*/
package jdk.jfr.internal.tracing;
+import java.lang.classfile.ClassModel;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.CodeElement;
+import java.lang.classfile.CodeModel;
import java.lang.classfile.CodeTransform;
+import java.lang.classfile.Label;
import java.lang.classfile.TypeKind;
+import java.lang.classfile.instruction.InvokeInstruction;
import java.lang.classfile.instruction.ReturnInstruction;
import java.lang.classfile.instruction.ThrowInstruction;
import java.lang.constant.ClassDesc;
+import java.util.ArrayList;
+import java.util.List;
import jdk.jfr.internal.util.Bytecode;
import jdk.jfr.internal.util.Bytecode.MethodDesc;
@@ -43,59 +49,208 @@
* The method ID is determined by native code.
*/
final class Transform implements CodeTransform {
+ private static class TryBlock {
+ Label start;
+ Label end;
+ }
private static final ClassDesc METHOD_TRACER_CLASS = ClassDesc.of(MethodTracer.class.getName());
private static final MethodDesc TRACE_METHOD = MethodDesc.of("trace", "(JJ)V");
private static final MethodDesc TIMING_METHOD = MethodDesc.of("timing", "(JJ)V");
private static final MethodDesc TRACE_TIMING_METHOD = MethodDesc.of("traceTiming", "(JJ)V");
private static final MethodDesc TIMESTAMP_METHOD = MethodDesc.of("timestamp", "()J");
+ private final List tryBlocks = new ArrayList<>();
+ private final boolean simplifiedInstrumentation;
+ private final ClassModel classModel;
private final Method method;
private int timestampSlot = -1;
- Transform(Method method) {
+ Transform(ClassModel classModel, CodeModel model, Method method) {
this.method = method;
+ this.classModel = classModel;
+ // The JVMS (not the JLS) allows multiple mutually exclusive super/this.
+ // invocations in a constructor body as long as only one lies on any given
+ // execution path. For example, this is valid bytecode:
+ //
+ // Foo(boolean value) {
+ // if (value) {
+ // staticMethodThatMayThrow();
+ // super();
+ // } else {
+ // try {
+ // if (value == 0) {
+ // throw new Exception("");
+ // }
+ // } catch (Throwable t) {
+ // throw t;
+ // }
+ // super();
+ // }
+ // }
+ //
+ // If such a method is found, instrumentation falls back to instrumenting only
+ // RET and ATHROW. This can cause exceptions to be missed or counted twice.
+ //
+ // An effect of this heuristic is that constructors like the one below
+ // will also trigger simplified instrumentation.
+ //
+ // class Bar {
+ // }
+ //
+ // class Foo extends Bar {
+ // Foo() {
+ // new Bar();
+ // }
+ // }
+ //
+ // java.lang.Object:: with zero constructor invocations should use simplified instrumentation
+ this.simplifiedInstrumentation = method.constructor() && constructorInvocations(model.elementList()) != 1;
+ }
+
+ private int constructorInvocations(List elementList) {
+ int count = 0;
+ for (CodeElement e : elementList) {
+ if (isConstructorInvocation(e)) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ private boolean isConstructorInvocation(CodeElement element) {
+ if (element instanceof InvokeInstruction inv && inv.name().equalsString("")) {
+ if (classModel.thisClass().equals(inv.owner())) {
+ return true;
+ }
+ if (classModel.superclass().isPresent()) {
+ return classModel.superclass().get().equals(inv.owner());
+ }
+ }
+ return false;
}
@Override
- public final void accept(CodeBuilder builder, CodeElement element) {
+ public void accept(CodeBuilder builder, CodeElement element) {
+ if (simplifiedInstrumentation) {
+ acceptSimplifiedInstrumentation(builder, element);
+ return;
+ }
+ if (method.constructor()) {
+ acceptConstructor(builder, element, isConstructorInvocation(element));
+ } else {
+ acceptMethod(builder, element);
+ }
+ }
+
+ @Override
+ public void atEnd(CodeBuilder builder) {
+ endTryBlock(builder);
+ for (TryBlock block : tryBlocks) {
+ addCatchHandler(block, builder);
+ }
+ }
+
+ private void acceptConstructor(CodeBuilder builder, CodeElement element, boolean isConstructorInvocation) {
+ if (timestampSlot == -1) {
+ timestampSlot = invokeTimestamp(builder);
+ builder.lstore(timestampSlot);
+ if (!isConstructorInvocation) {
+ beginTryBlock(builder);
+ }
+ }
+ if (isConstructorInvocation) {
+ endTryBlock(builder);
+ builder.with(element);
+ beginTryBlock(builder);
+ return;
+ }
+ if (element instanceof ReturnInstruction) {
+ addTracing(builder);
+ }
+ builder.with(element);
+ }
+
+ private void endTryBlock(CodeBuilder builder) {
+ if (tryBlocks.isEmpty()) {
+ return;
+ }
+ TryBlock last = tryBlocks.getLast();
+ if (last.end == null) {
+ last.end = builder.newBoundLabel();
+ }
+ }
+
+ private void beginTryBlock(CodeBuilder builder) {
+ TryBlock block = new TryBlock();
+ block.start = builder.newBoundLabel();
+ tryBlocks.add(block);
+ }
+
+ private void acceptSimplifiedInstrumentation(CodeBuilder builder, CodeElement element) {
if (timestampSlot == -1) {
timestampSlot = invokeTimestamp(builder);
builder.lstore(timestampSlot);
}
if (element instanceof ReturnInstruction || element instanceof ThrowInstruction) {
- builder.lload(timestampSlot);
- builder.ldc(method.methodId());
- Modification modification = method.modification();
- boolean objectInit = method.name().equals("java.lang.Object::");
- String suffix = objectInit ? "ObjectInit" : "";
- if (modification.timing()) {
- if (modification.tracing()) {
- invokeTraceTiming(builder, suffix);
- } else {
- invokeTiming(builder, suffix);
- }
+ addTracing(builder);
+ }
+ builder.with(element);
+ }
+
+ private void acceptMethod(CodeBuilder builder, CodeElement element) {
+ if (timestampSlot == -1) {
+ timestampSlot = invokeTimestamp(builder);
+ builder.lstore(timestampSlot);
+ beginTryBlock(builder);
+ }
+ if (element instanceof ReturnInstruction) {
+ addTracing(builder);
+ }
+ builder.with(element);
+ }
+
+ private void addCatchHandler(TryBlock block, CodeBuilder builder) {
+ Label catchHandler = builder.newBoundLabel();
+ int exceptionSlot = builder.allocateLocal(TypeKind.REFERENCE);
+ builder.astore(exceptionSlot);
+ addTracing(builder);
+ builder.aload(exceptionSlot);
+ builder.athrow();
+ builder.exceptionCatchAll(block.start, block.end, catchHandler);
+ }
+
+ private void addTracing(CodeBuilder builder) {
+ builder.lload(timestampSlot);
+ builder.ldc(method.methodId());
+ Modification modification = method.modification();
+ boolean objectInit = method.name().equals("java.lang.Object::");
+ String suffix = objectInit ? "ObjectInit" : "";
+ if (modification.timing()) {
+ if (modification.tracing()) {
+ invokeTraceTiming(builder, suffix);
} else {
- if (modification.tracing()) {
- invokeTrace(builder, suffix);
- }
+ invokeTiming(builder, suffix);
+ }
+ } else {
+ if (modification.tracing()) {
+ invokeTrace(builder, suffix);
}
}
- builder.with(element);
}
- public static void invokeTiming(CodeBuilder builder, String suffix) {
+ private static void invokeTiming(CodeBuilder builder, String suffix) {
builder.invokestatic(METHOD_TRACER_CLASS, TIMING_METHOD.name() + suffix, TIMING_METHOD.descriptor());
}
- public static void invokeTrace(CodeBuilder builder, String suffix) {
+ private static void invokeTrace(CodeBuilder builder, String suffix) {
builder.invokestatic(METHOD_TRACER_CLASS, TRACE_METHOD.name() + suffix, TRACE_METHOD.descriptor());
}
- public static void invokeTraceTiming(CodeBuilder builder, String suffix) {
+ private static void invokeTraceTiming(CodeBuilder builder, String suffix) {
builder.invokestatic(METHOD_TRACER_CLASS, TRACE_TIMING_METHOD.name() + suffix, TRACE_TIMING_METHOD.descriptor());
}
- public static int invokeTimestamp(CodeBuilder builder) {
+ private static int invokeTimestamp(CodeBuilder builder) {
Bytecode.invokestatic(builder, METHOD_TRACER_CLASS, TIMESTAMP_METHOD);
return builder.allocateLocal(TypeKind.LONG);
}
diff --git a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json
index 57ef5c8b859d..bf52bb3915d2 100644
--- a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json
+++ b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json
@@ -78,6 +78,10 @@
"description": "The blocker object responsible for the thread parking."
}
},
+ "owner": {
+ "type": "string",
+ "description": "The thread identifier of the owner when the parkBlocker is an AbstractOwnableSynchronizer."
+ }
"required": [
"object"
]
@@ -115,9 +119,9 @@
"items": {
"type": [
"string",
- null
+ "null"
],
- "description": "The object for which the monitor is owned by the thread, null if eliminated"
+ "description": "The object for which the monitor is owned by the thread, null if eliminated."
}
}
},
diff --git a/src/java.desktop/share/native/libjsound/Platform.c b/src/jdk.management/share/classes/com/sun/management/internal/HotSpotAOTCacheImpl.java
similarity index 58%
rename from src/java.desktop/share/native/libjsound/Platform.c
rename to src/jdk.management/share/classes/com/sun/management/internal/HotSpotAOTCacheImpl.java
index ecb389c89b7b..4bb556455ea4 100644
--- a/src/java.desktop/share/native/libjsound/Platform.c
+++ b/src/jdk.management/share/classes/com/sun/management/internal/HotSpotAOTCacheImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, Microsoft, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,22 +22,34 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+package com.sun.management.internal;
+import javax.management.ObjectName;
+import jdk.management.HotSpotAOTCacheMXBean;
+import sun.management.Util;
+import sun.management.VMManagement;
-#include "Utilities.h"
-// Platform.java includes
-#include "com_sun_media_sound_Platform.h"
-
-/*
- * Declare library specific JNI_Onload entry if static build
+/**
+ * Implementation class for the AOT Cache subsystem.
+ *
+ * ManagementFactory.getRuntimeMXBean() returns an instance
+ * of this class.
*/
-DEF_STATIC_JNI_OnLoad
+public class HotSpotAOTCacheImpl implements HotSpotAOTCacheMXBean {
-/*
- * Class: com_sun_media_sound_Platform
- * Method: nIsBigEndian
- * Signature: ()Z
- */
-JNIEXPORT jboolean JNICALL Java_com_sun_media_sound_Platform_nIsBigEndian(JNIEnv *env, jclass clss) {
- return UTIL_IsBigEndianPlatform();
-}
+ private final VMManagement jvm;
+ /**
+ * Constructor of HotSpotAOTCacheImpl class.
+ */
+ HotSpotAOTCacheImpl(VMManagement vm) {
+ this.jvm = vm;
+ }
+
+ public boolean endRecording() {
+ return jvm.endAOTRecording();
+ }
+
+ public ObjectName getObjectName() {
+ return Util.newObjectName("jdk.management:type=HotSpotAOTCache");
+ }
+}
\ No newline at end of file
diff --git a/src/jdk.management/share/classes/com/sun/management/internal/PlatformMBeanProviderImpl.java b/src/jdk.management/share/classes/com/sun/management/internal/PlatformMBeanProviderImpl.java
index 3a64fe6b858f..b000516e626c 100644
--- a/src/jdk.management/share/classes/com/sun/management/internal/PlatformMBeanProviderImpl.java
+++ b/src/jdk.management/share/classes/com/sun/management/internal/PlatformMBeanProviderImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -39,6 +39,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.management.DynamicMBean;
+import jdk.management.HotSpotAOTCacheMXBean;
import jdk.management.VirtualThreadSchedulerMXBean;
import sun.management.ManagementFactoryHelper;
import sun.management.spi.PlatformMBeanProvider;
@@ -159,6 +160,41 @@ public synchronized Map nameToMBeanMa
}
});
+ /**
+ * HotSpotAOTCacheMXBean.
+ */
+ initMBeanList.add(new PlatformComponent() {
+ private final Set> mbeanInterfaces =
+ Set.of(HotSpotAOTCacheMXBean.class);
+ private final Set mbeanInterfaceNames =
+ Set.of(HotSpotAOTCacheMXBean.class.getName());
+ private HotSpotAOTCacheMXBean impl;
+
+ @Override
+ public Set> mbeanInterfaces() {
+ return mbeanInterfaces;
+ }
+
+ @Override
+ public Set mbeanInterfaceNames() {
+ return mbeanInterfaceNames;
+ }
+
+ @Override
+ public String getObjectNamePattern() {
+ return "jdk.management:type=HotSpotAOTCache";
+ }
+
+ @Override
+ public Map nameToMBeanMap() {
+ HotSpotAOTCacheMXBean impl = this.impl;
+ if (impl == null) {
+ this.impl = impl = new HotSpotAOTCacheImpl(ManagementFactoryHelper.getVMManagement());
+ }
+ return Map.of("jdk.management:type=HotSpotAOTCache", impl);
+ }
+ });
+
/**
* VirtualThreadSchedulerMXBean.
*/
diff --git a/src/jdk.management/share/classes/jdk/management/HotSpotAOTCacheMXBean.java b/src/jdk.management/share/classes/jdk/management/HotSpotAOTCacheMXBean.java
new file mode 100644
index 000000000000..399f02c9044b
--- /dev/null
+++ b/src/jdk.management/share/classes/jdk/management/HotSpotAOTCacheMXBean.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2025, Microsoft, Inc. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.management;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.PlatformManagedObject;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+/**
+ * Management interface for the JDK's Ahead of Time (AOT) Cache.
+ *
+ *
The management interface is registered with the platform {@link MBeanServer
+ * MBeanServer}. The {@link ObjectName ObjectName} that uniquely identifies the management
+ * interface within the {@code MBeanServer} is {@code jdk.management:type=HotSpotAOTCache}.
+ *
+ *
Direct access to the MXBean interface can be obtained with
+ * {@link ManagementFactory#getPlatformMXBean(Class)}.
+ *
+ * @apiNote This interface is defined in JDK 25.0.3.
+ * @since 25
+ */
+public interface HotSpotAOTCacheMXBean extends PlatformManagedObject {
+ /**
+ * If an AOT recording is in progress, ends the recording. This method returns
+ * after the AOT artifacts have been completely written.
+ *
+ *
The JVM will start recording AOT artifacts upon start-up if appropriate JVM options are
+ * given in the command-line. The recording will stop when the JVM exits, or when
+ * the {@code endRecording} method is called. Examples:
+ *
+ *
+ * The JVM records optimization information for the current application in the AOT cache file
+ * {@code app.aot}. In a future run of the application, the option {@code -XX:AOTCache=app.aot} will
+ * cause the JVM to use the cache to improve the application's startup and warmup performance.
+ *
+ * The JVM records optimization information for the current application in the AOT configuration
+ * file {@code app.aotconfig}. Subsequently, an AOT cache file can be created with the command:
+ *
+ *
For more information about creating and using the AOT artifacts, and detailed
+ * specification of the corresponding JVM command-line options, please refer
+ * to JEP 483 and JEP 514.
+ *
+ *
Currently there are no APIs to start an AOT recording. AOT recordings must be
+ * started using JVM command-line options such as {@code -XX:AOTCacheOutput}.
+ * There are also no APIs to query whether an AOT recording is in progress, or what AOT
+ * artifacts are being recorded.
+ *
+ *
This method enables an application to end its own AOT recording
+ * programatically, but that is not necessarily the best approach. Doing so
+ * requires changing the application’s code, which might not be
+ * feasible. Even when it is feasible, injecting training-specific logic
+ * into the application reduces the similarity between training runs and
+ * production runs, potentially making the AOT cache less effective. It may
+ * be better to arrange for an external agent to end the training run,
+ * thereby creating an AOT cache without interfering with the application’s
+ * code.
+ *
+ * @return {@code true} if a recording was in progress and has been ended
+ * successfully; {@code false} otherwise.
+ */
+ public boolean endRecording();
+}
\ No newline at end of file
diff --git a/test/hotspot/gtest/x86/x86-asmtest.py b/test/hotspot/gtest/x86/x86-asmtest.py
index 714b7aaea403..6544e30579f7 100644
--- a/test/hotspot/gtest/x86/x86-asmtest.py
+++ b/test/hotspot/gtest/x86/x86-asmtest.py
@@ -172,10 +172,10 @@ class NFInstruction(Instruction):
def __init__(self, name, aname, no_flag):
super().__init__(name, aname)
self.no_flag = no_flag
-
+
def cstr(self):
return f'__ {self._name}(' + ', '.join([op.cstr() for op in self.operands]) + (f', {str(self.no_flag).lower()}' if self.no_flag is not None else '') + ');'
-
+
def astr(self):
# JDK assembler uses 'cl' for shift instructions with one operand by default
cl_str = (', cl' if self._name in shift_rot_ops and len(self.operands) == 2 else '')
@@ -305,10 +305,10 @@ def __init__(self, name, aname, width, cond, reg1, reg2, reg3):
self.cond = cond
self.generate_operands(self.reg1, self.reg2, self.reg3)
self.demote = True
-
+
def cstr(self):
return f'__ {self._name} (' + 'Assembler::Condition::' + self.cond + ', ' + ', '.join([reg.cstr() for reg in self.operands]) + ');'
-
+
def astr(self):
operands = self.operands
if self.demote:
@@ -326,10 +326,10 @@ def __init__(self, name, aname, width, cond, reg1, reg2, mem_base, mem_idx):
self.cond = cond
self.generate_operands(self.reg1, self.reg2, self.mem)
self.demote = True
-
+
def cstr(self):
return f'__ {self._name} (' + 'Assembler::Condition::' + self.cond + ', ' + ', '.join([reg.cstr() for reg in self.operands]) + ');'
-
+
def astr(self):
operands = self.operands
if self.demote:
diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
index 3ad704ff3dd3..2a7f31a0d09e 100644
--- a/test/hotspot/jtreg/ProblemList.txt
+++ b/test/hotspot/jtreg/ProblemList.txt
@@ -49,6 +49,7 @@ compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java 8190680 generic-all
compiler/runtime/Test8168712.java#with-dtrace 8211769,8211771 generic-ppc64,generic-ppc64le,linux-s390x
compiler/runtime/Test8168712.java#without-dtrace 8211769,8211771 generic-ppc64,generic-ppc64le,linux-s390x
+compiler/runtime/Test7196199.java 8365196 windows-x64
compiler/loopopts/TestUnreachableInnerLoop.java 8288981 linux-s390x
compiler/c2/Test8004741.java 8235801 generic-all
@@ -116,10 +117,9 @@ runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x
applications/jcstress/copy.java 8229852 linux-all
-containers/docker/TestJcmd.java 8278102 linux-all
containers/docker/TestMemoryAwareness.java 8303470 linux-all
containers/docker/TestJFREvents.java 8327723 linux-x64
-containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64
+containers/docker/TestJcmdWithSideCar.java 8341518 linux-all
#############################################################################
diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestCloneUnknownClassAtParseTime.java b/test/hotspot/jtreg/compiler/arraycopy/TestCloneUnknownClassAtParseTime.java
new file mode 100644
index 000000000000..1cd7cc21611c
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/arraycopy/TestCloneUnknownClassAtParseTime.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2025 IBM Corporation. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8339526
+ * @summary C2: store incorrectly removed for clone() transformed to series of loads/stores
+ * @run main/othervm -XX:-BackgroundCompilation compiler.arraycopy.TestCloneUnknownClassAtParseTime
+ * @run main compiler.arraycopy.TestCloneUnknownClassAtParseTime
+ */
+
+package compiler.arraycopy;
+
+public class TestCloneUnknownClassAtParseTime {
+ private static volatile int volatileField;
+ static A field;
+
+ public static void main(String[] args) throws CloneNotSupportedException {
+ A a = new A();
+ for (int i = 0; i < 20_000; i++) {
+ B b = (B)test1(-1);
+ if (b.field1 != 42 || b.field2 != 42|| b.field3 != 42) {
+ throw new RuntimeException("Clone wrongly initialized");
+ }
+ inlined1(42);
+ field = a;
+ inlined2();
+ }
+ }
+
+ private static A test1(int i) throws CloneNotSupportedException {
+ int[] nonEscapingArray = new int[1];
+ field = new B(42, 42, 42);
+
+ if (i > 0) {
+ throw new RuntimeException("never taken");
+ }
+ inlined1(i);
+
+ nonEscapingArray[0] = 42;
+ return inlined2();
+ }
+
+ private static A inlined2() throws CloneNotSupportedException {
+ A a = field;
+ return (A)a.clone();
+ }
+
+ private static void inlined1(int i) {
+ if (i > 0) {
+ volatileField = 42;
+ }
+ }
+
+ private static class A implements Cloneable {
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+ }
+
+ private static class B extends A {
+ int field1;
+ int field2;
+ int field3;
+
+ B(int v1, int v2, int v3) {
+ field1 = v1;
+ field2 = v2;
+ field3 = v3;
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java
index 22920eda8282..f67370f78fe6 100644
--- a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java
+++ b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java
@@ -34,6 +34,7 @@
/*
* @test
* @bug 8318446 8331054 8331311 8335392 8347405
+ * @key randomness
* @summary Test merging of consecutive stores
* @modules java.base/jdk.internal.misc
* @library /test/lib /
@@ -43,6 +44,7 @@
/*
* @test
* @bug 8318446 8331054 8331311 8335392 8347405
+ * @key randomness
* @summary Test merging of consecutive stores
* @modules java.base/jdk.internal.misc
* @library /test/lib /
@@ -52,6 +54,7 @@
/*
* @test
* @bug 8318446 8331054 8331311 8335392 8348959 8351414
+ * @key randomness
* @summary Test merging of consecutive stores
* @modules java.base/jdk.internal.misc
* @library /test/lib /
diff --git a/test/hotspot/jtreg/compiler/c2/TestMergeStoresMemorySegment.java b/test/hotspot/jtreg/compiler/c2/TestMergeStoresMemorySegment.java
index a5302d1b5158..a275570747b4 100644
--- a/test/hotspot/jtreg/compiler/c2/TestMergeStoresMemorySegment.java
+++ b/test/hotspot/jtreg/compiler/c2/TestMergeStoresMemorySegment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,7 @@
/*
* @test id=byte-array
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment ByteArray
@@ -42,6 +43,7 @@
/*
* @test id=char-array
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment CharArray
@@ -50,6 +52,7 @@
/*
* @test id=short-array
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment ShortArray
@@ -58,6 +61,7 @@
/*
* @test id=int-array
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment IntArray
@@ -66,6 +70,7 @@
/*
* @test id=long-array
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment LongArray
@@ -74,6 +79,7 @@
/*
* @test id=float-array
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment FloatArray
@@ -82,6 +88,7 @@
/*
* @test id=double-array
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment DoubleArray
@@ -90,6 +97,7 @@
/*
* @test id=byte-buffer
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment ByteBuffer
@@ -98,6 +106,7 @@
/*
* @test id=byte-buffer-direct
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment ByteBufferDirect
@@ -106,6 +115,7 @@
/*
* @test id=native
* @bug 8335392
+ * @key randomness
* @summary Test MergeStores optimization for MemorySegment
* @library /test/lib /
* @run driver compiler.c2.TestMergeStoresMemorySegment Native
diff --git a/test/hotspot/jtreg/compiler/c2/TestMinMaxSubword.java b/test/hotspot/jtreg/compiler/c2/TestMinMaxSubword.java
index 955aa4058f04..a7e90353f905 100644
--- a/test/hotspot/jtreg/compiler/c2/TestMinMaxSubword.java
+++ b/test/hotspot/jtreg/compiler/c2/TestMinMaxSubword.java
@@ -31,6 +31,7 @@
/*
* @test
* @bug 8294816
+ * @key randomness
* @summary Test Math.min/max vectorization miscompilation for integer subwords
* @library /test/lib /
* @requires vm.compiler2.enabled
diff --git a/test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java b/test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java
new file mode 100644
index 000000000000..f86dc495fd29
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8370502
+ * @summary Do not segfault while adding node to IGVN worklist
+ *
+ * @run main/othervm -Xbatch ${test.main.class}
+ */
+
+package compiler.c2;
+
+public class TestUnlockNodeNullMemprof {
+ public static void main(String[] args) {
+ int[] a = new int[0]; // test only valid when size is 0.
+ for (int i = 0; i < Integer.valueOf(10000); i++) // test only valid with boxed loop limit
+ try {
+ test(a);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ }
+
+ static void test(int[] a) {
+ for (int i = 0; i < 1;) {
+ a[i] = 0;
+ synchronized (TestUnlockNodeNullMemprof.class) {
+ }
+ for (int j = 0; Integer.valueOf(j) < 1;)
+ j = 0;
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java
index 3c28c936d310..7eed798871e8 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/ModDNodeTests.java
@@ -30,6 +30,7 @@
/*
* @test
* @bug 8345766
+ * @key randomness
* @summary Test that Ideal transformations of ModDNode are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.ModDNodeTests
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java
index 1b5e14b48357..886efe571241 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/ModFNodeTests.java
@@ -30,6 +30,7 @@
/*
* @test
* @bug 8345766
+ * @key randomness
* @summary Test that Ideal transformations of ModFNode are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.ModFNodeTests
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModINodeIdealizationTests.java
index d19e25627794..1ba715840315 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/ModINodeIdealizationTests.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/ModINodeIdealizationTests.java
@@ -32,6 +32,7 @@
/*
* @test
* @bug 8332268
+ * @key randomness
* @summary Test that Ideal transformations of ModINode* are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.ModINodeIdealizationTests
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ModLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ModLNodeIdealizationTests.java
index fc08ef60603e..719cc5962152 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/ModLNodeIdealizationTests.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/ModLNodeIdealizationTests.java
@@ -32,6 +32,7 @@
/*
* @test
* @bug 8267265
+ * @key randomness
* @summary Test that Ideal transformations of ModLNode* are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.ModLNodeIdealizationTests
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java
index fdc0a83fb8bc..bfcc775efeb4 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java
@@ -30,7 +30,7 @@
/*
* @test
- * @bug 8324655 8329797 8331090
+ * @bug 8324655 8331090
* @key randomness
* @summary Test that if expressions are properly folded into min/max nodes
* @library /test/lib /
@@ -139,42 +139,6 @@ public long testMaxL2E(long a, long b) {
return a <= b ? b : a;
}
- public class Dummy {
- long l;
- public Dummy(long l) { this.l = l; }
- }
-
- @Setup
- Object[] setupDummyArray() {
- Dummy[] arr = new Dummy[512];
- for (int i = 0; i < 512; i++) {
- arr[i] = new Dummy(RANDOM.nextLong());
- }
- return new Object[] { arr };
- }
-
- @Test
- @Arguments(setup = "setupDummyArray")
- @IR(failOn = { IRNode.MAX_L })
- public long testMaxLAndBarrierInLoop(Dummy[] arr) {
- long result = 0;
- for (int i = 0; i < arr.length; ++i) {
- result += Math.max(arr[i].l, 1);
- }
- return result;
- }
-
- @Test
- @Arguments(setup = "setupDummyArray")
- @IR(failOn = { IRNode.MIN_L })
- public long testMinLAndBarrierInLoop(Dummy[] arr) {
- long result = 0;
- for (int i = 0; i < arr.length; ++i) {
- result += Math.min(arr[i].l, 1);
- }
- return result;
- }
-
@Setup
static Object[] setupIntArrays() {
int[] a = new int[512];
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestMulNodeIdealization.java b/test/hotspot/jtreg/compiler/c2/irTests/TestMulNodeIdealization.java
index ca6d5fbe1185..e0307f2eb981 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/TestMulNodeIdealization.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/TestMulNodeIdealization.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
/*
* @test
* @bug 8291336
+ * @key randomness
* @summary Test that transformation of multiply-by-2 is appropriately turned into additions.
* @library /test/lib /
* @requires vm.compiler2.enabled
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java b/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java
index 2cdc6414685b..1413ee0cafa5 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java
@@ -31,6 +31,7 @@
/*
* @test
* @bug 8277850 8278949 8285793 8346664
+ * @key randomness
* @summary C2: optimize mask checks in counted loops
* @library /test/lib /
* @run driver compiler.c2.irTests.TestShiftAndMask
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/UDivINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/UDivINodeIdealizationTests.java
index 7183e0b311ac..dd135c93e0d7 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/UDivINodeIdealizationTests.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/UDivINodeIdealizationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
/*
* @test
* @bug 8332268
+ * @key randomness
* @summary Test that Ideal transformations of UDivINode* are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.UDivINodeIdealizationTests
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/UDivLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/UDivLNodeIdealizationTests.java
index a5cfc2cddc5f..bc80805cabd1 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/UDivLNodeIdealizationTests.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/UDivLNodeIdealizationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
/*
* @test
* @bug 8332268
+ * @key randomness
* @summary Test that Ideal transformations of UDivLNode* are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.UDivLNodeIdealizationTests
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/UModINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/UModINodeIdealizationTests.java
index fa24033ab46b..99982c70b6e4 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/UModINodeIdealizationTests.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/UModINodeIdealizationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
/*
* @test
* @bug 8332268
+ * @key randomness
* @summary Test that Ideal transformations of UModINode* are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.UModINodeIdealizationTests
diff --git a/test/hotspot/jtreg/compiler/c2/irTests/UModLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/UModLNodeIdealizationTests.java
index 93ae861cfaac..9bbded327e0d 100644
--- a/test/hotspot/jtreg/compiler/c2/irTests/UModLNodeIdealizationTests.java
+++ b/test/hotspot/jtreg/compiler/c2/irTests/UModLNodeIdealizationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
/*
* @test
* @bug 8332268
+ * @key randomness
* @summary Test that Ideal transformations of UModLNode* are being performed as expected.
* @library /test/lib /
* @run driver compiler.c2.irTests.UModLNodeIdealizationTests
diff --git a/test/hotspot/jtreg/compiler/controldependency/TestDivDependentOnMainLoopGuard.java b/test/hotspot/jtreg/compiler/controldependency/TestDivDependentOnMainLoopGuard.java
index a6b7a4627430..27fdc6b9e346 100644
--- a/test/hotspot/jtreg/compiler/controldependency/TestDivDependentOnMainLoopGuard.java
+++ b/test/hotspot/jtreg/compiler/controldependency/TestDivDependentOnMainLoopGuard.java
@@ -24,6 +24,7 @@
/*
* @test
* @bug 8349139
+ * @key randomness
* @summary C2: Div looses dependency on condition that guarantees divisor not null in counted loop
* @library /test/lib /
* @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileOnly=TestDivDependentOnMainLoopGuard::*
diff --git a/test/hotspot/jtreg/compiler/debug/TestStress.java b/test/hotspot/jtreg/compiler/debug/TestStress.java
index 6678d09e6499..7bb020e188de 100644
--- a/test/hotspot/jtreg/compiler/debug/TestStress.java
+++ b/test/hotspot/jtreg/compiler/debug/TestStress.java
@@ -71,7 +71,17 @@ static String macroExpansionTrace(int stressSeed) throws Exception {
static void sum(int n) {
int acc = 0;
- for (int i = 0; i < n; i++) acc += i;
+ int[] arr1 = new int[n];
+ int[] arr2 = new int[n];
+ int[] arr3 = new int[n];
+ int[] arr4 = new int[n];
+ for (int i = 0; i < n; i++) {
+ acc += i;
+ arr1[i] = i;
+ arr2[i] = acc;
+ arr3[i] = i * n;
+ arr4[i] = acc * n;
+ }
System.out.println(acc);
}
diff --git a/test/hotspot/jtreg/compiler/debug/TestStressDistinctSeed.java b/test/hotspot/jtreg/compiler/debug/TestStressDistinctSeed.java
new file mode 100644
index 000000000000..c91326b1fc17
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/debug/TestStressDistinctSeed.java
@@ -0,0 +1,118 @@
+/* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* This code is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License version 2 only, as
+* published by the Free Software Foundation.
+*
+* This code is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+* version 2 for more details (a copy is included in the LICENSE file that
+* accompanied this code).
+*
+* You should have received a copy of the GNU General Public License version
+* 2 along with this work; if not, write to the Free Software Foundation,
+* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+* or visit www.oracle.com if you need additional information or have any
+* questions.
+*/
+
+package compiler.debug;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Asserts;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Arrays;
+
+/*
+ * @test
+ * @key stress randomness
+ * @requires vm.debug == true & vm.compiler2.enabled & vm.flagless
+ * @summary Tests that stress compilations with the N different seeds yield different
+ * IGVN, CCP, and macro expansion traces.
+ * @library /test/lib /
+ * @run driver compiler.debug.TestStressDistinctSeed
+ */
+
+public class TestStressDistinctSeed {
+
+ private static int counter = 0;
+
+ static String phaseTrace(String stressOption, String traceOption,
+ int stressSeed) throws Exception {
+ String className = TestStressDistinctSeed.class.getName();
+ String[] procArgs = {
+ "-Xcomp", "-XX:-TieredCompilation", "-XX:-Inline", "-XX:+CICountNative",
+ "-XX:CompileOnly=" + className + "::sum", "-XX:" + traceOption,
+ "-XX:+" + stressOption, "-XX:StressSeed=" + stressSeed,
+ className, "5" };
+ ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(procArgs);
+ OutputAnalyzer out = new OutputAnalyzer(pb.start());
+ out.shouldHaveExitValue(0);
+ return out.getStdout();
+ }
+
+ static String igvnTrace(int stressSeed) throws Exception {
+ return phaseTrace("StressIGVN", "+TraceIterativeGVN", stressSeed);
+ }
+
+ static String ccpTrace(int stressSeed) throws Exception {
+ return phaseTrace("StressCCP", "+TracePhaseCCP", stressSeed);
+ }
+
+ static String macroExpansionTrace(int stressSeed) throws Exception {
+ return phaseTrace("StressMacroExpansion",
+ "CompileCommand=PrintIdealPhase,*::*,AFTER_MACRO_EXPANSION_STEP",
+ stressSeed);
+ }
+
+ static void sum(int n) {
+ int[] arr1 = new int[n];
+ for (int i = 0; i < n; i++) {
+ synchronized (TestStressDistinctSeed.class) {
+ counter += i;
+ arr1[i] = counter;
+ }
+ }
+ System.out.println(counter);
+ }
+
+ public static void main(String[] args) throws Exception {
+ Set igvnTraceSet = new HashSet<>();
+ Set ccpTraceSet = new HashSet<>();
+ Set macroExpansionTraceSet = new HashSet<>();
+ String igvnTraceOutput, ccpTraceOutput, macroExpansionTraceOutput;
+ if (args.length == 0) {
+ for (int s = 0; s < 5; s++) {
+ igvnTraceOutput = igvnTrace(s);
+ ccpTraceOutput = ccpTrace(s);
+ macroExpansionTraceOutput = macroExpansionTrace(s);
+ // Test same seed produce same result to test that different traces come from different seed and
+ // not indeterminism with the test.
+ Asserts.assertEQ(igvnTraceOutput, igvnTrace(s),
+ "got different IGVN traces for the same seed");
+ Asserts.assertEQ(ccpTraceOutput, ccpTrace(s),
+ "got different CCP traces for the same seed");
+ Asserts.assertEQ(macroExpansionTraceOutput, macroExpansionTrace(s),
+ "got different macro expansion traces for the same seed");
+
+ igvnTraceSet.add(igvnTraceOutput);
+ ccpTraceSet.add(ccpTraceOutput);
+ macroExpansionTraceSet.add(macroExpansionTraceOutput);
+ }
+ Asserts.assertGT(igvnTraceSet.size(), 1,
+ "got same IGVN traces for 5 different seeds");
+ Asserts.assertGT(ccpTraceSet.size(), 1,
+ "got same CCP traces for 5 different seeds");
+ Asserts.assertGT(macroExpansionTraceSet.size(), 1,
+ "got same macro expansion traces for 5 different seeds");
+ } else if (args.length > 0) {
+ sum(Integer.parseInt(args[0]));
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationNotReducibleAnymore.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationNotReducibleAnymore.java
new file mode 100644
index 000000000000..16bdd68c93c4
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationNotReducibleAnymore.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8361699
+ * @summary Check that NSR state propagate correctly to initially reducible Phis
+ * and turns them into non-reducible Phis.
+ * @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationNotReducibleAnymore*::*
+ * -XX:CompileCommand=dontinline,*TestReduceAllocationNotReducibleAnymore*::*
+ * -Xcomp compiler.escapeAnalysis.TestReduceAllocationNotReducibleAnymore
+ * @run main compiler.escapeAnalysis.TestReduceAllocationNotReducibleAnymore
+ */
+
+package compiler.escapeAnalysis;
+
+public class TestReduceAllocationNotReducibleAnymore {
+ public static void main(String[] args) {
+ for (int i = 0; i < 100; i++) {
+ test(4, null);
+ }
+ }
+
+ static void test(int x, A a) {
+ Object[] objects = { new Object() };
+ Object object = new Object();
+ for (int i = 0; i < 150; i++) {
+ try {
+ objects[x] = object;
+ object = new byte[10];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ try {
+ a.foo();
+ } catch (NullPointerException e) {
+ }
+ }
+ }
+
+ class A {
+ void foo() {}
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/gcbarriers/TestMinMaxLongLoopBarrier.java b/test/hotspot/jtreg/compiler/gcbarriers/TestMinMaxLongLoopBarrier.java
new file mode 100644
index 000000000000..9f2ddc0d20e5
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/gcbarriers/TestMinMaxLongLoopBarrier.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package compiler.gcbarriers;
+
+import compiler.lib.ir_framework.Arguments;
+import compiler.lib.ir_framework.IR;
+import compiler.lib.ir_framework.IRNode;
+import compiler.lib.ir_framework.Setup;
+import compiler.lib.ir_framework.Test;
+import compiler.lib.ir_framework.TestFramework;
+import jdk.test.lib.Utils;
+
+import java.util.Random;
+
+/*
+ * @test
+ * @bug 8329797
+ * @key randomness
+ * @summary Test that MinL/MaxL nodes are removed when GC barriers in loop
+ * @library /test/lib /
+ * @run driver ${test.main.class}
+ */
+public class TestMinMaxLongLoopBarrier {
+ private static final Random RANDOM = Utils.getRandomInstance();
+
+ public static void main(String[] args) {
+ TestFramework.run();
+ }
+
+ public class Dummy {
+ long l;
+ public Dummy(long l) { this.l = l; }
+ }
+
+ @Setup
+ Object[] setupDummyArray() {
+ Dummy[] arr = new Dummy[512];
+ for (int i = 0; i < 512; i++) {
+ arr[i] = new Dummy(RANDOM.nextLong());
+ }
+ return new Object[] { arr };
+ }
+
+ @Test
+ @Arguments(setup = "setupDummyArray")
+ @IR(failOn = { IRNode.MAX_L })
+ public long testMaxLAndBarrierInLoop(Dummy[] arr) {
+ long result = 0;
+ for (int i = 0; i < arr.length; ++i) {
+ result += Math.max(arr[i].l, 1);
+ }
+ return result;
+ }
+
+ @Test
+ @Arguments(setup = "setupDummyArray")
+ @IR(failOn = { IRNode.MIN_L })
+ public long testMinLAndBarrierInLoop(Dummy[] arr) {
+ long result = 0;
+ for (int i = 0; i < arr.length; ++i) {
+ result += Math.min(arr[i].l, 1);
+ }
+ return result;
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java
new file mode 100644
index 000000000000..570c59f0da2a
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8359412
+ * @key randomness
+ * @summary Use the template framework library to generate random expressions.
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib /
+ * @compile ../lib/verify/Verify.java
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:CompileTaskTimeout=10000 compiler.igvn.ExpressionFuzzer
+ */
+
+package compiler.igvn;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Random;
+import java.util.Collections;
+import jdk.test.lib.Utils;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import compiler.lib.compile_framework.*;
+import compiler.lib.template_framework.Template;
+import compiler.lib.template_framework.TemplateToken;
+import static compiler.lib.template_framework.Template.scope;
+import static compiler.lib.template_framework.Template.let;
+import static compiler.lib.template_framework.Template.$;
+import compiler.lib.template_framework.library.CodeGenerationDataNameType;
+import compiler.lib.template_framework.library.Expression;
+import compiler.lib.template_framework.library.Operations;
+import compiler.lib.template_framework.library.PrimitiveType;
+import compiler.lib.template_framework.library.TestFrameworkClass;
+import static compiler.lib.template_framework.library.CodeGenerationDataNameType.PRIMITIVE_TYPES;
+
+// We generate random Expressions from primitive type operators.
+//
+// The goal is to generate random inputs with constrained TypeInt / TypeLong ranges / KnownBits,
+// and then verify the output value, ranges and bits.
+//
+// Should this test fail and make a lot of noise in the CI, you have two choices:
+// - Problem-list this test: but other tests may also use the same broken operators.
+// - Temporarily remove the operator from {@code Operations.PRIMITIVE_OPERATIONS}.
+//
+// Future Work [FUTURE]:
+// - Constrain also the unsigned bounds
+// - Some basic IR tests to ensure that the constraints / checksum mechanics work.
+// We may even have to add some IGVN optimizations to be able to better observe things right.
+// - Lower the CompileTaskTimeout, if possible. It is chosen conservatively (rather high) for now.
+public class ExpressionFuzzer {
+ private static final Random RANDOM = Utils.getRandomInstance();
+
+ public static record MethodArgument(String name, CodeGenerationDataNameType type) {}
+ public static record StringPair(String s0, String s1) {}
+
+ public static void main(String[] args) {
+ // Create a new CompileFramework instance.
+ CompileFramework comp = new CompileFramework();
+
+ // Add a java source file.
+ comp.addJavaSourceCode("compiler.igvn.templated.ExpressionFuzzerInnerTest", generate(comp));
+
+ // Compile the source file.
+ comp.compile();
+
+ // compiler.igvn.templated.InnterTest.main(new String[] {});
+ comp.invoke("compiler.igvn.templated.ExpressionFuzzerInnerTest", "main", new Object[] {new String[] {}});
+ }
+
+ // Generate a Java source file as String
+ public static String generate(CompileFramework comp) {
+ // Generate a list of test methods.
+ List tests = new ArrayList<>();
+
+ // We are going to use some random numbers in our tests, so import some good methods for that.
+ tests.add(PrimitiveType.generateLibraryRNG());
+
+ // Create the body for the test. We use it twice: compiled and reference.
+ // Execute the expression and catch expected Exceptions.
+ var bodyTemplate = Template.make("expression", "arguments", "checksum", (Expression expression, List