Use this report style when reporting test results (sent to JTReg as
-report). Defaults to files.
+
MANUAL
+
Set to true to execute manual tests only.
Gtest keywords
REPEAT
The number of times to repeat the tests
diff --git a/doc/testing.md b/doc/testing.md
index bb56c05c295b..537560435202 100644
--- a/doc/testing.md
+++ b/doc/testing.md
@@ -499,6 +499,10 @@ helps to reproduce intermittent test failures. Defaults to 0.
Use this report style when reporting test results (sent to JTReg as `-report`).
Defaults to `files`.
+#### MANUAL
+
+Set to `true` to execute manual tests only.
+
### Gtest keywords
#### REPEAT
diff --git a/make/Bundles.gmk b/make/Bundles.gmk
index ba8ec0c864b0..d48a9c037dc9 100644
--- a/make/Bundles.gmk
+++ b/make/Bundles.gmk
@@ -125,13 +125,6 @@ define SetupBundleFileBody
&& $(TAR) cf - -$(TAR_INCLUDE_PARAM) $$($1_$$d_LIST_FILE) \
$(TAR_IGNORE_EXIT_VALUE) ) \
| ( $(CD) $(SUPPORT_OUTPUTDIR)/bundles/$1/$$($1_SUBDIR) && $(TAR) xf - )$$(NEWLINE) )
- # Rename stripped pdb files
- ifeq ($(call isTargetOs, windows)+$(SHIP_DEBUG_SYMBOLS), true+public)
- for f in `$(FIND) $(SUPPORT_OUTPUTDIR)/bundles/$1/$$($1_SUBDIR) -name "*.stripped.pdb"`; do \
- $(ECHO) Renaming $$$${f} to $$$${f%stripped.pdb}pdb $(LOG_INFO); \
- $(MV) $$$${f} $$$${f%stripped.pdb}pdb; \
- done
- endif
# Unzip any zipped debuginfo files
ifeq ($$($1_UNZIP_DEBUGINFO), true)
for f in `$(FIND) $(SUPPORT_OUTPUTDIR)/bundles/$1/$$($1_SUBDIR) -name "*.diz"`; do \
@@ -192,96 +185,30 @@ endif
ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), )
- SYMBOLS_EXCLUDE_PATTERN := %.debuginfo %.diz %.map
-
- # There may be files with spaces in the names, so use ShellFindFiles
- # explicitly.
+ # There may be files with spaces in the names, so use ShellFindFiles explicitly.
ALL_JDK_FILES := $(call ShellFindFiles, $(JDK_IMAGE_DIR))
- ifneq ($(JDK_IMAGE_DIR), $(JDK_SYMBOLS_IMAGE_DIR))
- ALL_JDK_SYMBOLS_FILES := $(call ShellFindFiles, $(JDK_SYMBOLS_IMAGE_DIR))
- else
- ALL_JDK_SYMBOLS_FILES := $(ALL_JDK_FILES)
- endif
ifneq ($(JDK_IMAGE_DIR), $(JDK_DEMOS_IMAGE_DIR))
ALL_JDK_DEMOS_FILES := $(call ShellFindFiles, $(JDK_DEMOS_IMAGE_DIR))
else
ALL_JDK_DEMOS_FILES := $(ALL_JDK_FILES)
endif
- # Create special filter rules when dealing with unzipped .dSYM directories on
- # macosx
- ifeq ($(call isTargetOs, macosx), true)
- ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), false)
- JDK_SYMBOLS_EXCLUDE_PATTERN := $(addprefix %, \
- $(call containing, .dSYM/, $(patsubst $(JDK_IMAGE_DIR)/%, %, \
- $(ALL_JDK_SYMBOLS_FILES))))
- endif
- endif
-
- # Create special filter rules when dealing with debug symbols on windows
- ifeq ($(call isTargetOs, windows), true)
- ifeq ($(SHIP_DEBUG_SYMBOLS), )
- JDK_SYMBOLS_EXCLUDE_PATTERN := %.pdb
- else
- ifeq ($(SHIP_DEBUG_SYMBOLS), public)
- JDK_SYMBOLS_EXCLUDE_PATTERN := \
- $(filter-out \
- %.stripped.pdb, \
- $(filter %.pdb, $(ALL_JDK_FILES)) \
- )
- endif
- endif
- endif
-
JDK_BUNDLE_FILES := \
$(filter-out \
- $(JDK_SYMBOLS_EXCLUDE_PATTERN) \
$(JDK_EXTRA_EXCLUDES) \
- $(SYMBOLS_EXCLUDE_PATTERN) \
$(JDK_IMAGE_HOMEDIR)/demo/% \
, \
$(ALL_JDK_FILES) \
)
- JDK_SYMBOLS_BUNDLE_FILES := \
- $(filter-out \
- %.stripped.pdb, \
- $(call FindFiles, $(SYMBOLS_IMAGE_DIR)) \
- )
+ JDK_SYMBOLS_BUNDLE_FILES := $(call FindFiles, $(SYMBOLS_IMAGE_DIR))
TEST_DEMOS_BUNDLE_FILES := $(filter $(JDK_DEMOS_IMAGE_HOMEDIR)/demo/%, \
$(ALL_JDK_DEMOS_FILES))
ALL_JRE_FILES := $(call ShellFindFiles, $(JRE_IMAGE_DIR))
- # Create special filter rules when dealing with unzipped .dSYM directories on
- # macosx
- ifeq ($(OPENJDK_TARGET_OS), macosx)
- ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), false)
- JRE_SYMBOLS_EXCLUDE_PATTERN := $(addprefix %, \
- $(call containing, .dSYM/, $(patsubst $(JRE_IMAGE_DIR)/%, %, $(ALL_JRE_FILES))))
- endif
- endif
-
- # Create special filter rules when dealing with debug symbols on windows
- ifeq ($(call isTargetOs, windows), true)
- ifeq ($(SHIP_DEBUG_SYMBOLS), )
- JRE_SYMBOLS_EXCLUDE_PATTERN := %.pdb
- else
- ifeq ($(SHIP_DEBUG_SYMBOLS), public)
- JRE_SYMBOLS_EXCLUDE_PATTERN := \
- $(filter-out \
- %.stripped.pdb, \
- $(filter %.pdb, $(ALL_JRE_FILES)) \
- )
- endif
- endif
- endif
-
- JRE_BUNDLE_FILES := $(filter-out \
- $(JRE_SYMBOLS_EXCLUDE_PATTERN) \
- $(SYMBOLS_EXCLUDE_PATTERN), \
- $(ALL_JRE_FILES))
+ JRE_BUNDLE_FILES := $(ALL_JRE_FILES)
ifeq ($(MACOSX_CODESIGN_MODE), hardened)
# Macosx release build and code signing available.
diff --git a/make/CreateJmods.gmk b/make/CreateJmods.gmk
index 40bceda69a97..b252ff017326 100644
--- a/make/CreateJmods.gmk
+++ b/make/CreateJmods.gmk
@@ -218,10 +218,14 @@ ifeq ($(call isTargetOs, windows), true)
ifeq ($(SHIP_DEBUG_SYMBOLS), )
JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.pdb,*.map}'
else
- JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.map}'
+ JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.map}'
endif
else
- JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.debuginfo,*.dSYM/**,*.dSYM}'
+ ifeq ($(SHIP_DEBUG_SYMBOLS), )
+ JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.debuginfo,*.dSYM/**,*.dSYM}'
+ else
+ JMOD_FLAGS += --exclude '**{_the.*,_*.marker*}'
+ endif
endif
# Unless we are creating a very large module, use the small tool JVM options
diff --git a/make/Images.gmk b/make/Images.gmk
index 22e3e43cb1f3..66ffda4f2e38 100644
--- a/make/Images.gmk
+++ b/make/Images.gmk
@@ -282,29 +282,33 @@ else
endif
CMDS_TARGET_SUBDIR := bin
-# Param 1 - either JDK or JRE
+# Copy debug info files into symbols bundle.
+# In case of Windows and --with-external-symbols-in-bundles=public, take care to remove *.stripped.pdb files
SetupCopyDebuginfo = \
$(foreach m, $(ALL_$1_MODULES), \
+ $(eval dbgfiles := $(call FindDebuginfoFiles, $(SUPPORT_OUTPUTDIR)/modules_libs/$m)) \
+ $(eval dbgfiles := $(if $(filter true+public,$(call isTargetOs,windows)+$(SHIP_DEBUG_SYMBOLS)), \
+ $(filter-out %.stripped.pdb,$(dbgfiles)),$(dbgfiles)) \
+ ) \
$(eval $(call SetupCopyFiles, COPY_$1_LIBS_DEBUGINFO_$m, \
SRC := $(SUPPORT_OUTPUTDIR)/modules_libs/$m, \
DEST := $($1_IMAGE_DIR)/$(LIBS_TARGET_SUBDIR), \
- FILES := $(call FindDebuginfoFiles, \
- $(SUPPORT_OUTPUTDIR)/modules_libs/$m), \
+ FILES := $(dbgfiles), \
)) \
$(eval $1_TARGETS += $$(COPY_$1_LIBS_DEBUGINFO_$m)) \
+ $(eval dbgfiles := $(call FindDebuginfoFiles, $(SUPPORT_OUTPUTDIR)/modules_cmds/$m)) \
+ $(eval dbgfiles := $(if $(filter true+public,$(call isTargetOs,windows)+$(SHIP_DEBUG_SYMBOLS)), \
+ $(filter-out %.stripped.pdb,$(dbgfiles)),$(dbgfiles)) \
+ ) \
$(eval $(call SetupCopyFiles, COPY_$1_CMDS_DEBUGINFO_$m, \
SRC := $(SUPPORT_OUTPUTDIR)/modules_cmds/$m, \
DEST := $($1_IMAGE_DIR)/$(CMDS_TARGET_SUBDIR), \
- FILES := $(call FindDebuginfoFiles, \
- $(SUPPORT_OUTPUTDIR)/modules_cmds/$m), \
+ FILES := $(dbgfiles), \
)) \
$(eval $1_TARGETS += $$(COPY_$1_CMDS_DEBUGINFO_$m)) \
)
-# No space before argument to avoid having to put $(strip ) everywhere in
-# implementation above.
-$(call SetupCopyDebuginfo,JDK)
-$(call SetupCopyDebuginfo,JRE)
+# No space before argument to avoid having to put $(strip ) everywhere in implementation above.
$(call SetupCopyDebuginfo,SYMBOLS)
################################################################################
diff --git a/make/RunTests.gmk b/make/RunTests.gmk
index 60ae1bd4763c..23cdcf57ec21 100644
--- a/make/RunTests.gmk
+++ b/make/RunTests.gmk
@@ -205,7 +205,8 @@ $(eval $(call SetTestOpt,AOT_JDK,JTREG))
$(eval $(call ParseKeywordVariable, JTREG, \
SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \
TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY MAX_MEM RUN_PROBLEM_LISTS \
- RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT AOT_JDK $(CUSTOM_JTREG_SINGLE_KEYWORDS), \
+ RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT AOT_JDK MANUAL \
+ $(CUSTOM_JTREG_SINGLE_KEYWORDS), \
STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \
EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS \
$(CUSTOM_JTREG_STRING_KEYWORDS), \
@@ -901,7 +902,13 @@ define SetupRunJtregTestBody
-vmoption:-Dtest.boot.jdk="$$(BOOT_JDK)" \
-vmoption:-Djava.io.tmpdir="$$($1_TEST_TMP_DIR)"
- $1_JTREG_BASIC_OPTIONS += -automatic -ignore:quiet
+ $1_JTREG_BASIC_OPTIONS += -ignore:quiet
+
+ ifeq ($$(JTREG_MANUAL), true)
+ $1_JTREG_BASIC_OPTIONS += -manual
+ else
+ $1_JTREG_BASIC_OPTIONS += -automatic
+ endif
# Make it possible to specify the JIB_DATA_DIR for tests using the
# JIB Artifact resolver
diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4
index 9bea6b5062e8..a2fbae9c43c6 100644
--- a/make/autoconf/flags-cflags.m4
+++ b/make/autoconf/flags-cflags.m4
@@ -531,12 +531,9 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER],
TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -fstack-protector"
TOOLCHAIN_CFLAGS_JDK="-fvisibility=hidden -pipe -fstack-protector"
# reduce lib size on linux in link step, this needs also special compile flags
- # do this on s390x also for libjvm (where serviceability agent is not supported)
if test "x$ENABLE_LINKTIME_GC" = xtrue; then
TOOLCHAIN_CFLAGS_JDK="$TOOLCHAIN_CFLAGS_JDK -ffunction-sections -fdata-sections"
- if test "x$OPENJDK_TARGET_CPU" = xs390x && test "x$DEBUG_LEVEL" == xrelease; then
- TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections"
- fi
+ TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections"
fi
# technically NOT for CXX (but since this gives *worse* performance, use
# no-strict-aliasing everywhere!)
diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4
index 77ea7a693c91..9d9fee79e751 100644
--- a/make/autoconf/flags-ldflags.m4
+++ b/make/autoconf/flags-ldflags.m4
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -51,17 +51,16 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
# add -z,now ("full relro" - more of the Global Offset Table GOT is marked read only)
# add --no-as-needed to disable default --as-needed link flag on some GCC toolchains
BASIC_LDFLAGS="-Wl,-z,defs -Wl,-z,relro -Wl,-z,now -Wl,--no-as-needed -Wl,--exclude-libs,ALL"
+
+ BASIC_LDFLAGS_JVM_ONLY=""
# Linux : remove unused code+data in link step
if test "x$ENABLE_LINKTIME_GC" = xtrue; then
- if test "x$OPENJDK_TARGET_CPU" = xs390x; then
- BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,--gc-sections"
- else
- BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,--gc-sections"
- fi
+ # keep vtables : -Wl,--undefined-glob=_ZTV* (but this seems not to work with gold ld)
+ # so keep at least the Metadata vtable that is used in the serviceability agent
+ BASIC_LDFLAGS_JVM_ONLY="$BASIC_LDFLAGS_JVM_ONLY -Wl,--gc-sections -Wl,--undefined=_ZTV8Metadata"
+ BASIC_LDFLAGS_JDK_ONLY="$BASIC_LDFLAGS_JDK_ONLY -Wl,--gc-sections"
fi
- BASIC_LDFLAGS_JVM_ONLY=""
-
LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r"
elif test "x$TOOLCHAIN_TYPE" = xclang; then
diff --git a/make/autoconf/flags-other.m4 b/make/autoconf/flags-other.m4
index f0fa82489df3..9d41cf047913 100644
--- a/make/autoconf/flags-other.m4
+++ b/make/autoconf/flags-other.m4
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -115,7 +115,11 @@ AC_DEFUN([FLAGS_SETUP_ASFLAGS],
# Force preprocessor to run, just to make sure
BASIC_ASFLAGS="-x assembler-with-cpp"
elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then
- BASIC_ASFLAGS="-nologo -c"
+ if test "x$OPENJDK_TARGET_CPU" = xaarch64; then
+ BASIC_ASFLAGS="-nologo"
+ else
+ BASIC_ASFLAGS="-nologo -c"
+ fi
fi
AC_SUBST(BASIC_ASFLAGS)
diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4
index c4d203aff3c9..6091d155bef1 100644
--- a/make/autoconf/jdk-options.m4
+++ b/make/autoconf/jdk-options.m4
@@ -327,23 +327,36 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS],
AC_MSG_CHECKING([if we should add external native debug symbols to the shipped bundles])
AC_ARG_WITH([external-symbols-in-bundles],
[AS_HELP_STRING([--with-external-symbols-in-bundles],
- [which type of external native debug symbol information shall be shipped in product bundles (none, public, full)
- (e.g. ship full/stripped pdbs on Windows) @<:@none@:>@])])
+ [which type of external native debug symbol information shall be shipped with bundles/images (none, public, full).
+ @<:@none in release builds, full otherwise. --with-native-debug-symbols=external/zipped is a prerequisite. public is only supported on Windows@:>@])],
+ [],
+ [with_external_symbols_in_bundles=default])
if test "x$with_external_symbols_in_bundles" = x || test "x$with_external_symbols_in_bundles" = xnone ; then
AC_MSG_RESULT([no])
elif test "x$with_external_symbols_in_bundles" = xfull || test "x$with_external_symbols_in_bundles" = xpublic ; then
- if test "x$OPENJDK_TARGET_OS" != xwindows ; then
- AC_MSG_ERROR([--with-external-symbols-in-bundles currently only works on windows!])
- elif test "x$COPY_DEBUG_SYMBOLS" != xtrue ; then
- AC_MSG_ERROR([--with-external-symbols-in-bundles only works when --with-native-debug-symbols=external is used!])
- elif test "x$with_external_symbols_in_bundles" = xfull ; then
+ if test "x$COPY_DEBUG_SYMBOLS" != xtrue ; then
+ AC_MSG_ERROR([--with-external-symbols-in-bundles only works when --with-native-debug-symbols=external/zipped is used!])
+ elif test "x$with_external_symbols_in_bundles" = xpublic && test "x$OPENJDK_TARGET_OS" != xwindows ; then
+ AC_MSG_ERROR([--with-external-symbols-in-bundles=public is only supported on Windows!])
+ fi
+
+ if test "x$with_external_symbols_in_bundles" = xfull ; then
AC_MSG_RESULT([full])
SHIP_DEBUG_SYMBOLS=full
else
AC_MSG_RESULT([public])
SHIP_DEBUG_SYMBOLS=public
fi
+ elif test "x$with_external_symbols_in_bundles" = xdefault ; then
+ if test "x$DEBUG_LEVEL" = xrelease ; then
+ AC_MSG_RESULT([no (default)])
+ elif test "x$COPY_DEBUG_SYMBOLS" = xtrue ; then
+ AC_MSG_RESULT([full (default)])
+ SHIP_DEBUG_SYMBOLS=full
+ else
+ AC_MSG_RESULT([no (default, native debug symbols are not external/zipped)])
+ fi
else
AC_MSG_ERROR([$with_external_symbols_in_bundles is an unknown value for --with-external-symbols-in-bundles])
fi
diff --git a/make/autoconf/lib-tests.m4 b/make/autoconf/lib-tests.m4
index 23f3d443a6c7..faaf229eacda 100644
--- a/make/autoconf/lib-tests.m4
+++ b/make/autoconf/lib-tests.m4
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,7 @@
################################################################################
# Minimum supported versions
-JTREG_MINIMUM_VERSION=8
+JTREG_MINIMUM_VERSION=8.2.1
GTEST_MINIMUM_VERSION=1.14.0
################################################################################
diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4
index b7a010746862..f3ef44d382b1 100644
--- a/make/autoconf/toolchain.m4
+++ b/make/autoconf/toolchain.m4
@@ -655,8 +655,11 @@ AC_DEFUN_ONCE([TOOLCHAIN_DETECT_TOOLCHAIN_CORE],
if test "x$TOOLCHAIN_TYPE" != xmicrosoft; then
AS="$CC -c"
else
- if test "x$OPENJDK_TARGET_CPU_BITS" = "x64"; then
- # On 64 bit windows, the assembler is "ml64.exe"
+ if test "x$OPENJDK_TARGET_CPU" = "xaarch64"; then
+ # On Windows aarch64, the assembler is "armasm64.exe"
+ UTIL_LOOKUP_TOOLCHAIN_PROGS(AS, armasm64)
+ elif test "x$OPENJDK_TARGET_CPU_BITS" = "x64"; then
+ # On Windows x64, the assembler is "ml64.exe"
UTIL_LOOKUP_TOOLCHAIN_PROGS(AS, ml64)
else
# otherwise, the assembler is "ml.exe"
diff --git a/make/common/native/CompileFile.gmk b/make/common/native/CompileFile.gmk
index 9c3d39d6edf3..39b5f34a4c5b 100644
--- a/make/common/native/CompileFile.gmk
+++ b/make/common/native/CompileFile.gmk
@@ -155,6 +155,12 @@ define CreateCompiledNativeFileBody
endif
$1_FLAGS := $$($1_FLAGS) -DASSEMBLY_SRC_FILE='"$$($1_REL_ASM_SRC)"' \
-include $(TOPDIR)/make/data/autoheaders/assemblyprefix.h
+ else ifeq ($(TOOLCHAIN_TYPE), microsoft)
+ ifeq ($(OPENJDK_TARGET_CPU), aarch64)
+ $1_NON_ASM_EXTENSION_FLAG :=
+ else
+ $1_NON_ASM_EXTENSION_FLAG := "-Ta"
+ endif
endif
else ifneq ($$(filter %.cpp %.cc %.mm, $$($1_FILENAME)), )
# Compile as a C++ or Objective-C++ file
@@ -236,7 +242,7 @@ define CreateCompiledNativeFileBody
# For assembler calls just create empty dependency lists
$$(call ExecuteWithLog, $$@, $$(call MakeCommandRelative, \
$$($1_COMPILER) $$($1_FLAGS) \
- $(CC_OUT_OPTION)$$($1_OBJ) -Ta $$($1_SRC_FILE))) \
+ $(CC_OUT_OPTION)$$($1_OBJ) $$($1_NON_ASM_EXTENSION_FLAG) $$($1_SRC_FILE))) \
| $(TR) -d '\r' | $(GREP) -v -e "Assembling:" || test "$$$$?" = "1" ; \
$(ECHO) > $$($1_DEPS_FILE) ; \
$(ECHO) > $$($1_DEPS_TARGETS_FILE)
diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf
index 16432a56ba28..9c4cdfcecb0f 100644
--- a/make/conf/github-actions.conf
+++ b/make/conf/github-actions.conf
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
# Versions and download locations for dependencies used by GitHub Actions (GHA)
GTEST_VERSION=1.14.0
-JTREG_VERSION=8+2
+JTREG_VERSION=8.2.1+1
LINUX_X64_BOOT_JDK_EXT=tar.gz
LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.3%2B9/OpenJDK25U-jdk_x64_linux_hotspot_25.0.3_9.tar.gz
diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js
index 4f2bd27d54c2..37cdf5315de4 100644
--- a/make/conf/jib-profiles.js
+++ b/make/conf/jib-profiles.js
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1174,9 +1174,9 @@ var getJibProfilesDependencies = function (input, common) {
jtreg: {
server: "jpg",
product: "jtreg",
- version: "8",
- build_number: "2",
- file: "bundles/jtreg-8+2.zip",
+ version: "8.2.1",
+ build_number: "1",
+ file: "bundles/jtreg-8.2.1+1.zip",
environment_name: "JT_HOME",
environment_path: input.get("jtreg", "home_path") + "/bin",
configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"),
diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf
index c6b3e674c7a4..ae507960576e 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=4
+DEFAULT_VERSION_UPDATE=5
DEFAULT_VERSION_PATCH=0
DEFAULT_VERSION_EXTRA1=0
DEFAULT_VERSION_EXTRA2=0
DEFAULT_VERSION_EXTRA3=0
-DEFAULT_VERSION_DATE=2026-07-21
+DEFAULT_VERSION_DATE=2026-10-20
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 cead9e644360..4b7bc1a365e3 100644
--- a/make/hotspot/lib/CompileJvm.gmk
+++ b/make/hotspot/lib/CompileJvm.gmk
@@ -149,6 +149,12 @@ JVM_STRIPFLAGS ?= $(STRIPFLAGS)
# This source set is reused so save in cache.
$(call FillFindCache, $(JVM_SRC_DIRS))
+ifeq ($(SHIP_DEBUG_SYMBOLS), full)
+ CFLAGS_SHIP_DEBUGINFO := -DSHIP_DEBUGINFO_FULL
+else ifeq ($(SHIP_DEBUG_SYMBOLS), public)
+ CFLAGS_SHIP_DEBUGINFO := -DSHIP_DEBUGINFO_PUBLIC
+endif
+
ifeq ($(call isTargetOs, windows), true)
ifeq ($(STATIC_LIBS), true)
WIN_EXPORT_FILE := $(JVM_OUTPUTDIR)/static-win-exports.def
@@ -156,10 +162,6 @@ ifeq ($(call isTargetOs, windows), true)
WIN_EXPORT_FILE := $(JVM_OUTPUTDIR)/win-exports.def
endif
- ifeq ($(SHIP_DEBUG_SYMBOLS), public)
- CFLAGS_STRIPPED_DEBUGINFO := -DHAS_STRIPPED_DEBUGINFO
- endif
-
JVM_LDFLAGS += -def:$(WIN_EXPORT_FILE)
endif
@@ -185,7 +187,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \
CFLAGS := $(JVM_CFLAGS), \
abstract_vm_version.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \
arguments.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \
- whitebox.cpp_CXXFLAGS := $(CFLAGS_STRIPPED_DEBUGINFO), \
+ whitebox.cpp_CXXFLAGS := $(CFLAGS_SHIP_DEBUGINFO), \
DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc), \
DISABLED_WARNINGS_gcc_ad_$(HOTSPOT_TARGET_CPU_ARCH).cpp := nonnull, \
DISABLED_WARNINGS_gcc_bytecodeInterpreter.cpp := unused-label, \
diff --git a/make/modules/java.desktop/lib/ClientLibraries.gmk b/make/modules/java.desktop/lib/ClientLibraries.gmk
index 6a115a81dba9..c9eb87fa7c40 100644
--- a/make/modules/java.desktop/lib/ClientLibraries.gmk
+++ b/make/modules/java.desktop/lib/ClientLibraries.gmk
@@ -397,6 +397,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBFONTMANAGER, \
AccelGlyphCache.c, \
CFLAGS := $(LIBFONTMANAGER_CFLAGS), \
CXXFLAGS := $(LIBFONTMANAGER_CFLAGS), \
+ CXXFLAGS_gcc := -fno-rtti -fno-exceptions, \
+ CXXFLAGS_clang := -fno-rtti -fno-exceptions, \
OPTIMIZATION := HIGHEST, \
CFLAGS_windows = -DCC_NOEX, \
EXTRA_HEADER_DIRS := $(LIBFONTMANAGER_EXTRA_HEADER_DIRS), \
diff --git a/make/modules/jdk.jconsole/Launcher.gmk b/make/modules/jdk.jconsole/Launcher.gmk
index 7cb40a1b13a1..adb3b05c400b 100644
--- a/make/modules/jdk.jconsole/Launcher.gmk
+++ b/make/modules/jdk.jconsole/Launcher.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
@@ -30,15 +30,15 @@ include LauncherCommon.gmk
################################################################################
## Build jconsole
################################################################################
-
-$(eval $(call SetupBuildLauncher, jconsole, \
- MAIN_CLASS := sun.tools.jconsole.JConsole, \
- JAVA_ARGS := \
- --add-opens java.base/java.io=jdk.jconsole \
- --add-modules ALL-DEFAULT \
- -Djconsole.showOutputViewer \
- -Djdk.attach.allowAttachSelf=true, \
- WINDOWS_JAVAW := true, \
-))
-
+ifneq ($(ENABLE_HEADLESS_ONLY), true)
+ $(eval $(call SetupBuildLauncher, jconsole, \
+ MAIN_CLASS := sun.tools.jconsole.JConsole, \
+ JAVA_ARGS := \
+ --add-opens java.base/java.io=jdk.jconsole \
+ --add-modules ALL-DEFAULT \
+ -Djconsole.showOutputViewer \
+ -Djdk.attach.allowAttachSelf=true, \
+ WINDOWS_JAVAW := true, \
+ ))
+endif
################################################################################
diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
index a0d1e22ff969..b7f064bcd8cb 100644
--- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
@@ -6024,14 +6024,18 @@ class StubGenerator: public StubCodeGenerator {
// static int implKyber12To16(
// byte[] condensed, int index, short[] parsed, int parsedLength) {}
//
- // (parsedLength or (parsedLength - 48) must be divisible by 64.)
+ // we assume that parsed and condensed are allocated such that for
+ // n = (parsedLength + 63) / 64
+ // n blocks of 96 bytes of input can be processed, i.e.
+ // index + n * 96 <= condensed.length and
+ // n * 64 <= parsed.length
//
// condensed (byte[]) = c_rarg0
// condensedIndex = c_rarg1
- // parsed (short[112 or 256]) = c_rarg2
- // parsedLength (112 or 256) = c_rarg3
+ // parsed (short[]) = c_rarg2
+ // parsedLength = c_rarg3
address generate_kyber12To16() {
- Label L_F00, L_loop, L_end;
+ Label L_F00, L_loop;
__ BIND(L_F00);
__ emit_int64(0x0f000f000f000f00);
@@ -6156,75 +6160,8 @@ class StubGenerator: public StubCodeGenerator {
vs_st2_post(vs_front(vb), __ T8H, parsed);
__ sub(parsedLength, parsedLength, 64);
- __ cmp(parsedLength, (u1)64);
- __ br(Assembler::GE, L_loop);
- __ cbz(parsedLength, L_end);
-
- // if anything is left it should be a final 72 bytes of input
- // i.e. a final 48 12-bit values. so we handle this by loading
- // 48 bytes into all 16B lanes of front(vin) and only 24
- // bytes into the lower 8B lane of back(vin)
- vs_ld3_post(vs_front(vin), __ T16B, condensed);
- vs_ld3(vs_back(vin), __ T8B, condensed);
-
- // Expand vin[0] into va[0:1], and vin[1] into va[2:3] and va[4:5]
- // n.b. target elements 2 and 3 of va duplicate elements 4 and
- // 5 and target element 2 of vb duplicates element 4.
- __ ushll(va[0], __ T8H, vin[0], __ T8B, 0);
- __ ushll2(va[1], __ T8H, vin[0], __ T16B, 0);
- __ ushll(va[2], __ T8H, vin[1], __ T8B, 0);
- __ ushll2(va[3], __ T8H, vin[1], __ T16B, 0);
- __ ushll(va[4], __ T8H, vin[1], __ T8B, 0);
- __ ushll2(va[5], __ T8H, vin[1], __ T16B, 0);
-
- // This time expand just the lower 8 lanes
- __ ushll(vb[0], __ T8H, vin[3], __ T8B, 0);
- __ ushll(vb[2], __ T8H, vin[4], __ T8B, 0);
- __ ushll(vb[4], __ T8H, vin[4], __ T8B, 0);
-
- // shift lo byte of copy 1 of the middle stripe into the high byte
- __ shl(va[2], __ T8H, va[2], 8);
- __ shl(va[3], __ T8H, va[3], 8);
- __ shl(vb[2], __ T8H, vb[2], 8);
-
- // expand vin[2] into va[6:7] and lower 8 lanes of vin[5] into
- // vb[6] pre-shifted by 4 to ensure top bits of the input 12-bit
- // int are in bit positions [4..11].
- __ ushll(va[6], __ T8H, vin[2], __ T8B, 4);
- __ ushll2(va[7], __ T8H, vin[2], __ T16B, 4);
- __ ushll(vb[6], __ T8H, vin[5], __ T8B, 4);
-
- // mask hi 4 bits of each 1st 12-bit int in pair from copy1 and
- // shift lo 4 bits of each 2nd 12-bit int in pair to bottom of
- // copy2
- __ andr(va[2], __ T16B, va[2], v31);
- __ andr(va[3], __ T16B, va[3], v31);
- __ ushr(va[4], __ T8H, va[4], 4);
- __ ushr(va[5], __ T8H, va[5], 4);
- __ andr(vb[2], __ T16B, vb[2], v31);
- __ ushr(vb[4], __ T8H, vb[4], 4);
-
-
-
- // sum hi 4 bits and lo 8 bits of each 1st 12-bit int in pair and
- // hi 8 bits plus lo 4 bits of each 2nd 12-bit int in pair
-
- // n.b. ordering ensures: i) inputs are consumed before they are
- // overwritten ii) order of 16-bit results across succsessive
- // pairs of vectors in va and then lower half of vb reflects order
- // of corresponding 12-bit inputs
- __ addv(va[0], __ T8H, va[0], va[2]);
- __ addv(va[2], __ T8H, va[1], va[3]);
- __ addv(va[1], __ T8H, va[4], va[6]);
- __ addv(va[3], __ T8H, va[5], va[7]);
- __ addv(vb[0], __ T8H, vb[0], vb[2]);
- __ addv(vb[1], __ T8H, vb[4], vb[6]);
-
- // store 48 results interleaved as shorts
- vs_st2_post(vs_front(va), __ T8H, parsed);
- vs_st2_post(vs_front(vs_front(vb)), __ T8H, parsed);
-
- __ BIND(L_end);
+ __ cmp(parsedLength, (u1)0);
+ __ br(Assembler::GT, L_loop);
__ leave(); // required for proper stackwalking of RuntimeStub frame
__ mov(r0, zr); // return 0
diff --git a/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp b/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp
index b1cdf38daf3a..eecdaa3f5d0e 100644
--- a/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp
+++ b/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -442,15 +442,13 @@ void ArrayCopyStub::emit_code(LIR_Assembler* ce) {
return; // CodeCache is full
}
- bool success = ce->emit_trampoline_stub_for_call(SharedRuntime::get_resolve_static_call_stub());
- if (!success) { return; }
-
- __ relocate(relocInfo::static_call_type);
- // Note: At this point we do not have the address of the trampoline
- // stub, and the entry point might be too far away for bl, so __ pc()
- // serves as dummy and the bl will be patched later.
- __ code()->set_insts_mark();
- __ bl(__ pc());
+ AddressLiteral resolve(SharedRuntime::get_resolve_static_call_stub(),
+ relocInfo::static_call_type);
+ address call_pc = __ trampoline_call(resolve);
+ if (call_pc == nullptr) {
+ ce->bailout("const/stub overflow in call with trampoline");
+ return;
+ }
ce->add_call_info_here(info());
ce->verify_oop_map(info());
diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
index 7dfde40364e6..29c69ac34108 100644
--- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2025 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
@@ -610,67 +610,25 @@ void LIR_Assembler::align_call(LIR_Code) {
// do nothing since all instructions are word aligned on ppc
}
-
-bool LIR_Assembler::emit_trampoline_stub_for_call(address target, Register Rtoc) {
- int start_offset = __ offset();
- // Put the entry point as a constant into the constant pool.
- const address entry_point_toc_addr = __ address_constant(target, RelocationHolder::none);
- if (entry_point_toc_addr == nullptr) {
- bailout("const section overflow");
- return false;
- }
- const int entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr);
-
- // Emit the trampoline stub which will be related to the branch-and-link below.
- address stub = __ emit_trampoline_stub(entry_point_toc_offset, start_offset, Rtoc);
- if (!stub) {
- bailout("no space for trampoline stub");
- return false;
- }
- return true;
-}
-
-
void LIR_Assembler::call(LIR_OpJavaCall* op, relocInfo::relocType rtype) {
assert(rtype==relocInfo::opt_virtual_call_type || rtype==relocInfo::static_call_type, "unexpected rtype");
- bool success = emit_trampoline_stub_for_call(op->addr());
- if (!success) { return; }
-
- __ relocate(rtype);
- // Note: At this point we do not have the address of the trampoline
- // stub, and the entry point might be too far away for bl, so __ pc()
- // serves as dummy and the bl will be patched later.
- __ code()->set_insts_mark();
- __ bl(__ pc());
+ address call_pc = __ trampoline_call(AddressLiteral(op->addr(), rtype));
+ if (call_pc == nullptr) {
+ bailout("const/stub overflow in call with trampoline");
+ return;
+ }
add_call_info(code_offset(), op->info());
__ post_call_nop();
}
-
void LIR_Assembler::ic_call(LIR_OpJavaCall* op) {
__ calculate_address_from_global_toc(R2_TOC, __ method_toc());
-
- // Virtual call relocation will point to ic load.
- address virtual_call_meta_addr = __ pc();
- // Load a clear inline cache.
- AddressLiteral empty_ic((address) Universe::non_oop_word());
- bool success = __ load_const_from_method_toc(R19_inline_cache_reg, empty_ic, R2_TOC);
+ bool success = __ ic_call(R2_TOC, op->addr());
if (!success) {
- bailout("const section overflow");
+ bailout("const/stub overflow in ic_call with trampoline");
return;
}
- // Call to fixup routine. Fixup routine uses ScopeDesc info
- // to determine who we intended to call.
- __ relocate(virtual_call_Relocation::spec(virtual_call_meta_addr));
-
- success = emit_trampoline_stub_for_call(op->addr(), R2_TOC);
- if (!success) { return; }
-
- // Note: At this point we do not have the address of the trampoline
- // stub, and the entry point might be too far away for bl, so __ pc()
- // serves as dummy and the bl will be patched later.
- __ bl(__ pc());
add_call_info(code_offset(), op->info());
__ post_call_nop();
}
diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.hpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.hpp
index e4de2eb5c468..79b0478bdb62 100644
--- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2015 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
@@ -26,6 +26,9 @@
#ifndef CPU_PPC_C1_LIRASSEMBLER_PPC_HPP
#define CPU_PPC_C1_LIRASSEMBLER_PPC_HPP
+// ArrayCopyStub needs access to bailout
+friend class ArrayCopyStub;
+
private:
//////////////////////////////////////////////////////////////////////////////
@@ -56,9 +59,6 @@
public:
static const ConditionRegister BOOL_RESULT;
- // Emit trampoline stub for call. Call bailout() if failed. Return true on success.
- bool emit_trampoline_stub_for_call(address target, Register Rtoc = noreg);
-
enum {
_static_call_stub_size = 4 * BytesPerInstWord + MacroAssembler::b64_patchable_size, // or smaller
_call_stub_size = _static_call_stub_size + MacroAssembler::trampoline_stub_size, // or smaller
diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
index 1bd74ca03d6c..55519812c7ab 100644
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
@@ -1210,6 +1210,75 @@ address MacroAssembler::call_c_using_toc(const FunctionDescriptor* fd,
}
#endif // ABI_ELFv2
+bool MacroAssembler::ic_call(Register Rmethod_toc,
+ address target,
+ jint method_index,
+ bool scratch_emit,
+ bool fixed_size) {
+ AddressLiteral target_al(target, virtual_call_Relocation::spec(pc(), method_index));
+ DEBUG_ONLY(int ic_load_offset = offset());
+
+ // Load a clear inline cache.
+ AddressLiteral empty_ic((address) Universe::non_oop_word());
+ bool success = load_const_from_method_toc(R19_inline_cache_reg, empty_ic, Rmethod_toc, fixed_size);
+ if (!success) return false;
+
+ assert(MacroAssembler::is_load_const_from_method_toc_at(addr_at(ic_load_offset)),
+ "should be load from TOC");
+
+ address call_pc = trampoline_call(target_al, Rmethod_toc, scratch_emit);
+ return call_pc != nullptr;
+}
+
+address MacroAssembler::trampoline_call(AddressLiteral target,
+ Register Rmethod_toc,
+ bool scratch_emit) {
+ // First, emit the trampoline stub
+ if (!scratch_emit) {
+ RelocationHolder rh = trampoline_stub_Relocation::spec(pc() /* of the bl below */);
+
+ // Put the target's entry point as a constant into the constant pool.
+ const address target_toc_addr = address_constant((address)target.value());
+ if (target_toc_addr == nullptr) return nullptr;
+
+ const int target_toc_offset = offset_to_method_toc(target_toc_addr);
+ address stub = start_a_stub(64);
+ if (stub == nullptr) return nullptr;
+
+ // Annotate the stub with a relocation that points to the owning call instruction.
+ relocate(rh);
+ DEBUG_ONLY(int stub_start_offset = offset());
+
+ // For java_to_interp stubs we use R11_scratch1 as scratch register
+ // and in call trampoline stubs we use R12_scratch2. This way we
+ // can distinguish them (see is_NativeCallTrampolineStub_at()).
+ Register reg_scratch = R12_scratch2;
+
+ if (Rmethod_toc == noreg) {
+ calculate_address_from_global_toc(reg_scratch, method_toc());
+ Rmethod_toc = reg_scratch;
+ }
+
+ ld_largeoffset_unchecked(reg_scratch, target_toc_offset, Rmethod_toc, false);
+ mtctr(reg_scratch);
+ bctr();
+
+ assert(target_toc_offset == NativeCallTrampolineStub_at(addr_at(stub_start_offset))->destination_toc_offset(),
+ "encoded offset into the constant pool must match");
+ assert((uint)(offset() - stub_start_offset) <= trampoline_stub_size, "should be good size");
+ assert(is_NativeCallTrampolineStub_at(addr_at(stub_start_offset)), "doesn't look like a trampoline");
+
+ // End the stub.
+ end_a_stub();
+ }
+
+ // The call will be resolved / patched later.
+ address call_pc = pc();
+ relocate(target.rspec());
+ bl(call_pc);
+ return call_pc;
+}
+
void MacroAssembler::post_call_nop() {
// Make inline again when loom is always enabled.
if (!Continuations::enabled()) {
@@ -2626,50 +2695,6 @@ void MacroAssembler::tlab_allocate(
//verify_tlab(); not implemented
}
-address MacroAssembler::emit_trampoline_stub(int destination_toc_offset,
- int insts_call_instruction_offset, Register Rtoc) {
- // Start the stub.
- address stub = start_a_stub(64);
- if (stub == nullptr) { return nullptr; } // CodeCache full: bail out
-
- // Create a trampoline stub relocation which relates this trampoline stub
- // with the call instruction at insts_call_instruction_offset in the
- // instructions code-section.
- relocate(trampoline_stub_Relocation::spec(code()->insts()->start() + insts_call_instruction_offset));
- const int stub_start_offset = offset();
-
- // For java_to_interp stubs we use R11_scratch1 as scratch register
- // and in call trampoline stubs we use R12_scratch2. This way we
- // can distinguish them (see is_NativeCallTrampolineStub_at()).
- Register reg_scratch = R12_scratch2;
-
- // Now, create the trampoline stub's code:
- // - load the TOC
- // - load the call target from the constant pool
- // - call
- if (Rtoc == noreg) {
- calculate_address_from_global_toc(reg_scratch, method_toc());
- Rtoc = reg_scratch;
- }
-
- ld_largeoffset_unchecked(reg_scratch, destination_toc_offset, Rtoc, false);
- mtctr(reg_scratch);
- bctr();
-
- const address stub_start_addr = addr_at(stub_start_offset);
-
- // Assert that the encoded destination_toc_offset can be identified and that it is correct.
- assert(destination_toc_offset == NativeCallTrampolineStub_at(stub_start_addr)->destination_toc_offset(),
- "encoded offset into the constant pool must match");
- // Trampoline_stub_size should be good.
- assert((uint)(offset() - stub_start_offset) <= trampoline_stub_size, "should be good size");
- assert(is_NativeCallTrampolineStub_at(stub_start_addr), "doesn't look like a trampoline");
-
- // End the stub.
- end_a_stub();
- return stub;
-}
-
// "The box" is the space on the stack where we copy the object mark.
void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register oop, Register box,
Register temp, Register displaced_header, Register current_header) {
@@ -3436,24 +3461,6 @@ void MacroAssembler::store_klass_gap(Register dst_oop, Register val) {
}
}
-int MacroAssembler::instr_size_for_load_klass() {
- static int computed_size = -1;
-
- // Not yet computed?
- if (computed_size == -1) {
-
- // 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;
-}
-
void MacroAssembler::decode_klass_not_null(Register dst, Register src) {
assert(dst != R0, "Dst reg may not be R0, as R0 is used here.");
if (src == noreg) src = dst;
diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
index ab7bb653d113..f81b2d8ac474 100644
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
@@ -380,9 +380,20 @@ class MacroAssembler: public Assembler {
Register toc);
#endif
+ // CompiledIC call
+ bool ic_call(Register Rmethod_toc,
+ address target,
+ jint method_index = 0,
+ bool scratch_emit = false,
+ bool fixed_size = false);
static int ic_check_size();
int ic_check(int end_alignment);
+ enum { trampoline_stub_size = 6 * 4 };
+ address trampoline_call(AddressLiteral target,
+ Register Rmethod_toc = noreg,
+ bool scratch_emit = false);
+
protected:
// It is imperative that all calls into the VM are handled via the
@@ -704,9 +715,6 @@ class MacroAssembler: public Assembler {
Label& slow_case // continuation point if fast allocation fails
);
- enum { trampoline_stub_size = 6 * 4 };
- address emit_trampoline_stub(int destination_toc_offset, int insts_call_instruction_offset, Register Rtoc = noreg);
-
void compiler_fast_lock_object(ConditionRegister flag, Register oop, Register box,
Register tmp1, Register tmp2, Register tmp3);
@@ -810,7 +818,6 @@ class MacroAssembler: public Assembler {
MacroAssembler::PreservationLevel preservation_level);
void load_method_holder(Register holder, Register method);
- 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 b68632f0b377..6b7c273ab5fe 100644
--- a/src/hotspot/cpu/ppc/ppc.ad
+++ b/src/hotspot/cpu/ppc/ppc.ad
@@ -1177,18 +1177,7 @@ int MachCallStaticJavaNode::ret_addr_offset() {
}
int MachCallDynamicJavaNode::ret_addr_offset() {
- // Offset is 4 with postalloc expanded calls (bl is one instruction). We use
- // postalloc expanded calls if we use inline caches and do not update method data.
- if (UseInlineCaches) return 4;
-
- int vtable_index = this->_vtable_index;
- if (vtable_index < 0) {
- // Must be invalid_vtable_index, not nonvirtual_vtable_index.
- assert(vtable_index == Method::invalid_vtable_index, "correct sentinel value");
- return 12;
- } else {
- return 20 + MacroAssembler::instr_size_for_load_klass();
- }
+ return 12;
}
int MachCallRuntimeNode::ret_addr_offset() {
@@ -1311,9 +1300,6 @@ class CallStubImpl {
public:
- // Emit call stub, compiled java to interpreter.
- static void emit_trampoline_stub(C2_MacroAssembler *masm, int destination_toc_offset, int insts_call_instruction_offset);
-
// Size of call trampoline stub.
// This doesn't need to be accurate to the byte, but it
// must be larger than or equal to the real size of the stub.
@@ -1332,81 +1318,6 @@ class CallStubImpl {
source %{
-// Emit a trampoline stub for a call to a target which is too far away.
-//
-// code sequences:
-//
-// call-site:
-// branch-and-link to or
-//
-// Related trampoline stub for this call-site in the stub section:
-// load the call target from the constant pool
-// branch via CTR (LR/link still points to the call-site above)
-
-void CallStubImpl::emit_trampoline_stub(C2_MacroAssembler *masm, int destination_toc_offset, int insts_call_instruction_offset) {
- address stub = __ emit_trampoline_stub(destination_toc_offset, insts_call_instruction_offset);
- if (stub == nullptr) {
- ciEnv::current()->record_out_of_memory_failure();
- }
-}
-
-//=============================================================================
-
-// Emit an inline branch-and-link call and a related trampoline stub.
-//
-// code sequences:
-//
-// call-site:
-// branch-and-link to or
-//
-// Related trampoline stub for this call-site in the stub section:
-// load the call target from the constant pool
-// branch via CTR (LR/link still points to the call-site above)
-//
-
-typedef struct {
- int insts_call_instruction_offset;
- int ret_addr_offset;
-} EmitCallOffsets;
-
-// Emit a branch-and-link instruction that branches to a trampoline.
-// - Remember the offset of the branch-and-link instruction.
-// - Add a relocation at the branch-and-link instruction.
-// - Emit a branch-and-link.
-// - Remember the return pc offset.
-EmitCallOffsets emit_call_with_trampoline_stub(C2_MacroAssembler *masm, address entry_point, relocInfo::relocType rtype) {
- EmitCallOffsets offsets = { -1, -1 };
- const int start_offset = __ offset();
- offsets.insts_call_instruction_offset = __ offset();
-
- // No entry point given, use the current pc.
- if (entry_point == nullptr) entry_point = __ pc();
-
- // Put the entry point as a constant into the constant pool.
- const address entry_point_toc_addr = __ address_constant(entry_point, RelocationHolder::none);
- if (entry_point_toc_addr == nullptr) {
- ciEnv::current()->record_out_of_memory_failure();
- return offsets;
- }
- const int entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr);
-
- // Emit the trampoline stub which will be related to the branch-and-link below.
- CallStubImpl::emit_trampoline_stub(masm, entry_point_toc_offset, offsets.insts_call_instruction_offset);
- if (ciEnv::current()->failing()) { return offsets; } // Code cache may be full.
- __ relocate(rtype);
-
- // Note: At this point we do not have the address of the trampoline
- // stub, and the entry point might be too far away for bl, so __ pc()
- // serves as dummy and the bl will be patched later.
- __ bl((address) __ pc());
-
- offsets.ret_addr_offset = __ offset() - start_offset;
-
- return offsets;
-}
-
-//=============================================================================
-
// Factory for creating loadConL* nodes for large/small constant pool.
static inline jlong replicate_immF(float con) {
@@ -3337,205 +3248,50 @@ encode %{
// Usage of r1 and r2 in the stubs allows to distinguish them.
enc_class enc_java_static_call(method meth) %{
address entry_point = (address)$meth$$method;
+ address call_pc;
if (!_method) {
// A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap.
- emit_call_with_trampoline_stub(masm, entry_point, relocInfo::runtime_call_type);
- if (ciEnv::current()->failing()) { return; } // Code cache may be full.
- } else {
- // Remember the offset not the address.
- const int start_offset = __ offset();
-
- // The trampoline stub.
- // No entry point given, use the current pc.
- // Make sure branch fits into
- if (entry_point == nullptr) entry_point = __ pc();
-
- // Put the entry point as a constant into the constant pool.
- const address entry_point_toc_addr = __ address_constant(entry_point, RelocationHolder::none);
- if (entry_point_toc_addr == nullptr) {
- ciEnv::current()->record_out_of_memory_failure();
+ call_pc = __ trampoline_call(AddressLiteral(entry_point, relocInfo::runtime_call_type));
+ if (call_pc == nullptr) {
+ ciEnv::current()->record_failure("CodeCache is full");
return;
}
- const int entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr);
-
- // Emit the trampoline stub which will be related to the branch-and-link below.
- CallStubImpl::emit_trampoline_stub(masm, entry_point_toc_offset, start_offset);
- if (ciEnv::current()->failing()) { return; } // Code cache may be full.
+ } else {
int method_index = resolved_method_index(masm);
- __ relocate(_optimized_virtual ? opt_virtual_call_Relocation::spec(method_index)
- : static_call_Relocation::spec(method_index));
-
- // The real call.
- // Note: At this point we do not have the address of the trampoline
- // stub, and the entry point might be too far away for bl, so __ pc()
- // serves as dummy and the bl will be patched later.
- __ set_inst_mark();
- __ bl(__ pc()); // Emits a relocation.
-
- // The stub for call to interpreter.
- address stub = CompiledDirectCall::emit_to_interp_stub(masm);
- __ clear_inst_mark();
- if (stub == nullptr) {
+ RelocationHolder rspec = _optimized_virtual ? opt_virtual_call_Relocation::spec(method_index)
+ : static_call_Relocation::spec(method_index);
+ call_pc = __ trampoline_call(AddressLiteral(entry_point, rspec));
+ if (call_pc == nullptr) {
ciEnv::current()->record_failure("CodeCache is full");
return;
}
- }
- __ post_call_nop();
- %}
- // Second node of expanded dynamic call - the call.
- enc_class enc_java_dynamic_call_sched(method meth) %{
- if (!ra_->C->output()->in_scratch_emit_size()) {
- // Create a call trampoline stub for the given method.
- const address entry_point = !($meth$$method) ? nullptr : (address)$meth$$method;
- const address entry_point_const = __ address_constant(entry_point, RelocationHolder::none);
- if (entry_point_const == nullptr) {
- ciEnv::current()->record_out_of_memory_failure();
+ // Emit stub for static call
+ address stub = CompiledDirectCall::emit_to_interp_stub(masm, call_pc);
+ if (stub == nullptr) {
+ ciEnv::current()->record_failure("CodeCache is full");
return;
}
- const int entry_point_const_toc_offset = __ offset_to_method_toc(entry_point_const);
- CallStubImpl::emit_trampoline_stub(masm, entry_point_const_toc_offset, __ offset());
- if (ra_->C->env()->failing()) { return; } // Code cache may be full.
-
- // Build relocation at call site with ic position as data.
- assert((_load_ic_hi_node != nullptr && _load_ic_node == nullptr) ||
- (_load_ic_hi_node == nullptr && _load_ic_node != nullptr),
- "must have one, but can't have both");
- assert((_load_ic_hi_node != nullptr && _load_ic_hi_node->_cbuf_insts_offset != -1) ||
- (_load_ic_node != nullptr && _load_ic_node->_cbuf_insts_offset != -1),
- "must contain instruction offset");
- const int virtual_call_oop_addr_offset = _load_ic_hi_node != nullptr
- ? _load_ic_hi_node->_cbuf_insts_offset
- : _load_ic_node->_cbuf_insts_offset;
- const address virtual_call_oop_addr = __ addr_at(virtual_call_oop_addr_offset);
- assert(MacroAssembler::is_load_const_from_method_toc_at(virtual_call_oop_addr),
- "should be load from TOC");
- int method_index = resolved_method_index(masm);
- __ relocate(virtual_call_Relocation::spec(virtual_call_oop_addr, method_index));
}
-
- // At this point I do not have the address of the trampoline stub,
- // and the entry point might be too far away for bl. Pc() serves
- // as dummy and bl will be patched later.
- __ bl((address) __ pc());
__ post_call_nop();
%}
- // postalloc expand emitter for virtual calls.
- enc_class postalloc_expand_java_dynamic_call_sched(method meth, iRegLdst toc) %{
-
- // Create the nodes for loading the IC from the TOC.
- loadConLNodesTuple loadConLNodes_IC =
- loadConLNodesTuple_create(ra_, n_toc, new immLOper((jlong) Universe::non_oop_word()),
- OptoReg::Name(R19_H_num), OptoReg::Name(R19_num));
-
- // Create the call node.
- CallDynamicJavaDirectSchedNode *call = new CallDynamicJavaDirectSchedNode();
- call->_method_handle_invoke = _method_handle_invoke;
- call->_vtable_index = _vtable_index;
- call->_method = _method;
- call->_optimized_virtual = _optimized_virtual;
- call->_tf = _tf;
- call->_entry_point = _entry_point;
- call->_cnt = _cnt;
- call->_guaranteed_safepoint = true;
- call->_oop_map = _oop_map;
- call->_jvms = _jvms;
- call->_jvmadj = _jvmadj;
- call->_has_ea_local_in_scope = _has_ea_local_in_scope;
- call->_in_rms = _in_rms;
- call->_nesting = _nesting;
- call->_override_symbolic_info = _override_symbolic_info;
- call->_arg_escape = _arg_escape;
-
- // New call needs all inputs of old call.
- // Req...
- for (uint i = 0; i < req(); ++i) {
- // The expanded node does not need toc any more.
- // Add the inline cache constant here instead. This expresses the
- // register of the inline cache must be live at the call.
- // Else we would have to adapt JVMState by -1.
- if (i == mach_constant_base_node_input()) {
- call->add_req(loadConLNodes_IC._last);
- } else {
- call->add_req(in(i));
- }
- }
- // ...as well as prec
- for (uint i = req(); i < len(); ++i) {
- call->add_prec(in(i));
- }
-
- // Remember nodes loading the inline cache into r19.
- call->_load_ic_hi_node = loadConLNodes_IC._large_hi;
- call->_load_ic_node = loadConLNodes_IC._small;
-
- // Operands for new nodes.
- call->_opnds[0] = _opnds[0];
- call->_opnds[1] = _opnds[1];
-
- // Only the inline cache is associated with a register.
- assert(Matcher::inline_cache_reg() == OptoReg::Name(R19_num), "ic reg should be R19");
-
- // Push new nodes.
- if (loadConLNodes_IC._large_hi) nodes->push(loadConLNodes_IC._large_hi);
- if (loadConLNodes_IC._last) nodes->push(loadConLNodes_IC._last);
- nodes->push(call);
- %}
-
// Compound version of call dynamic
// Toc is only passed so that it can be used in ins_encode statement.
// In the code we have to use $constanttablebase.
enc_class enc_java_dynamic_call(method meth, iRegLdst toc) %{
int start_offset = __ offset();
-
- Register Rtoc = (ra_) ? $constanttablebase : R2_TOC;
-
- int vtable_index = this->_vtable_index;
- if (vtable_index < 0) {
- // Must be invalid_vtable_index, not nonvirtual_vtable_index.
- assert(vtable_index == Method::invalid_vtable_index, "correct sentinel value");
- Register ic_reg = as_Register(Matcher::inline_cache_reg_encode());
-
- // Virtual call relocation will point to ic load.
- address virtual_call_meta_addr = __ pc();
- // Load a clear inline cache.
- AddressLiteral empty_ic((address) Universe::non_oop_word());
- bool success = __ load_const_from_method_toc(ic_reg, empty_ic, Rtoc, /*fixed_size*/ true);
- if (!success) {
- ciEnv::current()->record_out_of_memory_failure();
- return;
- }
- // CALL to fixup routine. Fixup routine uses ScopeDesc info
- // to determine who we intended to call.
- __ relocate(virtual_call_Relocation::spec(virtual_call_meta_addr));
- emit_call_with_trampoline_stub(masm, (address)$meth$$method, relocInfo::none);
- if (ciEnv::current()->failing()) { return; } // Code cache may be full.
- assert(((MachCallDynamicJavaNode*)this)->ret_addr_offset() == __ offset() - start_offset,
- "Fix constant in ret_addr_offset(), expected %d", __ offset() - start_offset);
- } else {
- assert(!UseInlineCaches, "expect vtable calls only if not using ICs");
- // Go thru the vtable. Get receiver klass. Receiver already
- // checked for non-null. If we'll go thru a C2I adapter, the
- // interpreter expects method in R19_method.
-
- __ load_klass(R11_scratch1, R3);
-
- int entry_offset = in_bytes(Klass::vtable_start_offset()) + vtable_index * vtableEntry::size_in_bytes();
- int v_off = entry_offset + in_bytes(vtableEntry::method_offset());
- __ li(R19_method, v_off);
- __ ldx(R19_method/*method*/, R19_method/*method offset*/, R11_scratch1/*class*/);
- // NOTE: for vtable dispatches, the vtable entry will never be
- // null. However it may very well end up in handle_wrong_method
- // if the method is abstract for the particular class.
- __ ld(R11_scratch1, in_bytes(Method::from_compiled_offset()), R19_method);
- // Call target. Either compiled code or C2I adapter.
- __ mtctr(R11_scratch1);
- __ bctrl();
- assert(((MachCallDynamicJavaNode*)this)->ret_addr_offset() == __ offset() - start_offset,
- "Fix constant in ret_addr_offset(), expected %d", __ offset() - start_offset);
+ int method_index = resolved_method_index(masm);
+ bool scratch_emit = ra_ == nullptr;
+ Register Rtoc = scratch_emit ? R2_TOC : $constanttablebase;
+ bool success = __ ic_call(Rtoc, (address)$meth$$method, method_index, scratch_emit, true /*fixed_size*/);
+ if (!success) {
+ ciEnv::current()->record_failure("CodeCache is full");
+ return;
}
+ assert(((MachCallDynamicJavaNode*)this)->ret_addr_offset() == __ offset() - start_offset,
+ "Fix constant in ret_addr_offset(), expected %d", __ offset() - start_offset);
__ post_call_nop();
%}
@@ -3736,30 +3492,34 @@ frame %{
// 4 what apparently works and saves us some spills.
return_addr(STACK 4);
- // Location of native (C/C++) and interpreter return values. This
- // is specified to be the same as Java. In the 32-bit VM, long
- // values are actually returned from native calls in O0:O1 and
- // returned to the interpreter in I0:I1. The copying to and from
- // the register pairs is done by the appropriate call and epilog
- // opcodes. This simplifies the register allocator.
- c_return_value %{
- assert((ideal_reg >= Op_RegI && ideal_reg <= Op_RegL) ||
- (ideal_reg == Op_RegN && CompressedOops::base() == nullptr && CompressedOops::shift() == 0),
- "only return normal values");
- // enum names from opcodes.hpp: Op_Node Op_Set Op_RegN Op_RegI Op_RegP Op_RegF Op_RegD Op_RegL
- static int typeToRegLo[Op_RegL+1] = { 0, 0, R3_num, R3_num, R3_num, F1_num, F1_num, R3_num };
- static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, R3_H_num, R3_H_num, OptoReg::Bad, F1_H_num, R3_H_num };
- return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]);
- %}
-
// Location of compiled Java return values. Same as C
return_value %{
assert((ideal_reg >= Op_RegI && ideal_reg <= Op_RegL) ||
(ideal_reg == Op_RegN && CompressedOops::base() == nullptr && CompressedOops::shift() == 0),
"only return normal values");
- // enum names from opcodes.hpp: Op_Node Op_Set Op_RegN Op_RegI Op_RegP Op_RegF Op_RegD Op_RegL
- static int typeToRegLo[Op_RegL+1] = { 0, 0, R3_num, R3_num, R3_num, F1_num, F1_num, R3_num };
- static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, R3_H_num, R3_H_num, OptoReg::Bad, F1_H_num, R3_H_num };
+ // enum names from opcodes.hpp
+ static int typeToRegLo[Op_RegL+1] = {
+ 0, // Op_Node
+ 0, // Op_Set
+ R3_num, // Op_RegN
+ R3_num, // Op_RegI
+ R3_num, // Op_RegP
+ F1_num, // Op_RegF
+ F1_num, // Op_RegD
+ R3_num, // Op_RegL
+ };
+
+ static int typeToRegHi[Op_RegL+1] = {
+ 0, // Op_Node
+ 0, // Op_Set
+ OptoReg::Bad, // Op_RegN
+ OptoReg::Bad, // Op_RegI
+ R3_H_num, // Op_RegP
+ OptoReg::Bad, // Op_RegF
+ F1_H_num, // Op_RegD
+ R3_H_num // Op_RegL
+ };
+
return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]);
%}
%}
@@ -13928,15 +13688,14 @@ instruct safePoint_poll(iRegPdst poll) %{
// ============================================================================
// Call Instructions
-// Call Java Static Instruction
-
source %{
#include "runtime/continuation.hpp"
%}
-// Schedulable version of call static node.
+// Call Java Static Instruction
+
instruct CallStaticJavaDirect(method meth) %{
match(CallStaticJava);
effect(USE meth);
@@ -13952,51 +13711,9 @@ instruct CallStaticJavaDirect(method meth) %{
// Call Java Dynamic Instruction
-// Used by postalloc expand of CallDynamicJavaDirectSchedEx (actual call).
-// Loading of IC was postalloc expanded. The nodes loading the IC are reachable
-// via fields ins_field_load_ic_hi_node and ins_field_load_ic_node.
-// The call destination must still be placed in the constant pool.
-instruct CallDynamicJavaDirectSched(method meth) %{
- match(CallDynamicJava); // To get all the data fields we need ...
- effect(USE meth);
- predicate(false); // ... but never match.
-
- ins_field_load_ic_hi_node(loadConL_hiNode*);
- ins_field_load_ic_node(loadConLNode*);
- ins_num_consts(1 /* 1 patchable constant: call destination */);
-
- format %{ "BL \t// dynamic $meth ==> " %}
- size((Continuations::enabled() ? 8 : 4));
- ins_encode( enc_java_dynamic_call_sched(meth) );
- ins_pipe(pipe_class_call);
-%}
-
-// Schedulable (i.e. postalloc expanded) version of call dynamic java.
-// We use postalloc expanded calls if we use inline caches
-// and do not update method data.
-//
-// This instruction has two constants: inline cache (IC) and call destination.
-// Loading the inline cache will be postalloc expanded, thus leaving a call with
-// one constant.
-instruct CallDynamicJavaDirectSched_Ex(method meth) %{
- match(CallDynamicJava);
- effect(USE meth);
- predicate(UseInlineCaches);
- ins_cost(CALL_COST);
-
- ins_num_consts(2 /* 2 patchable constants: inline cache, call destination. */);
-
- format %{ "CALL,dynamic $meth \t// postalloc expanded" %}
- postalloc_expand( postalloc_expand_java_dynamic_call_sched(meth, constanttablebase) );
-%}
-
-// Compound version of call dynamic java
-// We use postalloc expanded calls if we use inline caches
-// and do not update method data.
instruct CallDynamicJavaDirect(method meth) %{
match(CallDynamicJava);
effect(USE meth);
- predicate(!UseInlineCaches);
ins_cost(CALL_COST);
// Enc_java_to_runtime_call needs up to 4 constants (method data oop).
diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
index 8d34f494d96b..ee729cf7d39f 100644
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
@@ -1794,10 +1794,8 @@ static void gen_continuation_enter(MacroAssembler* masm,
check_continuation_enter_argument(regs[pos_is_cont].first(), reg_is_cont, "isContinue");
check_continuation_enter_argument(regs[pos_is_virtual].first(), reg_is_virtual, "isVirtualThread");
- address resolve_static_call = SharedRuntime::get_resolve_static_call_stub();
-
+ AddressLiteral resolve(SharedRuntime::get_resolve_static_call_stub(), relocInfo::static_call_type);
address start = __ pc();
-
Label L_thaw, L_exit;
// i2i entry used at interp_only_mode only
@@ -1834,33 +1832,17 @@ static void gen_continuation_enter(MacroAssembler* masm,
// Emit compiled static call. The call will be always resolved to the c2i
// entry of Continuation.enter(Continuation c, boolean isContinue).
- // There are special cases in SharedRuntime::resolve_static_call_C() and
- // SharedRuntime::resolve_sub_helper_internal() to achieve this
- // See also corresponding call below.
- address c2i_call_pc = __ pc();
- int start_offset = __ offset();
- // Put the entry point as a constant into the constant pool.
- const address entry_point_toc_addr = __ address_constant(resolve_static_call, RelocationHolder::none);
- const int entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr);
- guarantee(entry_point_toc_addr != nullptr, "const section overflow");
-
- // Emit the trampoline stub which will be related to the branch-and-link below.
- address stub = __ emit_trampoline_stub(entry_point_toc_offset, start_offset);
- guarantee(stub != nullptr, "no space for trampoline stub");
-
- __ relocate(relocInfo::static_call_type);
- // Note: At this point we do not have the address of the trampoline
- // stub, and the entry point might be too far away for bl, so __ pc()
- // serves as dummy and the bl will be patched later.
- __ bl(__ pc());
+ address c2i_call_pc = __ trampoline_call(resolve);
+ guarantee(c2i_call_pc != nullptr, "CodeCache is full at gen_continuation_enter");
+
+ // Emit stub for static call
+ address stub = CompiledDirectCall::emit_to_interp_stub(masm, c2i_call_pc);
+ guarantee(stub != nullptr, "CodeCache is full at gen_continuation_enter");
+
oop_maps->add_gc_map(__ pc() - start, map);
__ post_call_nop();
__ b(L_exit);
-
- // static stub for the call above
- stub = CompiledDirectCall::emit_to_interp_stub(masm, c2i_call_pc);
- guarantee(stub != nullptr, "no space for static stub");
}
// compiled entry
@@ -1885,22 +1867,9 @@ static void gen_continuation_enter(MacroAssembler* masm,
// SharedRuntime::find_callee_info_helper() which calls
// LinkResolver::resolve_continuation_enter() which resolves the call to
// Continuation.enter(Continuation c, boolean isContinue).
- address call_pc = __ pc();
- int start_offset = __ offset();
- // Put the entry point as a constant into the constant pool.
- const address entry_point_toc_addr = __ address_constant(resolve_static_call, RelocationHolder::none);
- const int entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr);
- guarantee(entry_point_toc_addr != nullptr, "const section overflow");
-
- // Emit the trampoline stub which will be related to the branch-and-link below.
- address stub = __ emit_trampoline_stub(entry_point_toc_offset, start_offset);
- guarantee(stub != nullptr, "no space for trampoline stub");
-
- __ relocate(relocInfo::static_call_type);
- // Note: At this point we do not have the address of the trampoline
- // stub, and the entry point might be too far away for bl, so __ pc()
- // serves as dummy and the bl will be patched later.
- __ bl(__ pc());
+ address call_pc = __ trampoline_call(resolve);
+ guarantee(call_pc != nullptr, "CodeCache is full at gen_continuation_enter");
+
oop_maps->add_gc_map(__ pc() - start, map);
__ post_call_nop();
@@ -1953,8 +1922,8 @@ static void gen_continuation_enter(MacroAssembler* masm,
__ blr();
// static stub for the call above
- stub = CompiledDirectCall::emit_to_interp_stub(masm, call_pc);
- guarantee(stub != nullptr, "no space for static stub");
+ address stub = CompiledDirectCall::emit_to_interp_stub(masm, call_pc);
+ guarantee(stub != nullptr, "CodeCache is full at gen_continuation_enter");
}
static void gen_continuation_yield(MacroAssembler* masm,
diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp
index cd4daf146397..fd62e9358bfb 100644
--- a/src/hotspot/cpu/x86/assembler_x86.cpp
+++ b/src/hotspot/cpu/x86/assembler_x86.cpp
@@ -2225,6 +2225,44 @@ void Assembler::cvttss2sil(Register dst, XMMRegister src) {
emit_int16(0x2C, (0xC0 | encode));
}
+void Assembler::evcvttss2sisl(Register dst, XMMRegister src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes);
+ emit_int16(0x6D, (0xC0 | encode));
+}
+
+void Assembler::evcvttss2sisl(Register dst, Address src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes);
+ emit_int8((unsigned char)0x6D);
+ emit_operand(dst, src, 0);
+}
+
+void Assembler::evcvttss2sisq(Register dst, XMMRegister src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes);
+ emit_int16(0x6D, (0xC0 | encode));
+}
+
+void Assembler::evcvttss2sisq(Register dst, Address src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes);
+ emit_int8((unsigned char)0x6D);
+ emit_operand(dst, src, 0);
+}
+
void Assembler::cvttpd2dq(XMMRegister dst, XMMRegister src) {
int vector_len = VM_Version::supports_avx512novl() ? AVX_512bit : AVX_128bit;
InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
@@ -2310,6 +2348,25 @@ void Assembler::vcvttps2dq(XMMRegister dst, XMMRegister src, int vector_len) {
emit_int16(0x5B, (0xC0 | encode));
}
+void Assembler::evcvttps2dqs(XMMRegister dst, XMMRegister src, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_MAP5, &attributes);
+ emit_int16(0x6D, (0xC0 | encode));
+}
+
+void Assembler::evcvttps2dqs(XMMRegister dst, Address src, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_MAP5, &attributes);
+ emit_int8((unsigned char)0x6D);
+ emit_operand(dst, src, 0);
+}
+
void Assembler::vcvttpd2dq(XMMRegister dst, XMMRegister src, int vector_len) {
assert(vector_len <= AVX_256bit ? VM_Version::supports_avx() : VM_Version::supports_evex(), "");
InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
@@ -2317,6 +2374,25 @@ void Assembler::vcvttpd2dq(XMMRegister dst, XMMRegister src, int vector_len) {
emit_int16((unsigned char)0xE6, (0xC0 | encode));
}
+void Assembler::evcvttpd2dqs(XMMRegister dst, XMMRegister src, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_MAP5, &attributes);
+ emit_int16(0x6D, (0xC0 | encode));
+}
+
+void Assembler::evcvttpd2dqs(XMMRegister dst, Address src, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_MAP5, &attributes);
+ emit_int8((unsigned char)0x6D);
+ emit_operand(dst, src, 0);
+}
+
void Assembler::vcvtps2dq(XMMRegister dst, XMMRegister src, int vector_len) {
assert(vector_len <= AVX_256bit ? VM_Version::supports_avx() : VM_Version::supports_evex(), "");
InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
@@ -2332,6 +2408,25 @@ void Assembler::evcvttps2qq(XMMRegister dst, XMMRegister src, int vector_len) {
emit_int16(0x7A, (0xC0 | encode));
}
+void Assembler::evcvttps2qqs(XMMRegister dst, XMMRegister src, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_MAP5, &attributes);
+ emit_int16(0x6D, (0xC0 | encode));
+}
+
+void Assembler::evcvttps2qqs(XMMRegister dst, Address src, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_HV, /* input_size_in_bits */ EVEX_32bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_MAP5, &attributes);
+ emit_int8((unsigned char)0x6D);
+ emit_operand(dst, src, 0);
+}
+
void Assembler::evcvtpd2qq(XMMRegister dst, XMMRegister src, int vector_len) {
assert(VM_Version::supports_avx512dq(), "");
InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
@@ -2356,6 +2451,25 @@ void Assembler::evcvttpd2qq(XMMRegister dst, XMMRegister src, int vector_len) {
emit_int16(0x7A, (0xC0 | encode));
}
+void Assembler::evcvttpd2qqs(XMMRegister dst, XMMRegister src, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_MAP5, &attributes);
+ emit_int16(0x6D, (0xC0 | encode));
+}
+
+void Assembler::evcvttpd2qqs(XMMRegister dst, Address src, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_MAP5, &attributes);
+ emit_int8((unsigned char)0x6D);
+ emit_operand(dst, src, 0);
+}
+
void Assembler::evcvtqq2pd(XMMRegister dst, XMMRegister src, int vector_len) {
assert(VM_Version::supports_avx512dq(), "");
InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
@@ -8222,6 +8336,14 @@ void Assembler::vmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src) {
emit_int16(0x5F, (0xC0 | encode));
}
+void Assembler::eminmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes);
+ emit_int24(0x53, (0xC0 | encode), imm8);
+}
+
void Assembler::vminsh(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16");
InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
@@ -8736,12 +8858,68 @@ void Assembler::vmaxps(XMMRegister dst, XMMRegister nds, XMMRegister src, int ve
emit_int16(0x5F, (0xC0 | encode));
}
+void Assembler::evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ attributes.set_embedded_opmask_register_specifier(mask);
+ if (merge) {
+ attributes.reset_is_clear_context();
+ }
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes);
+ emit_int24(0x52, (0xC0 | encode), imm8);
+}
+
+void Assembler::evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ attributes.set_embedded_opmask_register_specifier(mask);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_NObit);
+ if (merge) {
+ attributes.reset_is_clear_context();
+ }
+ vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes);
+ emit_int8(0x52);
+ emit_operand(dst, src, 0);
+ emit_int8(imm8);
+}
+
void Assembler::maxpd(XMMRegister dst, XMMRegister src) {
InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes);
emit_int16(0x5F, (0xC0 | encode));
}
+void Assembler::evminmaxpd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false,/* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ attributes.set_embedded_opmask_register_specifier(mask);
+ if (merge) {
+ attributes.reset_is_clear_context();
+ }
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes);
+ emit_int24(0x52, (0xC0 | encode), imm8);
+}
+
+void Assembler::evminmaxpd(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ attributes.set_embedded_opmask_register_specifier(mask);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_NObit);
+ if (merge) {
+ attributes.reset_is_clear_context();
+ }
+ vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes);
+ emit_int8(0x52);
+ emit_operand(dst, src, 0);
+ emit_int8(imm8);
+}
+
void Assembler::vmaxpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
assert(vector_len >= AVX_512bit ? VM_Version::supports_evex() : VM_Version::supports_avx(), "");
InstructionAttr attributes(vector_len, /* vex_w */true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
@@ -13130,6 +13308,14 @@ void Assembler::vminss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
emit_int16(0x5D, (0xC0 | encode));
}
+void Assembler::eminmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes);
+ emit_int24(0x53, (0xC0 | encode), imm8);
+}
+
void Assembler::vminsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
assert(VM_Version::supports_avx(), "");
InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
@@ -13138,6 +13324,14 @@ void Assembler::vminsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
emit_int16(0x5D, (0xC0 | encode));
}
+void Assembler::eminmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes);
+ emit_int24(0x53, (0xC0 | encode), imm8);
+}
+
void Assembler::vcmppd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len) {
assert(VM_Version::supports_avx(), "");
assert(vector_len <= AVX_256bit, "");
@@ -14908,6 +15102,44 @@ void Assembler::cvttsd2siq(Register dst, Address src) {
emit_operand(dst, src, 0);
}
+void Assembler::evcvttsd2sisl(Register dst, XMMRegister src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_MAP5, &attributes);
+ emit_int16(0x6D, (0xC0 | encode));
+}
+
+void Assembler::evcvttsd2sisl(Register dst, Address src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_MAP5, &attributes);
+ emit_int8((unsigned char)0x6D);
+ emit_operand(dst, src, 0);
+}
+
+void Assembler::evcvttsd2sisq(Register dst, XMMRegister src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_MAP5, &attributes);
+ emit_int16(0x6D, (0xC0 | encode));
+}
+
+void Assembler::evcvttsd2sisq(Register dst, Address src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_MAP5, &attributes);
+ emit_int8((unsigned char)0x6D);
+ emit_operand(dst, src, 0);
+}
+
void Assembler::cvttsd2siq(Register dst, XMMRegister src) {
InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -16506,6 +16738,34 @@ void Assembler::evminph(XMMRegister dst, XMMRegister nds, Address src, int vecto
emit_operand(dst, src, 0);
}
+void Assembler::evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false,/* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ attributes.set_embedded_opmask_register_specifier(mask);
+ if (merge) {
+ attributes.reset_is_clear_context();
+ }
+ int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes);
+ emit_int24(0x52, (0xC0 | encode), imm8);
+}
+
+void Assembler::evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+ attributes.set_is_evex_instruction();
+ attributes.set_embedded_opmask_register_specifier(mask);
+ if (merge) {
+ attributes.reset_is_clear_context();
+ }
+ attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_NObit);
+ vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes);
+ emit_int8(0x52);
+ emit_operand(dst, src, 0);
+ emit_int8(imm8);
+}
+
void Assembler::evmaxph(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16");
assert(vector_len == Assembler::AVX_512bit || VM_Version::supports_avx512vl(), "");
diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp
index 28e0cde21578..c863191df4cf 100644
--- a/src/hotspot/cpu/x86/assembler_x86.hpp
+++ b/src/hotspot/cpu/x86/assembler_x86.hpp
@@ -441,6 +441,17 @@ class InstructionAttr;
// See fxsave and xsave(EVEX enabled) documentation for layout
const int FPUStateSizeInWords = 2688 / wordSize;
+
+// AVX10 new minmax instruction control mask encoding.
+//
+// imm8[4] = 0 (please refer to Table 11.1 of section 11.2 of AVX10 manual[1] for details)
+// imm8[3:2] (sign control) = 01 (select sign, please refer to Table 11.5 of section 11.2 of AVX10 manual[1] for details)
+// imm8[1:0] = 00 (min) / 01 (max)
+//
+// [1] https://www.intel.com/content/www/us/en/content-details/856721/intel-advanced-vector-extensions-10-2-intel-avx10-2-architecture-specification.html?wapkw=AVX10
+const int AVX10_MINMAX_MAX_COMPARE_SIGN = 0x5;
+const int AVX10_MINMAX_MIN_COMPARE_SIGN = 0x4;
+
// The Intel x86/Amd64 Assembler: Pure assembler doing NO optimizations on the instruction
// level (e.g. mov rax, 0 is not translated into xor rax, rax!); i.e., what you write
// is what you get. The Assembler is generating code into a CodeBuffer.
@@ -1305,11 +1316,19 @@ class Assembler : public AbstractAssembler {
void cvttsd2sil(Register dst, XMMRegister src);
void cvttsd2siq(Register dst, Address src);
void cvttsd2siq(Register dst, XMMRegister src);
+ void evcvttsd2sisl(Register dst, XMMRegister src);
+ void evcvttsd2sisl(Register dst, Address src);
+ void evcvttsd2sisq(Register dst, XMMRegister src);
+ void evcvttsd2sisq(Register dst, Address src);
// Convert with Truncation Scalar Single-Precision Floating-Point Value to Doubleword Integer
void cvttss2sil(Register dst, XMMRegister src);
void cvttss2siq(Register dst, XMMRegister src);
void cvtss2sil(Register dst, XMMRegister src);
+ void evcvttss2sisl(Register dst, XMMRegister src);
+ void evcvttss2sisl(Register dst, Address src);
+ void evcvttss2sisq(Register dst, XMMRegister src);
+ void evcvttss2sisq(Register dst, Address src);
// Convert vector double to int
void cvttpd2dq(XMMRegister dst, XMMRegister src);
@@ -1321,7 +1340,11 @@ class Assembler : public AbstractAssembler {
// Convert vector float to int/long
void vcvtps2dq(XMMRegister dst, XMMRegister src, int vector_len);
void vcvttps2dq(XMMRegister dst, XMMRegister src, int vector_len);
+ void evcvttps2dqs(XMMRegister dst, XMMRegister src, int vector_len);
+ void evcvttps2dqs(XMMRegister dst, Address src, int vector_len);
void evcvttps2qq(XMMRegister dst, XMMRegister src, int vector_len);
+ void evcvttps2qqs(XMMRegister dst, XMMRegister src, int vector_len);
+ void evcvttps2qqs(XMMRegister dst, Address src, int vector_len);
// Convert vector long to vector FP
void evcvtqq2ps(XMMRegister dst, XMMRegister src, int vector_len);
@@ -1330,9 +1353,13 @@ class Assembler : public AbstractAssembler {
// Convert vector double to long
void evcvtpd2qq(XMMRegister dst, XMMRegister src, int vector_len);
void evcvttpd2qq(XMMRegister dst, XMMRegister src, int vector_len);
+ void evcvttpd2qqs(XMMRegister dst, XMMRegister src, int vector_len);
+ void evcvttpd2qqs(XMMRegister dst, Address src, int vector_len);
// Convert vector double to int
void vcvttpd2dq(XMMRegister dst, XMMRegister src, int vector_len);
+ void evcvttpd2dqs(XMMRegister dst, XMMRegister src, int vector_len);
+ void evcvttpd2dqs(XMMRegister dst, Address src, int vector_len);
// Evex casts with truncation
void evpmovwb(XMMRegister dst, XMMRegister src, int vector_len);
@@ -2752,6 +2779,17 @@ class Assembler : public AbstractAssembler {
void minpd(XMMRegister dst, XMMRegister src);
void vminpd(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len);
+ // AVX10.2 floating point minmax instructions
+ void eminmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8);
+ void eminmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8);
+ void eminmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8);
+ void evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len);
+ void evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len);
+ void evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len);
+ void evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len);
+ void evminmaxpd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len);
+ void evminmaxpd(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len);
+
// Maximum of packed integers
void pmaxsb(XMMRegister dst, XMMRegister src);
void vpmaxsb(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len);
diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
index 4317bb3d0182..d835631575b3 100644
--- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
@@ -1238,6 +1238,21 @@ void C2_MacroAssembler::evminmax_fp(int opcode, BasicType elem_bt,
}
}
+void C2_MacroAssembler::vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask,
+ XMMRegister src1, XMMRegister src2, int vlen_enc) {
+ assert(opc == Op_MinV || opc == Op_MinReductionV ||
+ opc == Op_MaxV || opc == Op_MaxReductionV, "sanity");
+
+ int imm8 = (opc == Op_MinV || opc == Op_MinReductionV) ? AVX10_MINMAX_MIN_COMPARE_SIGN
+ : AVX10_MINMAX_MAX_COMPARE_SIGN;
+ if (elem_bt == T_FLOAT) {
+ evminmaxps(dst, mask, src1, src2, true, imm8, vlen_enc);
+ } else {
+ assert(elem_bt == T_DOUBLE, "");
+ evminmaxpd(dst, mask, src1, src2, true, imm8, vlen_enc);
+ }
+}
+
// Float/Double signum
void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero, XMMRegister one) {
assert(opcode == Op_SignumF || opcode == Op_SignumD, "sanity");
@@ -2545,12 +2560,21 @@ void C2_MacroAssembler::reduceFloatMinMax(int opcode, int vlen, bool is_dst_vali
} else { // i = [0,1]
vpermilps(wtmp, wsrc, permconst[i], vlen_enc);
}
- vminmax_fp(opcode, T_FLOAT, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc);
+
+ if (VM_Version::supports_avx10_2()) {
+ vminmax_fp(opcode, T_FLOAT, wdst, k0, wtmp, wsrc, vlen_enc);
+ } else {
+ vminmax_fp(opcode, T_FLOAT, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc);
+ }
wsrc = wdst;
vlen_enc = Assembler::AVX_128bit;
}
if (is_dst_valid) {
- vminmax_fp(opcode, T_FLOAT, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit);
+ if (VM_Version::supports_avx10_2()) {
+ vminmax_fp(opcode, T_FLOAT, dst, k0, wdst, dst, Assembler::AVX_128bit);
+ } else {
+ vminmax_fp(opcode, T_FLOAT, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit);
+ }
}
}
@@ -2576,12 +2600,23 @@ void C2_MacroAssembler::reduceDoubleMinMax(int opcode, int vlen, bool is_dst_val
assert(i == 0, "%d", i);
vpermilpd(wtmp, wsrc, 1, vlen_enc);
}
- vminmax_fp(opcode, T_DOUBLE, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc);
+
+ if (VM_Version::supports_avx10_2()) {
+ vminmax_fp(opcode, T_DOUBLE, wdst, k0, wtmp, wsrc, vlen_enc);
+ } else {
+ vminmax_fp(opcode, T_DOUBLE, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc);
+ }
+
wsrc = wdst;
vlen_enc = Assembler::AVX_128bit;
}
+
if (is_dst_valid) {
- vminmax_fp(opcode, T_DOUBLE, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit);
+ if (VM_Version::supports_avx10_2()) {
+ vminmax_fp(opcode, T_DOUBLE, dst, k0, wdst, dst, Assembler::AVX_128bit);
+ } else {
+ vminmax_fp(opcode, T_DOUBLE, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit);
+ }
}
}
@@ -5244,12 +5279,12 @@ void C2_MacroAssembler::vector_cast_int_to_subword(BasicType to_elem_bt, XMMRegi
}
vpackuswb(dst, dst, zero, vec_enc);
break;
- default: assert(false, "%s", type2name(to_elem_bt));
+ default: assert(false, "Unexpected basic type for target of vector cast int to subword: %s", type2name(to_elem_bt));
}
}
/*
- * Algorithm for vector D2L and F2I conversions:-
+ * Algorithm for vector D2L and F2I conversions (AVX 10.2 unsupported):-
* a) Perform vector D2L/F2I cast.
* b) Choose fast path if none of the result vector lane contains 0x80000000 value.
* It signifies that source value could be any of the special floating point
@@ -5287,7 +5322,7 @@ void C2_MacroAssembler::vector_castF2X_evex(BasicType to_elem_bt, XMMRegister ds
case T_BYTE:
evpmovdb(dst, dst, vec_enc);
break;
- default: assert(false, "%s", type2name(to_elem_bt));
+ default: assert(false, "Unexpected basic type for target of vector castF2X EVEX: %s", type2name(to_elem_bt));
}
}
@@ -5334,7 +5369,7 @@ void C2_MacroAssembler::vector_castD2X_evex(BasicType to_elem_bt, XMMRegister ds
evpmovsqd(dst, dst, vec_enc);
evpmovdb(dst, dst, vec_enc);
break;
- default: assert(false, "%s", type2name(to_elem_bt));
+ default: assert(false, "Unexpected basic type for target of vector castD2X AVX512DQ EVEX: %s", type2name(to_elem_bt));
}
} else {
assert(type2aelembytes(to_elem_bt) <= 4, "");
@@ -5349,11 +5384,91 @@ void C2_MacroAssembler::vector_castD2X_evex(BasicType to_elem_bt, XMMRegister ds
case T_BYTE:
evpmovdb(dst, dst, vec_enc);
break;
- default: assert(false, "%s", type2name(to_elem_bt));
+ default: assert(false, "Unexpected basic type for target of vector castD2X EVEX: %s", type2name(to_elem_bt));
}
}
}
+void C2_MacroAssembler::vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc) {
+ switch(to_elem_bt) {
+ case T_LONG:
+ evcvttps2qqs(dst, src, vec_enc);
+ break;
+ case T_INT:
+ evcvttps2dqs(dst, src, vec_enc);
+ break;
+ case T_SHORT:
+ evcvttps2dqs(dst, src, vec_enc);
+ evpmovdw(dst, dst, vec_enc);
+ break;
+ case T_BYTE:
+ evcvttps2dqs(dst, src, vec_enc);
+ evpmovdb(dst, dst, vec_enc);
+ break;
+ default: assert(false, "Unexpected basic type for target of vector castF2X AVX10 (reg src): %s", type2name(to_elem_bt));
+ }
+}
+
+void C2_MacroAssembler::vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc) {
+ switch(to_elem_bt) {
+ case T_LONG:
+ evcvttps2qqs(dst, src, vec_enc);
+ break;
+ case T_INT:
+ evcvttps2dqs(dst, src, vec_enc);
+ break;
+ case T_SHORT:
+ evcvttps2dqs(dst, src, vec_enc);
+ evpmovdw(dst, dst, vec_enc);
+ break;
+ case T_BYTE:
+ evcvttps2dqs(dst, src, vec_enc);
+ evpmovdb(dst, dst, vec_enc);
+ break;
+ default: assert(false, "Unexpected basic type for target of vector castF2X AVX10 (mem src): %s", type2name(to_elem_bt));
+ }
+}
+
+void C2_MacroAssembler::vector_castD2X_avx10(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc) {
+ switch(to_elem_bt) {
+ case T_LONG:
+ evcvttpd2qqs(dst, src, vec_enc);
+ break;
+ case T_INT:
+ evcvttpd2dqs(dst, src, vec_enc);
+ break;
+ case T_SHORT:
+ evcvttpd2dqs(dst, src, vec_enc);
+ evpmovdw(dst, dst, vec_enc);
+ break;
+ case T_BYTE:
+ evcvttpd2dqs(dst, src, vec_enc);
+ evpmovdb(dst, dst, vec_enc);
+ break;
+ default: assert(false, "Unexpected basic type for target of vector castD2X AVX10 (reg src): %s", type2name(to_elem_bt));
+ }
+}
+
+void C2_MacroAssembler::vector_castD2X_avx10(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc) {
+ switch(to_elem_bt) {
+ case T_LONG:
+ evcvttpd2qqs(dst, src, vec_enc);
+ break;
+ case T_INT:
+ evcvttpd2dqs(dst, src, vec_enc);
+ break;
+ case T_SHORT:
+ evcvttpd2dqs(dst, src, vec_enc);
+ evpmovdw(dst, dst, vec_enc);
+ break;
+ case T_BYTE:
+ evcvttpd2dqs(dst, src, vec_enc);
+ evpmovdb(dst, dst, vec_enc);
+ break;
+ default: assert(false, "Unexpected basic type for target of vector castD2X AVX10 (mem src): %s", type2name(to_elem_bt));
+ }
+}
+
void C2_MacroAssembler::vector_round_double_evex(XMMRegister dst, XMMRegister src,
AddressLiteral double_sign_flip, AddressLiteral new_mxcsr, int vec_enc,
Register tmp, XMMRegister xtmp1, XMMRegister xtmp2, KRegister ktmp1, KRegister ktmp2) {
diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp
index 713eb73d68f3..d222cd37783c 100644
--- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp
@@ -72,6 +72,9 @@
XMMRegister tmp, XMMRegister atmp, XMMRegister btmp,
int vlen_enc);
+ void vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask,
+ XMMRegister src1, XMMRegister src2, int vlen_enc);
+
void vpuminmaxq(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc);
void evminmax_fp(int opcode, BasicType elem_bt,
@@ -349,6 +352,13 @@
XMMRegister xtmp2, XMMRegister xtmp3, XMMRegister xtmp4, XMMRegister xtmp5,
AddressLiteral float_sign_flip, Register rscratch, int vec_enc);
+ void vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc);
+
+ void vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc);
+
+ void vector_castD2X_avx10(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc);
+
+ void vector_castD2X_avx10(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc);
void vector_cast_double_to_int_special_cases_avx(XMMRegister dst, XMMRegister src, XMMRegister xtmp1, XMMRegister xtmp2,
XMMRegister xtmp3, XMMRegister xtmp4, XMMRegister xtmp5, Register rscratch,
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
index ac11ed7ee9f3..46a1a103d180 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
@@ -8866,6 +8866,10 @@ void MacroAssembler::evpmins(BasicType type, XMMRegister dst, KRegister mask, XM
evpminsd(dst, mask, nds, src, merge, vector_len); break;
case T_LONG:
evpminsq(dst, mask, nds, src, merge, vector_len); break;
+ case T_FLOAT:
+ evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
+ case T_DOUBLE:
+ evminmaxpd(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
default:
fatal("Unexpected type argument %s", type2name(type)); break;
}
@@ -8881,6 +8885,10 @@ void MacroAssembler::evpmaxs(BasicType type, XMMRegister dst, KRegister mask, XM
evpmaxsd(dst, mask, nds, src, merge, vector_len); break;
case T_LONG:
evpmaxsq(dst, mask, nds, src, merge, vector_len); break;
+ case T_FLOAT:
+ evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
+ case T_DOUBLE:
+ evminmaxpd(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
default:
fatal("Unexpected type argument %s", type2name(type)); break;
}
@@ -8896,6 +8904,10 @@ void MacroAssembler::evpmins(BasicType type, XMMRegister dst, KRegister mask, XM
evpminsd(dst, mask, nds, src, merge, vector_len); break;
case T_LONG:
evpminsq(dst, mask, nds, src, merge, vector_len); break;
+ case T_FLOAT:
+ evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
+ case T_DOUBLE:
+ evminmaxpd(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
default:
fatal("Unexpected type argument %s", type2name(type)); break;
}
@@ -8911,6 +8923,10 @@ void MacroAssembler::evpmaxs(BasicType type, XMMRegister dst, KRegister mask, XM
evpmaxsd(dst, mask, nds, src, merge, vector_len); break;
case T_LONG:
evpmaxsq(dst, mask, nds, src, merge, vector_len); break;
+ case T_FLOAT:
+ evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
+ case T_DOUBLE:
+ evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
default:
fatal("Unexpected type argument %s", type2name(type)); break;
}
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp
index 91c005e92de4..95c0ac0c758f 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -64,6 +64,39 @@ static address kyberAvx512ConstsAddr(int offset) {
const Register scratch = r10;
+ATTRIBUTE_ALIGNED(64) static const uint8_t kyberAvx512_12To16Dup[] = {
+// 0 - 63
+ 0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 12, 13, 13, 14, 15, 16,
+ 16, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 25, 26, 27, 28, 28, 29, 30,
+ 31, 31, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, 40, 41, 42, 43, 43, 44,
+ 45, 46, 46, 47
+ };
+
+static address kyberAvx512_12To16DupAddr() {
+ return (address) kyberAvx512_12To16Dup;
+}
+
+ATTRIBUTE_ALIGNED(64) static const uint16_t kyberAvx512_12To16Shift[] = {
+// 0 - 31
+ 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0,
+ 4, 0, 4, 0, 4, 0, 4
+ };
+
+static address kyberAvx512_12To16ShiftAddr() {
+ return (address) kyberAvx512_12To16Shift;
+}
+
+ATTRIBUTE_ALIGNED(64) static const uint64_t kyberAvx512_12To16And[] = {
+// 0 - 7
+ 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF,
+ 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF,
+ 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF
+ };
+
+static address kyberAvx512_12To16AndAddr() {
+ return (address) kyberAvx512_12To16And;
+}
+
ATTRIBUTE_ALIGNED(64) static const uint16_t kyberAvx512NttPerms[] = {
// 0
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
@@ -822,10 +855,65 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen,
const Register perms = r11;
- Label Loop;
+ Label Loop, VBMILoop;
__ addptr(condensed, condensedOffs);
+ if (VM_Version::supports_avx512_vbmi()) {
+ // mask load for the first 48 bytes of each vector
+ __ mov64(rax, 0x0000FFFFFFFFFFFF);
+ __ kmovql(k1, rax);
+
+ __ lea(perms, ExternalAddress(kyberAvx512_12To16DupAddr()));
+ __ evmovdqub(xmm20, Address(perms), Assembler::AVX_512bit);
+
+ __ lea(perms, ExternalAddress(kyberAvx512_12To16ShiftAddr()));
+ __ evmovdquw(xmm21, Address(perms), Assembler::AVX_512bit);
+
+ __ lea(perms, ExternalAddress(kyberAvx512_12To16AndAddr()));
+ __ evmovdquq(xmm22, Address(perms), Assembler::AVX_512bit);
+
+ __ align(OptoLoopAlignment);
+ __ BIND(VBMILoop);
+
+ __ evmovdqub(xmm0, k1, Address(condensed, 0), false,
+ Assembler::AVX_512bit);
+ __ evmovdqub(xmm1, k1, Address(condensed, 48), false,
+ Assembler::AVX_512bit);
+ __ evmovdqub(xmm2, k1, Address(condensed, 96), false,
+ Assembler::AVX_512bit);
+ __ evmovdqub(xmm3, k1, Address(condensed, 144), false,
+ Assembler::AVX_512bit);
+
+ __ evpermb(xmm4, k0, xmm20, xmm0, false, Assembler::AVX_512bit);
+ __ evpermb(xmm5, k0, xmm20, xmm1, false, Assembler::AVX_512bit);
+ __ evpermb(xmm6, k0, xmm20, xmm2, false, Assembler::AVX_512bit);
+ __ evpermb(xmm7, k0, xmm20, xmm3, false, Assembler::AVX_512bit);
+
+ __ evpsrlvw(xmm4, xmm4, xmm21, Assembler::AVX_512bit);
+ __ evpsrlvw(xmm5, xmm5, xmm21, Assembler::AVX_512bit);
+ __ evpsrlvw(xmm6, xmm6, xmm21, Assembler::AVX_512bit);
+ __ evpsrlvw(xmm7, xmm7, xmm21, Assembler::AVX_512bit);
+
+ __ evpandq(xmm0, xmm22, xmm4, Assembler::AVX_512bit);
+ __ evpandq(xmm1, xmm22, xmm5, Assembler::AVX_512bit);
+ __ evpandq(xmm2, xmm22, xmm6, Assembler::AVX_512bit);
+ __ evpandq(xmm3, xmm22, xmm7, Assembler::AVX_512bit);
+
+ store4regs(parsed, 0, xmm0_3, _masm);
+
+ __ addptr(condensed, 192);
+ __ addptr(parsed, 256);
+ __ subl(parsedLength, 128);
+ __ jcc(Assembler::greater, VBMILoop);
+
+ __ leave(); // required for proper stackwalking of RuntimeStub frame
+ __ mov64(rax, 0); // return 0
+ __ ret(0);
+
+ return start;
+ }
+
__ lea(perms, ExternalAddress(kyberAvx512_12To16PermsAddr()));
load4regs(xmm24_27, perms, 0, _masm);
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp
index 9f13233f1d21..7aa1d72515ca 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp
@@ -484,6 +484,8 @@ static address generate_double_keccak(StubGenerator *stubgen, MacroAssembler *_m
__ cmpl(roundsLeft, 0);
__ jcc(Assembler::notEqual, rounds24_loop);
+ __ xorq(rax, rax); // return 0
+
// store the states
for (int i = 0; i < 5; i++) {
__ evmovdquq(Address(state0, i * 40), k5, xmm(i), true, Assembler::AVX_512bit);
diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad
index 6ad6d0332b93..d9f9cc802fe0 100644
--- a/src/hotspot/cpu/x86/x86.ad
+++ b/src/hotspot/cpu/x86/x86.ad
@@ -2024,7 +2024,7 @@ bool Matcher::match_rule_supported_vector_masked(int opcode, int vlen, BasicType
if (is_subword_type(bt) && !VM_Version::supports_avx512bw()) {
return false; // Implementation limitation
}
- if (is_floating_point_type(bt)) {
+ if (is_floating_point_type(bt) && !VM_Version::supports_avx10_2()) {
return false; // Implementation limitation
}
return true;
@@ -5293,9 +5293,9 @@ instruct mul_reduction64B(rRegI dst, rRegI src1, legVec src2, legVec vtmp1, legV
//--------------------Min/Max Float Reduction --------------------
// Float Min Reduction
-instruct minmax_reduction2F(legRegF dst, immF src1, legVec src2, legVec tmp,
- legVec atmp, legVec btmp, legVec xmm_1, rFlagsReg cr) %{
- predicate(Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
+instruct minmax_reduction2F(legRegF dst, immF src1, legVec src2, legVec tmp, legVec atmp,
+ legVec btmp, legVec xmm_1, rFlagsReg cr) %{
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) ||
(n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) &&
Matcher::vector_length(n->in(2)) == 2);
@@ -5316,7 +5316,7 @@ instruct minmax_reduction2F(legRegF dst, immF src1, legVec src2, legVec tmp,
instruct minmax_reductionF(legRegF dst, immF src1, legVec src2, legVec tmp, legVec atmp,
legVec btmp, legVec xmm_0, legVec xmm_1, rFlagsReg cr) %{
- predicate(Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) ||
(n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) &&
Matcher::vector_length(n->in(2)) >= 4);
@@ -5335,9 +5335,9 @@ instruct minmax_reductionF(legRegF dst, immF src1, legVec src2, legVec tmp, legV
ins_pipe( pipe_slow );
%}
-instruct minmax_reduction2F_av(legRegF dst, legVec src, legVec tmp,
- legVec atmp, legVec btmp, legVec xmm_1, rFlagsReg cr) %{
- predicate(Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
+instruct minmax_reduction2F_av(legRegF dst, legVec src, legVec tmp, legVec atmp,
+ legVec btmp, legVec xmm_1, rFlagsReg cr) %{
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
Matcher::vector_length(n->in(2)) == 2);
match(Set dst (MinReductionV dst src));
match(Set dst (MaxReductionV dst src));
@@ -5355,9 +5355,9 @@ instruct minmax_reduction2F_av(legRegF dst, legVec src, legVec tmp,
%}
-instruct minmax_reductionF_av(legRegF dst, legVec src, legVec tmp,
- legVec atmp, legVec btmp, legVec xmm_0, legVec xmm_1, rFlagsReg cr) %{
- predicate(Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
+instruct minmax_reductionF_av(legRegF dst, legVec src, legVec tmp, legVec atmp, legVec btmp,
+ legVec xmm_0, legVec xmm_1, rFlagsReg cr) %{
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
Matcher::vector_length(n->in(2)) >= 4);
match(Set dst (MinReductionV dst src));
match(Set dst (MaxReductionV dst src));
@@ -5374,12 +5374,78 @@ instruct minmax_reductionF_av(legRegF dst, legVec src, legVec tmp,
ins_pipe( pipe_slow );
%}
+instruct minmax_reduction2F_avx10(regF dst, immF src1, vec src2, vec xtmp1) %{
+ predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
+ ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) ||
+ (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) &&
+ Matcher::vector_length(n->in(2)) == 2);
+ match(Set dst (MinReductionV src1 src2));
+ match(Set dst (MaxReductionV src1 src2));
+ effect(TEMP dst, TEMP xtmp1);
+ format %{ "vector_minmax_reduction $dst, $src1, $src2 \t; using $xtmp1 as TEMP" %}
+ ins_encode %{
+ int opcode = this->ideal_Opcode();
+ int vlen = Matcher::vector_length(this, $src2);
+ __ reduceFloatMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister,
+ xnoreg, xnoreg, xnoreg, $xtmp1$$XMMRegister);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+instruct minmax_reductionF_avx10(regF dst, immF src1, vec src2, vec xtmp1, vec xtmp2) %{
+ predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
+ ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) ||
+ (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) &&
+ Matcher::vector_length(n->in(2)) >= 4);
+ match(Set dst (MinReductionV src1 src2));
+ match(Set dst (MaxReductionV src1 src2));
+ effect(TEMP dst, TEMP xtmp1, TEMP xtmp2);
+ format %{ "vector_minmax_reduction $dst, $src1, $src2 \t; using $xtmp1 and $xtmp2 as TEMP" %}
+ ins_encode %{
+ int opcode = this->ideal_Opcode();
+ int vlen = Matcher::vector_length(this, $src2);
+ __ reduceFloatMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, xnoreg, xnoreg,
+ xnoreg, $xtmp1$$XMMRegister, $xtmp2$$XMMRegister);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+instruct minmax_reduction2F_avx10_av(regF dst, vec src, vec xtmp1) %{
+ predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
+ Matcher::vector_length(n->in(2)) == 2);
+ match(Set dst (MinReductionV dst src));
+ match(Set dst (MaxReductionV dst src));
+ effect(TEMP dst, TEMP xtmp1);
+ format %{ "vector_minmax2F_reduction $dst, $src \t; using $xtmp1 as TEMP" %}
+ ins_encode %{
+ int opcode = this->ideal_Opcode();
+ int vlen = Matcher::vector_length(this, $src);
+ __ reduceFloatMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, xnoreg, xnoreg, xnoreg,
+ $xtmp1$$XMMRegister);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+instruct minmax_reductionF_avx10_av(regF dst, vec src, vec xtmp1, vec xtmp2) %{
+ predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
+ Matcher::vector_length(n->in(2)) >= 4);
+ match(Set dst (MinReductionV dst src));
+ match(Set dst (MaxReductionV dst src));
+ effect(TEMP dst, TEMP xtmp1, TEMP xtmp2);
+ format %{ "vector_minmax2F_reduction $dst, $src \t; using $xtmp1 and $xtmp2 as TEMP" %}
+ ins_encode %{
+ int opcode = this->ideal_Opcode();
+ int vlen = Matcher::vector_length(this, $src);
+ __ reduceFloatMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister, xnoreg, xnoreg, xnoreg,
+ $xtmp1$$XMMRegister, $xtmp2$$XMMRegister);
+ %}
+ ins_pipe( pipe_slow );
+%}
//--------------------Min Double Reduction --------------------
-instruct minmax_reduction2D(legRegD dst, immD src1, legVec src2,
- legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, // TEMPs
- rFlagsReg cr) %{
- predicate(Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
+instruct minmax_reduction2D(legRegD dst, immD src1, legVec src2, legVec tmp1, legVec tmp2,
+ legVec tmp3, legVec tmp4, rFlagsReg cr) %{
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) ||
(n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) &&
Matcher::vector_length(n->in(2)) == 2);
@@ -5398,10 +5464,9 @@ instruct minmax_reduction2D(legRegD dst, immD src1, legVec src2,
ins_pipe( pipe_slow );
%}
-instruct minmax_reductionD(legRegD dst, immD src1, legVec src2,
- legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, legVec tmp5, // TEMPs
- rFlagsReg cr) %{
- predicate(Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
+instruct minmax_reductionD(legRegD dst, immD src1, legVec src2, legVec tmp1, legVec tmp2,
+ legVec tmp3, legVec tmp4, legVec tmp5, rFlagsReg cr) %{
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) ||
(n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) &&
Matcher::vector_length(n->in(2)) >= 4);
@@ -5421,10 +5486,9 @@ instruct minmax_reductionD(legRegD dst, immD src1, legVec src2,
%}
-instruct minmax_reduction2D_av(legRegD dst, legVec src,
- legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, // TEMPs
- rFlagsReg cr) %{
- predicate(Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
+instruct minmax_reduction2D_av(legRegD dst, legVec src, legVec tmp1, legVec tmp2,
+ legVec tmp3, legVec tmp4, rFlagsReg cr) %{
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
Matcher::vector_length(n->in(2)) == 2);
match(Set dst (MinReductionV dst src));
match(Set dst (MaxReductionV dst src));
@@ -5441,10 +5505,9 @@ instruct minmax_reduction2D_av(legRegD dst, legVec src,
ins_pipe( pipe_slow );
%}
-instruct minmax_reductionD_av(legRegD dst, legVec src,
- legVec tmp1, legVec tmp2, legVec tmp3, legVec tmp4, legVec tmp5, // TEMPs
- rFlagsReg cr) %{
- predicate(Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
+instruct minmax_reductionD_av(legRegD dst, legVec src, legVec tmp1, legVec tmp2, legVec tmp3,
+ legVec tmp4, legVec tmp5, rFlagsReg cr) %{
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
Matcher::vector_length(n->in(2)) >= 4);
match(Set dst (MinReductionV dst src));
match(Set dst (MaxReductionV dst src));
@@ -5461,6 +5524,75 @@ instruct minmax_reductionD_av(legRegD dst, legVec src,
ins_pipe( pipe_slow );
%}
+instruct minmax_reduction2D_avx10(regD dst, immD src1, vec src2, vec xtmp1) %{
+ predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
+ ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) ||
+ (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) &&
+ Matcher::vector_length(n->in(2)) == 2);
+ match(Set dst (MinReductionV src1 src2));
+ match(Set dst (MaxReductionV src1 src2));
+ effect(TEMP dst, TEMP xtmp1);
+ format %{ "vector_minmax2D_reduction $dst, $src1, $src2 ; using $xtmp1 as TEMP" %}
+ ins_encode %{
+ int opcode = this->ideal_Opcode();
+ int vlen = Matcher::vector_length(this, $src2);
+ __ reduceDoubleMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, xnoreg,
+ xnoreg, xnoreg, $xtmp1$$XMMRegister);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+instruct minmax_reductionD_avx10(regD dst, immD src1, vec src2, vec xtmp1, vec xtmp2) %{
+ predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
+ ((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) ||
+ (n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) &&
+ Matcher::vector_length(n->in(2)) >= 4);
+ match(Set dst (MinReductionV src1 src2));
+ match(Set dst (MaxReductionV src1 src2));
+ effect(TEMP dst, TEMP xtmp1, TEMP xtmp2);
+ format %{ "vector_minmaxD_reduction $dst, $src1, $src2 ; using $xtmp1 and $xtmp2 as TEMP" %}
+ ins_encode %{
+ int opcode = this->ideal_Opcode();
+ int vlen = Matcher::vector_length(this, $src2);
+ __ reduceDoubleMinMax(opcode, vlen, false, $dst$$XMMRegister, $src2$$XMMRegister, xnoreg, xnoreg,
+ xnoreg, $xtmp1$$XMMRegister, $xtmp2$$XMMRegister);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+
+instruct minmax_reduction2D_av_avx10(regD dst, vec src, vec xtmp1) %{
+ predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
+ Matcher::vector_length(n->in(2)) == 2);
+ match(Set dst (MinReductionV dst src));
+ match(Set dst (MaxReductionV dst src));
+ effect(TEMP dst, TEMP xtmp1);
+ format %{ "vector_minmax2D_reduction $dst, $src ; using $xtmp1 as TEMP" %}
+ ins_encode %{
+ int opcode = this->ideal_Opcode();
+ int vlen = Matcher::vector_length(this, $src);
+ __ reduceDoubleMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister,
+ xnoreg, xnoreg, xnoreg, $xtmp1$$XMMRegister);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+instruct minmax_reductionD_av_avx10(regD dst, vec src, vec xtmp1, vec xtmp2) %{
+ predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
+ Matcher::vector_length(n->in(2)) >= 4);
+ match(Set dst (MinReductionV dst src));
+ match(Set dst (MaxReductionV dst src));
+ effect(TEMP dst, TEMP xtmp1, TEMP xtmp2);
+ format %{ "vector_minmaxD_reduction $dst, $src ; using $xtmp1 and $xtmp2 as TEMP" %}
+ ins_encode %{
+ int opcode = this->ideal_Opcode();
+ int vlen = Matcher::vector_length(this, $src);
+ __ reduceDoubleMinMax(opcode, vlen, true, $dst$$XMMRegister, $src$$XMMRegister,
+ xnoreg, xnoreg, xnoreg, $xtmp1$$XMMRegister, $xtmp2$$XMMRegister);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
// ====================VECTOR ARITHMETIC=======================================
// --------------------------------- ADD --------------------------------------
@@ -6347,9 +6479,25 @@ instruct vminmaxL_reg_evex(vec dst, vec src1, vec src2) %{
ins_pipe( pipe_slow );
%}
+// Float/Double vector Min/Max
+instruct minmaxFP_avx10_reg(vec dst, vec a, vec b) %{
+ predicate(VM_Version::supports_avx10_2() &&
+ is_floating_point_type(Matcher::vector_element_basic_type(n))); // T_FLOAT, T_DOUBLE
+ match(Set dst (MinV a b));
+ match(Set dst (MaxV a b));
+ format %{ "vector_minmaxFP $dst, $a, $b" %}
+ ins_encode %{
+ int vlen_enc = vector_length_encoding(this);
+ int opcode = this->ideal_Opcode();
+ BasicType elem_bt = Matcher::vector_element_basic_type(this);
+ __ vminmax_fp(opcode, elem_bt, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister, vlen_enc);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
// Float/Double vector Min/Max
instruct minmaxFP_reg(legVec dst, legVec a, legVec b, legVec tmp, legVec atmp, legVec btmp) %{
- predicate(Matcher::vector_length_in_bytes(n) <= 32 &&
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_length_in_bytes(n) <= 32 &&
is_floating_point_type(Matcher::vector_element_basic_type(n)) && // T_FLOAT, T_DOUBLE
UseAVX > 0);
match(Set dst (MinV a b));
@@ -6370,8 +6518,8 @@ instruct minmaxFP_reg(legVec dst, legVec a, legVec b, legVec tmp, legVec atmp, l
ins_pipe( pipe_slow );
%}
-instruct evminmaxFP_reg_eavx(vec dst, vec a, vec b, vec atmp, vec btmp, kReg ktmp) %{
- predicate(Matcher::vector_length_in_bytes(n) == 64 &&
+instruct evminmaxFP_reg_evex(vec dst, vec a, vec b, vec atmp, vec btmp, kReg ktmp) %{
+ predicate(!VM_Version::supports_avx10_2() && Matcher::vector_length_in_bytes(n) == 64 &&
is_floating_point_type(Matcher::vector_element_basic_type(n))); // T_FLOAT, T_DOUBLE
match(Set dst (MinV a b));
match(Set dst (MaxV a b));
@@ -7630,8 +7778,11 @@ instruct vcastFtoD_reg(vec dst, vec src) %{
instruct castFtoX_reg_avx(vec dst, vec src, vec xtmp1, vec xtmp2, vec xtmp3, vec xtmp4, rFlagsReg cr) %{
- predicate(!VM_Version::supports_avx512vl() && Matcher::vector_length_in_bytes(n->in(1)) < 64 &&
- type2aelembytes(Matcher::vector_element_basic_type(n)) <= 4);
+ predicate(!VM_Version::supports_avx10_2() &&
+ !VM_Version::supports_avx512vl() &&
+ Matcher::vector_length_in_bytes(n->in(1)) < 64 &&
+ type2aelembytes(Matcher::vector_element_basic_type(n)) <= 4 &&
+ is_integral_type(Matcher::vector_element_basic_type(n)));
match(Set dst (VectorCastF2X src));
effect(TEMP dst, TEMP xtmp1, TEMP xtmp2, TEMP xtmp3, TEMP xtmp4, KILL cr);
format %{ "vector_cast_f2x $dst,$src\t! using $xtmp1, $xtmp2, $xtmp3 and $xtmp4 as TEMP" %}
@@ -7653,7 +7804,8 @@ instruct castFtoX_reg_avx(vec dst, vec src, vec xtmp1, vec xtmp2, vec xtmp3, vec
%}
instruct castFtoX_reg_evex(vec dst, vec src, vec xtmp1, vec xtmp2, kReg ktmp1, kReg ktmp2, rFlagsReg cr) %{
- predicate((VM_Version::supports_avx512vl() || Matcher::vector_length_in_bytes(n->in(1)) == 64) &&
+ predicate(!VM_Version::supports_avx10_2() &&
+ (VM_Version::supports_avx512vl() || Matcher::vector_length_in_bytes(n->in(1)) == 64) &&
is_integral_type(Matcher::vector_element_basic_type(n)));
match(Set dst (VectorCastF2X src));
effect(TEMP dst, TEMP xtmp1, TEMP xtmp2, TEMP ktmp1, TEMP ktmp2, KILL cr);
@@ -7675,6 +7827,33 @@ instruct castFtoX_reg_evex(vec dst, vec src, vec xtmp1, vec xtmp2, kReg ktmp1, k
ins_pipe( pipe_slow );
%}
+instruct castFtoX_reg_avx10(vec dst, vec src) %{
+ predicate(VM_Version::supports_avx10_2() &&
+ is_integral_type(Matcher::vector_element_basic_type(n)));
+ match(Set dst (VectorCastF2X src));
+ format %{ "vector_cast_f2x_avx10 $dst, $src\t!" %}
+ ins_encode %{
+ BasicType to_elem_bt = Matcher::vector_element_basic_type(this);
+ int vlen_enc = (to_elem_bt == T_LONG) ? vector_length_encoding(this) : vector_length_encoding(this, $src);
+ __ vector_castF2X_avx10(to_elem_bt, $dst$$XMMRegister, $src$$XMMRegister, vlen_enc);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+instruct castFtoX_mem_avx10(vec dst, memory src) %{
+ predicate(VM_Version::supports_avx10_2() &&
+ is_integral_type(Matcher::vector_element_basic_type(n)));
+ match(Set dst (VectorCastF2X (LoadVector src)));
+ format %{ "vector_cast_f2x_avx10 $dst, $src\t!" %}
+ ins_encode %{
+ int vlen = Matcher::vector_length(this);
+ BasicType to_elem_bt = Matcher::vector_element_basic_type(this);
+ int vlen_enc = (to_elem_bt == T_LONG) ? vector_length_encoding(this) : vector_length_encoding(vlen * sizeof(jfloat));
+ __ vector_castF2X_avx10(to_elem_bt, $dst$$XMMRegister, $src$$Address, vlen_enc);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
instruct vcastDtoF_reg(vec dst, vec src) %{
predicate(Matcher::vector_element_basic_type(n) == T_FLOAT);
match(Set dst (VectorCastD2X src));
@@ -7687,7 +7866,9 @@ instruct vcastDtoF_reg(vec dst, vec src) %{
%}
instruct castDtoX_reg_avx(vec dst, vec src, vec xtmp1, vec xtmp2, vec xtmp3, vec xtmp4, vec xtmp5, rFlagsReg cr) %{
- predicate(!VM_Version::supports_avx512vl() && Matcher::vector_length_in_bytes(n->in(1)) < 64 &&
+ predicate(!VM_Version::supports_avx10_2() &&
+ !VM_Version::supports_avx512vl() &&
+ Matcher::vector_length_in_bytes(n->in(1)) < 64 &&
is_integral_type(Matcher::vector_element_basic_type(n)));
match(Set dst (VectorCastD2X src));
effect(TEMP dst, TEMP xtmp1, TEMP xtmp2, TEMP xtmp3, TEMP xtmp4, TEMP xtmp5, KILL cr);
@@ -7703,7 +7884,8 @@ instruct castDtoX_reg_avx(vec dst, vec src, vec xtmp1, vec xtmp2, vec xtmp3, vec
%}
instruct castDtoX_reg_evex(vec dst, vec src, vec xtmp1, vec xtmp2, kReg ktmp1, kReg ktmp2, rFlagsReg cr) %{
- predicate((VM_Version::supports_avx512vl() || Matcher::vector_length_in_bytes(n->in(1)) == 64) &&
+ predicate(!VM_Version::supports_avx10_2() &&
+ (VM_Version::supports_avx512vl() || Matcher::vector_length_in_bytes(n->in(1)) == 64) &&
is_integral_type(Matcher::vector_element_basic_type(n)));
match(Set dst (VectorCastD2X src));
effect(TEMP dst, TEMP xtmp1, TEMP xtmp2, TEMP ktmp1, TEMP ktmp2, KILL cr);
@@ -7719,6 +7901,33 @@ instruct castDtoX_reg_evex(vec dst, vec src, vec xtmp1, vec xtmp2, kReg ktmp1, k
ins_pipe( pipe_slow );
%}
+instruct castDtoX_reg_avx10(vec dst, vec src) %{
+ predicate(VM_Version::supports_avx10_2() &&
+ is_integral_type(Matcher::vector_element_basic_type(n)));
+ match(Set dst (VectorCastD2X src));
+ format %{ "vector_cast_d2x_avx10 $dst, $src\t!" %}
+ ins_encode %{
+ int vlen_enc = vector_length_encoding(this, $src);
+ BasicType to_elem_bt = Matcher::vector_element_basic_type(this);
+ __ vector_castD2X_avx10(to_elem_bt, $dst$$XMMRegister, $src$$XMMRegister, vlen_enc);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+instruct castDtoX_mem_avx10(vec dst, memory src) %{
+ predicate(VM_Version::supports_avx10_2() &&
+ is_integral_type(Matcher::vector_element_basic_type(n)));
+ match(Set dst (VectorCastD2X (LoadVector src)));
+ format %{ "vector_cast_d2x_avx10 $dst, $src\t!" %}
+ ins_encode %{
+ int vlen = Matcher::vector_length(this);
+ int vlen_enc = vector_length_encoding(vlen * sizeof(jdouble));
+ BasicType to_elem_bt = Matcher::vector_element_basic_type(this);
+ __ vector_castD2X_avx10(to_elem_bt, $dst$$XMMRegister, $src$$Address, vlen_enc);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
instruct vucast(vec dst, vec src) %{
match(Set dst (VectorUCastB2X src));
match(Set dst (VectorUCastS2X src));
@@ -10687,8 +10896,22 @@ instruct scalar_binOps_HF_reg(regF dst, regF src1, regF src2)
ins_pipe(pipe_slow);
%}
+instruct scalar_minmax_HF_avx10_reg(regF dst, regF src1, regF src2)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (MaxHF src1 src2));
+ match(Set dst (MinHF src1 src2));
+ format %{ "scalar_min_max_fp16 $dst, $src1, $src2" %}
+ ins_encode %{
+ int function = this->ideal_Opcode() == Op_MinHF ? AVX10_MINMAX_MIN_COMPARE_SIGN : AVX10_MINMAX_MAX_COMPARE_SIGN;
+ __ eminmaxsh($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, function);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
instruct scalar_minmax_HF_reg(regF dst, regF src1, regF src2, kReg ktmp, regF xtmp1, regF xtmp2)
%{
+ predicate(!VM_Version::supports_avx10_2());
match(Set dst (MaxHF src1 src2));
match(Set dst (MinHF src1 src2));
effect(TEMP_DEF dst, TEMP ktmp, TEMP xtmp1, TEMP xtmp2);
@@ -10788,8 +11011,37 @@ instruct vector_fma_HF_mem(vec dst, memory src1, vec src2)
ins_pipe( pipe_slow );
%}
+instruct vector_minmax_HF_avx10_mem(vec dst, vec src1, memory src2)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (MinVHF src1 (VectorReinterpret (LoadVector src2))));
+ match(Set dst (MaxVHF src1 (VectorReinterpret (LoadVector src2))));
+ format %{ "vector_min_max_fp16_mem $dst, $src1, $src2" %}
+ ins_encode %{
+ int vlen_enc = vector_length_encoding(this);
+ int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_MINMAX_MIN_COMPARE_SIGN : AVX10_MINMAX_MAX_COMPARE_SIGN;
+ __ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$Address, true, function, vlen_enc);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
+instruct vector_minmax_HF_avx10_reg(vec dst, vec src1, vec src2)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (MinVHF src1 src2));
+ match(Set dst (MaxVHF src1 src2));
+ format %{ "vector_min_max_fp16 $dst, $src1, $src2" %}
+ ins_encode %{
+ int vlen_enc = vector_length_encoding(this);
+ int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_MINMAX_MIN_COMPARE_SIGN : AVX10_MINMAX_MAX_COMPARE_SIGN;
+ __ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$XMMRegister, true, function, vlen_enc);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
instruct vector_minmax_HF_reg(vec dst, vec src1, vec src2, kReg ktmp, vec xtmp1, vec xtmp2)
%{
+ predicate(!VM_Version::supports_avx10_2());
match(Set dst (MinVHF src1 src2));
match(Set dst (MaxVHF src1 src2));
effect(TEMP_DEF dst, TEMP ktmp, TEMP xtmp1, TEMP xtmp2);
diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad
index 7f8b7dbc9f3d..1f4a5f106f72 100644
--- a/src/hotspot/cpu/x86/x86_64.ad
+++ b/src/hotspot/cpu/x86/x86_64.ad
@@ -4446,9 +4446,20 @@ instruct loadD(regD dst, memory mem)
ins_pipe(pipe_slow); // XXX
%}
+// max = java.lang.Math.max(float a, float b)
+instruct maxF_avx10_reg(regF dst, regF a, regF b) %{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (MaxF a b));
+ format %{ "maxF $dst, $a, $b" %}
+ ins_encode %{
+ __ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MAX_COMPARE_SIGN);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
// max = java.lang.Math.max(float a, float b)
instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{
- predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n));
+ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n));
match(Set dst (MaxF a b));
effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp);
format %{ "maxF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %}
@@ -4459,7 +4470,7 @@ instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp,
%}
instruct maxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{
- predicate(UseAVX > 0 && VLoopReductions::is_reduction(n));
+ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n));
match(Set dst (MaxF a b));
effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr);
@@ -4471,9 +4482,20 @@ instruct maxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRe
ins_pipe( pipe_slow );
%}
+// max = java.lang.Math.max(double a, double b)
+instruct maxD_avx10_reg(regD dst, regD a, regD b) %{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (MaxD a b));
+ format %{ "maxD $dst, $a, $b" %}
+ ins_encode %{
+ __ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MAX_COMPARE_SIGN);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
// max = java.lang.Math.max(double a, double b)
instruct maxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{
- predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n));
+ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n));
match(Set dst (MaxD a b));
effect(USE a, USE b, TEMP atmp, TEMP btmp, TEMP tmp);
format %{ "maxD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %}
@@ -4484,7 +4506,7 @@ instruct maxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp,
%}
instruct maxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{
- predicate(UseAVX > 0 && VLoopReductions::is_reduction(n));
+ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n));
match(Set dst (MaxD a b));
effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr);
@@ -4496,9 +4518,20 @@ instruct maxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRe
ins_pipe( pipe_slow );
%}
+// max = java.lang.Math.min(float a, float b)
+instruct minF_avx10_reg(regF dst, regF a, regF b) %{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (MinF a b));
+ format %{ "minF $dst, $a, $b" %}
+ ins_encode %{
+ __ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MIN_COMPARE_SIGN);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
// min = java.lang.Math.min(float a, float b)
instruct minF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{
- predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n));
+ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n));
match(Set dst (MinF a b));
effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp);
format %{ "minF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %}
@@ -4509,7 +4542,7 @@ instruct minF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp,
%}
instruct minF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{
- predicate(UseAVX > 0 && VLoopReductions::is_reduction(n));
+ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n));
match(Set dst (MinF a b));
effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr);
@@ -4521,9 +4554,20 @@ instruct minF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRe
ins_pipe( pipe_slow );
%}
+// max = java.lang.Math.min(double a, double b)
+instruct minD_avx10_reg(regD dst, regD a, regD b) %{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (MinD a b));
+ format %{ "minD $dst, $a, $b" %}
+ ins_encode %{
+ __ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MIN_COMPARE_SIGN);
+ %}
+ ins_pipe( pipe_slow );
+%}
+
// min = java.lang.Math.min(double a, double b)
instruct minD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{
- predicate(UseAVX > 0 && !VLoopReductions::is_reduction(n));
+ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n));
match(Set dst (MinD a b));
effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp);
format %{ "minD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %}
@@ -4534,7 +4578,7 @@ instruct minD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp,
%}
instruct minD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{
- predicate(UseAVX > 0 && VLoopReductions::is_reduction(n));
+ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n));
match(Set dst (MinD a b));
effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr);
@@ -11671,6 +11715,7 @@ instruct convD2F_reg_mem(regF dst, memory src)
// XXX do mem variants
instruct convF2I_reg_reg(rRegI dst, regF src, rFlagsReg cr)
%{
+ predicate(!VM_Version::supports_avx10_2());
match(Set dst (ConvF2I src));
effect(KILL cr);
format %{ "convert_f2i $dst, $src" %}
@@ -11680,8 +11725,31 @@ instruct convF2I_reg_reg(rRegI dst, regF src, rFlagsReg cr)
ins_pipe(pipe_slow);
%}
+instruct convF2I_reg_reg_avx10(rRegI dst, regF src)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (ConvF2I src));
+ format %{ "evcvttss2sisl $dst, $src" %}
+ ins_encode %{
+ __ evcvttss2sisl($dst$$Register, $src$$XMMRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct convF2I_reg_mem_avx10(rRegI dst, memory src)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (ConvF2I (LoadF src)));
+ format %{ "evcvttss2sisl $dst, $src" %}
+ ins_encode %{
+ __ evcvttss2sisl($dst$$Register, $src$$Address);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct convF2L_reg_reg(rRegL dst, regF src, rFlagsReg cr)
%{
+ predicate(!VM_Version::supports_avx10_2());
match(Set dst (ConvF2L src));
effect(KILL cr);
format %{ "convert_f2l $dst, $src"%}
@@ -11691,8 +11759,31 @@ instruct convF2L_reg_reg(rRegL dst, regF src, rFlagsReg cr)
ins_pipe(pipe_slow);
%}
+instruct convF2L_reg_reg_avx10(rRegL dst, regF src)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (ConvF2L src));
+ format %{ "evcvttss2sisq $dst, $src" %}
+ ins_encode %{
+ __ evcvttss2sisq($dst$$Register, $src$$XMMRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct convF2L_reg_mem_avx10(rRegL dst, memory src)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (ConvF2L (LoadF src)));
+ format %{ "evcvttss2sisq $dst, $src" %}
+ ins_encode %{
+ __ evcvttss2sisq($dst$$Register, $src$$Address);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct convD2I_reg_reg(rRegI dst, regD src, rFlagsReg cr)
%{
+ predicate(!VM_Version::supports_avx10_2());
match(Set dst (ConvD2I src));
effect(KILL cr);
format %{ "convert_d2i $dst, $src"%}
@@ -11702,8 +11793,31 @@ instruct convD2I_reg_reg(rRegI dst, regD src, rFlagsReg cr)
ins_pipe(pipe_slow);
%}
+instruct convD2I_reg_reg_avx10(rRegI dst, regD src)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (ConvD2I src));
+ format %{ "evcvttsd2sisl $dst, $src" %}
+ ins_encode %{
+ __ evcvttsd2sisl($dst$$Register, $src$$XMMRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct convD2I_reg_mem_avx10(rRegI dst, memory src)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (ConvD2I (LoadD src)));
+ format %{ "evcvttsd2sisl $dst, $src" %}
+ ins_encode %{
+ __ evcvttsd2sisl($dst$$Register, $src$$Address);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct convD2L_reg_reg(rRegL dst, regD src, rFlagsReg cr)
%{
+ predicate(!VM_Version::supports_avx10_2());
match(Set dst (ConvD2L src));
effect(KILL cr);
format %{ "convert_d2l $dst, $src"%}
@@ -11713,6 +11827,28 @@ instruct convD2L_reg_reg(rRegL dst, regD src, rFlagsReg cr)
ins_pipe(pipe_slow);
%}
+instruct convD2L_reg_reg_avx10(rRegL dst, regD src)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (ConvD2L src));
+ format %{ "evcvttsd2sisq $dst, $src" %}
+ ins_encode %{
+ __ evcvttsd2sisq($dst$$Register, $src$$XMMRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct convD2L_reg_mem_avx10(rRegL dst, memory src)
+%{
+ predicate(VM_Version::supports_avx10_2());
+ match(Set dst (ConvD2L (LoadD src)));
+ format %{ "evcvttsd2sisq $dst, $src" %}
+ ins_encode %{
+ __ evcvttsd2sisq($dst$$Register, $src$$Address);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct round_double_reg(rRegL dst, regD src, rRegL rtmp, rcx_RegL rcx, rFlagsReg cr)
%{
match(Set dst (RoundD src));
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index 3ef58971c2c1..e4b8c9a970a0 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -162,10 +162,8 @@ 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;
-int (*os::Linux::_pthread_getcpuclockid)(pthread_t, clockid_t *) = nullptr;
int (*os::Linux::_pthread_setname_np)(pthread_t, const char*) = nullptr;
pthread_t os::Linux::_main_thread;
-bool os::Linux::_supports_fast_thread_cpu_time = false;
const char * os::Linux::_libc_version = nullptr;
const char * os::Linux::_libpthread_version = nullptr;
@@ -1529,29 +1527,6 @@ double os::elapsedVTime() {
}
}
-void os::Linux::fast_thread_clock_init() {
- clockid_t clockid;
- struct timespec tp;
- int (*pthread_getcpuclockid_func)(pthread_t, clockid_t *) =
- (int(*)(pthread_t, clockid_t *)) dlsym(RTLD_DEFAULT, "pthread_getcpuclockid");
-
- // Switch to using fast clocks for thread cpu time if
- // the clock_getres() returns 0 error code.
- // Note, that some kernels may support the current thread
- // clock (CLOCK_THREAD_CPUTIME_ID) but not the clocks
- // returned by the pthread_getcpuclockid().
- // If the fast POSIX clocks are supported then the clock_getres()
- // must return at least tp.tv_sec == 0 which means a resolution
- // better than 1 sec. This is extra check for reliability.
-
- if (pthread_getcpuclockid_func &&
- pthread_getcpuclockid_func(_main_thread, &clockid) == 0 &&
- clock_getres(clockid, &tp) == 0 && tp.tv_sec == 0) {
- _supports_fast_thread_cpu_time = true;
- _pthread_getcpuclockid = pthread_getcpuclockid_func;
- }
-}
-
// thread_id is kernel thread id (similar to Solaris LWP id)
intx os::current_thread_id() { return os::Linux::gettid(); }
int os::current_process_id() {
@@ -2189,6 +2164,10 @@ void os::print_os_info(outputStream* st) {
st->cr();
}
+ if (os::Linux::print_numa_info(st)) {
+ st->cr();
+ }
+
VM_Version::print_platform_virtualization_info(st);
os::Linux::print_steal_info(st);
@@ -2592,6 +2571,97 @@ bool os::Linux::print_container_info(outputStream* st) {
return true;
}
+#define SYS_DEVICES_NODE "/sys/devices/system/node"
+
+static size_t read_sysfs_file(const char* path, char* buf, size_t sz) {
+ FILE* f = os::fopen(path, "r");
+ if (f == nullptr) return 0;
+ size_t n = fread(buf, 1, sz - 1, f);
+ fclose(f);
+ buf[n] = '\0';
+ while (n > 0 && (buf[n-1] == '\n' || buf[n-1] == '\r')) buf[--n] = '\0';
+ return n;
+}
+
+static void print_numa_memory_info(outputStream* st, int node) {
+ char path[256];
+ char line[256];
+ long long mem_total = -1;
+ long long mem_free = -1;
+ os::snprintf_checked(path, sizeof(path), SYS_DEVICES_NODE "/node%d/meminfo", node);
+ FILE* f = os::fopen(path, "r");
+ if (f == nullptr) {
+ return;
+ }
+
+ while (fgets(line, sizeof(line), f) != nullptr) {
+ long long mval;
+ if (sscanf(line, "Node %*d MemTotal: %lld kB", &mval) == 1) mem_total = mval;
+ if (sscanf(line, "Node %*d MemFree: %lld kB", &mval) == 1) mem_free = mval;
+ }
+ fclose(f);
+
+ if (mem_total >= 0) { st->print_cr("mem size: %lld kB", mem_total); }
+ if (mem_free >= 0) { st->print_cr("mem free: %lld kB", mem_free); }
+}
+
+static void print_numa_cpu_list(outputStream* st, int node) {
+ char path[256];
+ char buf[1024];
+ os::snprintf_checked(path, sizeof(path), SYS_DEVICES_NODE "/node%d/cpulist", node);
+ if (read_sysfs_file(path, buf, sizeof(buf)) > 0) {
+ st->print_cr("cpus: %s", buf);
+ } else {
+ st->print_cr("cpus: (unavailable)");
+ }
+}
+
+bool os::Linux::print_numa_info(outputStream* st) {
+ if (!UseNUMA) {
+ // If NUMA optimizations are not enabled we don't print anything
+ return false;
+ }
+
+ char buf[1024];
+ if (read_sysfs_file("/sys/devices/system/node/online", buf, sizeof(buf)) > 0) {
+ st->print_cr("NUMA nodes online: %s", buf);
+ } else {
+ return false;
+ }
+
+ bool first = true;
+ int node_count = 0;
+
+ if (nindex_to_node() == nullptr) {
+ return false;
+ }
+
+ for (int node: *nindex_to_node()) {
+ char nodepath[256];
+ os::snprintf_checked(nodepath, sizeof(nodepath), SYS_DEVICES_NODE "/node%d", node);
+ DIR* currd = os::opendir(nodepath);
+ if (currd == nullptr) continue;
+ if (first) {
+ st->cr();
+ first = false;
+ }
+ os::closedir(currd);
+
+ st->print_cr("NUMA node %d", node);
+ StreamIndentor si(st);
+ print_numa_cpu_list(st, node);
+ print_numa_memory_info(st, node);
+ node_count++;
+ }
+
+ if (node_count == 0) {
+ return false;
+ }
+
+ st->print_cr("Total NUMA node count: %d", node_count);
+ return true;
+}
+
void os::Linux::print_steal_info(outputStream* st) {
if (has_initial_tick_info) {
CPUPerfTicks pticks;
@@ -4377,7 +4447,7 @@ OSReturn os::get_native_priority(const Thread* const thread,
// For reference, please, see IEEE Std 1003.1-2004:
// http://www.unix.org/single_unix_specification
-jlong os::Linux::fast_thread_cpu_time(clockid_t clockid) {
+jlong os::Linux::thread_cpu_time(clockid_t clockid) {
struct timespec tp;
int status = clock_gettime(clockid, &tp);
assert(status == 0, "clock_gettime error: %s", os::strerror(errno));
@@ -4690,8 +4760,6 @@ jint os::init_2(void) {
os::Posix::init_2();
- Linux::fast_thread_clock_init();
-
if (PosixSignals::init() == JNI_ERR) {
return JNI_ERR;
}
@@ -5118,20 +5186,42 @@ int os::open(const char *path, int oflag, int mode) {
return fd;
}
-static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time);
+// Since kernel v2.6.12 the Linux ABI has had support for encoding the clock
+// types in the last three bits. Bit 2 indicates whether a cpu clock refers to a
+// thread or a process. Bits 1 and 0 give the type: PROF=0, VIRT=1, SCHED=2, or
+// FD=3. The clock CPUCLOCK_VIRT (0b001) reports the thread's consumed user
+// time. POSIX compliant implementations of pthread_getcpuclockid return the
+// clock CPUCLOCK_SCHED (0b010) which reports the thread's consumed system+user
+// time (as mandated by the POSIX standard POSIX.1-2024/IEEE Std 1003.1-2024
+// §3.90).
+static bool get_thread_clockid(Thread* thread, clockid_t* clockid, bool total) {
+ constexpr clockid_t CLOCK_TYPE_MASK = 3;
+ constexpr clockid_t CPUCLOCK_VIRT = 1;
+
+ int rc = pthread_getcpuclockid(thread->osthread()->pthread_id(), clockid);
+ if (rc != 0) {
+ // It's possible to encounter a terminated native thread that failed
+ // to detach itself from the VM - which should result in ESRCH.
+ assert_status(rc == ESRCH, rc, "pthread_getcpuclockid failed");
+ return false;
+ }
-static jlong fast_cpu_time(Thread *thread) {
- clockid_t clockid;
- int rc = os::Linux::pthread_getcpuclockid(thread->osthread()->pthread_id(),
- &clockid);
- if (rc == 0) {
- return os::Linux::fast_thread_cpu_time(clockid);
- } else {
- // It's possible to encounter a terminated native thread that failed
- // to detach itself from the VM - which should result in ESRCH.
- assert_status(rc == ESRCH, rc, "pthread_getcpuclockid failed");
- return -1;
- }
+ if (!total) {
+ clockid_t clockid_tmp = *clockid;
+ clockid_tmp = (clockid_tmp & ~CLOCK_TYPE_MASK) | CPUCLOCK_VIRT;
+ *clockid = clockid_tmp;
+ }
+
+ return true;
+}
+
+static jlong user_thread_cpu_time(Thread *thread);
+
+static jlong total_thread_cpu_time(Thread *thread) {
+ clockid_t clockid;
+ bool success = get_thread_clockid(thread, &clockid, true);
+
+ return success ? os::Linux::thread_cpu_time(clockid) : -1;
}
// current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool)
@@ -5142,84 +5232,36 @@ static jlong fast_cpu_time(Thread *thread) {
// the fast estimate available on the platform.
jlong os::current_thread_cpu_time() {
- if (os::Linux::supports_fast_thread_cpu_time()) {
- return os::Linux::fast_thread_cpu_time(CLOCK_THREAD_CPUTIME_ID);
- } else {
- // return user + sys since the cost is the same
- return slow_thread_cpu_time(Thread::current(), true /* user + sys */);
- }
+ return os::Linux::thread_cpu_time(CLOCK_THREAD_CPUTIME_ID);
}
jlong os::thread_cpu_time(Thread* thread) {
- // consistent with what current_thread_cpu_time() returns
- if (os::Linux::supports_fast_thread_cpu_time()) {
- return fast_cpu_time(thread);
- } else {
- return slow_thread_cpu_time(thread, true /* user + sys */);
- }
+ return total_thread_cpu_time(thread);
}
jlong os::current_thread_cpu_time(bool user_sys_cpu_time) {
- if (user_sys_cpu_time && os::Linux::supports_fast_thread_cpu_time()) {
- return os::Linux::fast_thread_cpu_time(CLOCK_THREAD_CPUTIME_ID);
+ if (user_sys_cpu_time) {
+ return os::Linux::thread_cpu_time(CLOCK_THREAD_CPUTIME_ID);
} else {
- return slow_thread_cpu_time(Thread::current(), user_sys_cpu_time);
+ return user_thread_cpu_time(Thread::current());
}
}
jlong os::thread_cpu_time(Thread *thread, bool user_sys_cpu_time) {
- if (user_sys_cpu_time && os::Linux::supports_fast_thread_cpu_time()) {
- return fast_cpu_time(thread);
- } else {
- return slow_thread_cpu_time(thread, user_sys_cpu_time);
- }
-}
-
-// -1 on error.
-static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time) {
- pid_t tid = thread->osthread()->thread_id();
- char *s;
- char stat[2048];
- size_t statlen;
- char proc_name[64];
- int count;
- long sys_time, user_time;
- char cdummy;
- int idummy;
- long ldummy;
- FILE *fp;
-
- snprintf(proc_name, 64, "/proc/self/task/%d/stat", tid);
- fp = os::fopen(proc_name, "r");
- if (fp == nullptr) return -1;
- statlen = fread(stat, 1, 2047, fp);
- stat[statlen] = '\0';
- fclose(fp);
-
- // Skip pid and the command string. Note that we could be dealing with
- // weird command names, e.g. user could decide to rename java launcher
- // to "java 1.4.2 :)", then the stat file would look like
- // 1234 (java 1.4.2 :)) R ... ...
- // We don't really need to know the command string, just find the last
- // occurrence of ")" and then start parsing from there. See bug 4726580.
- s = strrchr(stat, ')');
- if (s == nullptr) return -1;
-
- // Skip blank chars
- do { s++; } while (s && isspace((unsigned char) *s));
-
- count = sscanf(s,"%c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu",
- &cdummy, &idummy, &idummy, &idummy, &idummy, &idummy,
- &ldummy, &ldummy, &ldummy, &ldummy, &ldummy,
- &user_time, &sys_time);
- if (count != 13) return -1;
if (user_sys_cpu_time) {
- return ((jlong)sys_time + (jlong)user_time) * (1000000000 / os::Posix::clock_tics_per_second());
+ return total_thread_cpu_time(thread);
} else {
- return (jlong)user_time * (1000000000 / os::Posix::clock_tics_per_second());
+ return user_thread_cpu_time(thread);
}
}
+static jlong user_thread_cpu_time(Thread *thread) {
+ clockid_t clockid;
+ bool success = get_thread_clockid(thread, &clockid, false);
+
+ return success ? os::Linux::thread_cpu_time(clockid) : -1;
+}
+
void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) {
info_ptr->max_value = all_bits_jlong; // will not wrap in less than 64 bits
info_ptr->may_skip_backward = false; // elapsed time not wall time
diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp
index e2bd8eb3d31f..7196aebf513a 100644
--- a/src/hotspot/os/linux/os_linux.hpp
+++ b/src/hotspot/os/linux/os_linux.hpp
@@ -32,7 +32,6 @@
class os::Linux {
friend class os;
- static int (*_pthread_getcpuclockid)(pthread_t, clockid_t *);
static int (*_pthread_setname_np)(pthread_t, const char*);
static address _initial_thread_stack_bottom;
@@ -41,8 +40,6 @@ class os::Linux {
static const char *_libc_version;
static const char *_libpthread_version;
- static bool _supports_fast_thread_cpu_time;
-
static GrowableArray* _cpu_to_node;
static GrowableArray* _nindex_to_node;
@@ -80,6 +77,7 @@ class os::Linux {
static void print_proc_sys_info(outputStream* st);
static bool print_ld_preload_file(outputStream* st);
static void print_uptime_info(outputStream* st);
+ static bool print_numa_info(outputStream* st);
public:
struct CPUPerfTicks {
@@ -145,18 +143,7 @@ class os::Linux {
static bool manually_expand_stack(JavaThread * t, address addr);
static void expand_stack_to(address bottom);
- // fast POSIX clocks support
- static void fast_thread_clock_init(void);
-
- static int pthread_getcpuclockid(pthread_t tid, clockid_t *clock_id) {
- return _pthread_getcpuclockid ? _pthread_getcpuclockid(tid, clock_id) : -1;
- }
-
- static bool supports_fast_thread_cpu_time() {
- return _supports_fast_thread_cpu_time;
- }
-
- static jlong fast_thread_cpu_time(clockid_t clockid);
+ static jlong thread_cpu_time(clockid_t clockid);
static jlong sendfile(int out_fd, int in_fd, jlong* offset, jlong count);
diff --git a/src/hotspot/os/windows/os_perf_windows.cpp b/src/hotspot/os/windows/os_perf_windows.cpp
index 9d04ae65954c..c1b8e0ac4c44 100644
--- a/src/hotspot/os/windows/os_perf_windows.cpp
+++ b/src/hotspot/os/windows/os_perf_windows.cpp
@@ -779,6 +779,114 @@ static OSReturn allocate_pdh_constants() {
return OS_OK;
}
+// Look up the PDH index by reading the English (locale 009) counter name
+// registry. See KB Q287159: Using PDH APIs Correctly in a Localized Language
+// for details.
+static OSReturn lookup_perf_index_by_english_name(const char* english_name,
+ DWORD* result) {
+ ResourceMark rm;
+
+ DWORD type = 0;
+ DWORD size = 0;
+
+ // Determine the required buffer size
+ if (RegQueryValueEx(HKEY_PERFORMANCE_DATA, "Counter 009",
+ nullptr, &type, nullptr, &size) != ERROR_SUCCESS) {
+ return OS_ERR;
+ }
+
+ // Since registry entries in `HKEY_PERFORMANCE_DATA` are generated on the fly,
+ // they could change between calls, so we can't rely just on the size returned
+ // by the first call. Instead, Microsoft's documentation suggests running
+ // these calls in a loop until the return code is no longer `ERROR_MORE_DATA`.
+
+ char* buffer;
+ do {
+ if (size == 0) {
+ return OS_ERR;
+ }
+
+ // When `RegQueryValueEx()` returns `ERROR_MORE_DATA`, the value in the
+ // callback argument is undefined, so we need to create a new variable whose
+ // address is passed as the callback size argument.
+ buffer = NEW_RESOURCE_ARRAY(char, size);
+
+ DWORD cb_size = size;
+ LSTATUS status = RegQueryValueEx(HKEY_PERFORMANCE_DATA, "Counter 009",
+ nullptr, &type, (LPBYTE)buffer,
+ &cb_size);
+ if (status == ERROR_MORE_DATA) {
+ // We need to increase the buffer size. Since we don't know _how much_ to
+ // increase it by, we use an estimate (4096) for the increment.
+ DWORD increment = 4096;
+ if (size > MAXDWORD - increment) {
+ return OS_ERR;
+ }
+ size += increment;
+ } else if (status == ERROR_SUCCESS) {
+ break;
+ } else {
+ // If there was some other problem fetching this registry entry, tell the
+ // caller that we couldn't lookup the index.
+ return OS_ERR;
+ }
+ } while (true);
+
+ if (type != REG_MULTI_SZ) {
+ return OS_ERR;
+ }
+
+ // The buffer contains indices and names in the form (\0\0)*, so
+ // iterate character by character to parse the name and if it matches the
+ // English name, then we return the integer value of the index.
+ for (const char* p = buffer; *p != '\0'; ) {
+ const char* idx_str = p;
+ p += strlen(p) + 1;
+ if (*p == '\0') {
+ break;
+ }
+
+ const char* name = p;
+ p += strlen(p) + 1;
+ if (strcmp(name, english_name) == 0) {
+ errno = 0;
+ char* end = nullptr;
+ unsigned long value = strtoul(idx_str, &end, 10);
+ if (errno == 0 && end != idx_str && value <= MAXDWORD) {
+ *result = (DWORD)value;
+ return OS_OK;
+ }
+ }
+ }
+
+ return OS_ERR;
+}
+
+// Return the counter index of the 'Processor Information' counter, if
+// available, or else the 'Processor' counter. The former is aware of the
+// possibility of multiple processor groups and thus provides a more accurate
+// processor count whereas the latter serves as fallback.
+static DWORD get_proc_counter() {
+ static DWORD pdh_idx = 0;
+ if (pdh_idx != 0) {
+ return pdh_idx;
+ }
+
+ // Some APIs accept English counter names whereas others accept counter names
+ // in the specific user's locale. We determine the locale-specific name using
+ // the counter index, but to find the counter index, we use the English name
+ // of the counter and look for it in a specific registry key.
+ DWORD info_idx;
+ if (lookup_perf_index_by_english_name("Processor Information",
+ &info_idx) != OS_OK) {
+ info_idx = PDH_PROCESSOR_IDX;
+ }
+
+ // Assign to the static variable so that the value persists across calls.
+ pdh_idx = info_idx;
+ return pdh_idx;
+}
+
/*
* Enuerate the Processor PDH object and returns a buffer containing the enumerated instances.
* Caller needs ResourceMark;
@@ -786,8 +894,11 @@ static OSReturn allocate_pdh_constants() {
* @return buffer if successful, null on failure.
*/
static const char* enumerate_cpu_instances() {
- char* processor; //'Processor' == PDH_PROCESSOR_IDX
- if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) {
+ // The `PdhEnumObjectItems()` function accepts a localized name of the perf
+ // counter. To obtain the name that is specific to the user's locale, we
+ // perform a reverse lookup from counter index to counter name.
+ char* processor;
+ if (lookup_name_by_index(get_proc_counter(), &processor) != OS_OK) {
return nullptr;
}
DWORD c_size = 0;
@@ -821,13 +932,17 @@ static const char* enumerate_cpu_instances() {
static int count_logical_cpus(const char* instances) {
assert(instances != nullptr, "invariant");
- // count logical instances.
- DWORD count;
- char* tmp;
- for (count = 0, tmp = const_cast(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], count++);
- // PDH reports an instance for each logical processor plus an instance for the total (_Total)
- assert(count == os::processor_count() + 1, "invalid enumeration!");
- return count - 1;
+ DWORD count = 0;
+ for (const char* tmp = instances; *tmp != '\0'; tmp += strlen(tmp) + 1) {
+ // In both the 'Processor' counter and the 'Processor Information' counter,
+ // the output contains totals for the processor group(s). We filter those
+ // out by looking for the `_Total` substring.
+ if (strstr(tmp, "_Total") == nullptr) {
+ count++;
+ }
+ }
+ assert(count >= 1, "invalid enumeration!");
+ return count;
}
static int number_of_logical_cpus() {
@@ -847,7 +962,16 @@ static double cpu_factor() {
static double cpuFactor = .0;
if (numCpus == 0) {
numCpus = number_of_logical_cpus();
- assert(os::processor_count() <= (int)numCpus, "invariant");
+
+ // If we are using the legacy 'Processor' counter, which counts processors
+ // only in the first processor group, then `numCpus` can undercount, in
+ // which case, `numCpus` will be likely smaller than `os_processor_count`.
+ // However, when we use the 'Processor Information' counter, we expect both
+ // `numCpus` and `os::processorCount` to be identical. In both cases, we
+ // expect to see at least one CPU.
+ assert(numCpus >= 1 && numCpus <= (DWORD)os::processor_count(),
+ "unexpected cpu count");
+
cpuFactor = numCpus * 100;
}
return cpuFactor;
@@ -861,8 +985,8 @@ static void log_error_message_on_no_PDH_artifact(const char* counter_path) {
static int initialize_cpu_query_counters(MultiCounterQueryP query, DWORD pdh_counter_idx) {
assert(query != nullptr, "invariant");
assert(query->counters != nullptr, "invariant");
- char* processor; //'Processor' == PDH_PROCESSOR_IDX
- if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) {
+ char* processor;
+ if (lookup_name_by_index(get_proc_counter(), &processor) != OS_OK) {
return OS_ERR;
}
char* counter_name = nullptr;
@@ -880,7 +1004,11 @@ static int initialize_cpu_query_counters(MultiCounterQueryP query, DWORD pdh_cou
counter_len += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN; // "\\%s(%s)\\%s"
const char* instances = enumerate_cpu_instances();
DWORD index = 0;
- for (char* tmp = const_cast(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], index++) {
+ for (char* tmp = const_cast(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1]) {
+ // Skip totals for each processor group.
+ if (strstr(tmp, ",_Total") != nullptr) {
+ continue;
+ }
const size_t tmp_len = strlen(tmp);
char* counter_path = NEW_RESOURCE_ARRAY(char, counter_len + tmp_len + 1);
const size_t jio_snprintf_result = jio_snprintf(counter_path,
@@ -896,6 +1024,7 @@ static int initialize_cpu_query_counters(MultiCounterQueryP query, DWORD pdh_cou
// return OS_OK to have the system continue to run without the missing counter
return OS_OK;
}
+ index++;
}
// Query once to initialize the counters which require at least two samples
// (like the % CPU usage) to calculate correctly.
diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index 4969f0ecf73d..5a255a7b82d9 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -2628,14 +2628,13 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
DWORD exception_code = exception_record->ExceptionCode;
#if defined(_M_ARM64)
address pc = (address) exceptionInfo->ContextRecord->Pc;
+
+ if (handle_safefetch(exception_code, pc, (void*)exceptionInfo->ContextRecord)) {
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
#elif defined(_M_AMD64)
address pc = (address) exceptionInfo->ContextRecord->Rip;
-#else
- #error unknown architecture
-#endif
- Thread* t = Thread::current_or_null_safe();
-#if defined(_M_AMD64)
if ((exception_code == EXCEPTION_ACCESS_VIOLATION) &&
VM_Version::is_cpuinfo_segv_addr(pc)) {
// Verify that OS save/restore AVX registers.
@@ -2648,6 +2647,8 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
VM_Version::clear_apx_test_state();
return Handle_Exception(exceptionInfo, VM_Version::cpuinfo_cont_addr_apx());
}
+#else
+ #error unknown architecture
#endif
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
@@ -2658,6 +2659,7 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
}
#endif
+ Thread* t = Thread::current_or_null_safe();
if (t != nullptr && t->is_Java_thread()) {
JavaThread* thread = JavaThread::cast(t);
bool in_java = thread->thread_state() == _thread_in_Java;
@@ -2688,10 +2690,8 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
// Fatal red zone violation.
overflow_state->disable_stack_red_zone();
tty->print_raw_cr("An unrecoverable stack overflow has occurred.");
-#if !defined(USE_VECTORED_EXCEPTION_HANDLING)
report_error(t, exception_code, pc, exception_record,
exceptionInfo->ContextRecord);
-#endif
return EXCEPTION_CONTINUE_SEARCH;
}
} else if (exception_code == EXCEPTION_ACCESS_VIOLATION) {
@@ -2743,10 +2743,8 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
}
// Stack overflow or null pointer exception in native code.
-#if !defined(USE_VECTORED_EXCEPTION_HANDLING)
report_error(t, exception_code, pc, exception_record,
exceptionInfo->ContextRecord);
-#endif
return EXCEPTION_CONTINUE_SEARCH;
} // /EXCEPTION_ACCESS_VIOLATION
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -2821,41 +2819,21 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
}
}
-#if !defined(USE_VECTORED_EXCEPTION_HANDLING)
- if (exception_code != EXCEPTION_BREAKPOINT) {
- report_error(t, exception_code, pc, exception_record,
- exceptionInfo->ContextRecord);
- }
-#endif
- return EXCEPTION_CONTINUE_SEARCH;
-}
+ bool should_report_error = (exception_code != EXCEPTION_BREAKPOINT);
-#if defined(USE_VECTORED_EXCEPTION_HANDLING)
-LONG WINAPI topLevelVectoredExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
- PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord;
#if defined(_M_ARM64)
- address pc = (address) exceptionInfo->ContextRecord->Pc;
-#elif defined(_M_AMD64)
- address pc = (address) exceptionInfo->ContextRecord->Rip;
-#else
- #error unknown architecture
+ should_report_error = should_report_error &&
+ FAILED(exception_code) &&
+ (exception_code != EXCEPTION_UNCAUGHT_CXX_EXCEPTION);
#endif
- // Fast path for code part of the code cache
- if (CodeCache::low_bound() <= pc && pc < CodeCache::high_bound()) {
- return topLevelExceptionFilter(exceptionInfo);
- }
-
- // If the exception occurred in the codeCache, pass control
- // to our normal exception handler.
- CodeBlob* cb = CodeCache::find_blob(pc);
- if (cb != nullptr) {
- return topLevelExceptionFilter(exceptionInfo);
+ if (should_report_error) {
+ report_error(t, exception_code, pc, exception_record,
+ exceptionInfo->ContextRecord);
}
return EXCEPTION_CONTINUE_SEARCH;
}
-#endif
#if defined(USE_VECTORED_EXCEPTION_HANDLING)
LONG WINAPI topLevelUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
@@ -4488,7 +4466,7 @@ jint os::init_2(void) {
// Setup Windows Exceptions
#if defined(USE_VECTORED_EXCEPTION_HANDLING)
- topLevelVectoredExceptionHandler = AddVectoredExceptionHandler(1, topLevelVectoredExceptionFilter);
+ topLevelVectoredExceptionHandler = AddVectoredExceptionHandler(1, topLevelExceptionFilter);
previousUnhandledExceptionFilter = SetUnhandledExceptionFilter(topLevelUnhandledExceptionFilter);
#endif
diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp
index efb7b4149897..f1153bbbfd3d 100644
--- a/src/hotspot/os/windows/os_windows.hpp
+++ b/src/hotspot/os/windows/os_windows.hpp
@@ -150,6 +150,8 @@ class os::win32 {
// signal support
static void* install_signal_handler(int sig, signal_handler_t handler);
static void* user_handler();
+
+ static void context_set_pc(CONTEXT* uc, address pc);
};
#endif // OS_WINDOWS_OS_WINDOWS_HPP
diff --git a/src/hotspot/os/windows/safefetch_static_windows.cpp b/src/hotspot/os/windows/safefetch_static_windows.cpp
new file mode 100644
index 000000000000..3ea8b96b32db
--- /dev/null
+++ b/src/hotspot/os/windows/safefetch_static_windows.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2022 SAP SE. 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
+ * 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 "os_windows.hpp"
+#include "runtime/os.hpp"
+#include "runtime/safefetch.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+#ifdef SAFEFETCH_METHOD_STATIC_ASSEMBLY
+
+// SafeFetch handling, static assembly style:
+//
+// SafeFetch32 and SafeFetchN are implemented via static assembly
+// and live in os_cpu/xx_xx/safefetch_xx_xx.S
+
+extern "C" char _SafeFetch32_continuation[];
+extern "C" char _SafeFetch32_fault[];
+
+#ifdef _LP64
+extern "C" char _SafeFetchN_continuation[];
+extern "C" char _SafeFetchN_fault[];
+#endif // _LP64
+
+bool handle_safefetch(int exception_code, address pc, void* context) {
+ CONTEXT* ctx = (CONTEXT*)context;
+ if (exception_code == EXCEPTION_ACCESS_VIOLATION && ctx != nullptr) {
+ if (pc == (address)_SafeFetch32_fault) {
+ os::win32::context_set_pc(ctx, (address)_SafeFetch32_continuation);
+ return true;
+ }
+#ifdef _LP64
+ if (pc == (address)_SafeFetchN_fault) {
+ os::win32::context_set_pc(ctx, (address)_SafeFetchN_continuation);
+ return true;
+ }
+#endif
+ }
+ return false;
+}
+
+#endif // SAFEFETCH_METHOD_STATIC_ASSEMBLY
diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp
index a95bfb4ff96d..bb2996a548cd 100644
--- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp
+++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, Rivos Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -167,13 +167,16 @@ static bool is_set(int64_t key, uint64_t value_mask) {
void RiscvHwprobe::add_features_from_query_result() {
assert(rw_hwprobe_completed, "hwprobe not init yet.");
- if (is_valid(RISCV_HWPROBE_KEY_MVENDORID)) {
+ // For value-type keys, the kernel returns (uint64_t)-1 when CPUs in the
+ // query set disagree (different core types). Skip these as the value is
+ // not meaningful for the system as a whole.
+ if (is_valid(RISCV_HWPROBE_KEY_MVENDORID) && query[RISCV_HWPROBE_KEY_MVENDORID].value != (uint64_t)-1) {
VM_Version::mvendorid.enable_feature(query[RISCV_HWPROBE_KEY_MVENDORID].value);
}
- if (is_valid(RISCV_HWPROBE_KEY_MARCHID)) {
+ if (is_valid(RISCV_HWPROBE_KEY_MARCHID) && query[RISCV_HWPROBE_KEY_MARCHID].value != (uint64_t)-1) {
VM_Version::marchid.enable_feature(query[RISCV_HWPROBE_KEY_MARCHID].value);
}
- if (is_valid(RISCV_HWPROBE_KEY_MIMPID)) {
+ if (is_valid(RISCV_HWPROBE_KEY_MIMPID) && query[RISCV_HWPROBE_KEY_MIMPID].value != (uint64_t)-1) {
VM_Version::mimpid.enable_feature(query[RISCV_HWPROBE_KEY_MIMPID].value);
}
if (is_set(RISCV_HWPROBE_KEY_BASE_BEHAVIOR, RISCV_HWPROBE_BASE_BEHAVIOR_IMA)) {
diff --git a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp
index 01105e6d51e8..d99e0167cbd4 100644
--- a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp
+++ b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp
@@ -115,6 +115,10 @@ intptr_t* os::fetch_bcp_from_context(const void* ucVoid) {
return reinterpret_cast(uc->REG_BCP);
}
+void os::win32::context_set_pc(CONTEXT* uc, address pc) {
+ uc->Pc = (intptr_t)pc;
+}
+
bool os::win32::get_frame_at_stack_banging_point(JavaThread* thread,
struct _EXCEPTION_POINTERS* exceptionInfo, address pc, frame* fr) {
PEXCEPTION_RECORD exceptionRecord = exceptionInfo->ExceptionRecord;
diff --git a/src/hotspot/os_cpu/windows_aarch64/safefetch_windows_aarch64.S b/src/hotspot/os_cpu/windows_aarch64/safefetch_windows_aarch64.S
new file mode 100644
index 000000000000..494b68fe4cdf
--- /dev/null
+++ b/src/hotspot/os_cpu/windows_aarch64/safefetch_windows_aarch64.S
@@ -0,0 +1,65 @@
+;
+; Copyright (c) 2022 SAP SE. 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
+; 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.
+;
+
+ ; Support for int SafeFetch32(int* address, int defaultval);
+ ;
+ ; x0 : address
+ ; w1 : defaultval
+
+ ; needed to align function start to 4 byte
+ ALIGN 4
+ EXPORT _SafeFetch32_fault
+ EXPORT _SafeFetch32_continuation
+ EXPORT SafeFetch32_impl
+ AREA safefetch_text, CODE
+
+SafeFetch32_impl
+_SafeFetch32_fault
+ ldr w0, [x0]
+ ret
+
+_SafeFetch32_continuation
+ mov x0, x1
+ ret
+
+ ; Support for intptr_t SafeFetchN(intptr_t* address, intptr_t defaultval);
+ ;
+ ; x0 : address
+ ; x1 : defaultval
+
+ ALIGN 4
+ EXPORT _SafeFetchN_fault
+ EXPORT _SafeFetchN_continuation
+ EXPORT SafeFetchN_impl
+
+SafeFetchN_impl
+_SafeFetchN_fault
+ ldr x0, [x0]
+ ret
+
+_SafeFetchN_continuation
+ mov x0, x1
+ ret
+
+ END
diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp
index cce2b29d2775..e754c5057666 100644
--- a/src/hotspot/share/c1/c1_Compiler.cpp
+++ b/src/hotspot/share/c1/c1_Compiler.cpp
@@ -142,7 +142,7 @@ bool Compiler::is_intrinsic_supported(vmIntrinsics::ID id) {
case vmIntrinsics::_arraycopy:
case vmIntrinsics::_currentTimeMillis:
case vmIntrinsics::_nanoTime:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
// Use the intrinsic version of Reference.get() so that the value in
// the referent field can be registered by the G1 pre-barrier code.
// Also to prevent commoning reads from this field across safepoint
diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp
index 8658bebdaeea..fa291874c85b 100644
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp
@@ -3341,7 +3341,7 @@ GraphBuilder::GraphBuilder(Compilation* compilation, IRScope* scope)
break;
}
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
{
{
// With java.lang.ref.reference.get() we must go through the
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp
index 341de0ac0c2b..850a196c898c 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.cpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp
@@ -1185,7 +1185,7 @@ void LIRGenerator::do_Return(Return* x) {
// Example: ref.get()
// Combination of LoadField and g1 pre-write barrier
-void LIRGenerator::do_Reference_get(Intrinsic* x) {
+void LIRGenerator::do_Reference_get0(Intrinsic* x) {
const int referent_offset = java_lang_ref_Reference::referent_offset();
@@ -2914,8 +2914,8 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) {
case vmIntrinsics::_onSpinWait:
__ on_spin_wait();
break;
- case vmIntrinsics::_Reference_get:
- do_Reference_get(x);
+ case vmIntrinsics::_Reference_get0:
+ do_Reference_get0(x);
break;
case vmIntrinsics::_updateCRC32:
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp
index e70bbd961893..ec0ea5dc047d 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.hpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp
@@ -266,7 +266,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure {
void do_CompareAndSwap(Intrinsic* x, ValueType* type);
void do_PreconditionsCheckIndex(Intrinsic* x, BasicType type);
void do_FPIntrinsics(Intrinsic* x);
- void do_Reference_get(Intrinsic* x);
+ void do_Reference_get0(Intrinsic* x);
void do_update_CRC32(Intrinsic* x);
void do_update_CRC32C(Intrinsic* x);
void do_vectorizedMismatch(Intrinsic* x);
diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
index 3ea0c76f8702..635735f4494d 100644
--- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp
+++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp
@@ -408,7 +408,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_signature(ConstantPool* c
}
bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
- int mt_index = cp->operand_argument_index_at(bsms_attribute_index, arg_i);
+ int mt_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument(arg_i);
if (!cp->tag_at(mt_index).is_method_type()) {
// malformed class?
return false;
@@ -424,7 +424,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPo
}
bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
- int mh_index = cp->operand_argument_index_at(bsms_attribute_index, arg_i);
+ int mh_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument(arg_i);
if (!cp->tag_at(mh_index).is_method_handle()) {
// malformed class?
return false;
@@ -563,7 +563,7 @@ bool AOTConstantPoolResolver::is_indy_resolution_deterministic(ConstantPool* cp,
}
int bsms_attribute_index = cp->bootstrap_methods_attribute_index(cp_index);
- int arg_count = cp->operand_argument_count_at(bsms_attribute_index);
+ int arg_count = cp->bsm_attribute_entry(bsms_attribute_index)->argument_count();
if (arg_count != 3) {
// Malformed class?
return false;
diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
index 9dd1db0c4c12..1afa13cabbe7 100644
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -1317,6 +1317,13 @@ bool FileMapInfo::map_aot_code_region(ReservedSpace rs) {
return false;
} else {
assert(mapped_base == requested_base, "must be");
+
+ if (VerifySharedSpaces && !r->check_region_crc(mapped_base)) {
+ aot_log_error(aot)("region %d CRC error", MetaspaceShared::ac);
+ os::unmap_memory(mapped_base, r->used_aligned());
+ return false;
+ }
+
r->set_mapped_from_file(true);
r->set_mapped_base(mapped_base);
aot_log_info(aot)("Mapped static region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)",
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index 5e28f3ec641a..c95737f4f0a9 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -47,6 +47,7 @@
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/annotations.hpp"
+#include "oops/bsmAttribute.inline.hpp"
#include "oops/constantPool.inline.hpp"
#include "oops/fieldInfo.hpp"
#include "oops/fieldStreams.inline.hpp"
@@ -3270,8 +3271,9 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
TRAPS) {
assert(cfs != nullptr, "invariant");
assert(cp != nullptr, "invariant");
+ const int cp_size = cp->length();
- const u1* const current_start = cfs->current();
+ const u1* const current_before_parsing = cfs->current();
guarantee_property(attribute_byte_length >= sizeof(u2),
"Invalid BootstrapMethods attribute length %u in class file %s",
@@ -3280,57 +3282,40 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
cfs->guarantee_more(attribute_byte_length, CHECK);
- const int attribute_array_length = cfs->get_u2_fast();
+ const int num_bootstrap_methods = cfs->get_u2_fast();
- guarantee_property(_max_bootstrap_specifier_index < attribute_array_length,
+ guarantee_property(_max_bootstrap_specifier_index < num_bootstrap_methods,
"Short length on BootstrapMethods in class file %s",
CHECK);
+ const u4 bootstrap_methods_u2_len = (attribute_byte_length - sizeof(u2)) / sizeof(u2);
- // The attribute contains a counted array of counted tuples of shorts,
- // represending bootstrap specifiers:
- // length*{bootstrap_method_index, argument_count*{argument_index}}
- const unsigned int operand_count = (attribute_byte_length - (unsigned)sizeof(u2)) / (unsigned)sizeof(u2);
- // operand_count = number of shorts in attr, except for leading length
-
- // The attribute is copied into a short[] array.
- // The array begins with a series of short[2] pairs, one for each tuple.
- const int index_size = (attribute_array_length * 2);
-
- Array* const operands =
- MetadataFactory::new_array(_loader_data, index_size + operand_count, CHECK);
-
- // Eagerly assign operands so they will be deallocated with the constant
+ // Eagerly assign the arrays so that they will be deallocated with the constant
// pool if there is an error.
- cp->set_operands(operands);
-
- int operand_fill_index = index_size;
- const int cp_size = cp->length();
-
- for (int n = 0; n < attribute_array_length; n++) {
- // Store a 32-bit offset into the header of the operand array.
- ConstantPool::operand_offset_at_put(operands, n, operand_fill_index);
+ BSMAttributeEntries::InsertionIterator iter =
+ cp->bsm_entries().start_extension(num_bootstrap_methods,
+ bootstrap_methods_u2_len,
+ _loader_data,
+ CHECK);
- // Read a bootstrap specifier.
+ for (int i = 0; i < num_bootstrap_methods; i++) {
cfs->guarantee_more(sizeof(u2) * 2, CHECK); // bsm, argc
- const u2 bootstrap_method_index = cfs->get_u2_fast();
- const u2 argument_count = cfs->get_u2_fast();
+ u2 bootstrap_method_ref = cfs->get_u2_fast();
+ u2 num_bootstrap_arguments = cfs->get_u2_fast();
guarantee_property(
- valid_cp_range(bootstrap_method_index, cp_size) &&
- cp->tag_at(bootstrap_method_index).is_method_handle(),
- "bootstrap_method_index %u has bad constant type in class file %s",
- bootstrap_method_index,
- CHECK);
-
- guarantee_property((operand_fill_index + 1 + argument_count) < operands->length(),
- "Invalid BootstrapMethods num_bootstrap_methods or num_bootstrap_arguments value in class file %s",
- CHECK);
-
- operands->at_put(operand_fill_index++, bootstrap_method_index);
- operands->at_put(operand_fill_index++, argument_count);
-
- cfs->guarantee_more(sizeof(u2) * argument_count, CHECK); // argv[argc]
- for (int j = 0; j < argument_count; j++) {
+ valid_cp_range(bootstrap_method_ref, cp_size) &&
+ cp->tag_at(bootstrap_method_ref).is_method_handle(),
+ "bootstrap_method_index %u has bad constant type in class file %s",
+ bootstrap_method_ref,
+ CHECK);
+ cfs->guarantee_more(sizeof(u2) * num_bootstrap_arguments, CHECK); // argv[argc]
+
+ BSMAttributeEntry* entry = iter.reserve_new_entry(bootstrap_method_ref, num_bootstrap_arguments);
+ guarantee_property(entry != nullptr,
+ "Invalid BootstrapMethods num_bootstrap_methods."
+ " The total amount of space reserved for the BootstrapMethod attribute was not sufficient", CHECK);
+
+ for (int argi = 0; argi < num_bootstrap_arguments; argi++) {
const u2 argument_index = cfs->get_u2_fast();
guarantee_property(
valid_cp_range(argument_index, cp_size) &&
@@ -3338,10 +3323,11 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
"argument_index %u has bad constant type in class file %s",
argument_index,
CHECK);
- operands->at_put(operand_fill_index++, argument_index);
+ entry->set_argument(argi, argument_index);
}
}
- guarantee_property(current_start + attribute_byte_length == cfs->current(),
+ cp->bsm_entries().end_extension(iter, _loader_data, CHECK);
+ guarantee_property(current_before_parsing + attribute_byte_length == cfs->current(),
"Bad length on BootstrapMethods in class file %s",
CHECK);
}
diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp
index 957ecd8ebe87..959abdcc37da 100644
--- a/src/hotspot/share/classfile/stringTable.cpp
+++ b/src/hotspot/share/classfile/stringTable.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -615,6 +615,10 @@ struct StringTableDeleteCheck : StackObj {
};
void StringTable::clean_dead_entries(JavaThread* jt) {
+ // BulkDeleteTask::prepare() may take ConcurrentHashTableResize_lock (nosafepoint-2).
+ // When NativeHeapTrimmer is enabled, SuspendMark may take NativeHeapTrimmer::_lock (nosafepoint).
+ // Take SuspendMark first to keep lock order and avoid deadlock.
+ NativeHeapTrimmer::SuspendMark sm("stringtable");
StringTableHash::BulkDeleteTask bdt(_local_table);
if (!bdt.prepare(jt)) {
return;
@@ -622,7 +626,6 @@ void StringTable::clean_dead_entries(JavaThread* jt) {
StringTableDeleteCheck stdc;
StringTableDoDelete stdd;
- NativeHeapTrimmer::SuspendMark sm("stringtable");
{
TraceTime timer("Clean", TRACETIME_LOG(Debug, stringtable, perf));
while(bdt.do_task(jt, stdc, stdd)) {
diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp
index e6889e6248d6..814259aa8850 100644
--- a/src/hotspot/share/classfile/symbolTable.cpp
+++ b/src/hotspot/share/classfile/symbolTable.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -765,6 +765,10 @@ struct SymbolTableDeleteCheck : StackObj {
};
void SymbolTable::clean_dead_entries(JavaThread* jt) {
+ // BulkDeleteTask::prepare() may take ConcurrentHashTableResize_lock (nosafepoint-2).
+ // When NativeHeapTrimmer is enabled, SuspendMark may take NativeHeapTrimmer::_lock (nosafepoint).
+ // Take SuspendMark first to keep lock order and avoid deadlock.
+ NativeHeapTrimmer::SuspendMark sm("symboltable");
SymbolTableHash::BulkDeleteTask bdt(_local_table);
if (!bdt.prepare(jt)) {
return;
@@ -772,7 +776,6 @@ void SymbolTable::clean_dead_entries(JavaThread* jt) {
SymbolTableDeleteCheck stdc;
SymbolTableDoDelete stdd;
- NativeHeapTrimmer::SuspendMark sm("symboltable");
{
TraceTime timer("Clean", TRACETIME_LOG(Debug, symboltable, perf));
while (bdt.do_task(jt, stdc, stdd)) {
diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp
index baa945cdddf1..dd28e1a898cd 100644
--- a/src/hotspot/share/classfile/vmIntrinsics.cpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.cpp
@@ -99,7 +99,7 @@ bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) {
case vmIntrinsics::_dpow:
case vmIntrinsics::_Preconditions_checkIndex:
case vmIntrinsics::_Preconditions_checkLongIndex:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
case vmIntrinsics::_Continuation_doYield:
case vmIntrinsics::_updateCRC32:
case vmIntrinsics::_updateBytesCRC32:
@@ -244,7 +244,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_storeFence:
case vmIntrinsics::_fullFence:
case vmIntrinsics::_countPositives:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
case vmIntrinsics::_Continuation_doYield:
case vmIntrinsics::_Continuation_enterSpecial:
case vmIntrinsics::_Continuation_pin:
diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp
index eeefddfedfc4..5be372075ed6 100644
--- a/src/hotspot/share/classfile/vmIntrinsics.hpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.hpp
@@ -461,7 +461,7 @@ class methodHandle;
do_signature(vectorizedMismatch_signature, "(Ljava/lang/Object;JLjava/lang/Object;JII)I") \
\
/* java/lang/ref/Reference */ \
- do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \
+ do_intrinsic(_Reference_get0, java_lang_ref_Reference, get0_name, void_object_signature, F_RN) \
do_intrinsic(_Reference_refersTo0, java_lang_ref_Reference, refersTo0_name, object_boolean_signature, F_RN) \
do_intrinsic(_PhantomReference_refersTo0, java_lang_ref_PhantomReference, refersTo0_name, object_boolean_signature, F_RN) \
do_intrinsic(_Reference_clear0, java_lang_ref_Reference, clear0_name, void_method_signature, F_RN) \
diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index dc9ce61627bf..c562e3e6ccf3 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -422,7 +422,7 @@ class SerializeClosure;
template(sp_name, "sp") \
template(pc_name, "pc") \
template(cs_name, "cs") \
- template(get_name, "get") \
+ template(get0_name, "get0") \
template(refersTo0_name, "refersTo0") \
template(clear0_name, "clear0") \
template(put_name, "put") \
diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp
index 9e4462ec1960..859916da9d29 100644
--- a/src/hotspot/share/code/aotCodeCache.cpp
+++ b/src/hotspot/share/code/aotCodeCache.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -141,6 +141,11 @@ void AOTCodeCache::initialize() {
return; // AOTCache must be specified to dump and use AOT code
}
+ // Disable adapters caching which requires CPU features matching checks
+ // (implemented by JDK-8377507 and JDK-8381975) which we don't
+ // have in this version of code.
+ FLAG_SET_ERGO_IF_DEFAULT(AOTAdapterCaching, false);
+
// Disable stubs caching until JDK-8357398 is fixed.
FLAG_SET_ERGO(AOTStubCaching, false);
@@ -158,14 +163,11 @@ void AOTCodeCache::initialize() {
bool is_dumping = false;
bool is_using = false;
if (CDSConfig::is_dumping_final_static_archive() && CDSConfig::is_dumping_aot_linked_classes()) {
- FLAG_SET_ERGO_IF_DEFAULT(AOTAdapterCaching, true);
- FLAG_SET_ERGO_IF_DEFAULT(AOTStubCaching, true);
is_dumping = true;
} else if (CDSConfig::is_using_archive() && CDSConfig::is_using_aot_linked_classes()) {
- FLAG_SET_ERGO_IF_DEFAULT(AOTAdapterCaching, true);
- FLAG_SET_ERGO_IF_DEFAULT(AOTStubCaching, true);
is_using = true;
} else {
+ FLAG_SET_ERGO(AOTAdapterCaching, false);
log_info(aot, codecache, init)("AOT Code Cache is not used: AOT Class Linking is not used.");
return; // nothing to do
}
diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp
index 6f84dbeb40c4..4a2614fe3f95 100644
--- a/src/hotspot/share/compiler/compilationPolicy.cpp
+++ b/src/hotspot/share/compiler/compilationPolicy.cpp
@@ -1430,7 +1430,7 @@ CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle&
// Determine if a method should be compiled with a normal entry point at a different level.
CompLevel CompilationPolicy::call_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD) {
CompLevel osr_level = MIN2((CompLevel) method->highest_osr_comp_level(), common(method, cur_level, THREAD, true));
- CompLevel next_level = common(method, cur_level, THREAD, !TrainingData::have_data() && is_old(method));
+ CompLevel next_level = common(method, cur_level, THREAD, is_old(method));
// If OSR method level is greater than the regular method level, the levels should be
// equalized by raising the regular method level in order to avoid OSRs during each
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp
index ad00ab2ada9a..34b4f235a00e 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp
@@ -30,6 +30,7 @@
#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
#include "gc/shenandoah/shenandoahUtils.hpp"
#include "memory/resourceArea.hpp"
+#include "runtime/orderAccess.hpp"
void print_raw_memory(ShenandoahMessageBuffer &msg, void* loc) {
// Be extra safe. Only access data that is guaranteed to be safe:
@@ -381,6 +382,15 @@ void ShenandoahAsserts::assert_marked_strong(void *interior_loc, oop obj, const
}
}
+void ShenandoahAsserts::assert_bitmap_clear_above_top(ShenandoahHeapRegion* region) {
+ ShenandoahMarkingContext* const ctx = ShenandoahHeap::heap()->marking_context();
+ const HeapWord* top_bitmap = ctx->top_bitmap(region);
+ // Make sure that top is loaded before any of the marks from the bitmap are loaded. If another
+ // thread has cleared the bitmap we must not allow any stale reads.
+ OrderAccess::loadload();
+ assert(ctx->is_bitmap_range_within_region_clear(top_bitmap, region->end()), "Bitmap above top_bitmap() must be clear");
+}
+
void ShenandoahAsserts::assert_in_cset(void* interior_loc, oop obj, const char* file, int line) {
assert_correct(interior_loc, obj, file, line);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp
index 31a99bf438cf..2d34c797566c 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp
@@ -30,6 +30,8 @@
#include "runtime/mutex.hpp"
#include "utilities/formatBuffer.hpp"
+class ShenandoahHeapRegion;
+
typedef FormatBuffer<8192> ShenandoahMessageBuffer;
class ShenandoahAsserts {
@@ -64,6 +66,7 @@ class ShenandoahAsserts {
static void assert_marked(void* interior_loc, oop obj, const char* file, int line);
static void assert_marked_weak(void* interior_loc, oop obj, const char* file, int line);
static void assert_marked_strong(void* interior_loc, oop obj, const char* file, int line);
+ static void assert_bitmap_clear_above_top(ShenandoahHeapRegion* region);
static void assert_in_cset(void* interior_loc, oop obj, const char* file, int line);
static void assert_not_in_cset(void* interior_loc, oop obj, const char* file, int line);
static void assert_not_in_cset_loc(void* interior_loc, const char* file, int line);
@@ -127,6 +130,9 @@ class ShenandoahAsserts {
#define shenandoah_assert_marked_strong(interior_loc, obj) \
ShenandoahAsserts::assert_marked_strong(interior_loc, obj, __FILE__, __LINE__)
+#define shenandoah_assert_clear_above_top(region) \
+ ShenandoahAsserts::assert_bitmap_clear_above_top(region)
+
#define shenandoah_assert_in_cset_if(interior_loc, obj, condition) \
if (condition) ShenandoahAsserts::assert_in_cset(interior_loc, obj, __FILE__, __LINE__)
#define shenandoah_assert_in_cset_except(interior_loc, obj, exception) \
@@ -211,6 +217,7 @@ class ShenandoahAsserts {
#define shenandoah_assert_marked_strong_except(interior_loc, obj, exception)
#define shenandoah_assert_marked_strong(interior_loc, obj)
+#define shenandoah_assert_clear_above_top(region)
#define shenandoah_assert_in_cset_if(interior_loc, obj, condition)
#define shenandoah_assert_in_cset_except(interior_loc, obj, exception)
#define shenandoah_assert_in_cset(interior_loc, obj)
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp
index 87c4943b2383..d32e940d646f 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2016, 2021, Red Hat, Inc. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,6 @@
#include "gc/shenandoah/shenandoahYoungGeneration.hpp"
#include "logging/logStream.hpp"
#include "memory/resourceArea.hpp"
-#include "runtime/orderAccess.hpp"
static const char* partition_name(ShenandoahFreeSetPartitionId t) {
switch (t) {
@@ -1049,11 +1048,10 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah
}
_heap->generation_for(r->affiliation())->increment_affiliated_region_count();
-#ifdef ASSERT
- ShenandoahMarkingContext* const ctx = _heap->marking_context();
- assert(ctx->top_at_mark_start(r) == r->bottom(), "Newly established allocation region starts with TAMS equal to bottom");
- assert(ctx->is_bitmap_range_within_region_clear(ctx->top_bitmap(r), r->end()), "Bitmap above top_bitmap() must be clear");
-#endif
+
+ assert(_heap->marking_context()->top_at_mark_start(r) == r->bottom(),
+ "Newly established allocation region (%zu) must start with TAMS equal to bottom", r->index());
+ shenandoah_assert_clear_above_top(r);
log_debug(gc, free)("Using new region (%zu) for %s (" PTR_FORMAT ").",
r->index(), ShenandoahAllocRequest::alloc_type_to_string(req.type()), p2i(&req));
} else {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
index 2736376fe9a2..39f25194a6d7 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2020, Red Hat, Inc. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -32,7 +32,6 @@
#include "gc/shenandoah/shenandoahGeneration.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
-#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp"
#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
#include "gc/shenandoah/shenandoahOldGeneration.hpp"
#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp"
@@ -40,15 +39,12 @@
#include "jfr/jfrEvents.hpp"
#include "memory/allocation.hpp"
#include "memory/iterator.inline.hpp"
-#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
#include "runtime/globals_extension.hpp"
#include "runtime/java.hpp"
-#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
-#include "runtime/safepoint.hpp"
#include "utilities/powerOfTwo.hpp"
size_t ShenandoahHeapRegion::RegionCount = 0;
@@ -863,16 +859,7 @@ void ShenandoahHeapRegion::set_affiliation(ShenandoahAffiliation new_affiliation
p2i(top()), p2i(ctx->top_at_mark_start(this)), p2i(_update_watermark), p2i(ctx->top_bitmap(this)));
}
-#ifdef ASSERT
- {
- size_t idx = this->index();
- HeapWord* top_bitmap = ctx->top_bitmap(this);
-
- assert(ctx->is_bitmap_range_within_region_clear(top_bitmap, _end),
- "Region %zu, bitmap should be clear between top_bitmap: " PTR_FORMAT " and end: " PTR_FORMAT, idx,
- p2i(top_bitmap), p2i(_end));
- }
-#endif
+ shenandoah_assert_clear_above_top(this);
if (region_affiliation == new_affiliation) {
return;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
index 20040eebafd8..c9a4aa5d44ce 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
@@ -269,6 +269,7 @@ class ShenandoahHeapRegion {
ShenandoahSharedFlag _recycling; // Used to indicate that the region is being recycled; see try_recycle*().
+ // This is only read/written by a gc worker to avoid unnecessary bitmap resets
bool _needs_bitmap_reset;
public:
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp
index 40eee8c342ba..87629cefb0d4 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,7 @@
#include "gc/shared/markBitMap.inline.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahMarkingContext.hpp"
-#include "shenandoahGlobalGeneration.hpp"
+#include "runtime/orderAccess.hpp"
ShenandoahMarkingContext::ShenandoahMarkingContext(MemRegion heap_region, MemRegion bitmap_region, size_t num_regions) :
_mark_bit_map(heap_region, bitmap_region),
@@ -91,6 +91,9 @@ void ShenandoahMarkingContext::clear_bitmap(ShenandoahHeapRegion* r) {
if (top_bitmap > bottom) {
_mark_bit_map.clear_range_large(MemRegion(bottom, top_bitmap));
+ // All bitmap writes must complete before we update top at bitmap. If these writes were reordered,
+ // other threads could see stale marks above top, which is not valid.
+ OrderAccess::storestore();
_top_bitmaps[r->index()] = bottom;
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp
index 774c4f7d9413..ec4b7c7217c1 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp
@@ -58,6 +58,7 @@ void ShenandoahRegulatorThread::run_service() {
void ShenandoahRegulatorThread::regulate_young_and_old_cycles() {
while (!should_terminate()) {
+ SuspendibleThreadSetJoiner joiner;
ShenandoahGenerationalControlThread::GCMode mode = _control_thread->gc_mode();
if (mode == ShenandoahGenerationalControlThread::none) {
if (should_start_metaspace_gc()) {
@@ -95,6 +96,7 @@ void ShenandoahRegulatorThread::regulate_young_and_old_cycles() {
void ShenandoahRegulatorThread::regulate_young_and_global_cycles() {
while (!should_terminate()) {
+ SuspendibleThreadSetJoiner joiner;
if (_control_thread->gc_mode() == ShenandoahGenerationalControlThread::none) {
if (start_global_cycle()) {
log_debug(gc)("Heuristics request for global collection accepted.");
@@ -122,6 +124,7 @@ void ShenandoahRegulatorThread::regulator_sleep() {
_last_sleep_adjust_time = current;
}
+ SuspendibleThreadSetLeaver leaver;
os::naked_short_sleep(_sleep);
if (LogTarget(Debug, gc, thread)::is_enabled()) {
double elapsed = os::elapsedTime() - current;
@@ -146,6 +149,13 @@ bool ShenandoahRegulatorThread::start_global_cycle() const {
bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGeneration* generation) const {
double now = os::elapsedTime();
+
+ // This call may find the control thread waiting on workers which have suspended
+ // to allow a safepoint to run. If this regulator thread does not yield, the safepoint
+ // will not run. The worker threads won't progress, the control thread won't progress,
+ // and the regulator thread may never yield. Therefore, we leave the suspendible
+ // thread set before making this call.
+ SuspendibleThreadSetLeaver leaver;
bool accepted = _control_thread->request_concurrent_gc(generation);
if (LogTarget(Debug, gc, thread)::is_enabled() && accepted) {
double wait_time = os::elapsedTime() - now;
diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h
index a01bad14ab70..94007ebd5a43 100644
--- a/src/hotspot/share/include/jvm.h
+++ b/src/hotspot/share/include/jvm.h
@@ -356,6 +356,9 @@ JVM_HasReferencePendingList(JNIEnv *env);
JNIEXPORT void JNICALL
JVM_WaitForReferencePendingList(JNIEnv *env);
+JNIEXPORT jobject JNICALL
+JVM_ReferenceGet(JNIEnv *env, jobject ref);
+
JNIEXPORT jboolean JNICALL
JVM_ReferenceRefersTo(JNIEnv *env, jobject ref, jobject o);
diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp
index 1de7dd824f8b..ad39169bca0b 100644
--- a/src/hotspot/share/interpreter/abstractInterpreter.cpp
+++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp
@@ -148,7 +148,7 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(const methodHan
case vmIntrinsics::_fmaF: return java_lang_math_fmaF;
case vmIntrinsics::_dsqrt: return java_lang_math_sqrt;
case vmIntrinsics::_dsqrt_strict: return java_lang_math_sqrt_strict;
- case vmIntrinsics::_Reference_get: return java_lang_ref_reference_get;
+ case vmIntrinsics::_Reference_get0: return java_lang_ref_reference_get0;
case vmIntrinsics::_Object_init:
if (m->code_size() == 1) {
// We need to execute the special return bytecode to check for
@@ -210,7 +210,7 @@ vmIntrinsics::ID AbstractInterpreter::method_intrinsic(MethodKind kind) {
case java_lang_math_exp : return vmIntrinsics::_dexp;
case java_lang_math_fmaD : return vmIntrinsics::_fmaD;
case java_lang_math_fmaF : return vmIntrinsics::_fmaF;
- case java_lang_ref_reference_get: return vmIntrinsics::_Reference_get;
+ case java_lang_ref_reference_get0: return vmIntrinsics::_Reference_get0;
case java_util_zip_CRC32_update : return vmIntrinsics::_updateCRC32;
case java_util_zip_CRC32_updateBytes
: return vmIntrinsics::_updateBytesCRC32;
@@ -320,7 +320,7 @@ void AbstractInterpreter::print_method_kind(MethodKind kind) {
case java_util_zip_CRC32_updateByteBuffer : tty->print("java_util_zip_CRC32_updateByteBuffer"); break;
case java_util_zip_CRC32C_updateBytes : tty->print("java_util_zip_CRC32C_updateBytes"); break;
case java_util_zip_CRC32C_updateDirectByteBuffer: tty->print("java_util_zip_CRC32C_updateDirectByteByffer"); break;
- case java_lang_ref_reference_get : tty->print("java_lang_ref_reference_get"); break;
+ case java_lang_ref_reference_get0 : tty->print("java_lang_ref_reference_get0"); break;
case java_lang_Thread_currentThread : tty->print("java_lang_Thread_currentThread"); break;
case java_lang_Float_float16ToFloat : tty->print("java_lang_Float_float16ToFloat"); break;
case java_lang_Float_floatToFloat16 : tty->print("java_lang_Float_floatToFloat16"); break;
diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp
index b6876b3a2da0..a3e93aa0a301 100644
--- a/src/hotspot/share/interpreter/abstractInterpreter.hpp
+++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp
@@ -83,7 +83,7 @@ class AbstractInterpreter: AllStatic {
java_lang_math_exp, // implementation of java.lang.Math.exp (x)
java_lang_math_fmaF, // implementation of java.lang.Math.fma (x, y, z)
java_lang_math_fmaD, // implementation of java.lang.Math.fma (x, y, z)
- java_lang_ref_reference_get, // implementation of java.lang.ref.Reference.get()
+ java_lang_ref_reference_get0, // implementation of java.lang.ref.Reference.get()
java_util_zip_CRC32_update, // implementation of java.util.zip.CRC32.update()
java_util_zip_CRC32_updateBytes, // implementation of java.util.zip.CRC32.updateBytes()
java_util_zip_CRC32_updateByteBuffer, // implementation of java.util.zip.CRC32.updateByteBuffer()
diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
index 533c88cce9ed..928d1ac9f9c9 100644
--- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
+++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp
@@ -204,7 +204,7 @@ void TemplateInterpreterGenerator::generate_all() {
method_entry(java_lang_math_pow )
method_entry(java_lang_math_fmaF )
method_entry(java_lang_math_fmaD )
- method_entry(java_lang_ref_reference_get)
+ method_entry(java_lang_ref_reference_get0)
AbstractInterpreter::initialize_method_handle_entries();
method_entry(java_util_zip_CRC32C_updateBytes)
@@ -228,6 +228,7 @@ void TemplateInterpreterGenerator::generate_all() {
// entries for `native` methods to use the same address in case
// intrinsic is disabled.
native_method_entry(java_lang_Thread_currentThread)
+ native_method_entry(java_lang_ref_reference_get0)
native_method_entry(java_util_zip_CRC32_update)
native_method_entry(java_util_zip_CRC32_updateBytes)
@@ -465,7 +466,7 @@ address TemplateInterpreterGenerator::generate_intrinsic_entry(AbstractInterpret
case Interpreter::java_lang_math_fmaF : entry_point = generate_math_entry(kind); break;
case Interpreter::java_lang_math_sqrt_strict
: entry_point = generate_math_entry(Interpreter::java_lang_math_sqrt); break;
- case Interpreter::java_lang_ref_reference_get
+ case Interpreter::java_lang_ref_reference_get0
: entry_point = generate_Reference_get_entry(); break;
case Interpreter::java_util_zip_CRC32_update
: entry_point = generate_CRC32_update_entry(); break;
diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
index c4eeb3fa8400..8fa0835216d0 100644
--- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
+++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.cpp
@@ -64,7 +64,7 @@ void ZeroInterpreterGenerator::generate_all() {
method_entry(java_lang_math_exp );
method_entry(java_lang_math_fmaD );
method_entry(java_lang_math_fmaF );
- method_entry(java_lang_ref_reference_get);
+ method_entry(java_lang_ref_reference_get0);
AbstractInterpreter::initialize_method_handle_entries();
@@ -107,7 +107,7 @@ address ZeroInterpreterGenerator::generate_method_entry(
case Interpreter::java_lang_math_exp : // fall thru
case Interpreter::java_lang_math_fmaD : // fall thru
case Interpreter::java_lang_math_fmaF : entry_point = generate_math_entry(kind); break;
- case Interpreter::java_lang_ref_reference_get
+ case Interpreter::java_lang_ref_reference_get0
: entry_point = generate_Reference_get_entry(); break;
default:
fatal("unexpected method kind: %d", kind);
diff --git a/src/hotspot/share/oops/bsmAttribute.hpp b/src/hotspot/share/oops/bsmAttribute.hpp
new file mode 100644
index 000000000000..a28d2757fb07
--- /dev/null
+++ b/src/hotspot/share/oops/bsmAttribute.hpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * 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_OOPS_BSMATTRIBUTE_HPP
+#define SHARE_OOPS_BSMATTRIBUTE_HPP
+
+#include "oops/array.hpp"
+#include "utilities/checkedCast.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class ClassLoaderData;
+
+class BSMAttributeEntry {
+ friend class ConstantPool;
+ friend class BSMAttributeEntries;
+
+ u2 _bootstrap_method_index;
+ u2 _argument_count;
+
+ // The argument indexes are stored right after the object, in a contiguous array.
+ // [ bsmi_0 argc_0 arg_00 arg_01 ... arg_0N bsmi_1 argc_1 arg_10 ... arg_1N ... ]
+ // So in order to find the argument array, jump over ourselves.
+ const u2* argument_indexes() const {
+ return reinterpret_cast(this + 1);
+ }
+ u2* argument_indexes() {
+ return reinterpret_cast(this + 1);
+ }
+ // These are overlays on top of the BSMAttributeEntries data array, do not construct.
+ BSMAttributeEntry() = delete;
+ NONCOPYABLE(BSMAttributeEntry);
+
+ void copy_args_into(BSMAttributeEntry* entry) const;
+
+public:
+ // Offsets for SA
+ enum {
+ _bsmi_offset = 0,
+ _argc_offset = 1,
+ _argv_offset = 2
+ };
+
+ int bootstrap_method_index() const {
+ return _bootstrap_method_index;
+ }
+ int argument_count() const {
+ return _argument_count;
+ }
+ int argument(int n) const {
+ assert(checked_cast(n) < _argument_count, "oob");
+ return argument_indexes()[n];
+ }
+
+ void set_argument(int index, u2 value) {
+ assert(index >= 0 && index < argument_count(), "invariant");
+ argument_indexes()[index] = value;
+ }
+
+ // How many u2s are required to store a BSM entry with argc arguments?
+ static int u2s_required (u2 argc) {
+ return 1 /* index */ + 1 /* argc */ + argc /* argv */;
+ }
+};
+
+// The BSMAttributeEntries stores the state of the BootstrapMethods attribute.
+class BSMAttributeEntries {
+ friend class VMStructs;
+ friend class JVMCIVMStructs;
+
+public:
+ class InsertionIterator {
+ friend BSMAttributeEntries;
+ BSMAttributeEntries* _insert_into;
+ // Current unused offset into BSMAEs offset array.
+ int _cur_offset;
+ // Current unused offset into BSMAEs bsm-data array.
+ int _cur_array;
+ public:
+ InsertionIterator() : _insert_into(nullptr), _cur_offset(-1), _cur_array(-1) {}
+ InsertionIterator(BSMAttributeEntries* insert_into, int cur_offset, int cur_array)
+ : _insert_into(insert_into),
+ _cur_offset(cur_offset),
+ _cur_array(cur_array) {}
+ InsertionIterator(const InsertionIterator&) = default;
+ InsertionIterator& operator=(const InsertionIterator&) = default;
+
+ int current_offset() const { return _cur_offset; }
+ // Add a new BSMAE, reserving the necessary memory for filling the argument vector.
+ // Returns null if there isn't enough space.
+ inline BSMAttributeEntry* reserve_new_entry(u2 bsmi, u2 argc);
+ };
+
+private:
+ // Each bootstrap method has a variable-sized array associated with it.
+ // We want constant-time lookup of the Nth BSM. Therefore, we use an offset table,
+ // such that the Nth BSM is located at _bootstrap_methods[_offsets[N]].
+ Array* _offsets;
+ Array* _bootstrap_methods;
+
+ // Copy the first num_entries into iter.
+ void copy_into(InsertionIterator& iter, int num_entries) const;
+
+public:
+ BSMAttributeEntries() : _offsets(nullptr), _bootstrap_methods(nullptr) {}
+ BSMAttributeEntries(Array* offsets, Array* bootstrap_methods)
+ : _offsets(offsets),
+ _bootstrap_methods(bootstrap_methods) {}
+
+ bool is_empty() const {
+ return _offsets == nullptr && _bootstrap_methods == nullptr;
+ }
+
+ Array*& offsets() { return _offsets; }
+ const Array* const& offsets() const { return _offsets; }
+ Array*& bootstrap_methods() { return _bootstrap_methods; }
+ const Array* const& bootstrap_methods() const { return _bootstrap_methods; }
+
+ BSMAttributeEntry* entry(int bsms_attribute_index) {
+ return reinterpret_cast(_bootstrap_methods->adr_at(_offsets->at(bsms_attribute_index)));
+ }
+ const BSMAttributeEntry* entry(int bsms_attribute_index) const {
+ return reinterpret_cast(_bootstrap_methods->adr_at(_offsets->at(bsms_attribute_index)));
+ }
+
+ int number_of_entries() const {
+ return _offsets == nullptr ? 0 : _offsets->length();
+ }
+
+ // The number of U2s the BSM data consists of.
+ int array_length() const {
+ return _bootstrap_methods == nullptr ? 0 : _bootstrap_methods->length();
+ }
+
+ void deallocate_contents(ClassLoaderData* loader_data);
+
+ // Extend to have the space for both this BSMAEntries and other's.
+ // Does not copy in the other's BSMAEntrys, that must be done via the InsertionIterator.
+ // This starts an insertion iterator. Any call to start_extension must have a matching end_extension call.
+ InsertionIterator start_extension(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS);
+ // Extend the BSMAEntries with an additional number_of_entries with a total data_size.
+ InsertionIterator start_extension(int number_of_entries, int data_size, ClassLoaderData* loader_data, TRAPS);
+ // Reallocates the underlying memory to fit the limits of the InsertionIterator precisely.
+ // This ends an insertion iteration. The memory is truncated to fit exactly the data used.
+ void end_extension(InsertionIterator& iter, ClassLoaderData* loader_data, TRAPS);
+ // Append all of the BSMAEs in other into this.
+ void append(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS);
+};
+
+#endif // SHARE_OOPS_BSMATTRIBUTE_HPP
diff --git a/src/hotspot/share/oops/bsmAttribute.inline.hpp b/src/hotspot/share/oops/bsmAttribute.inline.hpp
new file mode 100644
index 000000000000..e678c280c26e
--- /dev/null
+++ b/src/hotspot/share/oops/bsmAttribute.inline.hpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_OOPS_BSMATTRIBUTE_INLINE_HPP
+#define SHARE_OOPS_BSMATTRIBUTE_INLINE_HPP
+
+#include "oops/bsmAttribute.hpp"
+
+inline BSMAttributeEntry* BSMAttributeEntries::InsertionIterator::reserve_new_entry(u2 bsmi, u2 argc) {
+ assert(_insert_into->offsets() != nullptr, "must");
+ assert(_insert_into->bootstrap_methods() != nullptr, "must");
+
+ if (_cur_offset + 1 > _insert_into->offsets()->length() ||
+ _cur_array + BSMAttributeEntry::u2s_required(argc) > _insert_into->bootstrap_methods()->length()) {
+ return nullptr;
+ }
+ _insert_into->offsets()->at_put(_cur_offset, _cur_array);
+ BSMAttributeEntry* e = _insert_into->entry(_cur_offset);
+ e->_bootstrap_method_index = bsmi;
+ e->_argument_count = argc;
+
+ _cur_array += 1 + 1 + argc;
+ _cur_offset += 1;
+ return e;
+}
+
+inline void BSMAttributeEntry::copy_args_into(BSMAttributeEntry* entry) const {
+ assert(entry->argument_count() == this->argument_count(), "must be same");
+ for (int i = 0; i < argument_count(); i++) {
+ entry->set_argument(i, this->argument(i));
+ }
+}
+
+#endif // SHARE_OOPS_BSMATTRIBUTE_INLINE_HPP
diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp
index 5b0ee298ddc5..16dc8ced5693 100644
--- a/src/hotspot/share/oops/constantPool.cpp
+++ b/src/hotspot/share/oops/constantPool.cpp
@@ -133,8 +133,7 @@ void ConstantPool::deallocate_contents(ClassLoaderData* loader_data) {
MetadataFactory::free_array(loader_data, resolved_klasses());
set_resolved_klasses(nullptr);
- MetadataFactory::free_array(loader_data, operands());
- set_operands(nullptr);
+ bsm_entries().deallocate_contents(loader_data);
release_C_heap_structures();
@@ -154,7 +153,8 @@ void ConstantPool::metaspace_pointers_do(MetaspaceClosure* it) {
it->push(&_tags, MetaspaceClosure::_writable);
it->push(&_cache);
it->push(&_pool_holder);
- it->push(&_operands);
+ it->push(&bsm_entries().offsets());
+ it->push(&bsm_entries().bootstrap_methods());
it->push(&_resolved_klasses, MetaspaceClosure::_writable);
for (int i = 0; i < length(); i++) {
@@ -758,7 +758,7 @@ Method* ConstantPool::method_at_if_loaded(const constantPoolHandle& cpool,
if (cpool->cache() == nullptr) return nullptr; // nothing to load yet
if (!(which >= 0 && which < cpool->resolved_method_entries_length())) {
// FIXME: should be an assert
- log_debug(class, resolve)("bad operand %d in:", which); cpool->print();
+ log_debug(class, resolve)("bad BSM %d in:", which); cpool->print();
return nullptr;
}
return cpool->cache()->method_if_resolved(which);
@@ -1559,8 +1559,8 @@ bool ConstantPool::compare_entry_to(int index1, const constantPoolHandle& cp2,
int i1 = bootstrap_methods_attribute_index(index1);
int i2 = cp2->bootstrap_methods_attribute_index(index2);
bool match_entry = compare_entry_to(k1, cp2, k2);
- bool match_operand = compare_operand_to(i1, cp2, i2);
- return (match_entry && match_operand);
+ bool match_bsm = compare_bootstrap_entry_to(i1, cp2, i2);
+ return (match_entry && match_bsm);
} break;
case JVM_CONSTANT_InvokeDynamic:
@@ -1570,8 +1570,8 @@ bool ConstantPool::compare_entry_to(int index1, const constantPoolHandle& cp2,
int i1 = bootstrap_methods_attribute_index(index1);
int i2 = cp2->bootstrap_methods_attribute_index(index2);
bool match_entry = compare_entry_to(k1, cp2, k2);
- bool match_operand = compare_operand_to(i1, cp2, i2);
- return (match_entry && match_operand);
+ bool match_bsm = compare_bootstrap_entry_to(i1, cp2, i2);
+ return (match_entry && match_bsm);
} break;
case JVM_CONSTANT_String:
@@ -1605,140 +1605,29 @@ bool ConstantPool::compare_entry_to(int index1, const constantPoolHandle& cp2,
return false;
} // end compare_entry_to()
-
-// Resize the operands array with delta_len and delta_size.
-// Used in RedefineClasses for CP merge.
-void ConstantPool::resize_operands(int delta_len, int delta_size, TRAPS) {
- int old_len = operand_array_length(operands());
- int new_len = old_len + delta_len;
- int min_len = (delta_len > 0) ? old_len : new_len;
-
- int old_size = operands()->length();
- int new_size = old_size + delta_size;
- int min_size = (delta_size > 0) ? old_size : new_size;
-
- ClassLoaderData* loader_data = pool_holder()->class_loader_data();
- Array* new_ops = MetadataFactory::new_array(loader_data, new_size, CHECK);
-
- // Set index in the resized array for existing elements only
- for (int idx = 0; idx < min_len; idx++) {
- int offset = operand_offset_at(idx); // offset in original array
- operand_offset_at_put(new_ops, idx, offset + 2*delta_len); // offset in resized array
- }
- // Copy the bootstrap specifiers only
- Copy::conjoint_memory_atomic(operands()->adr_at(2*old_len),
- new_ops->adr_at(2*new_len),
- (min_size - 2*min_len) * sizeof(u2));
- // Explicitly deallocate old operands array.
- // Note, it is not needed for 7u backport.
- if ( operands() != nullptr) { // the safety check
- MetadataFactory::free_array(loader_data, operands());
- }
- set_operands(new_ops);
-} // end resize_operands()
-
-
-// Extend the operands array with the length and size of the ext_cp operands.
+// Extend the BSMAttributeEntries with the length and size of the ext_cp BSMAttributeEntries.
// Used in RedefineClasses for CP merge.
-void ConstantPool::extend_operands(const constantPoolHandle& ext_cp, TRAPS) {
- int delta_len = operand_array_length(ext_cp->operands());
- if (delta_len == 0) {
- return; // nothing to do
- }
- int delta_size = ext_cp->operands()->length();
-
- assert(delta_len > 0 && delta_size > 0, "extended operands array must be bigger");
-
- if (operand_array_length(operands()) == 0) {
- ClassLoaderData* loader_data = pool_holder()->class_loader_data();
- Array* new_ops = MetadataFactory::new_array(loader_data, delta_size, CHECK);
- // The first element index defines the offset of second part
- operand_offset_at_put(new_ops, 0, 2*delta_len); // offset in new array
- set_operands(new_ops);
- } else {
- resize_operands(delta_len, delta_size, CHECK);
- }
+BSMAttributeEntries::InsertionIterator
+ConstantPool::start_extension(const constantPoolHandle& ext_cp, TRAPS) {
+ BSMAttributeEntries::InsertionIterator iter =
+ bsm_entries().start_extension(ext_cp->bsm_entries(), pool_holder()->class_loader_data(),
+ CHECK_(BSMAttributeEntries::InsertionIterator()));
+ return iter;
+}
-} // end extend_operands()
+void ConstantPool::end_extension(BSMAttributeEntries::InsertionIterator iter, TRAPS) {
+ bsm_entries().end_extension(iter, pool_holder()->class_loader_data(), THREAD);
+}
-// Shrink the operands array to a smaller array with new_len length.
-// Used in RedefineClasses for CP merge.
-void ConstantPool::shrink_operands(int new_len, TRAPS) {
- int old_len = operand_array_length(operands());
- if (new_len == old_len) {
- return; // nothing to do
- }
- assert(new_len < old_len, "shrunken operands array must be smaller");
-
- int free_base = operand_next_offset_at(new_len - 1);
- int delta_len = new_len - old_len;
- int delta_size = 2*delta_len + free_base - operands()->length();
-
- resize_operands(delta_len, delta_size, CHECK);
-
-} // end shrink_operands()
-
-
-void ConstantPool::copy_operands(const constantPoolHandle& from_cp,
- const constantPoolHandle& to_cp,
- TRAPS) {
-
- int from_oplen = operand_array_length(from_cp->operands());
- int old_oplen = operand_array_length(to_cp->operands());
- if (from_oplen != 0) {
- ClassLoaderData* loader_data = to_cp->pool_holder()->class_loader_data();
- // append my operands to the target's operands array
- if (old_oplen == 0) {
- // Can't just reuse from_cp's operand list because of deallocation issues
- int len = from_cp->operands()->length();
- Array* new_ops = MetadataFactory::new_array(loader_data, len, CHECK);
- Copy::conjoint_memory_atomic(
- from_cp->operands()->adr_at(0), new_ops->adr_at(0), len * sizeof(u2));
- to_cp->set_operands(new_ops);
- } else {
- int old_len = to_cp->operands()->length();
- int from_len = from_cp->operands()->length();
- int old_off = old_oplen * sizeof(u2);
- int from_off = from_oplen * sizeof(u2);
- // Use the metaspace for the destination constant pool
- Array* new_operands = MetadataFactory::new_array(loader_data, old_len + from_len, CHECK);
- int fillp = 0, len = 0;
- // first part of dest
- Copy::conjoint_memory_atomic(to_cp->operands()->adr_at(0),
- new_operands->adr_at(fillp),
- (len = old_off) * sizeof(u2));
- fillp += len;
- // first part of src
- Copy::conjoint_memory_atomic(from_cp->operands()->adr_at(0),
- new_operands->adr_at(fillp),
- (len = from_off) * sizeof(u2));
- fillp += len;
- // second part of dest
- Copy::conjoint_memory_atomic(to_cp->operands()->adr_at(old_off),
- new_operands->adr_at(fillp),
- (len = old_len - old_off) * sizeof(u2));
- fillp += len;
- // second part of src
- Copy::conjoint_memory_atomic(from_cp->operands()->adr_at(from_off),
- new_operands->adr_at(fillp),
- (len = from_len - from_off) * sizeof(u2));
- fillp += len;
- assert(fillp == new_operands->length(), "");
-
- // Adjust indexes in the first part of the copied operands array.
- for (int j = 0; j < from_oplen; j++) {
- int offset = operand_offset_at(new_operands, old_oplen + j);
- assert(offset == operand_offset_at(from_cp->operands(), j), "correct copy");
- offset += old_len; // every new tuple is preceded by old_len extra u2's
- operand_offset_at_put(new_operands, old_oplen + j, offset);
- }
- // replace target operands array with combined array
- to_cp->set_operands(new_operands);
- }
- }
-} // end copy_operands()
+void ConstantPool::copy_bsm_entries(const constantPoolHandle& from_cp,
+ const constantPoolHandle& to_cp,
+ TRAPS) {
+ to_cp->bsm_entries().append(from_cp->bsm_entries(),
+ to_cp->pool_holder()->class_loader_data(),
+ THREAD);
+}
// Copy this constant pool's entries at start_i to end_i (inclusive)
@@ -1768,7 +1657,7 @@ void ConstantPool::copy_cp_to_impl(const constantPoolHandle& from_cp, int start_
break;
}
}
- copy_operands(from_cp, to_cp, CHECK);
+ copy_bsm_entries(from_cp, to_cp, THREAD);
} // end copy_cp_to_impl()
@@ -1892,7 +1781,7 @@ void ConstantPool::copy_entry_to(const constantPoolHandle& from_cp, int from_i,
{
int k1 = from_cp->bootstrap_methods_attribute_index(from_i);
int k2 = from_cp->bootstrap_name_and_type_ref_index_at(from_i);
- k1 += operand_array_length(to_cp->operands()); // to_cp might already have operands
+ k1 += to_cp->bsm_entries().array_length(); // to_cp might already have a BSM attribute
to_cp->dynamic_constant_at_put(to_i, k1, k2);
} break;
@@ -1900,7 +1789,7 @@ void ConstantPool::copy_entry_to(const constantPoolHandle& from_cp, int from_i,
{
int k1 = from_cp->bootstrap_methods_attribute_index(from_i);
int k2 = from_cp->bootstrap_name_and_type_ref_index_at(from_i);
- k1 += operand_array_length(to_cp->operands()); // to_cp might already have operands
+ k1 += to_cp->bsm_entries().array_length(); // to_cp might already have a BSM attribute
to_cp->invoke_dynamic_at_put(to_i, k1, k2);
} break;
@@ -1936,42 +1825,47 @@ int ConstantPool::find_matching_entry(int pattern_i,
// Compare this constant pool's bootstrap specifier at idx1 to the constant pool
// cp2's bootstrap specifier at idx2.
-bool ConstantPool::compare_operand_to(int idx1, const constantPoolHandle& cp2, int idx2) {
- int k1 = operand_bootstrap_method_ref_index_at(idx1);
- int k2 = cp2->operand_bootstrap_method_ref_index_at(idx2);
+bool ConstantPool::compare_bootstrap_entry_to(int idx1, const constantPoolHandle& cp2, int idx2) {
+ const BSMAttributeEntry* const e1 = bsm_attribute_entry(idx1);
+ const BSMAttributeEntry* const e2 = cp2->bsm_attribute_entry(idx2);
+ int k1 = e1->bootstrap_method_index();
+ int k2 = e2->bootstrap_method_index();
bool match = compare_entry_to(k1, cp2, k2);
if (!match) {
return false;
}
- int argc = operand_argument_count_at(idx1);
- if (argc == cp2->operand_argument_count_at(idx2)) {
- for (int j = 0; j < argc; j++) {
- k1 = operand_argument_index_at(idx1, j);
- k2 = cp2->operand_argument_index_at(idx2, j);
- match = compare_entry_to(k1, cp2, k2);
- if (!match) {
- return false;
- }
+
+ const int argc = e1->argument_count();
+ if (argc != e2->argument_count()) {
+ return false;
+ }
+
+ for (int j = 0; j < argc; j++) {
+ k1 = e1->argument(j);
+ k2 = e2->argument(j);
+ match = compare_entry_to(k1, cp2, k2);
+ if (!match) {
+ return false;
}
- return true; // got through loop; all elements equal
}
- return false;
-} // end compare_operand_to()
+
+ return true; // got through loop; all elements equal
+} // end compare_bootstrap_entry_to()
// Search constant pool search_cp for a bootstrap specifier that matches
// this constant pool's bootstrap specifier data at pattern_i index.
// Return the index of a matching bootstrap attribute record or (-1) if there is no match.
-int ConstantPool::find_matching_operand(int pattern_i,
- const constantPoolHandle& search_cp, int search_len) {
- for (int i = 0; i < search_len; i++) {
- bool found = compare_operand_to(pattern_i, search_cp, i);
+int ConstantPool::find_matching_bsm_entry(int pattern_i,
+ const constantPoolHandle& search_cp, int offset_limit) {
+ for (int i = 0; i < offset_limit; i++) {
+ bool found = compare_bootstrap_entry_to(pattern_i, search_cp, i);
if (found) {
return i;
}
}
return -1; // bootstrap specifier data not found; return unused index (-1)
-} // end find_matching_operand()
+} // end find_matching_bsm_entry()
#ifndef PRODUCT
@@ -2406,7 +2300,7 @@ void ConstantPool::print_value_on(outputStream* st) const {
assert(is_constantPool(), "must be constantPool");
st->print("constant pool [%d]", length());
if (has_preresolution()) st->print("/preresolution");
- if (operands() != nullptr) st->print("/operands[%d]", operands()->length());
+ if (!bsm_entries().is_empty()) st->print("/BSMs[%d]", bsm_entries().bootstrap_methods()->length());
print_address_on(st);
if (pool_holder() != nullptr) {
st->print(" for ");
@@ -2441,3 +2335,87 @@ void ConstantPool::verify_on(outputStream* st) {
guarantee(pool_holder()->is_klass(), "should be klass");
}
}
+
+void BSMAttributeEntries::deallocate_contents(ClassLoaderData* loader_data) {
+ MetadataFactory::free_array(loader_data, this->_offsets);
+ MetadataFactory::free_array(loader_data, this->_bootstrap_methods);
+ this->_offsets = nullptr;
+ this->_bootstrap_methods = nullptr;
+}
+
+void BSMAttributeEntries::copy_into(InsertionIterator& iter, int num_entries) const {
+ assert(num_entries + iter._cur_offset <= iter._insert_into->_offsets->length(), "must");
+ for (int i = 0; i < num_entries; i++) {
+ const BSMAttributeEntry* e = entry(i);
+ BSMAttributeEntry* e_new = iter.reserve_new_entry(e->bootstrap_method_index(), e->argument_count());
+ assert(e_new != nullptr, "must be");
+ e->copy_args_into(e_new);
+ }
+}
+
+BSMAttributeEntries::InsertionIterator
+BSMAttributeEntries::start_extension(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS) {
+ InsertionIterator iter = start_extension(other.number_of_entries(), other.array_length(),
+ loader_data, CHECK_(BSMAttributeEntries::InsertionIterator()));
+ return iter;
+}
+
+BSMAttributeEntries::InsertionIterator
+BSMAttributeEntries::start_extension(int number_of_entries, int array_length,
+ ClassLoaderData* loader_data, TRAPS) {
+ InsertionIterator extension_iterator(this, this->number_of_entries(), this->array_length());
+ int new_number_of_entries = this->number_of_entries() + number_of_entries;
+ int new_array_length = this->array_length() + array_length;
+ int invalid_index = new_array_length;
+
+ Array* new_offsets =
+ MetadataFactory::new_array(loader_data, new_number_of_entries, invalid_index, CHECK_(InsertionIterator()));
+ Array* new_array = MetadataFactory::new_array(loader_data, new_array_length, CHECK_(InsertionIterator()));
+ { // Copy over all the old BSMAEntry's and their respective offsets
+ BSMAttributeEntries carrier(new_offsets, new_array);
+ InsertionIterator copy_iter(&carrier, 0, 0);
+ copy_into(copy_iter, this->number_of_entries());
+ }
+ // Replace content
+ deallocate_contents(loader_data);
+ _offsets = new_offsets;
+ _bootstrap_methods = new_array;
+ return extension_iterator;
+}
+
+
+void BSMAttributeEntries::append(const BSMAttributeEntries& other, ClassLoaderData* loader_data, TRAPS) {
+ if (other.number_of_entries() == 0) {
+ return; // Done!
+ }
+ InsertionIterator iter = start_extension(other, loader_data, CHECK);
+ other.copy_into(iter, other.number_of_entries());
+ end_extension(iter, loader_data, THREAD);
+}
+
+void BSMAttributeEntries::end_extension(InsertionIterator& iter, ClassLoaderData* loader_data, TRAPS) {
+ assert(iter._insert_into == this, "must be");
+ assert(iter._cur_offset <= this->_offsets->length(), "must be");
+ assert(iter._cur_array <= this->_bootstrap_methods->length(), "must be");
+
+ // Did we fill up all of the available space? If so, do nothing.
+ if (iter._cur_offset == this->_offsets->length() &&
+ iter._cur_array == this->_bootstrap_methods->length()) {
+ return;
+ }
+
+ // We used less, truncate by allocating new arrays
+ Array* new_offsets =
+ MetadataFactory::new_array(loader_data, iter._cur_offset, 0, CHECK);
+ Array* new_array =
+ MetadataFactory::new_array(loader_data, iter._cur_array, CHECK);
+ { // Copy over the constructed BSMAEntry's
+ BSMAttributeEntries carrier(new_offsets, new_array);
+ InsertionIterator copy_iter(&carrier, 0, 0);
+ copy_into(copy_iter, iter._cur_offset);
+ }
+
+ deallocate_contents(loader_data);
+ _offsets = new_offsets;
+ _bootstrap_methods = new_array;
+}
diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp
index cc9491d7935d..101b80407f45 100644
--- a/src/hotspot/share/oops/constantPool.hpp
+++ b/src/hotspot/share/oops/constantPool.hpp
@@ -27,6 +27,7 @@
#include "memory/allocation.hpp"
#include "oops/arrayOop.hpp"
+#include "oops/bsmAttribute.inline.hpp"
#include "oops/cpCache.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/oopHandle.hpp"
@@ -89,7 +90,8 @@ class ConstantPool : public Metadata {
Array* _tags; // the tag array describing the constant pool's contents
ConstantPoolCache* _cache; // the cache holding interpreter runtime information
InstanceKlass* _pool_holder; // the corresponding class
- Array* _operands; // for variable-sized (InvokeDynamic) nodes, usually empty
+
+ BSMAttributeEntries _bsm_entries;
// Consider using an array of compressed klass pointers to
// save space on 64-bit platforms.
@@ -130,8 +132,6 @@ class ConstantPool : public Metadata {
u1* tag_addr_at(int cp_index) const { return tags()->adr_at(cp_index); }
- void set_operands(Array* operands) { _operands = operands; }
-
u2 flags() const { return _flags; }
void set_flags(u2 f) { _flags = f; }
@@ -171,7 +171,13 @@ class ConstantPool : public Metadata {
virtual bool is_constantPool() const { return true; }
Array* tags() const { return _tags; }
- Array* operands() const { return _operands; }
+
+ BSMAttributeEntries& bsm_entries() {
+ return _bsm_entries;
+ }
+ const BSMAttributeEntries& bsm_entries() const {
+ return _bsm_entries;
+ }
bool has_preresolution() const { return (_flags & _has_preresolution) != 0; }
void set_has_preresolution() {
@@ -519,121 +525,37 @@ class ConstantPool : public Metadata {
assert(tag_at(cp_index).has_bootstrap(), "Corrupted constant pool");
return extract_low_short_from_int(*int_at_addr(cp_index));
}
- int bootstrap_operand_base(int cp_index) {
- int bsms_attribute_index = bootstrap_methods_attribute_index(cp_index);
- return operand_offset_at(operands(), bsms_attribute_index);
- }
- // The first part of the operands array consists of an index into the second part.
- // Extract a 32-bit index value from the first part.
- static int operand_offset_at(Array* operands, int bsms_attribute_index) {
- int n = (bsms_attribute_index * 2);
- assert(n >= 0 && n+2 <= operands->length(), "oob");
- // The first 32-bit index points to the beginning of the second part
- // of the operands array. Make sure this index is in the first part.
- DEBUG_ONLY(int second_part = build_int_from_shorts(operands->at(0),
- operands->at(1)));
- assert(second_part == 0 || n+2 <= second_part, "oob (2)");
- int offset = build_int_from_shorts(operands->at(n+0),
- operands->at(n+1));
- // The offset itself must point into the second part of the array.
- assert(offset == 0 || (offset >= second_part && offset <= operands->length()), "oob (3)");
- return offset;
- }
- static void operand_offset_at_put(Array* operands, int bsms_attribute_index, int offset) {
- int n = bsms_attribute_index * 2;
- assert(n >= 0 && n+2 <= operands->length(), "oob");
- operands->at_put(n+0, extract_low_short_from_int(offset));
- operands->at_put(n+1, extract_high_short_from_int(offset));
- }
- static int operand_array_length(Array* operands) {
- if (operands == nullptr || operands->length() == 0) return 0;
- int second_part = operand_offset_at(operands, 0);
- return (second_part / 2);
- }
-
-#ifdef ASSERT
- // operand tuples fit together exactly, end to end
- static int operand_limit_at(Array* operands, int bsms_attribute_index) {
- int nextidx = bsms_attribute_index + 1;
- if (nextidx == operand_array_length(operands))
- return operands->length();
- else
- return operand_offset_at(operands, nextidx);
- }
- int bootstrap_operand_limit(int cp_index) {
- int bsms_attribute_index = bootstrap_methods_attribute_index(cp_index);
- return operand_limit_at(operands(), bsms_attribute_index);
- }
-#endif //ASSERT
-
- // Layout of InvokeDynamic and Dynamic bootstrap method specifier
- // data in second part of operands array. This encodes one record in
- // the BootstrapMethods attribute. The whole specifier also includes
- // the name and type information from the main constant pool entry.
- enum {
- _indy_bsm_offset = 0, // CONSTANT_MethodHandle bsm
- _indy_argc_offset = 1, // u2 argc
- _indy_argv_offset = 2 // u2 argv[argc]
- };
- // These functions are used in RedefineClasses for CP merge
-
- int operand_offset_at(int bsms_attribute_index) {
- assert(0 <= bsms_attribute_index &&
- bsms_attribute_index < operand_array_length(operands()),
- "Corrupted CP operands");
- return operand_offset_at(operands(), bsms_attribute_index);
- }
- u2 operand_bootstrap_method_ref_index_at(int bsms_attribute_index) {
- int offset = operand_offset_at(bsms_attribute_index);
- return operands()->at(offset + _indy_bsm_offset);
- }
- u2 operand_argument_count_at(int bsms_attribute_index) {
- int offset = operand_offset_at(bsms_attribute_index);
- u2 argc = operands()->at(offset + _indy_argc_offset);
- return argc;
- }
- u2 operand_argument_index_at(int bsms_attribute_index, int j) {
- int offset = operand_offset_at(bsms_attribute_index);
- return operands()->at(offset + _indy_argv_offset + j);
- }
- int operand_next_offset_at(int bsms_attribute_index) {
- int offset = operand_offset_at(bsms_attribute_index) + _indy_argv_offset
- + operand_argument_count_at(bsms_attribute_index);
- return offset;
- }
- // Compare a bootstrap specifier data in the operands arrays
- bool compare_operand_to(int bsms_attribute_index1, const constantPoolHandle& cp2,
- int bsms_attribute_index2);
- // Find a bootstrap specifier data in the operands array
- int find_matching_operand(int bsms_attribute_index, const constantPoolHandle& search_cp,
- int operands_cur_len);
- // Resize the operands array with delta_len and delta_size
- void resize_operands(int delta_len, int delta_size, TRAPS);
- // Extend the operands array with the length and size of the ext_cp operands
- void extend_operands(const constantPoolHandle& ext_cp, TRAPS);
- // Shrink the operands array to a smaller array with new_len length
- void shrink_operands(int new_len, TRAPS);
+ BSMAttributeEntry* bsm_attribute_entry(int bsms_attribute_index) {
+ return _bsm_entries.entry(bsms_attribute_index);
+ }
+
+ bool compare_bootstrap_entry_to(int bsms_attribute_index1, const constantPoolHandle& cp2,
+ int bsms_attribute_index2);
+ // Find a BSM entry in search_cp that matches the BSM at bsm_attribute_index.
+ // Return -1 if not found.
+ int find_matching_bsm_entry(int bsms_attribute_index, const constantPoolHandle& search_cp,
+ int offset_limit);
+ // Extend the BSM attribute storage to fit both the current data and the BSM data in ext_cp.
+ // Use the returned InsertionIterator to fill out the newly allocated space.
+ BSMAttributeEntries::InsertionIterator start_extension(const constantPoolHandle& ext_cp, TRAPS);
+ void end_extension(BSMAttributeEntries::InsertionIterator iter, TRAPS);
u2 bootstrap_method_ref_index_at(int cp_index) {
assert(tag_at(cp_index).has_bootstrap(), "Corrupted constant pool");
- int op_base = bootstrap_operand_base(cp_index);
- return operands()->at(op_base + _indy_bsm_offset);
+ int bsmai = bootstrap_methods_attribute_index(cp_index);
+ return bsm_attribute_entry(bsmai)->bootstrap_method_index();
}
u2 bootstrap_argument_count_at(int cp_index) {
assert(tag_at(cp_index).has_bootstrap(), "Corrupted constant pool");
- int op_base = bootstrap_operand_base(cp_index);
- u2 argc = operands()->at(op_base + _indy_argc_offset);
- DEBUG_ONLY(int end_offset = op_base + _indy_argv_offset + argc;
- int next_offset = bootstrap_operand_limit(cp_index));
- assert(end_offset == next_offset, "matched ending");
- return argc;
+ int bsmai = bootstrap_methods_attribute_index(cp_index);
+ return bsm_attribute_entry(bsmai)->argument_count();
}
u2 bootstrap_argument_index_at(int cp_index, int j) {
- int op_base = bootstrap_operand_base(cp_index);
- DEBUG_ONLY(int argc = operands()->at(op_base + _indy_argc_offset));
- assert((uint)j < (uint)argc, "oob");
- return operands()->at(op_base + _indy_argv_offset + j);
+ int bsmai = bootstrap_methods_attribute_index(cp_index);
+ BSMAttributeEntry* bsme = bsm_attribute_entry(bsmai);
+ assert((uint)j < (uint)bsme->argument_count(), "oob");
+ return bsm_attribute_entry(bsmai)->argument(j);
}
// The following methods (name/signature/klass_ref_at, klass_ref_at_noresolve,
@@ -840,7 +762,7 @@ class ConstantPool : public Metadata {
}
static void copy_cp_to_impl(const constantPoolHandle& from_cp, int start_cpi, int end_cpi, const constantPoolHandle& to_cp, int to_cpi, TRAPS);
static void copy_entry_to(const constantPoolHandle& from_cp, int from_cpi, const constantPoolHandle& to_cp, int to_cpi);
- static void copy_operands(const constantPoolHandle& from_cp, const constantPoolHandle& to_cp, TRAPS);
+ static void copy_bsm_entries(const constantPoolHandle& from_cp, const constantPoolHandle& to_cp, TRAPS);
int find_matching_entry(int pattern_i, const constantPoolHandle& search_cp);
int version() const { return _saved._version; }
void set_version(int version) { _saved._version = version; }
diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp
index c02aefc79437..65e5d1a04bdc 100644
--- a/src/hotspot/share/opto/arraycopynode.cpp
+++ b/src/hotspot/share/opto/arraycopynode.cpp
@@ -182,6 +182,12 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c
return nullptr;
}
+ Node* out_mem = proj_out_or_null(TypeFunc::Memory);
+ if (can_reshape && out_mem == nullptr) { // dead node?
+ return NodeSentinel;
+ }
+
+
Node* base_src = in(ArrayCopyNode::Src);
Node* base_dest = in(ArrayCopyNode::Dest);
Node* ctl = in(TypeFunc::Control);
diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp
index 0c642211e1fe..6552d41cc776 100644
--- a/src/hotspot/share/opto/c2compiler.cpp
+++ b/src/hotspot/share/opto/c2compiler.cpp
@@ -767,7 +767,7 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) {
case vmIntrinsics::_doubleToRawLongBits:
case vmIntrinsics::_doubleToLongBits:
case vmIntrinsics::_longBitsToDouble:
- case vmIntrinsics::_Reference_get:
+ case vmIntrinsics::_Reference_get0:
case vmIntrinsics::_Reference_refersTo0:
case vmIntrinsics::_PhantomReference_refersTo0:
case vmIntrinsics::_Reference_clear0:
diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp
index 6f13ce0f809a..cb6cc2082b38 100644
--- a/src/hotspot/share/opto/callnode.cpp
+++ b/src/hotspot/share/opto/callnode.cpp
@@ -42,6 +42,7 @@
#include "opto/rootnode.hpp"
#include "opto/runtime.hpp"
#include "runtime/sharedRuntime.hpp"
+#include "runtime/stubRoutines.hpp"
#include "utilities/powerOfTwo.hpp"
#include "code/vmreg.hpp"
@@ -935,7 +936,7 @@ Node *CallNode::result_cast() {
}
-void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts) {
+void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts) const {
projs->fallthrough_proj = nullptr;
projs->fallthrough_catchproj = nullptr;
projs->fallthrough_ioproj = nullptr;
@@ -1319,6 +1320,76 @@ void CallLeafVectorNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_
//=============================================================================
+bool CallLeafPureNode::is_unused() const {
+ return proj_out_or_null(TypeFunc::Parms) == nullptr;
+}
+
+bool CallLeafPureNode::is_dead() const {
+ return proj_out_or_null(TypeFunc::Control) == nullptr;
+}
+
+/* We make a tuple of the global input state + TOP for the output values.
+ * We use this to delete a pure function that is not used: by replacing the call with
+ * such a tuple, we let output Proj's idealization pick the corresponding input of the
+ * pure call, so jumping over it, and effectively, removing the call from the graph.
+ * This avoids doing the graph surgery manually, but leaves that to IGVN
+ * that is specialized for doing that right. We need also tuple components for output
+ * values of the function to respect the return arity, and in case there is a projection
+ * that would pick an output (which shouldn't happen at the moment).
+ */
+TupleNode* CallLeafPureNode::make_tuple_of_input_state_and_top_return_values(const Compile* C) const {
+ // Transparently propagate input state but parameters
+ TupleNode* tuple = TupleNode::make(
+ tf()->range(),
+ in(TypeFunc::Control),
+ in(TypeFunc::I_O),
+ in(TypeFunc::Memory),
+ in(TypeFunc::FramePtr),
+ in(TypeFunc::ReturnAdr));
+
+ // And add TOPs for the return values
+ for (uint i = TypeFunc::Parms; i < tf()->range()->cnt(); i++) {
+ tuple->set_req(i, C->top());
+ }
+
+ return tuple;
+}
+
+CallLeafPureNode* CallLeafPureNode::inline_call_leaf_pure_node(Node* control) const {
+ Node* top = Compile::current()->top();
+ if (control == nullptr) {
+ control = in(TypeFunc::Control);
+ }
+
+ CallLeafPureNode* call = new CallLeafPureNode(tf(), entry_point(), _name, nullptr);
+ call->init_req(TypeFunc::Control, control);
+ call->init_req(TypeFunc::I_O, top);
+ call->init_req(TypeFunc::Memory, top);
+ call->init_req(TypeFunc::ReturnAdr, top);
+ call->init_req(TypeFunc::FramePtr, top);
+ for (unsigned int i = 0; i < tf()->domain()->cnt() - TypeFunc::Parms; i++) {
+ call->init_req(TypeFunc::Parms + i, in(TypeFunc::Parms + i));
+ }
+
+ return call;
+}
+
+Node* CallLeafPureNode::Ideal(PhaseGVN* phase, bool can_reshape) {
+ if (is_dead()) {
+ return nullptr;
+ }
+
+ // We need to wait until IGVN because during parsing, usages might still be missing
+ // and we would remove the call immediately.
+ if (can_reshape && is_unused()) {
+ // The result is not used. We remove the call by replacing it with a tuple, that
+ // is later disintegrated by the projections.
+ return make_tuple_of_input_state_and_top_return_values(phase->C);
+ }
+
+ return CallRuntimeNode::Ideal(phase, can_reshape);
+}
+
#ifndef PRODUCT
void CallLeafNode::dump_spec(outputStream *st) const {
st->print("# ");
@@ -2373,3 +2444,158 @@ bool CallNode::may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeO
return true;
}
+
+PowDNode::PowDNode(Compile* C, Node* base, Node* exp)
+ : CallLeafPureNode(
+ OptoRuntime::Math_DD_D_Type(),
+ StubRoutines::dpow() != nullptr ? StubRoutines::dpow() : CAST_FROM_FN_PTR(address, SharedRuntime::dpow),
+ "pow",
+ nullptr) {
+ add_flag(Flag_is_macro);
+ C->add_macro_node(this);
+
+ init_req(TypeFunc::Parms + 0, base);
+ init_req(TypeFunc::Parms + 1, C->top()); // double slot padding
+ init_req(TypeFunc::Parms + 2, exp);
+ init_req(TypeFunc::Parms + 3, C->top()); // double slot padding
+}
+
+const Type* PowDNode::Value(PhaseGVN* phase) const {
+ const Type* t_base = phase->type(base());
+ const Type* t_exp = phase->type(exp());
+
+ if (t_base == Type::TOP || t_exp == Type::TOP) {
+ return Type::TOP;
+ }
+
+ const TypeD* base_con = t_base->isa_double_constant();
+ const TypeD* exp_con = t_exp->isa_double_constant();
+ const TypeD* result_t = nullptr;
+
+ // constant folding: both inputs are constants
+ if (base_con != nullptr && exp_con != nullptr) {
+ result_t = TypeD::make(SharedRuntime::dpow(base_con->getd(), exp_con->getd()));
+ }
+
+ // Special cases when only the exponent is known:
+ if (exp_con != nullptr) {
+ double e = exp_con->getd();
+
+ // If the second argument is positive or negative zero, then the result is 1.0.
+ // i.e., pow(x, +/-0.0D) => 1.0
+ if (e == 0.0) { // true for both -0.0 and +0.0
+ result_t = TypeD::ONE;
+ }
+
+ // If the second argument is NaN, then the result is NaN.
+ // i.e., pow(x, NaN) => NaN
+ if (g_isnan(e)) {
+ result_t = TypeD::make(NAN);
+ }
+ }
+
+ if (result_t != nullptr) {
+ // We can't simply return a TypeD here, it must be a tuple type to be compatible with call nodes.
+ const Type** fields = TypeTuple::fields(2);
+ fields[TypeFunc::Parms + 0] = result_t;
+ fields[TypeFunc::Parms + 1] = Type::HALF;
+ return TypeTuple::make(TypeFunc::Parms + 2, fields);
+ }
+
+ return tf()->range();
+}
+
+Node* PowDNode::Ideal(PhaseGVN* phase, bool can_reshape) {
+ if (!can_reshape) {
+ return nullptr; // wait for igvn
+ }
+
+ PhaseIterGVN* igvn = phase->is_IterGVN();
+ Node* base = this->base();
+ Node* exp = this->exp();
+
+ const Type* t_exp = phase->type(exp);
+ const TypeD* exp_con = t_exp->isa_double_constant();
+
+ // Special cases when only the exponent is known:
+ if (exp_con != nullptr) {
+ double e = exp_con->getd();
+
+ // If the second argument is 1.0, then the result is the same as the first argument.
+ // i.e., pow(x, 1.0) => x
+ if (e == 1.0) {
+ return make_tuple_of_input_state_and_result(igvn, base);
+ }
+
+ // If the second argument is 2.0, then strength reduce to multiplications.
+ // i.e., pow(x, 2.0) => x * x
+ if (e == 2.0) {
+ Node* mul = igvn->transform(new MulDNode(base, base));
+ return make_tuple_of_input_state_and_result(igvn, mul);
+ }
+
+ // If the second argument is 0.5, the strength reduce to square roots.
+ // i.e., pow(x, 0.5) => sqrt(x) iff x > 0
+ if (e == 0.5 && Matcher::match_rule_supported(Op_SqrtD)) {
+ Node* ctrl = in(TypeFunc::Control);
+ Node* zero = igvn->zerocon(T_DOUBLE);
+
+ // According to the API specs, pow(-0.0, 0.5) = 0.0 and sqrt(-0.0) = -0.0.
+ // So pow(-0.0, 0.5) shouldn't be replaced with sqrt(-0.0).
+ // -0.0/+0.0 are both excluded since floating-point comparison doesn't distinguish -0.0 from +0.0.
+ Node* cmp = igvn->register_new_node_with_optimizer(new CmpDNode(base, zero));
+ Node* test = igvn->register_new_node_with_optimizer(new BoolNode(cmp, BoolTest::le));
+
+ IfNode* iff = new IfNode(ctrl, test, PROB_UNLIKELY_MAG(3), COUNT_UNKNOWN);
+ igvn->register_new_node_with_optimizer(iff);
+ Node* if_slow = igvn->register_new_node_with_optimizer(new IfTrueNode(iff)); // x <= 0
+ Node* if_fast = igvn->register_new_node_with_optimizer(new IfFalseNode(iff)); // x > 0
+
+ // slow path: call pow(x, 0.5)
+ Node* call = igvn->register_new_node_with_optimizer(inline_call_leaf_pure_node(if_slow));
+ Node* call_ctrl = igvn->register_new_node_with_optimizer(new ProjNode(call, TypeFunc::Control));
+ Node* call_result = igvn->register_new_node_with_optimizer(new ProjNode(call, TypeFunc::Parms + 0));
+
+ // fast path: sqrt(x)
+ Node* sqrt = igvn->register_new_node_with_optimizer(new SqrtDNode(igvn->C, if_fast, base));
+
+ // merge paths
+ RegionNode* region = new RegionNode(3);
+ igvn->register_new_node_with_optimizer(region);
+ region->init_req(1, call_ctrl); // slow path
+ region->init_req(2, if_fast); // fast path
+
+ PhiNode* phi = new PhiNode(region, Type::DOUBLE);
+ igvn->register_new_node_with_optimizer(phi);
+ phi->init_req(1, call_result); // slow: pow() result
+ phi->init_req(2, sqrt); // fast: sqrt() result
+
+ igvn->C->set_has_split_ifs(true); // Has chance for split-if optimization
+
+ return make_tuple_of_input_state_and_result(igvn, phi, region);
+ }
+ }
+
+ return CallLeafPureNode::Ideal(phase, can_reshape);
+}
+
+// We can't simply have Ideal() returning a Con or MulNode since the users are still expecting a Call node, but we could
+// produce a tuple that follows the same pattern so users can still get control, io, memory, etc..
+TupleNode* PowDNode::make_tuple_of_input_state_and_result(PhaseIterGVN* phase, Node* result, Node* control) {
+ if (control == nullptr) {
+ control = in(TypeFunc::Control);
+ }
+
+ Compile* C = phase->C;
+ C->remove_macro_node(this);
+ TupleNode* tuple = TupleNode::make(
+ tf()->range(),
+ control,
+ in(TypeFunc::I_O),
+ in(TypeFunc::Memory),
+ in(TypeFunc::FramePtr),
+ in(TypeFunc::ReturnAdr),
+ result,
+ C->top());
+ return tuple;
+}
diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp
index 213fbda4e896..e8105f3d4b09 100644
--- a/src/hotspot/share/opto/callnode.hpp
+++ b/src/hotspot/share/opto/callnode.hpp
@@ -752,7 +752,7 @@ class CallNode : public SafePointNode {
// Collect all the interesting edges from a call for use in
// replacing the call by something else. Used by macro expansion
// and the late inlining support.
- void extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts = true);
+ void extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts = true) const;
virtual uint match_edge(uint idx) const;
@@ -820,11 +820,14 @@ class CallJavaNode : public CallNode {
// calls and optimized virtual calls, plus calls to wrappers for run-time
// routines); generates static stub.
class CallStaticJavaNode : public CallJavaNode {
+ // If this is an uncommon trap guarded by some condition, is it safe to change the condition to a narrower condition?
+ // See comment in PhaseIdealLoop::do_split_if()
+ bool _safe_for_fold_compare;
virtual bool cmp( const Node &n ) const;
virtual uint size_of() const; // Size is bigger
public:
CallStaticJavaNode(Compile* C, const TypeFunc* tf, address addr, ciMethod* method)
- : CallJavaNode(tf, addr, method) {
+ : CallJavaNode(tf, addr, method), _safe_for_fold_compare(true) {
init_class_id(Class_CallStaticJava);
if (C->eliminate_boxing() && (method != nullptr) && method->is_boxing_method()) {
init_flags(Flag_is_macro);
@@ -832,7 +835,7 @@ class CallStaticJavaNode : public CallJavaNode {
}
}
CallStaticJavaNode(const TypeFunc* tf, address addr, const char* name, const TypePtr* adr_type)
- : CallJavaNode(tf, addr, nullptr) {
+ : CallJavaNode(tf, addr, nullptr), _safe_for_fold_compare(true) {
init_class_id(Class_CallStaticJava);
// This node calls a runtime stub, which often has narrow memory effects.
_adr_type = adr_type;
@@ -856,6 +859,14 @@ class CallStaticJavaNode : public CallJavaNode {
virtual int Opcode() const;
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
+ void clear_safe_for_fold_compare() {
+ _safe_for_fold_compare = false;
+ }
+
+ bool safe_for_fold_compare() const {
+ return _safe_for_fold_compare;
+ }
+
#ifndef PRODUCT
virtual void dump_spec(outputStream *st) const;
virtual void dump_compact_spec(outputStream *st) const;
@@ -928,6 +939,35 @@ class CallLeafNode : public CallRuntimeNode {
#endif
};
+/* A pure function call, they are assumed not to be safepoints, not to read or write memory,
+ * have no exception... They just take parameters, return a value without side effect. It is
+ * always correct to create some, or remove them, if the result is not used.
+ *
+ * They still have control input to allow easy lowering into other kind of calls that require
+ * a control, but this is more a technical than a moral constraint.
+ *
+ * Pure calls must have only control and data input and output: I/O, Memory and so on must be top.
+ * Nevertheless, pure calls can typically be expensive math operations so care must be taken
+ * when letting the node float.
+ */
+class CallLeafPureNode : public CallLeafNode {
+protected:
+ bool is_unused() const;
+ bool is_dead() const;
+ TupleNode* make_tuple_of_input_state_and_top_return_values(const Compile* C) const;
+
+public:
+ CallLeafPureNode(const TypeFunc* tf, address addr, const char* name,
+ const TypePtr* adr_type)
+ : CallLeafNode(tf, addr, name, adr_type) {
+ init_class_id(Class_CallLeafPure);
+ }
+ int Opcode() const override;
+ Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
+
+ CallLeafPureNode* inline_call_leaf_pure_node(Node* control = nullptr) const;
+};
+
//------------------------------CallLeafNoFPNode-------------------------------
// CallLeafNode, not using floating point or using it in the same manner as
// the generated code
@@ -1277,4 +1317,19 @@ class UnlockNode : public AbstractLockNode {
JVMState* dbg_jvms() const { return nullptr; }
#endif
};
+
+//------------------------------PowDNode--------------------------------------
+class PowDNode : public CallLeafPureNode {
+ TupleNode* make_tuple_of_input_state_and_result(PhaseIterGVN* phase, Node* result, Node* control = nullptr);
+
+public:
+ PowDNode(Compile* C, Node* base, Node* exp);
+ int Opcode() const override;
+ const Type* Value(PhaseGVN* phase) const override;
+ Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
+
+ Node* base() const { return in(TypeFunc::Parms + 0); }
+ Node* exp() const { return in(TypeFunc::Parms + 2); }
+};
+
#endif // SHARE_OPTO_CALLNODE_HPP
diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp
index a0e780c0e574..b17326bb511d 100644
--- a/src/hotspot/share/opto/cfgnode.hpp
+++ b/src/hotspot/share/opto/cfgnode.hpp
@@ -457,6 +457,7 @@ class IfNode : public MultiBranchNode {
#endif
bool same_condition(const Node* dom, PhaseIterGVN* igvn) const;
+ void mark_projections_unsafe_for_fold_compare() const;
};
class RangeCheckNode : public IfNode {
diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp
index bc259eed2d10..b7ba16e99a05 100644
--- a/src/hotspot/share/opto/classes.hpp
+++ b/src/hotspot/share/opto/classes.hpp
@@ -61,6 +61,7 @@ macro(CallDynamicJava)
macro(CallJava)
macro(CallLeaf)
macro(CallLeafNoFP)
+macro(CallLeafPure)
macro(CallLeafVector)
macro(CallRuntime)
macro(CallStaticJava)
@@ -282,6 +283,7 @@ macro(OpaqueZeroTripGuard)
macro(OpaqueNotNull)
macro(OpaqueInitializedAssertionPredicate)
macro(OpaqueTemplateAssertionPredicate)
+macro(PowD)
macro(ProfileBoolean)
macro(OrI)
macro(OrL)
@@ -372,6 +374,7 @@ macro(SubI)
macro(SubL)
macro(TailCall)
macro(TailJump)
+macro(Tuple)
macro(MacroLogicV)
macro(ThreadLocal)
macro(Unlock)
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index 2b956dcb5d84..1c209a6462a8 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -783,19 +783,9 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci,
StartNode* s = new StartNode(root(), tf()->domain());
initial_gvn()->set_type_bottom(s);
verify_start(s);
- if (method()->intrinsic_id() == vmIntrinsics::_Reference_get) {
- // With java.lang.ref.reference.get() we must go through the
- // intrinsic - even when get() is the root
- // method of the compile - so that, if necessary, the value in
- // the referent field of the reference object gets recorded by
- // the pre-barrier code.
- cg = find_intrinsic(method(), false);
- }
- if (cg == nullptr) {
- float past_uses = method()->interpreter_invocation_count();
- float expected_uses = past_uses;
- cg = CallGenerator::for_inline(method(), expected_uses);
- }
+ float past_uses = method()->interpreter_invocation_count();
+ float expected_uses = past_uses;
+ cg = CallGenerator::for_inline(method(), expected_uses);
}
if (failing()) return;
if (cg == nullptr) {
@@ -3298,6 +3288,25 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
case Op_Opaque1: // Remove Opaque Nodes before matching
n->subsume_by(n->in(1), this);
break;
+ case Op_CallLeafPure: {
+ // If the pure call is not supported, then lower to a CallLeaf.
+ if (!Matcher::match_rule_supported(Op_CallLeafPure)) {
+ CallNode* call = n->as_Call();
+ CallNode* new_call = new CallLeafNode(call->tf(), call->entry_point(),
+ call->_name, TypeRawPtr::BOTTOM);
+ new_call->init_req(TypeFunc::Control, call->in(TypeFunc::Control));
+ new_call->init_req(TypeFunc::I_O, C->top());
+ new_call->init_req(TypeFunc::Memory, C->top());
+ new_call->init_req(TypeFunc::ReturnAdr, C->top());
+ new_call->init_req(TypeFunc::FramePtr, C->top());
+ for (unsigned int i = TypeFunc::Parms; i < call->tf()->domain()->cnt(); i++) {
+ new_call->init_req(i, call->in(i));
+ }
+ n->subsume_by(new_call, this);
+ }
+ frc.inc_call_count();
+ break;
+ }
case Op_CallStaticJava:
case Op_CallJava:
case Op_CallDynamicJava:
diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp
index a70194274a79..5dd8be877ffe 100644
--- a/src/hotspot/share/opto/divnode.cpp
+++ b/src/hotspot/share/opto/divnode.cpp
@@ -42,19 +42,19 @@
#include
-ModFloatingNode::ModFloatingNode(Compile* C, const TypeFunc* tf, const char* name) : CallLeafNode(tf, nullptr, name, TypeRawPtr::BOTTOM) {
+ModFloatingNode::ModFloatingNode(Compile* C, const TypeFunc* tf, address addr, const char* name) : CallLeafPureNode(tf, addr, name, TypeRawPtr::BOTTOM) {
add_flag(Flag_is_macro);
C->add_macro_node(this);
}
-ModDNode::ModDNode(Compile* C, Node* a, Node* b) : ModFloatingNode(C, OptoRuntime::Math_DD_D_Type(), "drem") {
+ModDNode::ModDNode(Compile* C, Node* a, Node* b) : ModFloatingNode(C, OptoRuntime::Math_DD_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::drem), "drem") {
init_req(TypeFunc::Parms + 0, a);
init_req(TypeFunc::Parms + 1, C->top());
init_req(TypeFunc::Parms + 2, b);
init_req(TypeFunc::Parms + 3, C->top());
}
-ModFNode::ModFNode(Compile* C, Node* a, Node* b) : ModFloatingNode(C, OptoRuntime::modf_Type(), "frem") {
+ModFNode::ModFNode(Compile* C, Node* a, Node* b) : ModFloatingNode(C, OptoRuntime::modf_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::frem), "frem") {
init_req(TypeFunc::Parms + 0, a);
init_req(TypeFunc::Parms + 1, b);
}
@@ -1516,137 +1516,109 @@ const Type* UModLNode::Value(PhaseGVN* phase) const {
return unsigned_mod_value(phase, this);
}
-Node* ModFNode::Ideal(PhaseGVN* phase, bool can_reshape) {
- if (!can_reshape) {
- return nullptr;
- }
- PhaseIterGVN* igvn = phase->is_IterGVN();
-
- bool result_is_unused = proj_out_or_null(TypeFunc::Parms) == nullptr;
- bool not_dead = proj_out_or_null(TypeFunc::Control) != nullptr;
- if (result_is_unused && not_dead) {
- return replace_with_con(igvn, TypeF::make(0.));
- }
-
- // Either input is TOP ==> the result is TOP
- const Type* t1 = phase->type(dividend());
- const Type* t2 = phase->type(divisor());
- if (t1 == Type::TOP || t2 == Type::TOP) {
- return phase->C->top();
- }
-
+const Type* ModFNode::get_result_if_constant(const Type* dividend, const Type* divisor) const {
// If either number is not a constant, we know nothing.
- if ((t1->base() != Type::FloatCon) || (t2->base() != Type::FloatCon)) {
+ if ((dividend->base() != Type::FloatCon) || (divisor->base() != Type::FloatCon)) {
return nullptr; // note: x%x can be either NaN or 0
}
- float f1 = t1->getf();
- float f2 = t2->getf();
- jint x1 = jint_cast(f1); // note: *(int*)&f1, not just (int)f1
- jint x2 = jint_cast(f2);
+ float dividend_f = dividend->getf();
+ float divisor_f = divisor->getf();
+ jint dividend_i = jint_cast(dividend_f); // note: *(int*)&f1, not just (int)f1
+ jint divisor_i = jint_cast(divisor_f);
// If either is a NaN, return an input NaN
- if (g_isnan(f1)) {
- return replace_with_con(igvn, t1);
+ if (g_isnan(dividend_f)) {
+ return dividend;
}
- if (g_isnan(f2)) {
- return replace_with_con(igvn, t2);
+ if (g_isnan(divisor_f)) {
+ return divisor;
}
// If an operand is infinity or the divisor is +/- zero, punt.
- if (!g_isfinite(f1) || !g_isfinite(f2) || x2 == 0 || x2 == min_jint) {
+ if (!g_isfinite(dividend_f) || !g_isfinite(divisor_f) || divisor_i == 0 || divisor_i == min_jint) {
return nullptr;
}
// We must be modulo'ing 2 float constants.
// Make sure that the sign of the fmod is equal to the sign of the dividend
- jint xr = jint_cast(fmod(f1, f2));
- if ((x1 ^ xr) < 0) {
+ jint xr = jint_cast(fmod(dividend_f, divisor_f));
+ if ((dividend_i ^ xr) < 0) {
xr ^= min_jint;
}
- return replace_with_con(igvn, TypeF::make(jfloat_cast(xr)));
+ return TypeF::make(jfloat_cast(xr));
}
-Node* ModDNode::Ideal(PhaseGVN* phase, bool can_reshape) {
- if (!can_reshape) {
- return nullptr;
- }
- PhaseIterGVN* igvn = phase->is_IterGVN();
-
- bool result_is_unused = proj_out_or_null(TypeFunc::Parms) == nullptr;
- bool not_dead = proj_out_or_null(TypeFunc::Control) != nullptr;
- if (result_is_unused && not_dead) {
- return replace_with_con(igvn, TypeD::make(0.));
- }
-
- // Either input is TOP ==> the result is TOP
- const Type* t1 = phase->type(dividend());
- const Type* t2 = phase->type(divisor());
- if (t1 == Type::TOP || t2 == Type::TOP) {
- return nullptr;
- }
-
+const Type* ModDNode::get_result_if_constant(const Type* dividend, const Type* divisor) const {
// If either number is not a constant, we know nothing.
- if ((t1->base() != Type::DoubleCon) || (t2->base() != Type::DoubleCon)) {
+ if ((dividend->base() != Type::DoubleCon) || (divisor->base() != Type::DoubleCon)) {
return nullptr; // note: x%x can be either NaN or 0
}
- double f1 = t1->getd();
- double f2 = t2->getd();
- jlong x1 = jlong_cast(f1); // note: *(long*)&f1, not just (long)f1
- jlong x2 = jlong_cast(f2);
+ double dividend_d = dividend->getd();
+ double divisor_d = divisor->getd();
+ jlong dividend_l = jlong_cast(dividend_d); // note: *(long*)&f1, not just (long)f1
+ jlong divisor_l = jlong_cast(divisor_d);
// If either is a NaN, return an input NaN
- if (g_isnan(f1)) {
- return replace_with_con(igvn, t1);
+ if (g_isnan(dividend_d)) {
+ return dividend;
}
- if (g_isnan(f2)) {
- return replace_with_con(igvn, t2);
+ if (g_isnan(divisor_d)) {
+ return divisor;
}
// If an operand is infinity or the divisor is +/- zero, punt.
- if (!g_isfinite(f1) || !g_isfinite(f2) || x2 == 0 || x2 == min_jlong) {
+ if (!g_isfinite(dividend_d) || !g_isfinite(divisor_d) || divisor_l == 0 || divisor_l == min_jlong) {
return nullptr;
}
// We must be modulo'ing 2 double constants.
// Make sure that the sign of the fmod is equal to the sign of the dividend
- jlong xr = jlong_cast(fmod(f1, f2));
- if ((x1 ^ xr) < 0) {
+ jlong xr = jlong_cast(fmod(dividend_d, divisor_d));
+ if ((dividend_l ^ xr) < 0) {
xr ^= min_jlong;
}
- return replace_with_con(igvn, TypeD::make(jdouble_cast(xr)));
+ return TypeD::make(jdouble_cast(xr));
}
-Node* ModFloatingNode::replace_with_con(PhaseIterGVN* phase, const Type* con) {
- Compile* C = phase->C;
- Node* con_node = phase->makecon(con);
- CallProjections projs;
- extract_projections(&projs, false, false);
- phase->replace_node(projs.fallthrough_proj, in(TypeFunc::Control));
- if (projs.fallthrough_catchproj != nullptr) {
- phase->replace_node(projs.fallthrough_catchproj, in(TypeFunc::Control));
- }
- if (projs.fallthrough_memproj != nullptr) {
- phase->replace_node(projs.fallthrough_memproj, in(TypeFunc::Memory));
- }
- if (projs.catchall_memproj != nullptr) {
- phase->replace_node(projs.catchall_memproj, C->top());
- }
- if (projs.fallthrough_ioproj != nullptr) {
- phase->replace_node(projs.fallthrough_ioproj, in(TypeFunc::I_O));
- }
- assert(projs.catchall_ioproj == nullptr, "no exceptions from floating mod");
- assert(projs.catchall_catchproj == nullptr, "no exceptions from floating mod");
- if (projs.resproj != nullptr) {
- phase->replace_node(projs.resproj, con_node);
+Node* ModFloatingNode::Ideal(PhaseGVN* phase, bool can_reshape) {
+ if (can_reshape) {
+ PhaseIterGVN* igvn = phase->is_IterGVN();
+
+ // Either input is TOP ==> the result is TOP
+ const Type* dividend_type = phase->type(dividend());
+ const Type* divisor_type = phase->type(divisor());
+ if (dividend_type == Type::TOP || divisor_type == Type::TOP) {
+ return phase->C->top();
+ }
+ const Type* constant_result = get_result_if_constant(dividend_type, divisor_type);
+ if (constant_result != nullptr) {
+ return make_tuple_of_input_state_and_constant_result(igvn, constant_result);
+ }
}
- phase->replace_node(this, C->top());
- C->remove_macro_node(this);
- disconnect_inputs(C);
- return nullptr;
+
+ return CallLeafPureNode::Ideal(phase, can_reshape);
+}
+
+/* Give a tuple node for ::Ideal to return, made of the input state (control to return addr)
+ * and the given constant result. Idealization of projections will make sure to transparently
+ * propagate the input state and replace the result by the said constant.
+ */
+TupleNode* ModFloatingNode::make_tuple_of_input_state_and_constant_result(PhaseIterGVN* phase, const Type* con) const {
+ Node* con_node = phase->makecon(con);
+ TupleNode* tuple = TupleNode::make(
+ tf()->range(),
+ in(TypeFunc::Control),
+ in(TypeFunc::I_O),
+ in(TypeFunc::Memory),
+ in(TypeFunc::FramePtr),
+ in(TypeFunc::ReturnAdr),
+ con_node);
+
+ return tuple;
}
//=============================================================================
diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp
index 127e2431b0b3..b13460c89f57 100644
--- a/src/hotspot/share/opto/divnode.hpp
+++ b/src/hotspot/share/opto/divnode.hpp
@@ -156,40 +156,45 @@ class ModLNode : public Node {
};
// Base class for float and double modulus
-class ModFloatingNode : public CallLeafNode {
+class ModFloatingNode : public CallLeafPureNode {
+ TupleNode* make_tuple_of_input_state_and_constant_result(PhaseIterGVN* phase, const Type* con) const;
+
protected:
- Node* replace_with_con(PhaseIterGVN* phase, const Type* con);
+ virtual Node* dividend() const = 0;
+ virtual Node* divisor() const = 0;
+ virtual const Type* get_result_if_constant(const Type* dividend, const Type* divisor) const = 0;
public:
- ModFloatingNode(Compile* C, const TypeFunc* tf, const char *name);
+ ModFloatingNode(Compile* C, const TypeFunc* tf, address addr, const char* name);
+ Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
};
// Float Modulus
class ModFNode : public ModFloatingNode {
private:
- Node* dividend() const { return in(TypeFunc::Parms + 0); }
- Node* divisor() const { return in(TypeFunc::Parms + 1); }
+ Node* dividend() const override { return in(TypeFunc::Parms + 0); }
+ Node* divisor() const override { return in(TypeFunc::Parms + 1); }
+ const Type* get_result_if_constant(const Type* dividend, const Type* divisor) const override;
public:
ModFNode(Compile* C, Node* a, Node* b);
- virtual int Opcode() const;
- virtual uint ideal_reg() const { return Op_RegF; }
- virtual uint size_of() const { return sizeof(*this); }
- virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
+ int Opcode() const override;
+ uint ideal_reg() const override { return Op_RegF; }
+ uint size_of() const override { return sizeof(*this); }
};
// Double Modulus
class ModDNode : public ModFloatingNode {
private:
- Node* dividend() const { return in(TypeFunc::Parms + 0); }
- Node* divisor() const { return in(TypeFunc::Parms + 2); }
+ Node* dividend() const override { return in(TypeFunc::Parms + 0); }
+ Node* divisor() const override { return in(TypeFunc::Parms + 2); }
+ const Type* get_result_if_constant(const Type* dividend, const Type* divisor) const override;
public:
ModDNode(Compile* C, Node* a, Node* b);
- virtual int Opcode() const;
- virtual uint ideal_reg() const { return Op_RegD; }
- virtual uint size_of() const { return sizeof(*this); }
- virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
+ int Opcode() const override;
+ uint ideal_reg() const override { return Op_RegD; }
+ uint size_of() const override { return sizeof(*this); }
};
//------------------------------UModINode---------------------------------------
diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp
index 20feca26ede5..1b8b7008578c 100644
--- a/src/hotspot/share/opto/graphKit.cpp
+++ b/src/hotspot/share/opto/graphKit.cpp
@@ -1880,14 +1880,20 @@ Node* GraphKit::set_results_for_java_call(CallJavaNode* call, bool separate_io_p
// after the call, if this call has restricted memory effects.
Node* GraphKit::set_predefined_input_for_runtime_call(SafePointNode* call, Node* narrow_mem) {
// Set fixed predefined input arguments
- Node* memory = reset_memory();
- Node* m = narrow_mem == nullptr ? memory : narrow_mem;
- call->init_req( TypeFunc::Control, control() );
- call->init_req( TypeFunc::I_O, top() ); // does no i/o
- call->init_req( TypeFunc::Memory, m ); // may gc ptrs
- call->init_req( TypeFunc::FramePtr, frameptr() );
- call->init_req( TypeFunc::ReturnAdr, top() );
- return memory;
+ call->init_req(TypeFunc::Control, control());
+ call->init_req(TypeFunc::I_O, top()); // does no i/o
+ call->init_req(TypeFunc::ReturnAdr, top());
+ if (call->is_CallLeafPure()) {
+ call->init_req(TypeFunc::Memory, top());
+ call->init_req(TypeFunc::FramePtr, top());
+ return nullptr;
+ } else {
+ Node* memory = reset_memory();
+ Node* m = narrow_mem == nullptr ? memory : narrow_mem;
+ call->init_req(TypeFunc::Memory, m); // may gc ptrs
+ call->init_req(TypeFunc::FramePtr, frameptr());
+ return memory;
+ }
}
//-------------------set_predefined_output_for_runtime_call--------------------
@@ -1905,6 +1911,11 @@ void GraphKit::set_predefined_output_for_runtime_call(Node* call,
const TypePtr* hook_mem) {
// no i/o
set_control(_gvn.transform( new ProjNode(call,TypeFunc::Control) ));
+ if (call->is_CallLeafPure()) {
+ // Pure function have only control (for now) and data output, in particular
+ // they don't touch the memory, so we don't want a memory proj that is set after.
+ return;
+ }
if (keep_mem) {
// First clone the existing memory state
set_all_memory(keep_mem);
@@ -2491,6 +2502,8 @@ Node* GraphKit::make_runtime_call(int flags,
} else if (flags & RC_VECTOR){
uint num_bits = call_type->range()->field_at(TypeFunc::Parms)->is_vect()->length_in_bytes() * BitsPerByte;
call = new CallLeafVectorNode(call_type, call_addr, call_name, adr_type, num_bits);
+ } else if (flags & RC_PURE) {
+ call = new CallLeafPureNode(call_type, call_addr, call_name, adr_type);
} else {
call = new CallLeafNode(call_type, call_addr, call_name, adr_type);
}
diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp
index 28773d75333e..806a211d7e25 100644
--- a/src/hotspot/share/opto/graphKit.hpp
+++ b/src/hotspot/share/opto/graphKit.hpp
@@ -784,6 +784,7 @@ class GraphKit : public Phase {
RC_NARROW_MEM = 16, // input memory is same as output
RC_UNCOMMON = 32, // freq. expected to be like uncommon trap
RC_VECTOR = 64, // CallLeafVectorNode
+ RC_PURE = 128, // CallLeaf is pure
RC_LEAF = 0 // null value: no flags set
};
diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp
index 8d810e4202fa..384e5f8673de 100644
--- a/src/hotspot/share/opto/ifnode.cpp
+++ b/src/hotspot/share/opto/ifnode.cpp
@@ -876,6 +876,10 @@ bool IfNode::has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNod
return false;
}
+ if (!dom_unc->safe_for_fold_compare()) {
+ return false;
+ }
+
// See merge_uncommon_traps: the reason of the uncommon trap
// will be changed and the state of the dominating If will be
// used. Checked that we didn't apply this transformation in a
@@ -1666,6 +1670,57 @@ bool IfNode::same_condition(const Node* dom, PhaseIterGVN* igvn) const {
return true;
}
+void IfNode::mark_projections_unsafe_for_fold_compare() const {
+ // With the following code pattern
+ //
+ // if (some_condition) {
+ // v = 0;
+ // } else {
+ // v = 1;
+ // } // v is Phi(0, 1)
+ // if (v == 0) {
+ // uncommon_trap(); // reexecutes the "if (v == 0) {" above, captures v as stack argument to ifeq bytecode
+ // }
+ // if (some_other_condition) {
+ // uncommon_trap(); // reexecutes the "if (some_other_condition) {"
+ // }
+ //
+ // if the second if is split thru Phi, the result is:
+ //
+ // if (some_condition) {
+ // uncommon_trap(); // reexecutes the "if (v == 0) {" that was removed above, captures v = 0 as stack argument to ifeq bytecode
+ // }
+ // if (some_other_condition) {
+ // uncommon_trap(); // reexecutes the "if (some_other_condition) {"
+ // }
+ //
+ // some_condition and some_other_condition could be folded into
+ // a single new condition that is narrower than some_condition
+ // (done by IfNode::fold_compares(), for instance):
+ //
+ // if (combined_narrower_condition) {
+ // uncommon_trap(); // reexecutes the "if (v == 0) {" that was removed, captures v = 0 as stack argument to ifeq bytecode
+ // }
+ //
+ // Then combined_narrower_condition is true for some input value for
+ // which some_condition is false. When such an input value is used
+ // at runtime, the trap is taken which causes "if (v == 0) {" to be
+ // reexecuted with v = 0 even though some_condition is wrong, causing
+ // the wrong branch to be executed.
+ //
+ // Mark the uncommon trap nodes to prevent such a transformation
+ // from happening.
+ IfProjNode* true_projection = proj_out(1)->as_IfProj();
+ IfProjNode* false_projection = proj_out(0)->as_IfProj();
+ CallStaticJavaNode* unc = true_projection->is_uncommon_trap_proj();
+ if (unc != nullptr) {
+ unc->clear_safe_for_fold_compare();
+ }
+ unc = false_projection->is_uncommon_trap_proj();
+ if (unc != nullptr) {
+ unc->clear_safe_for_fold_compare();
+ }
+}
static int subsuming_bool_test_encode(Node*);
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index f74af38387ca..310d94d80aac 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -564,7 +564,7 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_getCallerClass: return inline_native_Reflection_getCallerClass();
- case vmIntrinsics::_Reference_get: return inline_reference_get();
+ case vmIntrinsics::_Reference_get0: return inline_reference_get0();
case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false);
case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true);
case vmIntrinsics::_Reference_clear0: return inline_reference_clear0(false);
@@ -1802,7 +1802,7 @@ bool LibraryCallKit::runtime_math(const TypeFunc* call_type, address funcAddr, c
Node* b = (call_type == OptoRuntime::Math_DD_D_Type()) ? argument(2) : nullptr;
const TypePtr* no_memory_effects = nullptr;
- Node* trig = make_runtime_call(RC_LEAF, call_type, funcAddr, funcName,
+ Node* trig = make_runtime_call(RC_LEAF | RC_PURE, call_type, funcAddr, funcName,
no_memory_effects,
a, top(), b, b ? top() : nullptr);
Node* value = _gvn.transform(new ProjNode(trig, TypeFunc::Parms+0));
@@ -1817,61 +1817,17 @@ bool LibraryCallKit::runtime_math(const TypeFunc* call_type, address funcAddr, c
//------------------------------inline_math_pow-----------------------------
bool LibraryCallKit::inline_math_pow() {
+ Node* base = argument(0);
Node* exp = argument(2);
- const TypeD* d = _gvn.type(exp)->isa_double_constant();
- if (d != nullptr) {
- if (d->getd() == 2.0) {
- // Special case: pow(x, 2.0) => x * x
- Node* base = argument(0);
- set_result(_gvn.transform(new MulDNode(base, base)));
- return true;
- } else if (d->getd() == 0.5 && Matcher::match_rule_supported(Op_SqrtD)) {
- // Special case: pow(x, 0.5) => sqrt(x)
- Node* base = argument(0);
- Node* zero = _gvn.zerocon(T_DOUBLE);
-
- RegionNode* region = new RegionNode(3);
- Node* phi = new PhiNode(region, Type::DOUBLE);
-
- Node* cmp = _gvn.transform(new CmpDNode(base, zero));
- // According to the API specs, pow(-0.0, 0.5) = 0.0 and sqrt(-0.0) = -0.0.
- // So pow(-0.0, 0.5) shouldn't be replaced with sqrt(-0.0).
- // -0.0/+0.0 are both excluded since floating-point comparison doesn't distinguish -0.0 from +0.0.
- Node* test = _gvn.transform(new BoolNode(cmp, BoolTest::le));
-
- Node* if_pow = generate_slow_guard(test, nullptr);
- Node* value_sqrt = _gvn.transform(new SqrtDNode(C, control(), base));
- phi->init_req(1, value_sqrt);
- region->init_req(1, control());
-
- if (if_pow != nullptr) {
- set_control(if_pow);
- address target = StubRoutines::dpow() != nullptr ? StubRoutines::dpow() :
- CAST_FROM_FN_PTR(address, SharedRuntime::dpow);
- const TypePtr* no_memory_effects = nullptr;
- Node* trig = make_runtime_call(RC_LEAF, OptoRuntime::Math_DD_D_Type(), target, "POW",
- no_memory_effects, base, top(), exp, top());
- Node* value_pow = _gvn.transform(new ProjNode(trig, TypeFunc::Parms+0));
-#ifdef ASSERT
- Node* value_top = _gvn.transform(new ProjNode(trig, TypeFunc::Parms+1));
- assert(value_top == top(), "second value must be top");
-#endif
- phi->init_req(2, value_pow);
- region->init_req(2, _gvn.transform(new ProjNode(trig, TypeFunc::Control)));
- }
-
- C->set_has_split_ifs(true); // Has chance for split-if optimization
- set_control(_gvn.transform(region));
- record_for_igvn(region);
- set_result(_gvn.transform(phi));
- return true;
- }
- }
-
- return StubRoutines::dpow() != nullptr ?
- runtime_math(OptoRuntime::Math_DD_D_Type(), StubRoutines::dpow(), "dpow") :
- runtime_math(OptoRuntime::Math_DD_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dpow), "POW");
+ CallNode* pow = new PowDNode(C, base, exp);
+ set_predefined_input_for_runtime_call(pow);
+ pow = _gvn.transform(pow)->as_CallLeafPure();
+ set_predefined_output_for_runtime_call(pow);
+ Node* result = _gvn.transform(new ProjNode(pow, TypeFunc::Parms + 0));
+ record_for_igvn(pow);
+ set_result(result);
+ return true;
}
//------------------------------inline_math_native-----------------------------
@@ -6919,9 +6875,9 @@ bool LibraryCallKit::inline_updateByteBufferAdler32() {
return true;
}
-//----------------------------inline_reference_get----------------------------
+//----------------------------inline_reference_get0----------------------------
// public T java.lang.ref.Reference.get();
-bool LibraryCallKit::inline_reference_get() {
+bool LibraryCallKit::inline_reference_get0() {
const int referent_offset = java_lang_ref_Reference::referent_offset();
// Get the argument:
diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp
index 1be08df32aea..1739758aa452 100644
--- a/src/hotspot/share/opto/library_call.hpp
+++ b/src/hotspot/share/opto/library_call.hpp
@@ -300,7 +300,7 @@ class LibraryCallKit : public GraphKit {
bool inline_bitshuffle_methods(vmIntrinsics::ID id);
bool inline_compare_unsigned(vmIntrinsics::ID id);
bool inline_divmod_methods(vmIntrinsics::ID id);
- bool inline_reference_get();
+ bool inline_reference_get0();
bool inline_reference_refersTo0(bool is_phantom);
bool inline_reference_clear0(bool is_phantom);
bool inline_Class_cast();
diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp
index acf1dbabf198..a28043c8d8f5 100644
--- a/src/hotspot/share/opto/macro.cpp
+++ b/src/hotspot/share/opto/macro.cpp
@@ -2484,6 +2484,7 @@ void PhaseMacroExpand::eliminate_macro_nodes() {
assert(n->Opcode() == Op_LoopLimit ||
n->Opcode() == Op_ModD ||
n->Opcode() == Op_ModF ||
+ n->Opcode() == Op_PowD ||
n->is_OpaqueNotNull() ||
n->is_OpaqueInitializedAssertionPredicate() ||
n->Opcode() == Op_MaxL ||
@@ -2637,22 +2638,11 @@ bool PhaseMacroExpand::expand_macro_nodes() {
default:
switch (n->Opcode()) {
case Op_ModD:
- case Op_ModF: {
- bool is_drem = n->Opcode() == Op_ModD;
- CallNode* mod_macro = n->as_Call();
- CallNode* call = new CallLeafNode(mod_macro->tf(),
- is_drem ? CAST_FROM_FN_PTR(address, SharedRuntime::drem)
- : CAST_FROM_FN_PTR(address, SharedRuntime::frem),
- is_drem ? "drem" : "frem", TypeRawPtr::BOTTOM);
- call->init_req(TypeFunc::Control, mod_macro->in(TypeFunc::Control));
- call->init_req(TypeFunc::I_O, mod_macro->in(TypeFunc::I_O));
- call->init_req(TypeFunc::Memory, mod_macro->in(TypeFunc::Memory));
- call->init_req(TypeFunc::ReturnAdr, mod_macro->in(TypeFunc::ReturnAdr));
- call->init_req(TypeFunc::FramePtr, mod_macro->in(TypeFunc::FramePtr));
- for (unsigned int i = 0; i < mod_macro->tf()->domain()->cnt() - TypeFunc::Parms; i++) {
- call->init_req(TypeFunc::Parms + i, mod_macro->in(TypeFunc::Parms + i));
- }
- _igvn.replace_node(mod_macro, call);
+ case Op_ModF:
+ case Op_PowD: {
+ CallLeafPureNode* call_macro = n->as_CallLeafPure();
+ CallLeafPureNode* call = call_macro->inline_call_leaf_pure_node();
+ _igvn.replace_node(call_macro, call);
transform_later(call);
break;
}
diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp
index 24b81b894cb1..9145b7f06a58 100644
--- a/src/hotspot/share/opto/memnode.cpp
+++ b/src/hotspot/share/opto/memnode.cpp
@@ -1585,11 +1585,15 @@ bool LoadNode::can_split_through_phi_base(PhaseGVN* phase) {
intptr_t ignore = 0;
Node* base = AddPNode::Ideal_base_and_offset(address, phase, ignore);
+ if (base == nullptr) {
+ return false;
+ }
+
if (base->is_CastPP()) {
base = base->in(1);
}
- if (req() > 3 || base == nullptr || !base->is_Phi()) {
+ if (req() > 3 || !base->is_Phi()) {
return false;
}
diff --git a/src/hotspot/share/opto/multnode.cpp b/src/hotspot/share/opto/multnode.cpp
index 736e84315eee..f429d5daac07 100644
--- a/src/hotspot/share/opto/multnode.cpp
+++ b/src/hotspot/share/opto/multnode.cpp
@@ -120,6 +120,10 @@ const TypePtr *ProjNode::adr_type() const {
if (bottom_type() == Type::MEMORY) {
// in(0) might be a narrow MemBar; otherwise we will report TypePtr::BOTTOM
Node* ctrl = in(0);
+ if (ctrl->Opcode() == Op_Tuple) {
+ // Jumping over Tuples: the i-th projection of a Tuple is the i-th input of the Tuple.
+ ctrl = ctrl->in(_con);
+ }
if (ctrl == nullptr) return nullptr; // node is dead
const TypePtr* adr_type = ctrl->adr_type();
#ifdef ASSERT
@@ -163,6 +167,15 @@ void ProjNode::check_con() const {
assert(_con < t->is_tuple()->cnt(), "ProjNode::_con must be in range");
}
+//------------------------------Identity---------------------------------------
+Node* ProjNode::Identity(PhaseGVN* phase) {
+ if (in(0) != nullptr && in(0)->Opcode() == Op_Tuple) {
+ // Jumping over Tuples: the i-th projection of a Tuple is the i-th input of the Tuple.
+ return in(0)->in(_con);
+ }
+ return this;
+}
+
//------------------------------Value------------------------------------------
const Type* ProjNode::Value(PhaseGVN* phase) const {
if (in(0) == nullptr) return Type::TOP;
diff --git a/src/hotspot/share/opto/multnode.hpp b/src/hotspot/share/opto/multnode.hpp
index dff2caed38d1..834dcfdca6de 100644
--- a/src/hotspot/share/opto/multnode.hpp
+++ b/src/hotspot/share/opto/multnode.hpp
@@ -82,6 +82,7 @@ class ProjNode : public Node {
virtual const Type *bottom_type() const;
virtual const TypePtr *adr_type() const;
virtual bool pinned() const;
+ virtual Node* Identity(PhaseGVN* phase);
virtual const Type* Value(PhaseGVN* phase) const;
virtual uint ideal_reg() const;
virtual const RegMask &out_RegMask() const;
@@ -105,4 +106,49 @@ class ProjNode : public Node {
ProjNode* other_if_proj() const;
};
+/* Tuples are used to avoid manual graph surgery. When a node with Proj outputs (such as a call)
+ * must be removed and its ouputs replaced by its input, or some other value, we can make its
+ * ::Ideal return a tuple of what we want for each output: the ::Identity of output Proj will
+ * take care to jump over the Tuple and directly pick up the right input of the Tuple.
+ *
+ * For instance, if a function call is proven to have no side effect and return the constant 0,
+ * we can replace it with the 6-tuple:
+ * (control input, IO input, memory input, frame ptr input, return addr input, Con:0)
+ * all the output projections will pick up the input of the now gone call, except for the result
+ * projection that is replaced by 0.
+ *
+ * Using TupleNode avoid manual graph surgery and leave that to our expert surgeon: IGVN.
+ * Since the user of a Tuple are expected to be Proj, when creating a tuple during idealization,
+ * the output Proj should be enqueued for IGVN immediately after, and the tuple should not survive
+ * after the current IGVN.
+ */
+class TupleNode : public MultiNode {
+ const TypeTuple* _tf;
+
+ template
+ static void make_helper(TupleNode* tn, uint i, Node* node, NN... nn) {
+ tn->set_req(i, node);
+ make_helper(tn, i + 1, nn...);
+ }
+
+ static void make_helper(TupleNode*, uint) {}
+
+public:
+ TupleNode(const TypeTuple* tf) : MultiNode(tf->cnt()), _tf(tf) {}
+
+ int Opcode() const override;
+ const Type* bottom_type() const override { return _tf; }
+
+ /* Give as many `Node*` as you want in the `nn` pack:
+ * TupleNode::make(tf, input1)
+ * TupleNode::make(tf, input1, input2, input3, input4)
+ */
+ template
+ static TupleNode* make(const TypeTuple* tf, NN... nn) {
+ TupleNode* tn = new TupleNode(tf);
+ make_helper(tn, 0, nn...);
+ return tn;
+ }
+};
+
#endif // SHARE_OPTO_MULTNODE_HPP
diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp
index 8f6c67c16f52..5ecc038954dd 100644
--- a/src/hotspot/share/opto/node.cpp
+++ b/src/hotspot/share/opto/node.cpp
@@ -2946,23 +2946,13 @@ bool Node::is_dead_loop_safe() const {
bool Node::is_div_or_mod(BasicType bt) const { return Opcode() == Op_Div(bt) || Opcode() == Op_Mod(bt) ||
Opcode() == Op_UDiv(bt) || Opcode() == Op_UMod(bt); }
-bool Node::is_pure_function() const {
- switch (Opcode()) {
- case Op_ModD:
- case Op_ModF:
- return true;
- default:
- return false;
- }
-}
-
// `maybe_pure_function` is assumed to be the input of `this`. This is a bit redundant,
// but we already have and need maybe_pure_function in all the call sites, so
// it makes it obvious that the `maybe_pure_function` is the same node as in the caller,
// while it takes more thinking to realize that a locally computed in(0) must be equal to
// the local in the caller.
bool Node::is_data_proj_of_pure_function(const Node* maybe_pure_function) const {
- return Opcode() == Op_Proj && as_Proj()->_con == TypeFunc::Parms && maybe_pure_function->is_pure_function();
+ return Opcode() == Op_Proj && as_Proj()->_con == TypeFunc::Parms && maybe_pure_function->is_CallLeafPure();
}
//=============================================================================
diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp
index 2bbb10879f59..dc0ac474c4bc 100644
--- a/src/hotspot/share/opto/node.hpp
+++ b/src/hotspot/share/opto/node.hpp
@@ -54,6 +54,7 @@ class CallDynamicJavaNode;
class CallJavaNode;
class CallLeafNode;
class CallLeafNoFPNode;
+class CallLeafPureNode;
class CallNode;
class CallRuntimeNode;
class CallStaticJavaNode;
@@ -673,6 +674,7 @@ class Node {
DEFINE_CLASS_ID(CallRuntime, Call, 1)
DEFINE_CLASS_ID(CallLeaf, CallRuntime, 0)
DEFINE_CLASS_ID(CallLeafNoFP, CallLeaf, 0)
+ DEFINE_CLASS_ID(CallLeafPure, CallLeaf, 1)
DEFINE_CLASS_ID(Allocate, Call, 2)
DEFINE_CLASS_ID(AllocateArray, Allocate, 0)
DEFINE_CLASS_ID(AbstractLock, Call, 3)
@@ -907,6 +909,7 @@ class Node {
DEFINE_CLASS_QUERY(CallJava)
DEFINE_CLASS_QUERY(CallLeaf)
DEFINE_CLASS_QUERY(CallLeafNoFP)
+ DEFINE_CLASS_QUERY(CallLeafPure)
DEFINE_CLASS_QUERY(CallRuntime)
DEFINE_CLASS_QUERY(CallStaticJava)
DEFINE_CLASS_QUERY(Catch)
@@ -1289,8 +1292,6 @@ class Node {
bool is_div_or_mod(BasicType bt) const;
- bool is_pure_function() const;
-
bool is_data_proj_of_pure_function(const Node* maybe_pure_function) const;
//----------------- Printing, etc
diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp
index 1a4c3c91c4f0..04b6e49b620c 100644
--- a/src/hotspot/share/opto/parse2.cpp
+++ b/src/hotspot/share/opto/parse2.cpp
@@ -1097,11 +1097,11 @@ void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi,
Node* Parse::floating_point_mod(Node* a, Node* b, BasicType type) {
assert(type == BasicType::T_FLOAT || type == BasicType::T_DOUBLE, "only float and double are floating points");
- CallNode* mod = type == BasicType::T_DOUBLE ? static_cast(new ModDNode(C, a, b)) : new ModFNode(C, a, b);
+ CallLeafPureNode* mod = type == BasicType::T_DOUBLE ? static_cast(new ModDNode(C, a, b)) : new ModFNode(C, a, b);
- Node* prev_mem = set_predefined_input_for_runtime_call(mod);
- mod = _gvn.transform(mod)->as_Call();
- set_predefined_output_for_runtime_call(mod, prev_mem, TypeRawPtr::BOTTOM);
+ set_predefined_input_for_runtime_call(mod);
+ mod = _gvn.transform(mod)->as_CallLeafPure();
+ set_predefined_output_for_runtime_call(mod);
Node* result = _gvn.transform(new ProjNode(mod, TypeFunc::Parms + 0));
record_for_igvn(mod);
return result;
diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp
index bede04c6b2c5..b27b0553320c 100644
--- a/src/hotspot/share/opto/split_if.cpp
+++ b/src/hotspot/share/opto/split_if.cpp
@@ -578,7 +578,7 @@ void PhaseIdealLoop::handle_use( Node *use, Node *def, small_cache *cache, Node
// Found an If getting its condition-code input from a Phi in the same block.
// Split thru the Region.
void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, RegionNode** new_true_region) {
-
+ iff->as_If()->mark_projections_unsafe_for_fold_compare();
C->set_major_progress();
RegionNode *region = iff->in(0)->as_Region();
Node *region_dom = idom(region);
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 98ec029db189..5dcfdc6a23fe 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -3053,9 +3053,17 @@ JVM_ENTRY(void, JVM_WaitForReferencePendingList(JNIEnv* env))
}
JVM_END
+JVM_ENTRY(jobject, JVM_ReferenceGet(JNIEnv* env, jobject ref))
+ oop ref_oop = JNIHandles::resolve_non_null(ref);
+ // PhantomReference has its own implementation of get().
+ assert(!java_lang_ref_Reference::is_phantom(ref_oop), "precondition");
+ oop referent = java_lang_ref_Reference::weak_referent(ref_oop);
+ return JNIHandles::make_local(THREAD, referent);
+JVM_END
+
JVM_ENTRY(jboolean, JVM_ReferenceRefersTo(JNIEnv* env, jobject ref, jobject o))
oop ref_oop = JNIHandles::resolve_non_null(ref);
- // PhantomReference has it's own implementation of refersTo().
+ // PhantomReference has its own implementation of refersTo().
// See: JVM_PhantomReferenceRefersTo
assert(!java_lang_ref_Reference::is_phantom(ref_oop), "precondition");
oop referent = java_lang_ref_Reference::weak_referent_no_keepalive(ref_oop);
diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
index 381ad7d12fbe..5077a1743b9f 100644
--- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
+++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
@@ -25,6 +25,7 @@
#include "classfile/symbolTable.hpp"
#include "interpreter/bytecodeStream.hpp"
#include "memory/universe.hpp"
+#include "oops/bsmAttribute.inline.hpp"
#include "oops/constantPool.inline.hpp"
#include "oops/fieldStreams.inline.hpp"
#include "oops/instanceKlass.inline.hpp"
@@ -389,29 +390,22 @@ void JvmtiClassFileReconstituter::write_annotations_attribute(const char* attr_n
// } bootstrap_methods[num_bootstrap_methods];
// }
void JvmtiClassFileReconstituter::write_bootstrapmethod_attribute() {
- Array* operands = cpool()->operands();
write_attribute_name_index("BootstrapMethods");
- int num_bootstrap_methods = ConstantPool::operand_array_length(operands);
-
- // calculate length of attribute
- u4 length = sizeof(u2); // num_bootstrap_methods
- for (int n = 0; n < num_bootstrap_methods; n++) {
- u2 num_bootstrap_arguments = cpool()->operand_argument_count_at(n);
- length += sizeof(u2); // bootstrap_method_ref
- length += sizeof(u2); // num_bootstrap_arguments
- length += (u4)sizeof(u2) * num_bootstrap_arguments; // bootstrap_arguments[num_bootstrap_arguments]
- }
+ u4 length = sizeof(u2) + // Size of num_bootstrap_methods
+ // The rest of the data for the attribute is exactly the u2s in the data array.
+ sizeof(u2) * cpool()->bsm_entries().array_length();
write_u4(length);
+ int num_bootstrap_methods = cpool()->bsm_entries().number_of_entries();
// write attribute
write_u2(checked_cast(num_bootstrap_methods));
for (int n = 0; n < num_bootstrap_methods; n++) {
- u2 bootstrap_method_ref = cpool()->operand_bootstrap_method_ref_index_at(n);
- u2 num_bootstrap_arguments = cpool()->operand_argument_count_at(n);
- write_u2(bootstrap_method_ref);
+ BSMAttributeEntry* bsme = cpool()->bsm_attribute_entry(n);
+ u2 num_bootstrap_arguments = bsme->argument_count();
+ write_u2(bsme->bootstrap_method_index());
write_u2(num_bootstrap_arguments);
for (int arg = 0; arg < num_bootstrap_arguments; arg++) {
- u2 bootstrap_argument = cpool()->operand_argument_index_at(n, arg);
+ u2 bootstrap_argument = bsme->argument(arg);
write_u2(bootstrap_argument);
}
}
@@ -798,7 +792,7 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
if (type_anno != nullptr) {
++attr_count; // has RuntimeVisibleTypeAnnotations attribute
}
- if (cpool()->operands() != nullptr) {
+ if (!cpool()->bsm_entries().is_empty()) {
++attr_count;
}
if (ik()->nest_host_index() != 0) {
@@ -843,7 +837,7 @@ void JvmtiClassFileReconstituter::write_class_attributes() {
if (ik()->record_components() != nullptr) {
write_record_attribute();
}
- if (cpool()->operands() != nullptr) {
+ if (!cpool()->bsm_entries().is_empty()) {
write_bootstrapmethod_attribute();
}
if (inner_classes_length > 0) {
diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
index d5b144ec28e0..42637c20b19e 100644
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
@@ -44,7 +44,8 @@
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/annotations.hpp"
-#include "oops/constantPool.hpp"
+#include "oops/bsmAttribute.inline.hpp"
+#include "oops/constantPool.inline.hpp"
#include "oops/fieldStreams.inline.hpp"
#include "oops/klass.inline.hpp"
#include "oops/klassVtable.hpp"
@@ -572,9 +573,9 @@ void VM_RedefineClasses::append_entry(const constantPoolHandle& scratch_cp,
case JVM_CONSTANT_Dynamic: // fall through
case JVM_CONSTANT_InvokeDynamic:
{
- // Index of the bootstrap specifier in the operands array
+ // Index of the bootstrap specifier in the BSM array
int old_bs_i = scratch_cp->bootstrap_methods_attribute_index(scratch_i);
- int new_bs_i = find_or_append_operand(scratch_cp, old_bs_i, merge_cp_p,
+ int new_bs_i = find_or_append_bsm_entry(scratch_cp, old_bs_i, merge_cp_p,
merge_cp_length_p);
// The bootstrap method NameAndType_info index
int old_ref_i = scratch_cp->bootstrap_name_and_type_ref_index_at(scratch_i);
@@ -590,10 +591,11 @@ void VM_RedefineClasses::append_entry(const constantPoolHandle& scratch_cp,
("Dynamic entry@%d name_and_type_index change: %d to %d", *merge_cp_length_p, old_ref_i, new_ref_i);
}
- if (scratch_cp->tag_at(scratch_i).is_dynamic_constant())
+ if (scratch_cp->tag_at(scratch_i).is_dynamic_constant()) {
(*merge_cp_p)->dynamic_constant_at_put(*merge_cp_length_p, new_bs_i, new_ref_i);
- else
+ } else {
(*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, new_bs_i, new_ref_i);
+ }
if (scratch_i != *merge_cp_length_p) {
// The new entry in *merge_cp_p is at a different index than
// the new entry in scratch_cp so we need to map the index values.
@@ -659,101 +661,94 @@ u2 VM_RedefineClasses::find_or_append_indirect_entry(const constantPoolHandle& s
} // end find_or_append_indirect_entry()
-// Append a bootstrap specifier into the merge_cp operands that is semantically equal
-// to the scratch_cp operands bootstrap specifier passed by the old_bs_i index.
+// Append a bootstrap specifier into the merge_cp BSM entries that is semantically equal
+// to the scratch_cp BSM entries' bootstrap specifier passed by the old_bs_i index.
// Recursively append new merge_cp entries referenced by the new bootstrap specifier.
-void VM_RedefineClasses::append_operand(const constantPoolHandle& scratch_cp, int old_bs_i,
+int VM_RedefineClasses::append_bsm_entry(const constantPoolHandle& scratch_cp, const int old_bs_i,
constantPoolHandle *merge_cp_p, int *merge_cp_length_p) {
- u2 old_ref_i = scratch_cp->operand_bootstrap_method_ref_index_at(old_bs_i);
+ BSMAttributeEntry* old_bsme = scratch_cp->bsm_attribute_entry(old_bs_i);
+ u2 old_ref_i = old_bsme->bootstrap_method_index();
u2 new_ref_i = find_or_append_indirect_entry(scratch_cp, old_ref_i, merge_cp_p,
merge_cp_length_p);
if (new_ref_i != old_ref_i) {
log_trace(redefine, class, constantpool)
- ("operands entry@%d bootstrap method ref_index change: %d to %d", _operands_cur_length, old_ref_i, new_ref_i);
+ ("BSM attribute entry@%d bootstrap method ref_index change: %d to %d", _bsmae_iter.current_offset() - 1, old_ref_i, new_ref_i);
}
- Array* merge_ops = (*merge_cp_p)->operands();
- int new_bs_i = _operands_cur_length;
- // We have _operands_cur_length == 0 when the merge_cp operands is empty yet.
- // However, the operand_offset_at(0) was set in the extend_operands() call.
- int new_base = (new_bs_i == 0) ? (*merge_cp_p)->operand_offset_at(0)
- : (*merge_cp_p)->operand_next_offset_at(new_bs_i - 1);
- u2 argc = scratch_cp->operand_argument_count_at(old_bs_i);
-
- ConstantPool::operand_offset_at_put(merge_ops, _operands_cur_length, new_base);
- merge_ops->at_put(new_base++, new_ref_i);
- merge_ops->at_put(new_base++, argc);
-
- for (int i = 0; i < argc; i++) {
- u2 old_arg_ref_i = scratch_cp->operand_argument_index_at(old_bs_i, i);
+ const int new_bs_i = _bsmae_iter.current_offset();
+ BSMAttributeEntry* new_bsme =
+ _bsmae_iter.reserve_new_entry(new_ref_i, old_bsme->argument_count());
+ assert(new_bsme != nullptr, "must be");
+ for (int i = 0; i < new_bsme->argument_count(); i++) {
+ u2 old_arg_ref_i = old_bsme->argument(i);
u2 new_arg_ref_i = find_or_append_indirect_entry(scratch_cp, old_arg_ref_i, merge_cp_p,
merge_cp_length_p);
- merge_ops->at_put(new_base++, new_arg_ref_i);
+ new_bsme->set_argument(i, new_arg_ref_i);
+
if (new_arg_ref_i != old_arg_ref_i) {
log_trace(redefine, class, constantpool)
- ("operands entry@%d bootstrap method argument ref_index change: %d to %d",
- _operands_cur_length, old_arg_ref_i, new_arg_ref_i);
+ ("BSM attribute entry@%d bootstrap method argument ref_index change: %d to %d",
+ _bsmae_iter.current_offset() - 1, old_arg_ref_i, new_arg_ref_i);
}
}
- if (old_bs_i != _operands_cur_length) {
- // The bootstrap specifier in *merge_cp_p is at a different index than
- // that in scratch_cp so we need to map the index values.
- map_operand_index(old_bs_i, new_bs_i);
- }
- _operands_cur_length++;
-} // end append_operand()
+ // This is only for the logging
+ map_bsm_index(old_bs_i, new_bs_i);
+ return new_bs_i;
+} // end append_bsm_entry()
-int VM_RedefineClasses::find_or_append_operand(const constantPoolHandle& scratch_cp,
+int VM_RedefineClasses::find_or_append_bsm_entry(const constantPoolHandle& scratch_cp,
int old_bs_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p) {
+ const int max_offset_in_merge = _bsmae_iter.current_offset();
int new_bs_i = old_bs_i; // bootstrap specifier index
- bool match = (old_bs_i < _operands_cur_length) &&
- scratch_cp->compare_operand_to(old_bs_i, *merge_cp_p, old_bs_i);
+ // Has the old_bs_i index been used already? Check if it's the same so we know
+ // whether or not a remapping is required.
+ bool match = (old_bs_i < max_offset_in_merge) &&
+ scratch_cp->compare_bootstrap_entry_to(old_bs_i, *merge_cp_p, old_bs_i);
if (!match) {
// forward reference in *merge_cp_p or not a direct match
- int found_i = scratch_cp->find_matching_operand(old_bs_i, *merge_cp_p,
- _operands_cur_length);
+ int found_i = scratch_cp->find_matching_bsm_entry(old_bs_i, *merge_cp_p,
+ max_offset_in_merge);
if (found_i != -1) {
- guarantee(found_i != old_bs_i, "compare_operand_to() and find_matching_operand() disagree");
- // found a matching operand somewhere else in *merge_cp_p so just need a mapping
+ guarantee(found_i != old_bs_i, "compare_bootstrap_entry_to() and find_matching_bsm_entry() disagree");
+ // found a matching BSM entry somewhere else in *merge_cp_p so just need a mapping
new_bs_i = found_i;
- map_operand_index(old_bs_i, found_i);
+ map_bsm_index(old_bs_i, found_i);
} else {
// no match found so we have to append this bootstrap specifier to *merge_cp_p
- append_operand(scratch_cp, old_bs_i, merge_cp_p, merge_cp_length_p);
- new_bs_i = _operands_cur_length - 1;
+ new_bs_i = append_bsm_entry(scratch_cp, old_bs_i, merge_cp_p, merge_cp_length_p);
}
}
return new_bs_i;
-} // end find_or_append_operand()
+} // end find_or_append_bsm_entry()
-void VM_RedefineClasses::finalize_operands_merge(const constantPoolHandle& merge_cp, TRAPS) {
- if (merge_cp->operands() == nullptr) {
+void VM_RedefineClasses::finalize_bsm_entries_merge(const constantPoolHandle& merge_cp, TRAPS) {
+ if (merge_cp->bsm_entries().number_of_entries() == 0) {
return;
}
- // Shrink the merge_cp operands
- merge_cp->shrink_operands(_operands_cur_length, CHECK);
+ // Finished extending the BSMAEs
+ merge_cp->end_extension(_bsmae_iter, CHECK);
if (log_is_enabled(Trace, redefine, class, constantpool)) {
// don't want to loop unless we are tracing
int count = 0;
- for (int i = 1; i < _operands_index_map_p->length(); i++) {
- int value = _operands_index_map_p->at(i);
+ for (int i = 1; i < _bsm_index_map_p->length(); i++) {
+ int value = _bsm_index_map_p->at(i);
if (value != -1) {
- log_trace(redefine, class, constantpool)("operands_index_map[%d]: old=%d new=%d", count, i, value);
+ log_trace(redefine, class, constantpool)("bsm_index_map[%d]: old=%d new=%d", count, i, value);
count++;
}
}
}
// Clean-up
- _operands_index_map_p = nullptr;
- _operands_cur_length = 0;
- _operands_index_map_count = 0;
-} // end finalize_operands_merge()
+ _bsm_index_map_p = nullptr;
+ _bsm_index_map_count = 0;
+ _bsmae_iter = BSMAttributeEntries::InsertionIterator();
+} // end finalize_bsmentries_merge()
// Symbol* comparator for qsort
// The caller must have an active ResourceMark.
@@ -1270,26 +1265,26 @@ u2 VM_RedefineClasses::find_new_index(int old_index) {
// Find new bootstrap specifier index value for old bootstrap specifier index
// value by searching the index map. Returns unused index (-1) if there is
// no mapped value for the old bootstrap specifier index.
-int VM_RedefineClasses::find_new_operand_index(int old_index) {
- if (_operands_index_map_count == 0) {
+int VM_RedefineClasses::find_new_bsm_index(int old_index) {
+ if (_bsm_index_map_count == 0) {
// map is empty so nothing can be found
return -1;
}
- if (old_index == -1 || old_index >= _operands_index_map_p->length()) {
+ if (old_index == -1 || old_index >= _bsm_index_map_p->length()) {
// The old_index is out of range so it is not mapped.
// This should not happen in regular constant pool merging use.
return -1;
}
- int value = _operands_index_map_p->at(old_index);
+ int value = _bsm_index_map_p->at(old_index);
if (value == -1) {
// the old_index is not mapped
return -1;
}
return value;
-} // end find_new_operand_index()
+} // end find_new_bsm_index()
// The bug 6214132 caused the verification to fail.
@@ -1560,22 +1555,15 @@ void VM_RedefineClasses::map_index(const constantPoolHandle& scratch_cp,
// Map old_index to new_index as needed.
-void VM_RedefineClasses::map_operand_index(int old_index, int new_index) {
- if (find_new_operand_index(old_index) != -1) {
- // old_index is already mapped
- return;
- }
-
+void VM_RedefineClasses::map_bsm_index(int old_index, int new_index) {
if (old_index == new_index) {
// no mapping is needed
return;
}
-
- _operands_index_map_p->at_put(old_index, new_index);
- _operands_index_map_count++;
-
+ _bsm_index_map_p->at_put(old_index, new_index);
+ _bsm_index_map_count++;
log_trace(redefine, class, constantpool)("mapped bootstrap specifier at index %d to %d", old_index, new_index);
-} // end map_index()
+} // end map_bsm_index()
// Merge old_cp and scratch_cp and return the results of the merge via
@@ -1639,8 +1627,8 @@ bool VM_RedefineClasses::merge_constant_pools(const constantPoolHandle& old_cp,
}
} // end for each old_cp entry
- ConstantPool::copy_operands(old_cp, merge_cp_p, CHECK_false);
- merge_cp_p->extend_operands(scratch_cp, CHECK_false);
+ ConstantPool::copy_bsm_entries(old_cp, merge_cp_p, CHECK_false);
+ _bsmae_iter = merge_cp_p->start_extension(scratch_cp, CHECK_false);
// We don't need to sanity check that *merge_cp_length_p is within
// *merge_cp_p bounds since we have the minimum on-entry check above.
@@ -1737,7 +1725,7 @@ bool VM_RedefineClasses::merge_constant_pools(const constantPoolHandle& old_cp,
("after pass 1b: merge_cp_len=%d, scratch_i=%d, index_map_len=%d",
merge_cp_length_p, scratch_i, _index_map_count);
}
- finalize_operands_merge(merge_cp_p, CHECK_false);
+ finalize_bsm_entries_merge(merge_cp_p, CHECK_false);
return true;
} // end merge_constant_pools()
@@ -1807,12 +1795,11 @@ jvmtiError VM_RedefineClasses::merge_cp_and_rewrite(
_index_map_count = 0;
_index_map_p = new intArray(scratch_cp->length(), scratch_cp->length(), -1);
- _operands_cur_length = ConstantPool::operand_array_length(old_cp->operands());
- _operands_index_map_count = 0;
- int operands_index_map_len = ConstantPool::operand_array_length(scratch_cp->operands());
- _operands_index_map_p = new intArray(operands_index_map_len, operands_index_map_len, -1);
+ _bsm_index_map_count = 0;
+ int bsm_data_len = scratch_cp->bsm_entries().array_length();
+ _bsm_index_map_p = new intArray(bsm_data_len, bsm_data_len, -1);
- // reference to the cp holder is needed for copy_operands()
+ // reference to the cp holder is needed for reallocating the BSM attribute
merge_cp->set_pool_holder(scratch_class);
bool result = merge_constant_pools(old_cp, scratch_cp, merge_cp,
merge_cp_length, THREAD);
@@ -3496,7 +3483,7 @@ void VM_RedefineClasses::set_new_constant_pool(
smaller_cp->set_version(version);
// attach klass to new constant pool
- // reference to the cp holder is needed for copy_operands()
+ // reference to the cp holder is needed for reallocating the BSM attribute
smaller_cp->set_pool_holder(scratch_class);
smaller_cp->copy_fields(scratch_cp());
diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.hpp b/src/hotspot/share/prims/jvmtiRedefineClasses.hpp
index d2eda1f3eede..3f1b555b175e 100644
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.hpp
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.hpp
@@ -363,11 +363,16 @@ class VM_RedefineClasses: public VM_Operation {
int _index_map_count;
intArray * _index_map_p;
- // _operands_index_map_count is just an optimization for knowing if
- // _operands_index_map_p contains any entries.
- int _operands_cur_length;
- int _operands_index_map_count;
- intArray * _operands_index_map_p;
+ // _bsm_index_map_count is just an optimization for knowing if
+ // _bsm_index_map_p contains any entries.
+ int _bsm_index_map_count;
+ intArray * _bsm_index_map_p;
+
+ // After merge_constant_pools "Pass 0", the BSMAttribute entries of merge_cp_p will have been expanded to fit
+ // scratch_cp's BSMAttribute entries as well.
+ // However, the newly acquired space will not have been filled in yet.
+ // To append to this new space, the iterator is used.
+ BSMAttributeEntries::InsertionIterator _bsmae_iter;
// ptr to _class_count scratch_classes
InstanceKlass** _scratch_classes;
@@ -429,17 +434,18 @@ class VM_RedefineClasses: public VM_Operation {
// Support for constant pool merging (these routines are in alpha order):
void append_entry(const constantPoolHandle& scratch_cp, int scratch_i,
constantPoolHandle *merge_cp_p, int *merge_cp_length_p);
- void append_operand(const constantPoolHandle& scratch_cp, int scratch_bootstrap_spec_index,
+ // Returns the index of the appended BSM
+ int append_bsm_entry(const constantPoolHandle& scratch_cp, int scratch_bootstrap_spec_index,
constantPoolHandle *merge_cp_p, int *merge_cp_length_p);
- void finalize_operands_merge(const constantPoolHandle& merge_cp, TRAPS);
+ void finalize_bsm_entries_merge(const constantPoolHandle& merge_cp, TRAPS);
u2 find_or_append_indirect_entry(const constantPoolHandle& scratch_cp, int scratch_i,
constantPoolHandle *merge_cp_p, int *merge_cp_length_p);
- int find_or_append_operand(const constantPoolHandle& scratch_cp, int scratch_bootstrap_spec_index,
+ int find_or_append_bsm_entry(const constantPoolHandle& scratch_cp, int scratch_bootstrap_spec_index,
constantPoolHandle *merge_cp_p, int *merge_cp_length_p);
u2 find_new_index(int old_index);
- int find_new_operand_index(int old_bootstrap_spec_index);
+ int find_new_bsm_index(int old_bootstrap_spec_index);
void map_index(const constantPoolHandle& scratch_cp, int old_index, int new_index);
- void map_operand_index(int old_bootstrap_spec_index, int new_bootstrap_spec_index);
+ void map_bsm_index(int old_bootstrap_spec_index, int new_bootstrap_spec_index);
bool merge_constant_pools(const constantPoolHandle& old_cp,
const constantPoolHandle& scratch_cp, constantPoolHandle& merge_cp_p,
int& merge_cp_length_p, TRAPS);
diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
index 9999b59decee..b5dbc92e40cf 100644
--- a/src/hotspot/share/prims/whitebox.cpp
+++ b/src/hotspot/share/prims/whitebox.cpp
@@ -509,8 +509,16 @@ WB_ENTRY(jboolean, WB_ConcurrentGCRunTo(JNIEnv* env, jobject o, jobject at))
return ConcurrentGCBreakpoints::run_to(c_name);
WB_END
-WB_ENTRY(jboolean, WB_HasExternalSymbolsStripped(JNIEnv* env, jobject o))
-#if defined(HAS_STRIPPED_DEBUGINFO)
+WB_ENTRY(jboolean, WB_ShipDebugInfoFull(JNIEnv* env, jobject o))
+#if defined(SHIP_DEBUGINFO_FULL)
+ return true;
+#else
+ return false;
+#endif
+WB_END
+
+WB_ENTRY(jboolean, WB_ShipDebugInfoPublic(JNIEnv* env, jobject o))
+#if defined(SHIP_DEBUGINFO_PUBLIC)
return true;
#else
return false;
@@ -2794,7 +2802,8 @@ static JNINativeMethod methods[] = {
{CC"getVMLargePageSize", CC"()J", (void*)&WB_GetVMLargePageSize},
{CC"getHeapSpaceAlignment", CC"()J", (void*)&WB_GetHeapSpaceAlignment},
{CC"getHeapAlignment", CC"()J", (void*)&WB_GetHeapAlignment},
- {CC"hasExternalSymbolsStripped", CC"()Z", (void*)&WB_HasExternalSymbolsStripped},
+ {CC"shipsFullDebugInfo", CC"()Z", (void*)&WB_ShipDebugInfoFull},
+ {CC"shipsPublicDebugInfo", CC"()Z", (void*)&WB_ShipDebugInfoPublic},
{CC"countAliveClasses0", CC"(Ljava/lang/String;)I", (void*)&WB_CountAliveClasses },
{CC"getSymbolRefcount", CC"(Ljava/lang/String;)I", (void*)&WB_GetSymbolRefcount },
{CC"parseCommandLine0",
diff --git a/src/hotspot/share/runtime/cpuTimeCounters.cpp b/src/hotspot/share/runtime/cpuTimeCounters.cpp
index 5b2e76fed7f6..1d7e75161675 100644
--- a/src/hotspot/share/runtime/cpuTimeCounters.cpp
+++ b/src/hotspot/share/runtime/cpuTimeCounters.cpp
@@ -121,8 +121,5 @@ ThreadTotalCPUTimeClosure::~ThreadTotalCPUTimeClosure() {
}
void ThreadTotalCPUTimeClosure::do_thread(Thread* thread) {
- // The default code path (fast_thread_cpu_time()) asserts that
- // pthread_getcpuclockid() and clock_gettime() must return 0. Thus caller
- // must ensure the thread exists and has not terminated.
_total += os::thread_cpu_time(thread);
}
diff --git a/src/hotspot/share/runtime/safefetch.hpp b/src/hotspot/share/runtime/safefetch.hpp
index 71a542e25e80..a1781962ec0e 100644
--- a/src/hotspot/share/runtime/safefetch.hpp
+++ b/src/hotspot/share/runtime/safefetch.hpp
@@ -31,8 +31,8 @@
// Safefetch allows to load a value from a location that's not known
// to be valid. If the load causes a fault, the error value is returned.
-#ifdef _WIN32
- // Windows uses Structured Exception Handling
+#if defined(_WIN32) && !defined(_M_ARM64)
+ // Windows x86_64 uses Structured Exception Handling
#include "safefetch_windows.hpp"
#elif defined(ZERO) || defined (_AIX)
// These platforms implement safefetch via Posix sigsetjmp/longjmp.
diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
index 7e164ed16e2e..54fbf120f56b 100644
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -1366,10 +1366,24 @@ void Threads::print_on(outputStream* st, bool print_stacks,
char buf[32];
st->print_raw_cr(os::local_time_string(buf, sizeof(buf)));
- st->print_cr("Full thread dump %s (%s %s):",
+ st->print_cr("Full thread dump %s (%s %s)",
VM_Version::vm_name(),
VM_Version::vm_release(),
VM_Version::vm_info_string());
+ JDK_Version::current().to_string(buf, sizeof(buf));
+ const char* runtime_name = JDK_Version::runtime_name() != nullptr ?
+ JDK_Version::runtime_name() : "";
+ const char* runtime_version = JDK_Version::runtime_version() != nullptr ?
+ JDK_Version::runtime_version() : "";
+ const char* vendor_version = JDK_Version::runtime_vendor_version() != nullptr ?
+ JDK_Version::runtime_vendor_version() : "";
+ const char* jdk_debug_level = VM_Version::printable_jdk_debug_level() != nullptr ?
+ VM_Version::printable_jdk_debug_level() : "";
+
+ st->print_cr(" JDK version: %s%s%s (%s) (%sbuild %s)", runtime_name,
+ (*vendor_version != '\0') ? " " : "", vendor_version,
+ buf, jdk_debug_level, runtime_version);
+
st->cr();
#if INCLUDE_SERVICES
diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp
index 879a2324e549..5a5e1dc6bce7 100644
--- a/src/hotspot/share/runtime/vmStructs.cpp
+++ b/src/hotspot/share/runtime/vmStructs.cpp
@@ -56,6 +56,7 @@
#include "oops/array.hpp"
#include "oops/arrayKlass.hpp"
#include "oops/arrayOop.hpp"
+#include "oops/bsmAttribute.hpp"
#include "oops/constMethod.hpp"
#include "oops/constantPool.hpp"
#include "oops/cpCache.hpp"
@@ -172,10 +173,12 @@
nonstatic_field(ArrayKlass, _dimension, int) \
volatile_nonstatic_field(ArrayKlass, _higher_dimension, ObjArrayKlass*) \
volatile_nonstatic_field(ArrayKlass, _lower_dimension, ArrayKlass*) \
+ nonstatic_field(BSMAttributeEntries, _offsets, Array*) \
+ nonstatic_field(BSMAttributeEntries, _bootstrap_methods, Array*) \
+ nonstatic_field(ConstantPool, _bsm_entries, BSMAttributeEntries) \
nonstatic_field(ConstantPool, _tags, Array*) \
nonstatic_field(ConstantPool, _cache, ConstantPoolCache*) \
nonstatic_field(ConstantPool, _pool_holder, InstanceKlass*) \
- nonstatic_field(ConstantPool, _operands, Array*) \
nonstatic_field(ConstantPool, _resolved_klasses, Array*) \
nonstatic_field(ConstantPool, _length, int) \
nonstatic_field(ConstantPool, _minor_version, u2) \
@@ -743,6 +746,7 @@
unchecked_nonstatic_field(Array, _data, sizeof(int)) \
unchecked_nonstatic_field(Array, _data, sizeof(u1)) \
unchecked_nonstatic_field(Array, _data, sizeof(u2)) \
+ unchecked_nonstatic_field(Array, _data, sizeof(u4)) \
unchecked_nonstatic_field(Array, _data, sizeof(Method*)) \
unchecked_nonstatic_field(Array, _data, sizeof(Klass*)) \
unchecked_nonstatic_field(Array, _data, sizeof(ResolvedFieldEntry)) \
@@ -974,6 +978,7 @@
declare_toplevel_type(volatile Metadata*) \
\
declare_toplevel_type(DataLayout) \
+ declare_toplevel_type(BSMAttributeEntries) \
\
/********/ \
/* Oops */ \
@@ -1502,13 +1507,13 @@
\
declare_constant(Symbol::max_symbol_length) \
\
- /***********************************************/ \
- /* ConstantPool* layout enum for InvokeDynamic */ \
- /***********************************************/ \
+ /******************************************************/ \
+ /* BSMAttributeEntry* - layout enum for InvokeDynamic */ \
+ /******************************************************/ \
\
- declare_constant(ConstantPool::_indy_bsm_offset) \
- declare_constant(ConstantPool::_indy_argc_offset) \
- declare_constant(ConstantPool::_indy_argv_offset) \
+ declare_constant(BSMAttributeEntry::_bsmi_offset) \
+ declare_constant(BSMAttributeEntry::_argc_offset) \
+ declare_constant(BSMAttributeEntry::_argv_offset) \
\
/***************************************/ \
/* JavaThreadStatus enum */ \
diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp
index bd5adc7acf9a..5304a3dd5243 100644
--- a/src/hotspot/share/utilities/debug.cpp
+++ b/src/hotspot/share/utilities/debug.cpp
@@ -322,20 +322,20 @@ class Command : public StackObj {
int Command::level = 0;
-extern "C" DEBUGEXPORT void blob(CodeBlob* cb) {
+extern "C" NOINLINE void blob(CodeBlob* cb) {
Command c("blob");
cb->print();
}
-extern "C" DEBUGEXPORT void dump_vtable(address p) {
+extern "C" NOINLINE void dump_vtable(address p) {
Command c("dump_vtable");
Klass* k = (Klass*)p;
k->vtable().print();
}
-extern "C" DEBUGEXPORT void nm(intptr_t p) {
+extern "C" NOINLINE void nm(intptr_t p) {
// Actually we look through all CodeBlobs (the nm name has been kept for backwards compatibility)
Command c("nm");
CodeBlob* cb = CodeCache::find_blob((address)p);
@@ -347,7 +347,7 @@ extern "C" DEBUGEXPORT void nm(intptr_t p) {
}
-extern "C" DEBUGEXPORT void disnm(intptr_t p) {
+extern "C" NOINLINE void disnm(intptr_t p) {
Command c("disnm");
CodeBlob* cb = CodeCache::find_blob((address) p);
if (cb != nullptr) {
@@ -362,7 +362,7 @@ extern "C" DEBUGEXPORT void disnm(intptr_t p) {
}
-extern "C" DEBUGEXPORT void printnm(intptr_t p) {
+extern "C" NOINLINE void printnm(intptr_t p) {
char buffer[256];
os::snprintf_checked(buffer, sizeof(buffer), "printnm: " INTPTR_FORMAT, p);
Command c(buffer);
@@ -376,13 +376,13 @@ extern "C" DEBUGEXPORT void printnm(intptr_t p) {
}
-extern "C" DEBUGEXPORT void universe() {
+extern "C" NOINLINE void universe() {
Command c("universe");
Universe::print_on(tty);
}
-extern "C" DEBUGEXPORT void verify() {
+extern "C" NOINLINE void verify() {
// try to run a verify on the entire system
// note: this may not be safe if we're not at a safepoint; for debugging,
// this manipulates the safepoint settings to avoid assertion failures
@@ -399,7 +399,7 @@ extern "C" DEBUGEXPORT void verify() {
}
-extern "C" DEBUGEXPORT void pp(void* p) {
+extern "C" NOINLINE void pp(void* p) {
Command c("pp");
FlagSetting fl(DisplayVMOutput, true);
if (p == nullptr) {
@@ -423,10 +423,7 @@ extern "C" DEBUGEXPORT void pp(void* p) {
}
}
-
-extern "C" DEBUGEXPORT void findpc(intptr_t x);
-
-extern "C" DEBUGEXPORT void ps() { // print stack
+extern "C" NOINLINE void ps() { // print stack
if (Thread::current_or_null() == nullptr) return;
Command c("ps");
@@ -455,7 +452,7 @@ extern "C" DEBUGEXPORT void ps() { // print stack
}
}
-extern "C" DEBUGEXPORT void pfl() {
+extern "C" NOINLINE void pfl() {
// print frame layout
Command c("pfl");
JavaThread* p = JavaThread::active();
@@ -467,7 +464,7 @@ extern "C" DEBUGEXPORT void pfl() {
}
}
-extern "C" DEBUGEXPORT void psf() { // print stack frames
+extern "C" NOINLINE void psf() { // print stack frames
{
Command c("psf");
JavaThread* p = JavaThread::active();
@@ -481,19 +478,19 @@ extern "C" DEBUGEXPORT void psf() { // print stack frames
}
-extern "C" DEBUGEXPORT void threads() {
+extern "C" NOINLINE void threads() {
Command c("threads");
Threads::print(false, true);
}
-extern "C" DEBUGEXPORT void psd() {
+extern "C" NOINLINE void psd() {
Command c("psd");
SystemDictionary::print();
}
-extern "C" DEBUGEXPORT void pss() { // print all stacks
+extern "C" NOINLINE void pss() { // print all stacks
if (Thread::current_or_null() == nullptr) return;
Command c("pss");
Threads::print(true, PRODUCT_ONLY(false) NOT_PRODUCT(true));
@@ -501,7 +498,7 @@ extern "C" DEBUGEXPORT void pss() { // print all stacks
// #ifndef PRODUCT
-extern "C" DEBUGEXPORT void debug() { // to set things up for compiler debugging
+extern "C" NOINLINE void debug() { // to set things up for compiler debugging
Command c("debug");
NOT_PRODUCT(WizardMode = true;)
PrintCompilation = true;
@@ -510,7 +507,7 @@ extern "C" DEBUGEXPORT void debug() { // to set things up for comp
}
-extern "C" DEBUGEXPORT void ndebug() { // undo debug()
+extern "C" NOINLINE void ndebug() { // undo debug()
Command c("ndebug");
PrintCompilation = false;
PrintInlining = PrintAssembly = false;
@@ -518,35 +515,35 @@ extern "C" DEBUGEXPORT void ndebug() { // undo debug()
}
-extern "C" DEBUGEXPORT void flush() {
+extern "C" NOINLINE void flush() {
Command c("flush");
tty->flush();
}
-extern "C" DEBUGEXPORT void events() {
+extern "C" NOINLINE void events() {
Command c("events");
Events::print();
}
-extern "C" DEBUGEXPORT Method* findm(intptr_t pc) {
+extern "C" NOINLINE Method* findm(intptr_t pc) {
Command c("findm");
nmethod* nm = CodeCache::find_nmethod((address)pc);
return (nm == nullptr) ? (Method*)nullptr : nm->method();
}
-extern "C" DEBUGEXPORT nmethod* findnm(intptr_t addr) {
+extern "C" NOINLINE nmethod* findnm(intptr_t addr) {
Command c("findnm");
return CodeCache::find_nmethod((address)addr);
}
-extern "C" DEBUGEXPORT void find(intptr_t x) {
+extern "C" NOINLINE void find(intptr_t x) {
Command c("find");
os::print_location(tty, x, false);
}
-extern "C" DEBUGEXPORT void findpc(intptr_t x) {
+extern "C" NOINLINE void findpc(intptr_t x) {
Command c("findpc");
os::print_location(tty, x, true);
}
@@ -557,21 +554,20 @@ extern "C" DEBUGEXPORT void findpc(intptr_t x) {
// call findclass("java/lang/Object", 0x3) -> find j.l.Object and disasm all of its methods
// call findmethod("*ang/Object*", "wait", 0xff) -> detailed disasm of all "wait" methods in j.l.Object
// call findmethod("*ang/Object*", "wait:(*J*)V", 0x1) -> list all "wait" methods in j.l.Object that have a long parameter
-extern "C" DEBUGEXPORT void findclass(const char* class_name_pattern, int flags) {
+extern "C" NOINLINE void findclass(const char* class_name_pattern, int flags) {
Command c("findclass");
ClassPrinter::print_flags_help(tty);
ClassPrinter::print_classes(class_name_pattern, flags, tty);
}
-extern "C" DEBUGEXPORT void findmethod(const char* class_name_pattern,
- const char* method_pattern, int flags) {
+extern "C" NOINLINE void findmethod(const char* class_name_pattern, const char* method_pattern, int flags) {
Command c("findmethod");
ClassPrinter::print_flags_help(tty);
ClassPrinter::print_methods(class_name_pattern, method_pattern, flags, tty);
}
// Need method pointer to find bcp
-extern "C" DEBUGEXPORT void findbcp(intptr_t method, intptr_t bcp) {
+extern "C" NOINLINE void findbcp(intptr_t method, intptr_t bcp) {
Command c("findbcp");
Method* mh = (Method*)method;
if (!mh->is_native()) {
@@ -582,7 +578,7 @@ extern "C" DEBUGEXPORT void findbcp(intptr_t method, intptr_t bcp) {
}
// check and decode a single u5 value
-extern "C" DEBUGEXPORT u4 u5decode(intptr_t addr) {
+extern "C" NOINLINE u4 u5decode(intptr_t addr) {
Command c("u5decode");
u1* arr = (u1*)addr;
size_t off = 0, lim = 5;
@@ -599,9 +595,7 @@ extern "C" DEBUGEXPORT u4 u5decode(intptr_t addr) {
// there is no limit on the count of items printed; the
// printing stops when an null is printed or at limit.
// See documentation for UNSIGNED5::Reader::print(count).
-extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr,
- intptr_t limit,
- int count) {
+extern "C" NOINLINE intptr_t u5p(intptr_t addr, intptr_t limit, int count) {
Command c("u5p");
u1* arr = (u1*)addr;
if (limit && limit < addr) limit = addr;
@@ -614,10 +608,10 @@ extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr,
// int versions of all methods to avoid having to type type casts in the debugger
-void pp(intptr_t p) { pp((void*)p); }
-void pp(oop p) { pp((void*)p); }
+NOINLINE void pp(intptr_t p) { pp((void*)p); }
+NOINLINE void pp(oop p) { pp((void*)p); }
-extern "C" DEBUGEXPORT void help() {
+extern "C" NOINLINE void help() {
Command c("help");
tty->print_cr("basic");
tty->print_cr(" pp(void* p) - try to make sense of p");
@@ -674,7 +668,7 @@ extern "C" DEBUGEXPORT void help() {
}
#ifndef PRODUCT
-extern "C" DEBUGEXPORT void pns(void* sp, void* fp, void* pc) { // print native stack
+extern "C" NOINLINE void pns(void* sp, void* fp, void* pc) { // print native stack
Command c("pns");
static char buf[O_BUFLEN];
// Call generic frame constructor (certain arguments may be ignored)
@@ -692,7 +686,7 @@ extern "C" DEBUGEXPORT void pns(void* sp, void* fp, void* pc) { // print native
// WARNING: Only intended for use when debugging. Do not leave calls to
// pns2() in committed source (product or debug).
//
-extern "C" DEBUGEXPORT void pns2() { // print native stack
+extern "C" NOINLINE void pns2() { // print native stack
Command c("pns2");
static char buf[O_BUFLEN];
address lastpc = nullptr;
@@ -702,6 +696,43 @@ extern "C" DEBUGEXPORT void pns2() { // print native stack
}
#endif
+// just an exported helper; to avoid link time elimination of the referenced functions
+extern "C" JNIEXPORT void JVM_debug_helpers_keeper(void* p1, void* p2, void* p3, intptr_t ip, oop oh, address adr) {
+ blob((CodeBlob*)p1);
+ dump_vtable(adr);
+ nm(ip);
+ disnm(ip);
+ printnm(ip);
+ universe();
+ verify();
+ pp(p1);
+ ps();
+ pfl();
+ psf();
+ threads();
+ psd();
+ pss();
+ debug();
+ ndebug();
+ flush();
+ events();
+ findm(ip);
+ findnm(ip);
+ find(ip);
+ findpc(ip);
+ findclass("", 0);
+ findmethod("", "", 0);
+ findbcp(ip, ip);
+ u5decode(ip);
+ u5p(ip, ip, 0);
+ pp(ip);
+ pp(oh);
+ help();
+#ifndef PRODUCT
+ pns(p1, p2, p3);
+ pns2();
+#endif
+}
// Returns true iff the address p is readable and *(intptr_t*)p != errvalue
extern "C" bool dbg_is_safe(const void* p, intptr_t errvalue) {
diff --git a/src/hotspot/share/utilities/ostream.hpp b/src/hotspot/share/utilities/ostream.hpp
index 29761f52c2cb..9408372d1aec 100644
--- a/src/hotspot/share/utilities/ostream.hpp
+++ b/src/hotspot/share/utilities/ostream.hpp
@@ -182,7 +182,7 @@ class StreamIndentor {
NONCOPYABLE(StreamIndentor);
public:
- StreamIndentor(outputStream* os, int indentation) :
+ StreamIndentor(outputStream* os, int indentation = 2) :
_stream(os),
_indentation(indentation),
_old_autoindent(_stream->set_autoindent(true)) {
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 b463f3599952..56a119893a79 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
@@ -1363,22 +1363,16 @@ private static void implKyber12To16Java(byte[] condensed, int index, short[] par
}
}
- // The intrinsic implementations assume that the input and output buffers
- // are such that condensed can be read in 96-byte chunks and
- // parsed can be written in 64 shorts chunks except for the last chunk
- // that can be either 48 or 64 shorts. In other words,
- // if (i - 1) * 64 < parsedLengths <= i * 64 then
- // parsed.length should be either i * 64 or (i-1) * 64 + 48 and
- // condensed.length should be at least index + i * 96.
+ // An intrinsic implementation assumes that the input and output buffers
+ // are such that condensed can be read in chunks of 192 bytes and
+ // parsed can be written in chunks of 128 shorts, so callers should allocate
+ // the condensed and parsed arrays accordingly, see the assert()
private void twelve2Sixteen(byte[] condensed, int index,
short[] parsed, int parsedLength) {
- int i = parsedLength / 64;
- int remainder = parsedLength - i * 64;
- if (remainder != 0) {
- i++;
- }
- assert ((remainder == 0) || (remainder == 48)) &&
- (index + i * 96 <= condensed.length);
+ int n = (parsedLength + 127) / 128;
+ assert ((parsed.length >= n * 128) &&
+ (condensed.length >= index + n * 192));
+
implKyber12To16(condensed, index, parsed, parsedLength);
}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java
index 64b276a1c79a..9d33b6689d23 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/PBES2Parameters.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEParameterSpec;
+import sun.security.util.PBKDF2Parameters;
import sun.security.util.*;
/**
@@ -93,7 +94,7 @@
abstract class PBES2Parameters extends AlgorithmParametersSpi {
private static final ObjectIdentifier pkcs5PBKDF2_OID =
- ObjectIdentifier.of(KnownOIDs.PBKDF2WithHmacSHA1);
+ ObjectIdentifier.of(KnownOIDs.PBKDF2);
private static final ObjectIdentifier pkcs5PBES2_OID =
ObjectIdentifier.of(KnownOIDs.PBES2);
private static final ObjectIdentifier aes128CBC_OID =
@@ -224,77 +225,32 @@ protected void engineInit(byte[] encoded)
// next DerValue as the real PBES2-params.
if (kdf.getTag() == DerValue.tag_ObjectId) {
pBES2_params = pBES2_params.data.getDerValue();
+ if (pBES2_params.tag != DerValue.tag_Sequence) {
+ throw new IOException("PBE parameter parsing error: "
+ + "not an ASN.1 SEQUENCE tag");
+ }
kdf = pBES2_params.data.getDerValue();
}
- String kdfAlgo = parseKDF(kdf);
-
- if (pBES2_params.tag != DerValue.tag_Sequence) {
- throw new IOException("PBE parameter parsing error: "
- + "not an ASN.1 SEQUENCE tag");
- }
- String cipherAlgo = parseES(pBES2_params.data.getDerValue());
-
- this.pbes2AlgorithmName = "PBEWith" + kdfAlgo + "And" + cipherAlgo;
- }
-
- private String parseKDF(DerValue keyDerivationFunc) throws IOException {
-
- if (!pkcs5PBKDF2_OID.equals(keyDerivationFunc.data.getOID())) {
+ if (!pkcs5PBKDF2_OID.equals(kdf.data.getOID())) {
throw new IOException("PBE parameter parsing error: "
+ "expecting the object identifier for PBKDF2");
}
- if (keyDerivationFunc.tag != DerValue.tag_Sequence) {
+ if (kdf.tag != DerValue.tag_Sequence) {
throw new IOException("PBE parameter parsing error: "
+ "not an ASN.1 SEQUENCE tag");
}
- DerValue pBKDF2_params = keyDerivationFunc.data.getDerValue();
- if (pBKDF2_params.tag != DerValue.tag_Sequence) {
- throw new IOException("PBE parameter parsing error: "
- + "not an ASN.1 SEQUENCE tag");
- }
- DerValue specified = pBKDF2_params.data.getDerValue();
- // the 'specified' ASN.1 CHOICE for 'salt' is supported
- if (specified.tag == DerValue.tag_OctetString) {
- salt = specified.getOctetString();
- } else {
- // the 'otherSource' ASN.1 CHOICE for 'salt' is not supported
- throw new IOException("PBE parameter parsing error: "
- + "not an ASN.1 OCTET STRING tag");
- }
- iCount = pBKDF2_params.data.getInteger();
+ DerValue pBKDF2_params = kdf.data.getDerValue();
- // keyLength INTEGER (1..MAX) OPTIONAL,
- var ksDer = pBKDF2_params.data.getOptional(DerValue.tag_Integer);
- if (ksDer.isPresent()) {
- keysize = ksDer.get().getInteger() * 8; // keysize (in bits)
- }
+ var kdfParams = new PBKDF2Parameters(pBKDF2_params);
+ String kdfAlgo = kdfParams.getPrfAlgo();
+ salt = kdfParams.getSalt();
+ iCount = kdfParams.getIterationCount();
+ keysize = kdfParams.getKeyLength();
- // prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
- String kdfAlgo;
- var prfDer = pBKDF2_params.data.getOptional(DerValue.tag_Sequence);
- if (prfDer.isPresent()) {
- DerValue prf = prfDer.get();
- kdfAlgo_OID = prf.data.getOID();
- KnownOIDs o = KnownOIDs.findMatch(kdfAlgo_OID.toString());
- if (o == null || (!o.stdName().equals("HmacSHA1") &&
- !o.stdName().equals("HmacSHA224") &&
- !o.stdName().equals("HmacSHA256") &&
- !o.stdName().equals("HmacSHA384") &&
- !o.stdName().equals("HmacSHA512") &&
- !o.stdName().equals("HmacSHA512/224") &&
- !o.stdName().equals("HmacSHA512/256"))) {
- throw new IOException("PBE parameter parsing error: "
- + "expecting the object identifier for a HmacSHA key "
- + "derivation function");
- }
- kdfAlgo = o.stdName();
- prf.data.getOptional(DerValue.tag_Null);
- prf.data.atEnd();
- } else {
- kdfAlgo = "HmacSHA1";
- }
- return kdfAlgo;
+ String cipherAlgo = parseES(pBES2_params.data.getDerValue());
+
+ this.pbes2AlgorithmName = "PBEWith" + kdfAlgo + "And" + cipherAlgo;
}
private String parseES(DerValue encryptionScheme) throws IOException {
@@ -345,26 +301,9 @@ protected byte[] engineGetEncoded() throws IOException {
DerOutputStream pBES2_params = new DerOutputStream();
- DerOutputStream keyDerivationFunc = new DerOutputStream();
- keyDerivationFunc.putOID(pkcs5PBKDF2_OID);
-
- DerOutputStream pBKDF2_params = new DerOutputStream();
- pBKDF2_params.putOctetString(salt); // choice: 'specified OCTET STRING'
- pBKDF2_params.putInteger(iCount);
-
- if (keysize > 0) {
- pBKDF2_params.putInteger(keysize / 8); // derived key length (in octets)
- }
-
- DerOutputStream prf = new DerOutputStream();
- // algorithm is id-hmacWith
- prf.putOID(kdfAlgo_OID);
- // parameters is 'NULL'
- prf.putNull();
- pBKDF2_params.write(DerValue.tag_Sequence, prf);
-
- keyDerivationFunc.write(DerValue.tag_Sequence, pBKDF2_params);
- pBES2_params.write(DerValue.tag_Sequence, keyDerivationFunc);
+ // keysize encoded as octets
+ pBES2_params.writeBytes(PBKDF2Parameters.encode(salt, iCount,
+ keysize/8, kdfAlgo_OID));
DerOutputStream encryptionScheme = new DerOutputStream();
// algorithm is id-aes128-CBC or id-aes256-CBC
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java
index 6a0ecb6d462a..9f3e041eebc9 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java
@@ -55,7 +55,7 @@
* @author Valerie Peng
*
*/
-final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
+public final class PBKDF2KeyImpl implements javax.crypto.interfaces.PBEKey {
@java.io.Serial
private static final long serialVersionUID = -2234868909660948157L;
diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java
index c83f197380a3..ef2e5e0d0c4f 100644
--- a/src/java.base/share/classes/java/lang/ref/Reference.java
+++ b/src/java.base/share/classes/java/lang/ref/Reference.java
@@ -357,11 +357,18 @@ public void runFinalization() {
* {@code null} if this reference object has been cleared
* @see #refersTo
*/
- @IntrinsicCandidate
public T get() {
- return this.referent;
+ return get0();
}
+ /* Implementation of get(). This method exists to avoid making get() all
+ * of virtual, native, and intrinsic candidate. That could have the
+ * undesirable effect of having the native method used instead of the
+ * intrinsic when devirtualization fails.
+ */
+ @IntrinsicCandidate
+ private native T get0();
+
/**
* Tests if the referent of this reference object is {@code obj}.
* Using a {@code null} {@code obj} returns {@code true} if the
diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java
index 6969fe8a8e14..30a22b05742d 100644
--- a/src/java.base/share/classes/java/security/Security.java
+++ b/src/java.base/share/classes/java/security/Security.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,7 @@
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
@@ -112,7 +113,7 @@ private enum LoadingMode {OVERRIDE, APPEND}
private static Path currentPath;
- private static final Set activePaths = new HashSet<>();
+ private static final List activePaths = new ArrayList<>();
static void loadAll() {
// first load the master properties file to
@@ -262,30 +263,40 @@ static void loadInclude(String propFile) {
}
}
+ private static void checkCyclicInclude(Path path) {
+ for (Path activePath : activePaths) {
+ try {
+ if (Files.isSameFile(path, activePath)) {
+ throw new InternalError(
+ "Cyclic include of '" + path + "'");
+ }
+ } catch (IOException e) {
+ if (sdebug != null) {
+ sdebug.println("skipped exception when checking for " +
+ "cyclic inclusion of " + path + ":");
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
private static void loadFromPath(Path path, LoadingMode mode)
throws IOException {
- boolean isRegularFile = Files.isRegularFile(path);
- if (isRegularFile) {
- path = path.toRealPath();
- } else if (Files.isDirectory(path)) {
+ if (Files.isDirectory(path)) {
throw new IOException("Is a directory");
- } else {
- path = path.toAbsolutePath();
- }
- if (activePaths.contains(path)) {
- throw new InternalError("Cyclic include of '" + path + "'");
}
try (InputStream is = Files.newInputStream(path)) {
+ checkCyclicInclude(path);
reset(mode);
Path previousPath = currentPath;
- currentPath = isRegularFile ? path : null;
+ currentPath = Files.isRegularFile(path) ? path : null;
activePaths.add(path);
try {
debugLoad(true, path);
props.load(is);
debugLoad(false, path);
} finally {
- activePaths.remove(path);
+ activePaths.removeLast();
currentPath = previousPath;
}
}
diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java
index 173821818761..e28fdecde1b0 100644
--- a/src/java.base/share/classes/java/util/zip/ZipFile.java
+++ b/src/java.base/share/classes/java/util/zip/ZipFile.java
@@ -1706,8 +1706,10 @@ private void initCEN(final int knownTotal, final ZipCoder zipCoder) throws IOExc
this.cen = null;
return; // only END header present
}
- if (end.cenlen > end.endpos)
+ // Validate END header
+ if (end.cenlen > end.endpos) {
zerror("invalid END header (bad central directory size)");
+ }
long cenpos = end.endpos - end.cenlen; // position of CEN table
// Get position of first local file (LOC) header, taking into
// account that there may be a stub prefixed to the ZIP file.
@@ -1715,18 +1717,22 @@ private void initCEN(final int knownTotal, final ZipCoder zipCoder) throws IOExc
if (locpos < 0) {
zerror("invalid END header (bad central directory offset)");
}
- // read in the CEN
if (end.cenlen > MAX_CEN_SIZE) {
zerror("invalid END header (central directory size too large)");
}
if (end.centot < 0 || end.centot > end.cenlen / CENHDR) {
zerror("invalid END header (total entries count too large)");
}
- cen = this.cen = new byte[(int)end.cenlen];
- if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen) {
+ // Validation ensures these are <= Integer.MAX_VALUE
+ int cenlen = Math.toIntExact(end.cenlen);
+ int centot = Math.toIntExact(end.centot);
+
+ // read in the CEN
+ cen = this.cen = new byte[cenlen];
+ if (readFullyAt(cen, 0, cen.length, cenpos) != cenlen) {
zerror("read CEN tables failed");
}
- this.total = Math.toIntExact(end.centot);
+ this.total = centot;
} else {
cen = this.cen;
this.total = knownTotal;
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java b/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java
index f567d7bd643d..9c9766e2ce2d 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
package sun.net.www.protocol.http;
+import java.io.IOException;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.HashMap;
@@ -428,9 +429,10 @@ public String getHeaderName() {
* @param conn The connection to apply the header(s) to
* @param p A source of header values for this connection, if needed.
* @param raw The raw header field (if needed)
- * @return true if all goes well, false if no headers were set.
+ * @throws IOException if no headers were set
*/
- public abstract boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw);
+ public abstract void setHeaders(HttpURLConnection conn, HeaderParser p, String raw)
+ throws IOException;
/**
* Check if the header indicates that the current auth. parameters are stale.
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/BasicAuthentication.java b/src/java.base/share/classes/sun/net/www/protocol/http/BasicAuthentication.java
index f008c185b5d6..aa2a9625a01e 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/BasicAuthentication.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/BasicAuthentication.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -125,15 +125,13 @@ public boolean supportsPreemptiveAuthorization() {
* @param conn The connection to apply the header(s) to
* @param p A source of header values for this connection, if needed.
* @param raw The raw header values for this connection, if needed.
- * @return true if all goes well, false if no headers were set.
*/
@Override
- public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
+ public void setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
// no need to synchronize here:
// already locked by s.n.w.p.h.HttpURLConnection
assert conn.isLockHeldByCurrentThread();
conn.setAuthenticationProperty(getHeaderName(), getHeaderValue(null,null));
- return true;
}
/**
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/DigestAuthentication.java b/src/java.base/share/classes/sun/net/www/protocol/http/DigestAuthentication.java
index 28d7bc5cf4e0..87ea7c17085f 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/DigestAuthentication.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/DigestAuthentication.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -321,7 +321,11 @@ public boolean supportsPreemptiveAuthorization() {
*/
@Override
public String getHeaderValue(URL url, String method) {
- return getHeaderValueImpl(url.getFile(), method);
+ try {
+ return getHeaderValueImpl(url.getFile(), method);
+ } catch (IOException _) {
+ return null;
+ }
}
/**
@@ -339,7 +343,11 @@ public String getHeaderValue(URL url, String method) {
* @return the value of the HTTP header this authentication wants set
*/
String getHeaderValue(String requestURI, String method) {
- return getHeaderValueImpl(requestURI, method);
+ try {
+ return getHeaderValueImpl(requestURI, method);
+ } catch (IOException _) {
+ return null;
+ }
}
/**
@@ -369,10 +377,11 @@ public boolean isAuthorizationStale (String header) {
* @param conn The connection to apply the header(s) to
* @param p A source of header values for this connection, if needed.
* @param raw Raw header values for this connection, if needed.
- * @return true if all goes well, false if no headers were set.
+ * @throws IOException if no headers were set
*/
@Override
- public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
+ public void setHeaders(HttpURLConnection conn, HeaderParser p, String raw)
+ throws IOException {
// no need to synchronize here:
// already locked by s.n.w.p.h.HttpURLConnection
assert conn.isLockHeldByCurrentThread();
@@ -380,14 +389,14 @@ public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
params.setNonce (p.findValue("nonce"));
params.setOpaque (p.findValue("opaque"));
params.setQop (p.findValue("qop"));
- params.setUserhash (Boolean.valueOf(p.findValue("userhash")));
+ params.setUserhash (Boolean.parseBoolean(p.findValue("userhash")));
String charset = p.findValue("charset");
if (charset == null) {
charset = "ISO_8859_1";
} else if (!charset.equalsIgnoreCase("UTF-8")) {
// UTF-8 is only valid value. ISO_8859_1 represents default behavior
// when the parameter is not set.
- return false;
+ throw new IOException("Illegal charset in header");
}
params.setCharset(charset.toUpperCase(Locale.ROOT));
@@ -405,7 +414,7 @@ public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
}
if (params.nonce == null || authMethod == null || pw == null || realm == null) {
- return false;
+ throw new IOException("Server challenge incomplete");
}
if (authMethod.length() >= 1) {
// Method seems to get converted to all lower case elsewhere.
@@ -415,8 +424,7 @@ public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
+ authMethod.substring(1).toLowerCase(Locale.ROOT);
}
- if (!setAlgorithmNames(p, params))
- return false;
+ setAlgorithmNames(p, params);
// If authQop is true, then the server is doing RFC2617 and
// has offered qop=auth. We do not support any other modes
@@ -426,20 +434,17 @@ public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
params.setNewCnonce();
}
- String value = getHeaderValueImpl (uri, method);
- if (value != null) {
- conn.setAuthenticationProperty(getHeaderName(), value);
- return true;
- } else {
- return false;
- }
+ String value = getHeaderValueImpl(uri, method);
+ assert value != null;
+ conn.setAuthenticationProperty(getHeaderName(), value);
}
// Algorithm name is stored in two separate fields (of Paramaeters)
// This allows for variations in digest algorithm name (aliases)
// and also allow for the -sess variant defined in HTTP Digest protocol
- // returns false if algorithm not supported
- private static boolean setAlgorithmNames(HeaderParser p, Parameters params) {
+ // throws IOException if algorithm not supported
+ private static void setAlgorithmNames(HeaderParser p, Parameters params)
+ throws IOException {
String algorithm = p.findValue("algorithm");
String digestName = algorithm;
if (algorithm == null || algorithm.isEmpty()) {
@@ -459,18 +464,17 @@ private static boolean setAlgorithmNames(HeaderParser p, Parameters params) {
var oid = KnownOIDs.findMatch(digestName);
if (oid == null) {
log("unknown algorithm: " + algorithm);
- return false;
+ throw new IOException("Unknown algorithm: " + algorithm);
}
digestName = oid.stdName();
params.setAlgorithm (algorithm);
params.setDigestName (digestName);
- return true;
}
/* Calculate the Authorization header field given the request URI
* and based on the authorization information in params
*/
- private String getHeaderValueImpl (String uri, String method) {
+ private String getHeaderValueImpl (String uri, String method) throws IOException {
String response;
char[] passwd = pw.getPassword();
boolean qop = params.authQop();
@@ -479,11 +483,7 @@ private String getHeaderValueImpl (String uri, String method) {
String nonce = params.getNonce ();
String algorithm = params.getAlgorithm ();
String digest = params.getDigestName ();
- try {
- validateDigest(digest);
- } catch (IOException e) {
- return null;
- }
+ validateDigest(digest);
Charset charset = params.getCharset();
boolean userhash = params.getUserhash ();
params.incrementNC ();
@@ -505,7 +505,7 @@ private String getHeaderValueImpl (String uri, String method) {
digest, session, charset);
} catch (CharacterCodingException | NoSuchAlgorithmException ex) {
log(ex.getMessage());
- return null;
+ throw new IOException("Failed to compute digest", ex);
}
String ncfield = "\"";
@@ -534,7 +534,7 @@ private String getHeaderValueImpl (String uri, String method) {
}
} catch (CharacterCodingException | NoSuchAlgorithmException ex) {
log(ex.getMessage());
- return null;
+ throw new IOException("Failed to compute user hash", ex);
}
String value = authMethod
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 2905003d5cba..d19d30504bc5 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
@@ -61,6 +61,7 @@
import java.util.StringJoiner;
import jdk.internal.access.JavaNetHttpCookieAccess;
import jdk.internal.access.SharedSecrets;
+import jdk.internal.util.Exceptions;
import sun.net.NetProperties;
import sun.net.NetworkClient;
import sun.net.util.IPAddressUtil;
@@ -1464,16 +1465,29 @@ private InputStream getInputStream0() throws IOException {
/* in this case, only one header field will be present */
String raw = responses.findValue ("Proxy-Authenticate");
reset ();
- if (!proxyAuthentication.setHeaders(this,
- authhdr.headerParser(), raw)) {
+ try {
+ proxyAuthentication.setHeaders(this,
+ authhdr.headerParser(), raw);
+ } catch (IOException ex) {
disconnectInternal();
- throw new IOException ("Authentication failure");
+ if (Exceptions.enhancedNonSocketExceptions()) {
+ throw new IOException ("Authentication failure", ex);
+ } else {
+ throw new IOException ("Authentication failure");
+ }
}
- if (serverAuthentication != null && srvHdr != null &&
- !serverAuthentication.setHeaders(this,
- srvHdr.headerParser(), raw)) {
- disconnectInternal ();
- throw new IOException ("Authentication failure");
+ if (serverAuthentication != null && srvHdr != null) {
+ try {
+ serverAuthentication.setHeaders(this,
+ srvHdr.headerParser(), raw);
+ } catch (IOException ex) {
+ disconnectInternal();
+ if (Exceptions.enhancedNonSocketExceptions()) {
+ throw new IOException ("Authentication failure", ex);
+ } else {
+ throw new IOException ("Authentication failure");
+ }
+ }
}
authObj = null;
doingNTLMp2ndStage = false;
@@ -1552,9 +1566,15 @@ private InputStream getInputStream0() throws IOException {
} else {
reset ();
/* header not used for ntlm */
- if (!serverAuthentication.setHeaders(this, null, raw)) {
+ try {
+ serverAuthentication.setHeaders(this, null, raw);
+ } catch (IOException ex) {
disconnectWeb();
- throw new IOException ("Authentication failure");
+ if (Exceptions.enhancedNonSocketExceptions()) {
+ throw new IOException ("Authentication failure", ex);
+ } else {
+ throw new IOException ("Authentication failure");
+ }
}
doingNTLM2ndStage = false;
authObj = null;
@@ -1944,10 +1964,16 @@ private void doTunneling0() throws IOException {
} else {
String raw = responses.findValue ("Proxy-Authenticate");
reset ();
- if (!proxyAuthentication.setHeaders(this,
- authhdr.headerParser(), raw)) {
+ try {
+ proxyAuthentication.setHeaders(this,
+ authhdr.headerParser(), raw);
+ } catch (IOException ex) {
disconnectInternal();
- throw new IOException ("Authentication failure");
+ if (Exceptions.enhancedNonSocketExceptions()) {
+ throw new IOException ("Authentication failure", ex);
+ } else {
+ throw new IOException ("Authentication failure");
+ }
}
authObj = null;
doingNTLMp2ndStage = false;
@@ -2210,7 +2236,9 @@ yield new DigestAuthentication(true, host, port, realm,
};
}
if (ret != null) {
- if (!ret.setHeaders(this, p, raw)) {
+ try {
+ ret.setHeaders(this, p, raw);
+ } catch (IOException e) {
ret.disposeContext();
ret = null;
}
@@ -2367,7 +2395,9 @@ private AuthenticationInfo getServerAuthentication(AuthenticationHeader authhdr)
}
}
if (ret != null ) {
- if (!ret.setHeaders(this, p, raw)) {
+ try {
+ ret.setHeaders(this, p, raw);
+ } catch (IOException e) {
ret.disposeContext();
ret = null;
}
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
index c27d866f5eff..c016b0dae298 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -168,29 +168,24 @@ public boolean isAuthorizationStale (String header) {
* @param p A source of header values for this connection, not used because
* HeaderParser converts the fields to lower case, use raw instead
* @param raw The raw header field.
- * @return true if all goes well, false if no headers were set.
+ * @throws IOException if no headers were set
*/
@Override
- public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) {
+ public void setHeaders(HttpURLConnection conn, HeaderParser p, String raw) throws IOException {
// no need to synchronize here:
// already locked by s.n.w.p.h.HttpURLConnection
assert conn.isLockHeldByCurrentThread();
- try {
- String response;
- byte[] incoming = null;
- String[] parts = raw.split("\\s+");
- if (parts.length > 1) {
- incoming = Base64.getDecoder().decode(parts[1]);
- }
- response = hci.scheme + " " + Base64.getEncoder().encodeToString(
- incoming==null?firstToken():nextToken(incoming));
-
- conn.setAuthenticationProperty(getHeaderName(), response);
- return true;
- } catch (IOException e) {
- return false;
+ String response;
+ byte[] incoming = null;
+ String[] parts = raw.split("\\s+");
+ if (parts.length > 1) {
+ incoming = Base64.getDecoder().decode(parts[1]);
}
+ response = hci.scheme + " " + Base64.getEncoder().encodeToString(
+ incoming==null?firstToken():nextToken(incoming));
+
+ conn.setAuthenticationProperty(getHeaderName(), response);
}
/**
diff --git a/src/java.base/share/classes/sun/security/pkcs12/MacData.java b/src/java.base/share/classes/sun/security/pkcs12/MacData.java
index 9a712f28ccc5..d45b50ad7048 100644
--- a/src/java.base/share/classes/sun/security/pkcs12/MacData.java
+++ b/src/java.base/share/classes/sun/security/pkcs12/MacData.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,32 +25,58 @@
package sun.security.pkcs12;
-import java.io.*;
+import java.io.IOException;
import java.security.*;
+import java.security.spec.InvalidKeySpecException;
+import static java.util.Locale.ENGLISH;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
-import sun.security.util.DerInputStream;
-import sun.security.util.DerOutputStream;
-import sun.security.util.DerValue;
-import sun.security.x509.AlgorithmId;
import sun.security.pkcs.ParsingException;
+import sun.security.util.*;
+import sun.security.x509.AlgorithmId;
/**
- * A MacData type, as defined in PKCS#12.
+ * The MacData type, as defined in PKCS#12.
+ *
+ * The ASN.1 definition is as follows:
+ *
+ *
+ *
+ * MacData ::= SEQUENCE {
+ * mac DigestInfo,
+ * macSalt OCTET STRING,
+ * iterations INTEGER DEFAULT 1
+ * -- Note: The default is for historical reasons and its use is
+ * -- deprecated.
+ * }
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithmIdentifier,
+ * digest OCTET STRING
+ * }
+ *
+ *
*
* @author Sharon Liu
*/
class MacData {
- private final String digestAlgorithmName;
- private AlgorithmParameters digestAlgorithmParams;
+ private static final Debug debug = Debug.getInstance("pkcs12");
+ private final String macAlgorithm;
private final byte[] digest;
private final byte[] macSalt;
private final int iterations;
- // the ASN.1 encoded contents of this class
- private byte[] encoded = null;
+ // The following three fields are for PBMAC1.
+ private final int keyLength;
+ private final String kdfHmac;
+ private final String hmac;
/**
* Parses a PKCS#12 MAC data.
@@ -70,103 +96,276 @@ class MacData {
// Parse the DigestAlgorithmIdentifier.
AlgorithmId digestAlgorithmId = AlgorithmId.parse(digestInfo[0]);
- this.digestAlgorithmName = digestAlgorithmId.getName();
- this.digestAlgorithmParams = digestAlgorithmId.getParameters();
+ String digestAlgorithmName = digestAlgorithmId.getName();
+
// Get the digest.
this.digest = digestInfo[1].getOctetString();
- // Get the salt.
- this.macSalt = macData[1].getOctetString();
+ if (digestAlgorithmName.equals("PBMAC1")) {
+ PBMAC1Parameters algParams;
+
+ algParams = new PBMAC1Parameters(digestAlgorithmId
+ .getEncodedParams());
+
+ this.iterations = algParams.getKdfParams().getIterationCount();
+ this.macSalt = algParams.getKdfParams().getSalt();
+ this.kdfHmac = algParams.getKdfParams().getPrfAlgo();
+ this.keyLength = algParams.getKdfParams().getKeyLength();
- // Iterations is optional. The default value is 1.
- if (macData.length > 2) {
- this.iterations = macData[2].getInteger();
+ // Implementations MUST NOT accept params that omit keyLength.
+ if (this.keyLength == -1) {
+ throw new IOException("error: missing keyLength field");
+ }
+ this.hmac = algParams.getHmac();
+ this.macAlgorithm = "pbewith" + this.kdfHmac + "and" + this.hmac;
} else {
- this.iterations = 1;
+ this.kdfHmac = null;
+ this.hmac = null;
+ this.keyLength = -1;
+ this.macSalt = macData[1].getOctetString();
+ if (macData.length > 2) {
+ this.iterations = macData[2].getInteger();
+ } else {
+ this.iterations = 1;
+ }
+ // Remove "-" from digest algorithm names
+ this.macAlgorithm = "hmacpbe"
+ + digestAlgorithmName.replace("-", "");
}
}
- MacData(String algName, byte[] digest, byte[] salt, int iterations)
- throws NoSuchAlgorithmException
- {
- if (algName == null)
- throw new NullPointerException("the algName parameter " +
- "must be non-null");
-
- AlgorithmId algid = AlgorithmId.get(algName);
- this.digestAlgorithmName = algid.getName();
- this.digestAlgorithmParams = algid.getParameters();
-
- if (digest == null) {
- throw new NullPointerException("the digest " +
- "parameter must be non-null");
- } else if (digest.length == 0) {
- throw new IllegalArgumentException("the digest " +
- "parameter must not be empty");
+ /**
+ * Computes a MAC on the data.
+ *
+ * This is a two-step process: first generate a key and then use the
+ * key to generate the MAC. PBMAC1 and non-PBMAC1 keys use different
+ * key factories. PBMAC1 uses a pseudorandom function (kdfHmac)
+ * to generate keys while non-PBMAC1 does not. The MAC is computed
+ * according to the specified hmac algorithm.
+ *
+ * @param macAlgorithm the algorithm used to compute the MAC
+ * @param password the password used to generate the key
+ * @param params a PBEParameterSpec object
+ * @param data the data on which the MAC is computed
+ * @param kdfHmac the pseudorandom function used to compute the key
+ * for PBMAC1
+ * @param hmac the algorithm used to compute the MAC
+ * @param keyLength the length of the key generated by the pseudorandom
+ * function
+ *
+ * @return the computed MAC as a byte array
+ *
+ * @exception NoSuchAlgorithmException if either kdfHmac or hmac is
+ * unknown to the Mac or SecretKeyFactory
+ */
+ private static byte[] calculateMac(String macAlgorithm, char[] password,
+ PBEParameterSpec params, byte[] data,
+ String kdfHmac, String hmac, int keyLength)
+ throws InvalidAlgorithmParameterException, InvalidKeyException,
+ InvalidKeySpecException, NoSuchAlgorithmException {
+ SecretKeyFactory skf;
+ SecretKey pbeKey = null;
+ Mac m;
+
+ PBEKeySpec keySpec;
+
+ /*
+ * The Hmac has to be extracted from the algorithm name for
+ * PBMAC1 algorithms. For non-PBMAC1 macAlgorithms, the name
+ * and Hmac are the same.
+ *
+ * The prefix used in Algorithm names is guaranteed to be lowercase.
+ */
+ if (macAlgorithm.startsWith("pbewith")) {
+ m = Mac.getInstance(hmac);
+ int len = keyLength == -1 ? m.getMacLength()*8 : keyLength;
+ skf = SecretKeyFactory.getInstance("PBKDF2With" +kdfHmac);
+ keySpec = new PBEKeySpec(password, params.getSalt(),
+ params.getIterationCount(), len);
} else {
- this.digest = digest.clone();
+ m = Mac.getInstance(macAlgorithm);
+ skf = SecretKeyFactory.getInstance("PBE");
+ keySpec = new PBEKeySpec(password);
}
- this.macSalt = salt;
- this.iterations = iterations;
+ try {
+ pbeKey = skf.generateSecret(keySpec);
+ if (macAlgorithm.startsWith("pbewith")) {
+ m.init(pbeKey);
+ } else {
+ m.init(pbeKey, params);
+ }
+ m.update(data);
+ return m.doFinal();
+ } finally {
+ keySpec.clearPassword();
+ KeyUtil.destroySecretKeys(pbeKey);
+ }
+ }
- // delay the generation of ASN.1 encoding until
- // getEncoded() is called
- this.encoded = null;
+ /**
+ * Verify Mac on the data.
+ *
+ * Calculate Mac on the data and compare with Mac found in input stream.
+ *
+ * @param password the password used to generate the key
+ * @param data the data on which the MAC is computed
+ *
+ * @exception UnrecoverableKeyException if calculated Mac and
+ * Mac found in input stream are different
+ */
+ void verifyMac(char[] password, byte[] data)
+ throws InvalidAlgorithmParameterException, InvalidKeyException,
+ InvalidKeySpecException, NoSuchAlgorithmException,
+ UnrecoverableKeyException {
+
+ byte[] macResult = calculateMac(this.macAlgorithm, password,
+ new PBEParameterSpec(this.macSalt, this.iterations),
+ data, this.kdfHmac, this.hmac, this.keyLength);
+
+ if (debug != null) {
+ debug.println("Checking keystore integrity " +
+ "(" + this.macAlgorithm + " iterations: "
+ + this.iterations + ")");
+ }
+ if (!MessageDigest.isEqual(this.digest, macResult)) {
+ throw new UnrecoverableKeyException("Failed PKCS12" +
+ " integrity checking");
+ }
}
- String getDigestAlgName() {
- return digestAlgorithmName;
- }
+ /*
+ * Gathers parameters and generates a MAC of the data
+ *
+ * @param password the password used to generate the key
+ * @param data the data on which the MAC is computed
+ * @param macAlgorithm the algorithm used to compute the MAC
+ * @param macIterationCount the iteration count
+ * @param salt the salt
+ *
+ * @exception IOException if the MAC cannot be calculated
+ *
+ * @return the computed MAC as a byte array
+ */
+ static byte[] generateMac(char[] passwd, byte[] data,
+ String macAlgorithm, int macIterationCount, byte[] salt)
+ throws IOException, NoSuchAlgorithmException {
+ final PBEParameterSpec params;
+ String algName;
+ String kdfHmac;
+ String hmac;
+
+ macAlgorithm = macAlgorithm.toLowerCase(ENGLISH);
+ // The prefix used in Algorithm names is guaranteed to be lowercase.
+ if (macAlgorithm.startsWith("pbewith")) {
+ algName = "PBMAC1";
+ kdfHmac = MacData.parseKdfHmac(macAlgorithm);
+ hmac = MacData.parseHmac(macAlgorithm);
+ if (hmac == null) {
+ hmac = kdfHmac;
+ }
+ } else if (macAlgorithm.startsWith("hmacpbe")) {
+ algName = macAlgorithm.substring(7);
+ kdfHmac = null;
+ hmac = macAlgorithm;
+ } else {
+ throw new ParsingException("unexpected algorithm '"
+ + macAlgorithm + "'");
+ }
+
+ params = new PBEParameterSpec(salt, macIterationCount);
+
+ try {
+ byte[] macResult = calculateMac(macAlgorithm, passwd, params, data,
+ kdfHmac, hmac, -1);
- byte[] getSalt() {
- return macSalt;
+ DerOutputStream bytes = new DerOutputStream();
+ bytes.write(encode(algName, macResult, params, kdfHmac, hmac,
+ macResult.length));
+ return bytes.toByteArray();
+ } catch (InvalidKeySpecException | InvalidKeyException |
+ InvalidAlgorithmParameterException e) {
+ throw new IOException("calculateMac failed: " + e, e);
+ }
}
- int getIterations() {
- return iterations;
+ String getMacAlgorithm() {
+ return this.macAlgorithm;
}
- byte[] getDigest() {
- return digest;
+ int getIterations() {
+ return this.iterations;
}
/**
- * Returns the ASN.1 encoding of this object.
- * @return the ASN.1 encoding.
- * @exception IOException if error occurs when constructing its
+ * Returns the ASN.1 encoding.
+ * @return the ASN.1 encoding
+ * @exception NoSuchAlgorithmException if error occurs when constructing its
* ASN.1 encoding.
*/
- public byte[] getEncoded() throws NoSuchAlgorithmException
- {
- if (this.encoded != null)
- return this.encoded.clone();
+ static byte[] encode(String algName, byte[] digest, PBEParameterSpec p,
+ String kdfHmac, String hmac, int keyLength)
+ throws IOException, NoSuchAlgorithmException {
+
+ final int iterations = p.getIterationCount();
+ final byte[] macSalt = p.getSalt();
- DerOutputStream out = new DerOutputStream();
DerOutputStream tmp = new DerOutputStream();
+ DerOutputStream out = new DerOutputStream();
- DerOutputStream tmp2 = new DerOutputStream();
- // encode encryption algorithm
- AlgorithmId algid = AlgorithmId.get(digestAlgorithmName);
- algid.encode(tmp2);
+ if (algName.equals("PBMAC1")) {
+ DerOutputStream tmp1 = new DerOutputStream();
+ DerOutputStream tmp2 = new DerOutputStream();
- // encode digest data
- tmp2.putOctetString(digest);
+ // id-PBMAC1 OBJECT IDENTIFIER ::= { pkcs-5 14 }
+ tmp2.putOID(ObjectIdentifier.of(KnownOIDs.PBMAC1));
+ tmp2.writeBytes(PBMAC1Parameters.encode(macSalt, iterations,
+ keyLength, kdfHmac, hmac));
- tmp.write(DerValue.tag_Sequence, tmp2);
+ tmp1.write(DerValue.tag_Sequence, tmp2);
+ tmp1.putOctetString(digest);
- // encode salt
- tmp.putOctetString(macSalt);
+ tmp.write(DerValue.tag_Sequence, tmp1);
+ tmp.putOctetString(
+ new byte[]{ 'N', 'O', 'T', ' ', 'U', 'S', 'E', 'D' });
+ // Unused, but must have non-zero positive value.
+ tmp.putInteger(1);
+ } else {
+ final AlgorithmId digestAlgorithm = AlgorithmId.get(algName);
+ DerOutputStream tmp2 = new DerOutputStream();
- // encode iterations
- tmp.putInteger(iterations);
+ tmp2.write(digestAlgorithm);
+ tmp2.putOctetString(digest);
+ // wrap into a SEQUENCE
+ tmp.write(DerValue.tag_Sequence, tmp2);
+ tmp.putOctetString(macSalt);
+ tmp.putInteger(iterations);
+ }
// wrap everything into a SEQUENCE
out.write(DerValue.tag_Sequence, tmp);
- this.encoded = out.toByteArray();
+ return out.toByteArray();
+ }
- return this.encoded.clone();
+ private static String parseKdfHmac(String text) {
+ int index1 = text.indexOf("with") + 4;
+ int index2 = text.indexOf("and");
+ if (index1 == 3) { // -1 + 4
+ return null;
+ } else if (index2 == -1) {
+ return text.substring(index1);
+ } else {
+ return text.substring(index1, index2);
+ }
}
+ private static String parseHmac(String text) {
+ int index1 = text.indexOf("and") + 3;
+ if (index1 == 2) { // -1 + 3
+ return null;
+ } else {
+ return text.substring(index1);
+ }
+ }
}
diff --git a/src/java.base/share/classes/sun/security/pkcs12/PBMAC1Parameters.java b/src/java.base/share/classes/sun/security/pkcs12/PBMAC1Parameters.java
new file mode 100644
index 000000000000..2c3c6fa81717
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/pkcs12/PBMAC1Parameters.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.pkcs12;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+
+import sun.security.util.*;
+import sun.security.x509.AlgorithmId;
+
+/**
+ * This class implements the parameter set used with password-based
+ * mac scheme 1 (PBMAC1), which is defined in PKCS#5 as follows:
+ *
+ *