diff --git a/.editorconfig b/.editorconfig index 0e6c17e76740d..98f07b3c5fbbd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,3 +5,7 @@ trim_trailing_whitespace = true [Makefile] trim_trailing_whitespace = true + +[src/hotspot/**.{cpp,hpp,h}] +indent_style = space +indent_size = 2 diff --git a/doc/building.html b/doc/building.html index 1e6f99e97c93d..722fe6b8d1256 100644 --- a/doc/building.html +++ b/doc/building.html @@ -376,7 +376,7 @@

Operating System macOS -macOS 13.x (Ventura) +macOS 14.x Windows @@ -513,8 +513,8 @@

macOS

a continuously updated machine running macOS. See the section on Apple Xcode on some strategies to deal with this.

-

It is recommended that you use at least macOS 13 (Ventura) and Xcode -14, but earlier versions may also work.

+

It is recommended that you use at least macOS 14 and Xcode 15.4, but +earlier versions may also work.

The standard macOS environment contains the basic tooling needed to build, but for external libraries a package manager is recommended. The JDK uses homebrew in the examples, but @@ -590,11 +590,11 @@

Native Compiler macOS -Apple Xcode 14.3.1 (using clang 14.0.3) +Apple Xcode 15.4 (using clang 15.0.0) Windows -Microsoft Visual Studio 2022 version 17.6.5 +Microsoft Visual Studio 2022 version 17.13.2 diff --git a/doc/building.md b/doc/building.md index 18e030baa9e29..56bc860a13d97 100644 --- a/doc/building.md +++ b/doc/building.md @@ -175,7 +175,7 @@ time of writing. | ----------------- | ---------------------------------- | | Linux/x64 | Oracle Enterprise Linux 6.4 / 8.x | | Linux/aarch64 | Oracle Enterprise Linux 7.6 / 8.x | -| macOS | macOS 13.x (Ventura) | +| macOS | macOS 14.x | | Windows | Windows Server 2016 | The double version numbers for Linux are due to the hybrid model used at @@ -327,7 +327,7 @@ difficult for a project such as the JDK to keep pace with a continuously updated machine running macOS. See the section on [Apple Xcode](#apple-xcode) on some strategies to deal with this. -It is recommended that you use at least macOS 13 (Ventura) and Xcode 14, but +It is recommended that you use at least macOS 14 and Xcode 15.4, but earlier versions may also work. The standard macOS environment contains the basic tooling needed to build, but @@ -390,11 +390,11 @@ possible to compile the JDK with both older and newer versions, but the closer you stay to this list, the more likely you are to compile successfully without issues. -| Operating system | Toolchain version | -| ------------------ | ------------------------------------------- | -| Linux | gcc 14.2.0 | -| macOS | Apple Xcode 14.3.1 (using clang 14.0.3) | -| Windows | Microsoft Visual Studio 2022 version 17.6.5 | +| Operating system | Toolchain version | +| ------------------ | -------------------------------------------- | +| Linux | gcc 14.2.0 | +| macOS | Apple Xcode 15.4 (using clang 15.0.0) | +| Windows | Microsoft Visual Studio 2022 version 17.13.2 | All compilers are expected to be able to handle the C11 language standard for C, and C++14 for C++. diff --git a/make/Bundles.gmk b/make/Bundles.gmk index 8962b596278bd..ba8ec0c864b0e 100644 --- a/make/Bundles.gmk +++ b/make/Bundles.gmk @@ -174,9 +174,11 @@ else JRE_IMAGE_HOMEDIR := $(JRE_IMAGE_DIR) JDK_BUNDLE_SUBDIR := jdk-$(VERSION_NUMBER) JRE_BUNDLE_SUBDIR := jre-$(VERSION_NUMBER) + STATIC_JDK_BUNDLE_SUBDIR := static-jdk-$(VERSION_NUMBER) ifneq ($(DEBUG_LEVEL), release) JDK_BUNDLE_SUBDIR := $(JDK_BUNDLE_SUBDIR)/$(DEBUG_LEVEL) JRE_BUNDLE_SUBDIR := $(JRE_BUNDLE_SUBDIR)/$(DEBUG_LEVEL) + STATIC_JDK_BUNDLE_SUBDIR := $(STATIC_JDK_BUNDLE_SUBDIR)/$(DEBUG_LEVEL) endif # In certain situations, the JDK_IMAGE_DIR points to an image without the # the symbols and demos. If so, the symobls and demos can be found in a @@ -500,6 +502,21 @@ ifneq ($(filter static-libs-graal-bundles, $(MAKECMDGOALS)), ) STATIC_LIBS_GRAAL_TARGETS += $(BUILD_STATIC_LIBS_GRAAL_BUNDLE) endif +################################################################################# + +ifneq ($(filter static-jdk-bundles, $(MAKECMDGOALS)), ) + STATIC_JDK_BUNDLE_FILES := $(call FindFiles, $(STATIC_JDK_IMAGE_DIR)) + + $(eval $(call SetupBundleFile, BUILD_STATIC_JDK_BUNDLE, \ + BUNDLE_NAME := $(STATIC_JDK_BUNDLE_NAME), \ + FILES := $(STATIC_JDK_BUNDLE_FILES), \ + BASE_DIRS := $(STATIC_JDK_IMAGE_DIR), \ + SUBDIR := $(STATIC_JDK_BUNDLE_SUBDIR), \ + )) + + STATIC_JDK_TARGETS += $(BUILD_STATIC_JDK_BUNDLE) +endif + ################################################################################ product-bundles: $(PRODUCT_TARGETS) @@ -510,11 +527,12 @@ docs-javase-bundles: $(DOCS_JAVASE_TARGETS) docs-reference-bundles: $(DOCS_REFERENCE_TARGETS) static-libs-bundles: $(STATIC_LIBS_TARGETS) static-libs-graal-bundles: $(STATIC_LIBS_GRAAL_TARGETS) +static-jdk-bundles: $(STATIC_JDK_TARGETS) jcov-bundles: $(JCOV_TARGETS) .PHONY: product-bundles test-bundles \ docs-jdk-bundles docs-javase-bundles docs-reference-bundles \ - static-libs-bundles static-libs-graal-bundles jcov-bundles + static-libs-bundles static-libs-graal-bundles static-jdk-bundles jcov-bundles ################################################################################ diff --git a/make/Docs.gmk b/make/Docs.gmk index 60c029ce8f94a..965ae689e2185 100644 --- a/make/Docs.gmk +++ b/make/Docs.gmk @@ -98,7 +98,7 @@ JAVADOC_DISABLED_DOCLINT_PACKAGES := org.w3c.* javax.smartcardio JAVADOC_OPTIONS := -use -keywords -notimestamp \ -serialwarn -encoding ISO-8859-1 -docencoding UTF-8 -breakiterator \ -splitIndex --system none -javafx --expand-requires transitive \ - --override-methods=summary + --override-methods=summary --syntax-highlight # The reference options must stay stable to allow for comparisons across the # development cycle. diff --git a/make/Main.gmk b/make/Main.gmk index eda3b79265ad7..3535ad16aae3d 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -875,6 +875,12 @@ $(eval $(call SetupTarget, static-libs-graal-bundles, \ DEPS := static-libs-graal-image, \ )) +$(eval $(call SetupTarget, static-jdk-bundles, \ + MAKEFILE := Bundles, \ + TARGET := static-jdk-bundles, \ + DEPS := static-jdk-image, \ +)) + ifeq ($(JCOV_ENABLED), true) $(eval $(call SetupTarget, jcov-bundles, \ MAKEFILE := Bundles, \ diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 80c1ff99b2e40..69dc47a0c611b 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -929,6 +929,11 @@ define SetupRunJtregTestBody JTREG_AUTO_PROBLEM_LISTS += ProblemList-shenandoah.txt endif + ifneq ($$(findstring --enable-preview, $$(JTREG_ALL_OPTIONS)), ) + JTREG_AUTO_PROBLEM_LISTS += ProblemList-enable-preview.txt + endif + + ifneq ($$(JTREG_EXTRA_PROBLEM_LISTS), ) # Accept both absolute paths as well as relative to the current test root. $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 79e44dd4ad17e..f8a512f503d23 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -520,8 +520,21 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_UNDEFINED_BEHAVIOR_SANITIZER], # Silence them for now. UBSAN_CHECKS="-fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize=shift-base -fno-sanitize=alignment \ $ADDITIONAL_UBSAN_CHECKS" - UBSAN_CFLAGS="$UBSAN_CHECKS -Wno-stringop-truncation -Wno-format-overflow -Wno-array-bounds -Wno-stringop-overflow -fno-omit-frame-pointer -DUNDEFINED_BEHAVIOR_SANITIZER" + UBSAN_CFLAGS="$UBSAN_CHECKS -Wno-array-bounds -fno-omit-frame-pointer -DUNDEFINED_BEHAVIOR_SANITIZER" + if test "x$TOOLCHAIN_TYPE" = "xgcc"; then + UBSAN_CFLAGS="$UBSAN_CFLAGS -Wno-format-overflow -Wno-stringop-overflow -Wno-stringop-truncation" + fi UBSAN_LDFLAGS="$UBSAN_CHECKS" + # On AIX, the llvm_symbolizer is not found out of the box, so we have to provide the + # full qualified llvm_symbolizer path in the __ubsan_default_options() function in + # make/data/ubsan/ubsan_default_options.c. To get it there we compile our sources + # with an additional define LLVM_SYMBOLIZER, which we set here. + # To calculate the correct llvm_symbolizer path we can use the location of the compiler, because + # their relation is fixed. + if test "x$TOOLCHAIN_TYPE" = "xclang" && test "x$OPENJDK_TARGET_OS" = "xaix"; then + UBSAN_CFLAGS="$UBSAN_CFLAGS -fno-sanitize=function,vptr -DLLVM_SYMBOLIZER=$(dirname $(dirname $CC))/tools/ibm-llvm-symbolizer" + UBSAN_LDFLAGS="$UBSAN_LDFLAGS -fno-sanitize=function,vptr -Wl,-bbigtoc" + fi UTIL_ARG_ENABLE(NAME: ubsan, DEFAULT: false, RESULT: UBSAN_ENABLED, DESC: [enable UndefinedBehaviorSanitizer], CHECK_AVAILABLE: [ diff --git a/make/autoconf/spec.gmk.template b/make/autoconf/spec.gmk.template index 907a60290ecf9..ebaa487b40a18 100644 --- a/make/autoconf/spec.gmk.template +++ b/make/autoconf/spec.gmk.template @@ -846,10 +846,12 @@ SVE_CFLAGS := @SVE_CFLAGS@ JDK_IMAGE_SUBDIR := jdk JRE_IMAGE_SUBDIR := jre JCOV_IMAGE_SUBDIR := jdk-jcov +STATIC_JDK_IMAGE_SUBDIR := static-jdk # Colon left out to be able to override output dir for bootcycle-images JDK_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(JDK_IMAGE_SUBDIR) JRE_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(JRE_IMAGE_SUBDIR) +STATIC_JDK_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(STATIC_JDK_IMAGE_SUBDIR) JCOV_IMAGE_DIR = $(IMAGES_OUTPUTDIR)/$(JCOV_IMAGE_SUBDIR) # Test image, as above @@ -929,6 +931,7 @@ DOCS_JAVASE_BUNDLE_NAME := javase-$(BASE_NAME)_doc-api-spec$(DEBUG_PART).tar.gz DOCS_REFERENCE_BUNDLE_NAME := jdk-reference-$(BASE_NAME)_doc-api-spec$(DEBUG_PART).tar.gz STATIC_LIBS_BUNDLE_NAME := jdk-$(BASE_NAME)_bin-static-libs$(DEBUG_PART).tar.gz STATIC_LIBS_GRAAL_BUNDLE_NAME := jdk-$(BASE_NAME)_bin-static-libs-graal$(DEBUG_PART).tar.gz +STATIC_JDK_BUNDLE_NAME := static-jdk-$(BASE_NAME)_bin$(DEBUG_PART).$(JDK_BUNDLE_EXTENSION) JCOV_BUNDLE_NAME := jdk-jcov-$(BASE_NAME)_bin$(DEBUG_PART).$(JDK_BUNDLE_EXTENSION) JDK_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JDK_BUNDLE_NAME) @@ -939,6 +942,7 @@ TEST_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(TEST_BUNDLE_NAME) DOCS_JDK_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_JDK_BUNDLE_NAME) DOCS_JAVASE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_JAVASE_BUNDLE_NAME) DOCS_REFERENCE_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(DOCS_REFERENCE_BUNDLE_NAME) +STATIC_JDK_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(STATIC_JDK_BUNDLE_NAME) JCOV_BUNDLE := $(BUNDLES_OUTPUTDIR)/$(JCOV_BUNDLE_NAME) # This macro is called to allow inclusion of closed source counterparts. diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index aa4d846280ed3..f0cb6f451a398 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -1089,8 +1089,8 @@ var getJibProfilesDependencies = function (input, common) { var devkit_platform_revisions = { linux_x64: "gcc14.2.0-OL6.4+1.0", - macosx: "Xcode14.3.1+1.0", - windows_x64: "VS2022-17.6.5+1.0", + macosx: "Xcode15.4+1.0", + windows_x64: "VS2022-17.13.2+1.0", linux_aarch64: "gcc14.2.0-OL7.6+1.0", linux_arm: "gcc8.2.0-Fedora27+1.0", linux_ppc64le: "gcc14.2.0-Fedora_41+1.0", diff --git a/make/data/ubsan/ubsan_default_options.c b/make/data/ubsan/ubsan_default_options.c index 011d1a675a90f..05e4722e45a27 100644 --- a/make/data/ubsan/ubsan_default_options.c +++ b/make/data/ubsan/ubsan_default_options.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,6 +43,18 @@ #define ATTRIBUTE_USED #endif +// On AIX, the llvm_symbolizer is not found out of the box, so we have to provide the +// full qualified llvm_symbolizer path in the __ubsan_default_options() function. +// To get it here we compile our sources with an additional define LLVM_SYMBOLIZER +// containing the path, which we set in make/autoconf/jdk-options.m4. +#ifdef LLVM_SYMBOLIZER +#define _LLVM_SYMBOLIZER(X) ",external_symbolizer_path=" X_LLVM_SYMBOLIZER(X) +#define X_LLVM_SYMBOLIZER(X) #X +#else +#define LLVM_SYMBOLIZER +#define _LLVM_SYMBOLIZER(X) +#endif + // Override weak symbol exposed by UBSan to override default options. This is called by UBSan // extremely early during library loading, before main is called. We need to override the default // options because by default UBSan only prints a warning for each occurrence. We want jtreg tests @@ -50,5 +62,5 @@ // thread so it is easier to track down. You can override these options by setting the environment // variable UBSAN_OPTIONS. ATTRIBUTE_DEFAULT_VISIBILITY ATTRIBUTE_USED const char* __ubsan_default_options() { - return "halt_on_error=1,print_stacktrace=1"; + return "halt_on_error=1,print_stacktrace=1" _LLVM_SYMBOLIZER(LLVM_SYMBOLIZER); } diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk index f4323f58638be..1b9240df49c8c 100644 --- a/make/devkit/Tools.gmk +++ b/make/devkit/Tools.gmk @@ -39,6 +39,8 @@ # Fix this... # +uppercase = $(shell echo $1 | tr a-z A-Z) + $(info TARGET=$(TARGET)) $(info HOST=$(HOST)) $(info BUILD=$(BUILD)) @@ -91,99 +93,28 @@ endif ################################################################################ # Define external dependencies -# Latest that could be made to work. -GCC_VER := 14.2.0 -ifeq ($(GCC_VER), 14.2.0) - gcc_ver := gcc-14.2.0 - binutils_ver := binutils-2.43 - ccache_ver := ccache-4.10.2 - CCACHE_CMAKE_BASED := 1 - mpfr_ver := mpfr-4.2.1 - gmp_ver := gmp-6.3.0 - mpc_ver := mpc-1.3.1 - gdb_ver := gdb-15.2 - REQUIRED_MIN_MAKE_MAJOR_VERSION := 4 -else ifeq ($(GCC_VER), 13.2.0) - gcc_ver := gcc-13.2.0 - binutils_ver := binutils-2.41 - ccache_ver := ccache-3.7.12 - mpfr_ver := mpfr-4.2.0 - gmp_ver := gmp-6.3.0 - mpc_ver := mpc-1.3.1 - gdb_ver := gdb-13.2 - REQUIRED_MIN_MAKE_MAJOR_VERSION := 4 -else ifeq ($(GCC_VER), 11.3.0) - gcc_ver := gcc-11.3.0 - binutils_ver := binutils-2.39 - ccache_ver := ccache-3.7.12 - mpfr_ver := mpfr-4.1.1 - gmp_ver := gmp-6.2.1 - mpc_ver := mpc-1.2.1 - gdb_ver := gdb-11.2 - REQUIRED_MIN_MAKE_MAJOR_VERSION := 4 -else ifeq ($(GCC_VER), 11.2.0) - gcc_ver := gcc-11.2.0 - binutils_ver := binutils-2.37 - ccache_ver := ccache-3.7.12 - mpfr_ver := mpfr-4.1.0 - gmp_ver := gmp-6.2.1 - mpc_ver := mpc-1.2.1 - gdb_ver := gdb-11.1 - REQUIRED_MIN_MAKE_MAJOR_VERSION := 4 -else ifeq ($(GCC_VER), 10.3.0) - gcc_ver := gcc-10.3.0 - binutils_ver := binutils-2.36.1 - ccache_ver := ccache-3.7.11 - mpfr_ver := mpfr-4.1.0 - gmp_ver := gmp-6.2.0 - mpc_ver := mpc-1.1.0 - gdb_ver := gdb-10.1 - REQUIRED_MIN_MAKE_MAJOR_VERSION := 4 -else ifeq ($(GCC_VER), 10.2.0) - gcc_ver := gcc-10.2.0 - binutils_ver := binutils-2.35 - ccache_ver := ccache-3.7.11 - mpfr_ver := mpfr-4.1.0 - gmp_ver := gmp-6.2.0 - mpc_ver := mpc-1.1.0 - gdb_ver := gdb-9.2 - REQUIRED_MIN_MAKE_MAJOR_VERSION := 4 -else ifeq ($(GCC_VER), 9.2.0) - gcc_ver := gcc-9.2.0 - binutils_ver := binutils-2.34 - ccache_ver := ccache-3.7.3 - mpfr_ver := mpfr-3.1.5 - gmp_ver := gmp-6.1.2 - mpc_ver := mpc-1.0.3 - gdb_ver := gdb-8.3 -else ifeq ($(GCC_VER), 8.3.0) - gcc_ver := gcc-8.3.0 - binutils_ver := binutils-2.32 - ccache_ver := ccache-3.7.3 - mpfr_ver := mpfr-3.1.5 - gmp_ver := gmp-6.1.2 - mpc_ver := mpc-1.0.3 - gdb_ver := gdb-8.3 -else ifeq ($(GCC_VER), 7.3.0) - gcc_ver := gcc-7.3.0 - binutils_ver := binutils-2.30 - ccache_ver := ccache-3.3.6 - mpfr_ver := mpfr-3.1.5 - gmp_ver := gmp-6.1.2 - mpc_ver := mpc-1.0.3 - gdb_ver := gdb-8.1 -else ifeq ($(GCC_VER), 4.9.2) - gcc_ver := gcc-4.9.2 - binutils_ver := binutils-2.25 - ccache_ver := ccache-3.2.1 - mpfr_ver := mpfr-3.0.1 - gmp_ver := gmp-4.3.2 - mpc_ver := mpc-1.0.1 - gdb_ver := gdb-7.12.1 -else - $(error Unsupported GCC version) -endif +gcc_ver_only := 14.2.0 +binutils_ver_only := 2.43 +ccache_ver_only := 4.10.2 +CCACHE_CMAKE_BASED := 1 +mpfr_ver_only := 4.2.1 +gmp_ver_only := 6.3.0 +mpc_ver_only := 1.3.1 +gdb_ver_only := 15.2 + +dependencies := gcc binutils ccache mpfr gmp mpc gdb +$(foreach dep,$(dependencies),$(eval $(dep)_ver := $(dep)-$($(dep)_ver_only))) + +GCC := http://ftp.gnu.org/pub/gnu/gcc/$(gcc_ver)/$(gcc_ver).tar.xz +BINUTILS := http://ftp.gnu.org/pub/gnu/binutils/$(binutils_ver).tar.gz +CCACHE := https://github.com/ccache/ccache/releases/download/v$(ccache_ver_only)/$(ccache_ver).tar.xz +MPFR := https://www.mpfr.org/$(mpfr_ver)/$(mpfr_ver).tar.bz2 +GMP := http://ftp.gnu.org/pub/gnu/gmp/$(gmp_ver).tar.bz2 +MPC := http://ftp.gnu.org/pub/gnu/mpc/$(mpc_ver).tar.gz +GDB := http://ftp.gnu.org/gnu/gdb/$(gdb_ver).tar.xz + +REQUIRED_MIN_MAKE_MAJOR_VERSION := 4 ifneq ($(REQUIRED_MIN_MAKE_MAJOR_VERSION),) MAKE_MAJOR_VERSION := $(word 1,$(subst ., ,$(MAKE_VERSION))) SUPPORTED_MAKE_VERSION := $(shell [ $(MAKE_MAJOR_VERSION) -ge $(REQUIRED_MIN_MAKE_MAJOR_VERSION) ] && echo true) @@ -192,17 +123,6 @@ ifneq ($(REQUIRED_MIN_MAKE_MAJOR_VERSION),) endif endif -ccache_ver_only := $(patsubst ccache-%,%,$(ccache_ver)) - - -GCC := http://ftp.gnu.org/pub/gnu/gcc/$(gcc_ver)/$(gcc_ver).tar.xz -BINUTILS := http://ftp.gnu.org/pub/gnu/binutils/$(binutils_ver).tar.gz -CCACHE := https://github.com/ccache/ccache/releases/download/v$(ccache_ver_only)/$(ccache_ver).tar.xz -MPFR := https://www.mpfr.org/${mpfr_ver}/${mpfr_ver}.tar.bz2 -GMP := http://ftp.gnu.org/pub/gnu/gmp/${gmp_ver}.tar.bz2 -MPC := http://ftp.gnu.org/pub/gnu/mpc/${mpc_ver}.tar.gz -GDB := http://ftp.gnu.org/gnu/gdb/${gdb_ver}.tar.xz - # RPMs used by all BASE_OS RPM_LIST := \ $(KERNEL_HEADERS_RPM) \ @@ -297,7 +217,7 @@ define Download endef # Download and unpack all source packages -$(foreach p,GCC BINUTILS CCACHE MPFR GMP MPC GDB,$(eval $(call Download,$(p)))) +$(foreach dep,$(dependencies),$(eval $(call Download,$(call uppercase,$(dep))))) ################################################################################ # Unpack RPMS @@ -374,7 +294,7 @@ endif ################################################################################ # Define marker files for each source package to be compiled -$(foreach t,binutils mpfr gmp mpc gcc ccache gdb,$(eval $(t) = $(TARGETDIR)/$($(t)_ver).done)) +$(foreach dep,$(dependencies),$(eval $(dep) = $(TARGETDIR)/$($(dep)_ver).done)) ################################################################################ @@ -721,12 +641,12 @@ ifeq ($(TARGET), $(HOST)) ln -s $(TARGET)-$* $@ missing-links := $(addprefix $(PREFIX)/bin/, \ - addr2line ar as c++ c++filt dwp elfedit g++ gcc gcc-$(GCC_VER) gprof ld ld.bfd \ + addr2line ar as c++ c++filt dwp elfedit g++ gcc gcc-$(gcc_ver_only) gprof ld ld.bfd \ ld.gold nm objcopy objdump ranlib readelf size strings strip) endif # Add link to work around "plugin needed to handle lto object" (JDK-8344272) -$(PREFIX)/lib/bfd-plugins/liblto_plugin.so: $(PREFIX)/libexec/gcc/$(TARGET)/$(GCC_VER)/liblto_plugin.so +$(PREFIX)/lib/bfd-plugins/liblto_plugin.so: $(PREFIX)/libexec/gcc/$(TARGET)/$(gcc_ver_only)/liblto_plugin.so @echo 'Creating missing $(@F) soft link' @mkdir -p $(@D) ln -s $$(realpath -s --relative-to=$(@D) $<) $@ diff --git a/make/devkit/createAutoconfBundle.sh b/make/devkit/createAutoconfBundle.sh index 7363b9cd8a71a..ebe9c427f76ea 100644 --- a/make/devkit/createAutoconfBundle.sh +++ b/make/devkit/createAutoconfBundle.sh @@ -1,6 +1,6 @@ #!/bin/bash -e # -# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. +# 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 @@ -24,10 +24,21 @@ # questions. # -# Create a bundle in the current directory, containing what's needed to run +# Create a bundle in OpenJDK build folder, containing what's needed to run # the 'autoconf' program by the OpenJDK build. To override TARGET_PLATFORM # just set the variable before running this script. +# This script fetches sources from network so make sure your proxy is setup appropriately. + +# colored print to highlight some of the logs +function print_log() +{ + Color_Cyan='\033[1;36m' # Cyan + Color_Off='\033[0m' # Reset color + printf "${Color_Cyan}> $1${Color_Off}\n" +} + + # Autoconf depends on m4, so download and build that first. AUTOCONF_VERSION=2.69 M4_VERSION=1.4.18 @@ -58,11 +69,12 @@ MODULE_NAME=autoconf-$TARGET_PLATFORM-$AUTOCONF_VERSION+$PACKAGE_VERSION BUNDLE_NAME=$MODULE_NAME.tar.gz SCRIPT_DIR="$(cd "$(dirname $0)" > /dev/null && pwd)" -OUTPUT_ROOT="${SCRIPT_DIR}/../../build/autoconf" +BASEDIR="$(cd "$SCRIPT_DIR/../.." > /dev/null && pwd)" +OUTPUT_ROOT="$BASEDIR/build/autoconf" -cd $OUTPUT_ROOT IMAGE_DIR=$OUTPUT_ROOT/$MODULE_NAME mkdir -p $IMAGE_DIR/usr +cd $OUTPUT_ROOT # Download and build m4 @@ -76,7 +88,7 @@ elif test "x$TARGET_PLATFORM" = xcygwin_x86; then cp /usr/bin/m4 $IMAGE_DIR/usr/bin elif test "x$TARGET_PLATFORM" = xlinux_x64; then M4_VERSION=1.4.13-5 - wget http://yum.oracle.com/repo/OracleLinux/OL6/latest/x86_64/getPackage/m4-$M4_VERSION.el6.x86_64.rpm + wget https://yum.oracle.com/repo/OracleLinux/OL6/latest/x86_64/getPackage/m4-$M4_VERSION.el6.x86_64.rpm cd $IMAGE_DIR rpm2cpio $OUTPUT_ROOT/m4-$M4_VERSION.el6.x86_64.rpm | cpio -d -i elif test "x$TARGET_PLATFORM" = xlinux_x86; then @@ -85,27 +97,38 @@ elif test "x$TARGET_PLATFORM" = xlinux_x86; then cd $IMAGE_DIR rpm2cpio $OUTPUT_ROOT/m4-$M4_VERSION.el6.i686.rpm | cpio -d -i else + print_log "m4: download" wget https://ftp.gnu.org/gnu/m4/m4-$M4_VERSION.tar.gz - tar xzf m4-$M4_VERSION.tar.gz + tar -xzf m4-$M4_VERSION.tar.gz cd m4-$M4_VERSION + print_log "m4: configure" ./configure --prefix=$IMAGE_DIR/usr CFLAGS="-w -Wno-everything" + print_log "m4: make" make + print_log "m4: make install" make install cd .. fi # Download and build autoconf +print_log "autoconf: download" wget https://ftp.gnu.org/gnu/autoconf/autoconf-$AUTOCONF_VERSION.tar.gz -tar xzf autoconf-$AUTOCONF_VERSION.tar.gz +tar -xzf autoconf-$AUTOCONF_VERSION.tar.gz cd autoconf-$AUTOCONF_VERSION +print_log "autoconf: configure" ./configure --prefix=$IMAGE_DIR/usr M4=$IMAGE_DIR/usr/bin/m4 +print_log "autoconf: make" make +print_log "autoconf: make install" make install cd .. +# The resulting scripts from installation folder use absolute paths to reference other files within installation folder +print_log "replace absolue paths from installation files with a relative ." perl -pi -e "s!$IMAGE_DIR/!./!" $IMAGE_DIR/usr/bin/auto* $IMAGE_DIR/usr/share/autoconf/autom4te.cfg +print_log "creating $IMAGE_DIR/autoconf wrapper script" cat > $IMAGE_DIR/autoconf << EOF #!/bin/bash # Get an absolute path to this script @@ -123,6 +146,9 @@ PREPEND_INCLUDE="--prepend-include \$this_script_dir/usr/share/autoconf" exec \$this_script_dir/usr/bin/autoconf \$PREPEND_INCLUDE "\$@" EOF + chmod +x $IMAGE_DIR/autoconf + +print_log "archiving $IMAGE_DIR directory as $OUTPUT_ROOT/$BUNDLE_NAME" cd $IMAGE_DIR tar -cvzf $OUTPUT_ROOT/$BUNDLE_NAME * diff --git a/make/devkit/createWindowsDevkit.sh b/make/devkit/createWindowsDevkit.sh index 0646cb68ef44d..757fb157ad443 100644 --- a/make/devkit/createWindowsDevkit.sh +++ b/make/devkit/createWindowsDevkit.sh @@ -56,16 +56,22 @@ BUILD_DIR="${SCRIPT_DIR}/../../build/devkit" UNAME_SYSTEM=`uname -s` UNAME_RELEASE=`uname -r` +UNAME_OS=`uname -o` # Detect cygwin or WSL IS_CYGWIN=`echo $UNAME_SYSTEM | grep -i CYGWIN` IS_WSL=`echo $UNAME_RELEASE | grep Microsoft` +IS_MSYS=`echo $UNAME_OS | grep -i Msys` +MSYS2_ARG_CONV_EXCL="*" # make "cmd.exe /c" work for msys2 +CMD_EXE="cmd.exe /c" if test "x$IS_CYGWIN" != "x"; then BUILD_ENV="cygwin" +elif test "x$IS_MSYS" != "x"; then + BUILD_ENV="cygwin" elif test "x$IS_WSL" != "x"; then BUILD_ENV="wsl" else - echo "Unknown environment; only Cygwin and WSL are supported." + echo "Unknown environment; only Cygwin/MSYS2/WSL are supported." exit 1 fi @@ -76,7 +82,7 @@ elif test "x$BUILD_ENV" = "xwsl"; then fi # Work around the insanely named ProgramFiles(x86) env variable -PROGRAMFILES_X86="$($WINDOWS_PATH_TO_UNIX_PATH "$(cmd.exe /c set | sed -n 's/^ProgramFiles(x86)=//p' | tr -d '\r')")" +PROGRAMFILES_X86="$($WINDOWS_PATH_TO_UNIX_PATH "$(${CMD_EXE} set | sed -n 's/^ProgramFiles(x86)=//p' | tr -d '\r')")" PROGRAMFILES="$($WINDOWS_PATH_TO_UNIX_PATH "$PROGRAMFILES")" case $VS_VERSION in @@ -99,13 +105,15 @@ esac # Find Visual Studio installation dir -VSNNNCOMNTOOLS=`cmd.exe /c echo %VS${VS_VERSION_NUM_NODOT}COMNTOOLS% | tr -d '\r'` +VSNNNCOMNTOOLS=`${CMD_EXE} echo %VS${VS_VERSION_NUM_NODOT}COMNTOOLS% | tr -d '\r'` +VSNNNCOMNTOOLS="$($WINDOWS_PATH_TO_UNIX_PATH "$VSNNNCOMNTOOLS")" if [ -d "$VSNNNCOMNTOOLS" ]; then - VS_INSTALL_DIR="$($WINDOWS_PATH_TO_UNIX_PATH "$VSNNNCOMNTOOLS/../..")" + VS_INSTALL_DIR="$VSNNNCOMNTOOLS/../.." else VS_INSTALL_DIR="${MSVC_PROGRAMFILES_DIR}/Microsoft Visual Studio/$VS_VERSION" VS_INSTALL_DIR="$(ls -d "${VS_INSTALL_DIR}/"{Community,Professional,Enterprise} 2>/dev/null | head -n1)" fi +echo "VSNNNCOMNTOOLS: $VSNNNCOMNTOOLS" echo "VS_INSTALL_DIR: $VS_INSTALL_DIR" # Extract semantic version @@ -180,7 +188,11 @@ cp $DEVKIT_ROOT/VC/redist/arm64/$MSVCP_DLL $DEVKIT_ROOT/VC/bin/arm64 ################################################################################ # Copy SDK files -SDK_INSTALL_DIR="$PROGRAMFILES_X86/Windows Kits/$SDK_VERSION" +SDK_INSTALL_DIR=`${CMD_EXE} echo %WindowsSdkDir% | tr -d '\r'` +SDK_INSTALL_DIR="$($WINDOWS_PATH_TO_UNIX_PATH "$SDK_INSTALL_DIR")" +if [ ! -d "$SDK_INSTALL_DIR" ]; then + SDK_INSTALL_DIR="$PROGRAMFILES_X86/Windows Kits/$SDK_VERSION" +fi echo "SDK_INSTALL_DIR: $SDK_INSTALL_DIR" SDK_FULL_VERSION="$(ls "$SDK_INSTALL_DIR/bin" | sort -r -n | head -n1)" diff --git a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk index 0a897230f835b..ffea9aa3926b9 100644 --- a/make/hotspot/lib/JvmFeatures.gmk +++ b/make/hotspot/lib/JvmFeatures.gmk @@ -125,6 +125,7 @@ endif ifneq ($(call check-jvm-feature, cds), true) JVM_CFLAGS_FEATURES += -DINCLUDE_CDS=0 JVM_EXCLUDE_FILES += \ + aotCodeCache.cpp \ classLoaderDataShared.cpp \ classLoaderExt.cpp \ systemDictionaryShared.cpp diff --git a/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java b/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java index 1b930ca752777..fa1b33bb03e34 100644 --- a/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java +++ b/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -59,6 +59,7 @@ public class HelloClasslist { private static final Logger LOGGER = Logger.getLogger("Hello"); + @SuppressWarnings("restricted") public static void main(String ... args) throws Throwable { FileSystems.getDefault(); @@ -141,6 +142,7 @@ public static void main(String ... args) throws Throwable { HelloClasslist.class.getMethod("staticMethod_V").invoke(null); var obj = HelloClasslist.class.getMethod("staticMethod_L_L", Object.class).invoke(null, instance); HelloClasslist.class.getField("field").get(instance); + MethodHandles.Lookup.ClassOption.class.getEnumConstants(); // A selection of trivial and relatively common MH operations invoke(MethodHandles.identity(double.class), 1.0); @@ -160,6 +162,9 @@ record B(int b) { } case B b -> b.b; default -> 17; }; + // record run-time methods + o.equals(new B(5)); + o.hashCode(); LOGGER.log(Level.FINE, "Value: " + value); // The Striped64$Cell is loaded rarely only when there's a contention among diff --git a/make/modules/java.base/Lib.gmk b/make/modules/java.base/Lib.gmk index 84ee309dadd11..51d323a0344f2 100644 --- a/make/modules/java.base/Lib.gmk +++ b/make/modules/java.base/Lib.gmk @@ -158,6 +158,7 @@ endif $(eval $(call SetupJdkLibrary, BUILD_LIBSYSLOOKUP, \ NAME := syslookup, \ + EXTRA_HEADER_DIRS := java.base:libjava, \ LD_SET_ORIGIN := false, \ LDFLAGS_linux := -Wl$(COMMA)--no-as-needed, \ LDFLAGS_aix := -brtl -bexpfull, \ diff --git a/make/scripts/update_copyright_year.sh b/make/scripts/update_copyright_year.sh index bb61d48c91cc9..578ab4cbc9923 100644 --- a/make/scripts/update_copyright_year.sh +++ b/make/scripts/update_copyright_year.sh @@ -1,7 +1,7 @@ #!/bin/bash -f # -# Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -41,10 +41,6 @@ set -e # To allow total changes counting shopt -s lastpipe -# Get an absolute path to this script, since that determines the top-level directory. -this_script_dir=`dirname $0` -this_script_dir=`cd $this_script_dir > /dev/null && pwd` - # Temp area tmp=/tmp/`basename $0`.${USER}.$$ rm -f -r ${tmp} @@ -98,10 +94,16 @@ while getopts "c:fhy:" option; do done # VCS check +git_installed=false +which git > /dev/null && git_installed=true +if [ "$git_installed" != "true" ]; then + echo "Error: This script requires git. Please install it." + exit 1 +fi git_found=false -[ -d "${this_script_dir}/../../.git" ] && git_found=true +git status &> /dev/null && git_found=true if [ "$git_found" != "true" ]; then - echo "Error: Please execute script from within make/scripts." + echo "Error: Please execute script from within a JDK git repository." exit 1 else echo "Using Git version control system" diff --git a/src/hotspot/.editorconfig b/src/hotspot/.editorconfig deleted file mode 100644 index 48e63362b542e..0000000000000 --- a/src/hotspot/.editorconfig +++ /dev/null @@ -1,3 +0,0 @@ -[*.{cpp,hpp,c,h}] -indent_style = space -indent_size = 2 diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 76e3c92ddc261..1b128f2a0cec9 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. // Copyright (c) 2014, 2024, Red Hat, Inc. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // @@ -2296,6 +2296,26 @@ bool Matcher::match_rule_supported(int opcode) { return false; } break; + case Op_FmaHF: + // UseFMA flag also needs to be checked along with FEAT_FP16 + if (!UseFMA || !is_feat_fp16_supported()) { + return false; + } + break; + case Op_AddHF: + case Op_SubHF: + case Op_MulHF: + case Op_DivHF: + case Op_MinHF: + case Op_MaxHF: + case Op_SqrtHF: + // Half-precision floating point scalar operations require FEAT_FP16 + // to be available. FEAT_FP16 is enabled if both "fphp" and "asimdhp" + // features are supported. + if (!is_feat_fp16_supported()) { + return false; + } + break; } return true; // Per default match rules are supported. @@ -2306,11 +2326,11 @@ const RegMask* Matcher::predicate_reg_mask(void) { } bool Matcher::supports_vector_calling_convention(void) { - return EnableVectorSupport && UseVectorStubs; + return EnableVectorSupport; } OptoRegPair Matcher::vector_return_value(uint ideal_reg) { - assert(EnableVectorSupport && UseVectorStubs, "sanity"); + assert(EnableVectorSupport, "sanity"); int lo = V0_num; int hi = V0_H_num; if (ideal_reg == Op_VecX || ideal_reg == Op_VecA) { @@ -4599,6 +4619,15 @@ operand immF0() interface(CONST_INTER); %} +// Half Float (FP16) Immediate +operand immH() +%{ + match(ConH); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + // operand immFPacked() %{ @@ -6942,6 +6971,21 @@ instruct loadConD(vRegD dst, immD con) %{ ins_pipe(fp_load_constant_d); %} +// Load Half Float Constant +// The "ldr" instruction loads a 32-bit word from the constant pool into a +// 32-bit register but only the bottom half will be populated and the top +// 16 bits are zero. +instruct loadConH(vRegF dst, immH con) %{ + match(Set dst con); + format %{ + "ldrs $dst, [$constantaddress]\t# load from constant table: half float=$con\n\t" + %} + ins_encode %{ + __ ldrs(as_FloatRegister($dst$$reg), $constantaddress($con)); + %} + ins_pipe(fp_load_constant_s); +%} + // Store Instructions // Store Byte @@ -8144,6 +8188,7 @@ instruct castPP(iRegPNoSp dst) instruct castII(iRegI dst) %{ + predicate(VerifyConstraintCasts == 0); match(Set dst (CastII dst)); size(0); @@ -8153,8 +8198,22 @@ instruct castII(iRegI dst) ins_pipe(pipe_class_empty); %} +instruct castII_checked(iRegI dst, rFlagsReg cr) +%{ + predicate(VerifyConstraintCasts > 0); + match(Set dst (CastII dst)); + effect(KILL cr); + + format %{ "# castII_checked of $dst" %} + ins_encode %{ + __ verify_int_in_range(_idx, bottom_type()->is_int(), $dst$$Register, rscratch1); + %} + ins_pipe(pipe_slow); +%} + instruct castLL(iRegL dst) %{ + predicate(VerifyConstraintCasts == 0); match(Set dst (CastLL dst)); size(0); @@ -8164,6 +8223,19 @@ instruct castLL(iRegL dst) ins_pipe(pipe_class_empty); %} +instruct castLL_checked(iRegL dst, rFlagsReg cr) +%{ + predicate(VerifyConstraintCasts > 0); + match(Set dst (CastLL dst)); + effect(KILL cr); + + format %{ "# castLL_checked of $dst" %} + ins_encode %{ + __ verify_long_in_range(_idx, bottom_type()->is_long(), $dst$$Register, rscratch1); + %} + ins_pipe(pipe_slow); +%} + instruct castFF(vRegF dst) %{ match(Set dst (CastFF dst)); @@ -13606,6 +13678,17 @@ instruct bits_reverse_L(iRegLNoSp dst, iRegL src) // ============================================================================ // Floating Point Arithmetic Instructions +instruct addHF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ + match(Set dst (AddHF src1 src2)); + format %{ "faddh $dst, $src1, $src2" %} + ins_encode %{ + __ faddh($dst$$FloatRegister, + $src1$$FloatRegister, + $src2$$FloatRegister); + %} + ins_pipe(fp_dop_reg_reg_s); +%} + instruct addF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ match(Set dst (AddF src1 src2)); @@ -13636,6 +13719,17 @@ instruct addD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ ins_pipe(fp_dop_reg_reg_d); %} +instruct subHF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ + match(Set dst (SubHF src1 src2)); + format %{ "fsubh $dst, $src1, $src2" %} + ins_encode %{ + __ fsubh($dst$$FloatRegister, + $src1$$FloatRegister, + $src2$$FloatRegister); + %} + ins_pipe(fp_dop_reg_reg_s); +%} + instruct subF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ match(Set dst (SubF src1 src2)); @@ -13666,6 +13760,17 @@ instruct subD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ ins_pipe(fp_dop_reg_reg_d); %} +instruct mulHF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ + match(Set dst (MulHF src1 src2)); + format %{ "fmulh $dst, $src1, $src2" %} + ins_encode %{ + __ fmulh($dst$$FloatRegister, + $src1$$FloatRegister, + $src2$$FloatRegister); + %} + ins_pipe(fp_dop_reg_reg_s); +%} + instruct mulF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ match(Set dst (MulF src1 src2)); @@ -13696,6 +13801,20 @@ instruct mulD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ ins_pipe(fp_dop_reg_reg_d); %} +// src1 * src2 + src3 (half-precision float) +instruct maddHF_reg_reg(vRegF dst, vRegF src1, vRegF src2, vRegF src3) %{ + match(Set dst (FmaHF src3 (Binary src1 src2))); + format %{ "fmaddh $dst, $src1, $src2, $src3" %} + ins_encode %{ + assert(UseFMA, "Needs FMA instructions support."); + __ fmaddh($dst$$FloatRegister, + $src1$$FloatRegister, + $src2$$FloatRegister, + $src3$$FloatRegister); + %} + ins_pipe(pipe_class_default); +%} + // src1 * src2 + src3 instruct maddF_reg_reg(vRegF dst, vRegF src1, vRegF src2, vRegF src3) %{ match(Set dst (FmaF src3 (Binary src1 src2))); @@ -13837,6 +13956,29 @@ instruct mnsubD_reg_reg(vRegD dst, vRegD src1, vRegD src2, vRegD src3, immD0 zer ins_pipe(pipe_class_default); %} +// Math.max(HH)H (half-precision float) +instruct maxHF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ + match(Set dst (MaxHF src1 src2)); + format %{ "fmaxh $dst, $src1, $src2" %} + ins_encode %{ + __ fmaxh($dst$$FloatRegister, + $src1$$FloatRegister, + $src2$$FloatRegister); + %} + ins_pipe(fp_dop_reg_reg_s); +%} + +// Math.min(HH)H (half-precision float) +instruct minHF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ + match(Set dst (MinHF src1 src2)); + format %{ "fminh $dst, $src1, $src2" %} + ins_encode %{ + __ fminh($dst$$FloatRegister, + $src1$$FloatRegister, + $src2$$FloatRegister); + %} + ins_pipe(fp_dop_reg_reg_s); +%} // Math.max(FF)F instruct maxF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ @@ -13894,6 +14036,16 @@ instruct minD_reg_reg(vRegD dst, vRegD src1, vRegD src2) %{ ins_pipe(fp_dop_reg_reg_d); %} +instruct divHF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ + match(Set dst (DivHF src1 src2)); + format %{ "fdivh $dst, $src1, $src2" %} + ins_encode %{ + __ fdivh($dst$$FloatRegister, + $src1$$FloatRegister, + $src2$$FloatRegister); + %} + ins_pipe(fp_div_s); +%} instruct divF_reg_reg(vRegF dst, vRegF src1, vRegF src2) %{ match(Set dst (DivF src1 src2)); @@ -14067,6 +14219,16 @@ instruct sqrtF_reg(vRegF dst, vRegF src) %{ ins_pipe(fp_div_d); %} +instruct sqrtHF_reg(vRegF dst, vRegF src) %{ + match(Set dst (SqrtHF src)); + format %{ "fsqrth $dst, $src" %} + ins_encode %{ + __ fsqrth($dst$$FloatRegister, + $src$$FloatRegister); + %} + ins_pipe(fp_div_s); +%} + // Math.rint, floor, ceil instruct roundD_reg(vRegD dst, vRegD src, immI rmode) %{ match(Set dst (RoundDoubleMode src rmode)); @@ -17116,6 +17278,64 @@ instruct expandBitsL_memcon(iRegINoSp dst, memory8 mem, immL mask, ins_pipe(pipe_slow); %} +//----------------------------- Reinterpret ---------------------------------- +// Reinterpret a half-precision float value in a floating point register to a general purpose register +instruct reinterpretHF2S(iRegINoSp dst, vRegF src) %{ + match(Set dst (ReinterpretHF2S src)); + format %{ "reinterpretHF2S $dst, $src" %} + ins_encode %{ + __ smov($dst$$Register, $src$$FloatRegister, __ H, 0); + %} + ins_pipe(pipe_slow); +%} + +// Reinterpret a half-precision float value in a general purpose register to a floating point register +instruct reinterpretS2HF(vRegF dst, iRegINoSp src) %{ + match(Set dst (ReinterpretS2HF src)); + format %{ "reinterpretS2HF $dst, $src" %} + ins_encode %{ + __ mov($dst$$FloatRegister, __ H, 0, $src$$Register); + %} + ins_pipe(pipe_slow); +%} + +// Without this optimization, ReinterpretS2HF (ConvF2HF src) would result in the following +// instructions (the first two are for ConvF2HF and the last instruction is for ReinterpretS2HF) - +// fcvt $tmp1_fpr, $src_fpr // Convert float to half-precision float +// mov $tmp2_gpr, $tmp1_fpr // Move half-precision float in FPR to a GPR +// mov $dst_fpr, $tmp2_gpr // Move the result from a GPR to an FPR +// The move from FPR to GPR in ConvF2HF and the move from GPR to FPR in ReinterpretS2HF +// can be omitted in this pattern, resulting in - +// fcvt $dst, $src // Convert float to half-precision float +instruct convF2HFAndS2HF(vRegF dst, vRegF src) +%{ + match(Set dst (ReinterpretS2HF (ConvF2HF src))); + format %{ "convF2HFAndS2HF $dst, $src" %} + ins_encode %{ + __ fcvtsh($dst$$FloatRegister, $src$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// Without this optimization, ConvHF2F (ReinterpretHF2S src) would result in the following +// instructions (the first one is for ReinterpretHF2S and the last two are for ConvHF2F) - +// mov $tmp1_gpr, $src_fpr // Move the half-precision float from an FPR to a GPR +// mov $tmp2_fpr, $tmp1_gpr // Move the same value from GPR to an FPR +// fcvt $dst_fpr, $tmp2_fpr // Convert the half-precision float to 32-bit float +// The move from FPR to GPR in ReinterpretHF2S and the move from GPR to FPR in ConvHF2F +// can be omitted as the input (src) is already in an FPR required for the fcvths instruction +// resulting in - +// fcvt $dst, $src // Convert half-precision float to a 32-bit float +instruct convHF2SAndHF2F(vRegF dst, vRegF src) +%{ + match(Set dst (ConvHF2F (ReinterpretHF2S src))); + format %{ "convHF2SAndHF2F $dst, $src" %} + ins_encode %{ + __ fcvths($dst$$FloatRegister, $src$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + // ============================================================================ // This name is KNOWN by the ADLC and cannot be changed. // The ADLC forces a 'TypeRawPtr::BOTTOM' output type diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 3db7d30884429..5c02e30963eaa 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -2032,6 +2032,8 @@ void mvnw(Register Rd, Register Rm, INSN(fsqrtd, 0b01, 0b000011); INSN(fcvtd, 0b01, 0b000100); // Double-precision to single-precision + INSN(fsqrth, 0b11, 0b000011); // Half-precision sqrt + private: void _fcvt_narrow_extend(FloatRegister Vd, SIMD_Arrangement Ta, FloatRegister Vn, SIMD_Arrangement Tb, bool do_extend) { @@ -2059,37 +2061,68 @@ void mvnw(Register Rd, Register Rm, #undef INSN // Floating-point data-processing (2 source) - void data_processing(unsigned op31, unsigned type, unsigned opcode, + void data_processing(unsigned op31, unsigned type, unsigned opcode, unsigned op21, FloatRegister Vd, FloatRegister Vn, FloatRegister Vm) { starti; f(op31, 31, 29); f(0b11110, 28, 24); - f(type, 23, 22), f(1, 21), f(opcode, 15, 10); + f(type, 23, 22), f(op21, 21), f(opcode, 15, 10); rf(Vm, 16), rf(Vn, 5), rf(Vd, 0); } -#define INSN(NAME, op31, type, opcode) \ +#define INSN(NAME, op31, type, opcode, op21) \ void NAME(FloatRegister Vd, FloatRegister Vn, FloatRegister Vm) { \ - data_processing(op31, type, opcode, Vd, Vn, Vm); \ - } - - INSN(fabds, 0b011, 0b10, 0b110101); - INSN(fmuls, 0b000, 0b00, 0b000010); - INSN(fdivs, 0b000, 0b00, 0b000110); - INSN(fadds, 0b000, 0b00, 0b001010); - INSN(fsubs, 0b000, 0b00, 0b001110); - INSN(fmaxs, 0b000, 0b00, 0b010010); - INSN(fmins, 0b000, 0b00, 0b010110); - INSN(fnmuls, 0b000, 0b00, 0b100010); - - INSN(fabdd, 0b011, 0b11, 0b110101); - INSN(fmuld, 0b000, 0b01, 0b000010); - INSN(fdivd, 0b000, 0b01, 0b000110); - INSN(faddd, 0b000, 0b01, 0b001010); - INSN(fsubd, 0b000, 0b01, 0b001110); - INSN(fmaxd, 0b000, 0b01, 0b010010); - INSN(fmind, 0b000, 0b01, 0b010110); - INSN(fnmuld, 0b000, 0b01, 0b100010); + data_processing(op31, type, opcode, op21, Vd, Vn, Vm); \ + } + + INSN(fmuls, 0b000, 0b00, 0b000010, 0b1); + INSN(fdivs, 0b000, 0b00, 0b000110, 0b1); + INSN(fadds, 0b000, 0b00, 0b001010, 0b1); + INSN(fsubs, 0b000, 0b00, 0b001110, 0b1); + INSN(fmaxs, 0b000, 0b00, 0b010010, 0b1); + INSN(fmins, 0b000, 0b00, 0b010110, 0b1); + INSN(fnmuls, 0b000, 0b00, 0b100010, 0b1); + + INSN(fmuld, 0b000, 0b01, 0b000010, 0b1); + INSN(fdivd, 0b000, 0b01, 0b000110, 0b1); + INSN(faddd, 0b000, 0b01, 0b001010, 0b1); + INSN(fsubd, 0b000, 0b01, 0b001110, 0b1); + INSN(fmaxd, 0b000, 0b01, 0b010010, 0b1); + INSN(fmind, 0b000, 0b01, 0b010110, 0b1); + INSN(fnmuld, 0b000, 0b01, 0b100010, 0b1); + + // Half-precision floating-point instructions + INSN(fmulh, 0b000, 0b11, 0b000010, 0b1); + INSN(fdivh, 0b000, 0b11, 0b000110, 0b1); + INSN(faddh, 0b000, 0b11, 0b001010, 0b1); + INSN(fsubh, 0b000, 0b11, 0b001110, 0b1); + INSN(fmaxh, 0b000, 0b11, 0b010010, 0b1); + INSN(fminh, 0b000, 0b11, 0b010110, 0b1); + INSN(fnmulh, 0b000, 0b11, 0b100010, 0b1); +#undef INSN + +// Advanced SIMD scalar three same +#define INSN(NAME, U, size, opcode) \ + void NAME(FloatRegister Vd, FloatRegister Vn, FloatRegister Vm) { \ + starti; \ + f(0b01, 31, 30), f(U, 29), f(0b11110, 28, 24), f(size, 23, 22), f(1, 21); \ + rf(Vm, 16), f(opcode, 15, 11), f(1, 10), rf(Vn, 5), rf(Vd, 0); \ + } + + INSN(fabds, 0b1, 0b10, 0b11010); // Floating-point Absolute Difference (single-precision) + INSN(fabdd, 0b1, 0b11, 0b11010); // Floating-point Absolute Difference (double-precision) + +#undef INSN + +// Advanced SIMD scalar three same FP16 +#define INSN(NAME, U, a, opcode) \ + void NAME(FloatRegister Vd, FloatRegister Vn, FloatRegister Vm) { \ + starti; \ + f(0b01, 31, 30), f(U, 29), f(0b11110, 28, 24), f(a, 23), f(0b10, 22, 21); \ + rf(Vm, 16), f(0b00, 15, 14), f(opcode, 13, 11), f(1, 10), rf(Vn, 5), rf(Vd, 0); \ + } + + INSN(fabdh, 0b1, 0b1, 0b010); // Floating-point Absolute Difference (half-precision float) #undef INSN @@ -2120,6 +2153,7 @@ void mvnw(Register Rd, Register Rm, INSN(fnmaddd, 0b000, 0b01, 1, 0); INSN(fnmsub, 0b000, 0b01, 1, 1); + INSN(fmaddh, 0b000, 0b11, 0, 0); // half-precision fused multiply-add (scalar) #undef INSN // Floating-point conditional select diff --git a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp index 2334cbdff24e4..2e53ecb805829 100644 --- a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp @@ -69,7 +69,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ far_call(RuntimeAddress(a)); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); return; } @@ -90,7 +90,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ blr(lr); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } PredicateFailedStub::PredicateFailedStub(CodeEmitInfo* info) { @@ -103,7 +103,7 @@ void PredicateFailedStub::emit_code(LIR_Assembler* ce) { __ far_call(RuntimeAddress(a)); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } void DivByZeroStub::emit_code(LIR_Assembler* ce) { @@ -274,7 +274,7 @@ void ImplicitNullCheckStub::emit_code(LIR_Assembler* ce) { __ far_call(RuntimeAddress(a)); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } @@ -289,7 +289,7 @@ void SimpleExceptionStub::emit_code(LIR_Assembler* ce) { } __ far_call(RuntimeAddress(Runtime1::entry_for(_stub)), rscratch2); ce->add_call_info_here(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 56a91310dcded..585812a99eec2 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -2743,3 +2743,107 @@ bool C2_MacroAssembler::in_scratch_emit_size() { } return MacroAssembler::in_scratch_emit_size(); } + +static void abort_verify_int_in_range(uint idx, jint val, jint lo, jint hi) { + fatal("Invalid CastII, idx: %u, val: %d, lo: %d, hi: %d", idx, val, lo, hi); +} + +void C2_MacroAssembler::verify_int_in_range(uint idx, const TypeInt* t, Register rval, Register rtmp) { + assert(!t->empty() && !t->singleton(), "%s", Type::str(t)); + if (t == TypeInt::INT) { + return; + } + BLOCK_COMMENT("verify_int_in_range {"); + Label L_success, L_failure; + + jint lo = t->_lo; + jint hi = t->_hi; + + if (lo != min_jint && hi != max_jint) { + subsw(rtmp, rval, lo); + br(Assembler::LT, L_failure); + subsw(rtmp, rval, hi); + br(Assembler::LE, L_success); + } else if (lo != min_jint) { + subsw(rtmp, rval, lo); + br(Assembler::GE, L_success); + } else if (hi != max_jint) { + subsw(rtmp, rval, hi); + br(Assembler::LE, L_success); + } else { + ShouldNotReachHere(); + } + + bind(L_failure); + movw(c_rarg0, idx); + mov(c_rarg1, rval); + movw(c_rarg2, lo); + movw(c_rarg3, hi); + reconstruct_frame_pointer(rtmp); + rt_call(CAST_FROM_FN_PTR(address, abort_verify_int_in_range), rtmp); + hlt(0); + + bind(L_success); + BLOCK_COMMENT("} verify_int_in_range"); +} + +static void abort_verify_long_in_range(uint idx, jlong val, jlong lo, jlong hi) { + fatal("Invalid CastLL, idx: %u, val: " JLONG_FORMAT ", lo: " JLONG_FORMAT ", hi: " JLONG_FORMAT, idx, val, lo, hi); +} + +void C2_MacroAssembler::verify_long_in_range(uint idx, const TypeLong* t, Register rval, Register rtmp) { + assert(!t->empty() && !t->singleton(), "%s", Type::str(t)); + if (t == TypeLong::LONG) { + return; + } + BLOCK_COMMENT("verify_long_in_range {"); + Label L_success, L_failure; + + jlong lo = t->_lo; + jlong hi = t->_hi; + + if (lo != min_jlong && hi != max_jlong) { + subs(rtmp, rval, lo); + br(Assembler::LT, L_failure); + subs(rtmp, rval, hi); + br(Assembler::LE, L_success); + } else if (lo != min_jlong) { + subs(rtmp, rval, lo); + br(Assembler::GE, L_success); + } else if (hi != max_jlong) { + subs(rtmp, rval, hi); + br(Assembler::LE, L_success); + } else { + ShouldNotReachHere(); + } + + bind(L_failure); + movw(c_rarg0, idx); + mov(c_rarg1, rval); + mov(c_rarg2, lo); + mov(c_rarg3, hi); + reconstruct_frame_pointer(rtmp); + rt_call(CAST_FROM_FN_PTR(address, abort_verify_long_in_range), rtmp); + hlt(0); + + bind(L_success); + BLOCK_COMMENT("} verify_long_in_range"); +} + +void C2_MacroAssembler::reconstruct_frame_pointer(Register rtmp) { + const int framesize = Compile::current()->output()->frame_size_in_bytes(); + if (PreserveFramePointer) { + // frame pointer is valid +#ifdef ASSERT + // Verify frame pointer value in rfp. + add(rtmp, sp, framesize - 2 * wordSize); + Label L_success; + cmp(rfp, rtmp); + br(Assembler::EQ, L_success); + stop("frame pointer mismatch"); + bind(L_success); +#endif // ASSERT + } else { + add(rfp, sp, framesize - 2 * wordSize); + } +} diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index e0eaa0b76e6e9..70e4265c7cc5e 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -188,4 +188,9 @@ void vector_signum_sve(FloatRegister dst, FloatRegister src, FloatRegister zero, FloatRegister one, FloatRegister vtmp, PRegister pgtmp, SIMD_RegVariant T); + void verify_int_in_range(uint idx, const TypeInt* t, Register val, Register tmp); + void verify_long_in_range(uint idx, const TypeLong* t, Register val, Register tmp); + + void reconstruct_frame_pointer(Register rtmp); + #endif // CPU_AARCH64_C2_MACROASSEMBLER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp b/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp index 0c2d9a32c8c13..3874c8cd54ef9 100644 --- a/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/compressedKlass_aarch64.cpp @@ -70,7 +70,7 @@ static char* reserve_at_eor_compatible_address(size_t size, bool aslr) { const uint64_t immediate = ((uint64_t)immediates[index]) << 32; assert(immediate > 0 && Assembler::operand_valid_for_logical_immediate(/*is32*/false, immediate), "Invalid immediate %d " UINT64_FORMAT, index, immediate); - result = os::attempt_reserve_memory_at((char*)immediate, size, false); + result = os::attempt_reserve_memory_at((char*)immediate, size, mtNone); if (result == nullptr) { log_trace(metaspace, map)("Failed to attach at " UINT64_FORMAT_X, immediate); } @@ -114,7 +114,7 @@ char* CompressedKlassPointers::reserve_address_space_for_compressed_classes(size if (result == nullptr) { constexpr size_t alignment = nth_bit(32); log_debug(metaspace, map)("Trying to reserve at a 32-bit-aligned address"); - result = os::reserve_memory_aligned(size, alignment, false); + result = os::reserve_memory_aligned(size, alignment, mtNone); } return result; diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp index e33ef47cf3c38..e4db8a9ab1f82 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp @@ -27,9 +27,9 @@ #include "c1/c1_MacroAssembler.hpp" #include "compiler/compilerDefinitions.inline.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" -#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #define __ masm->masm()-> diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index ac22b43faaf02..a2b3f44c68b72 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -23,6 +23,8 @@ * */ +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahForwarding.hpp" @@ -30,10 +32,8 @@ #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" -#include "interpreter/interpreter.hpp" #include "interpreter/interp_masm.hpp" +#include "interpreter/interpreter.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" #ifdef COMPILER1 diff --git a/src/hotspot/cpu/aarch64/gc/z/zAddress_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zAddress_aarch64.cpp index 20e37528c047c..7008615ed438a 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zAddress_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zAddress_aarch64.cpp @@ -21,8 +21,8 @@ * questions. */ -#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcLogPrecious.hpp" #include "gc/z/zAddress.hpp" #include "gc/z/zBarrierSetAssembler.hpp" #include "gc/z/zGlobals.hpp" diff --git a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp index faf635dc33282..948ba97aa2234 100644 --- a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -46,7 +46,7 @@ const bool CCallingConventionRequiresIntsAsLongs = false; #define DEFAULT_CACHE_LINE_SIZE 64 // The default padding size for data structures to avoid false sharing. -#define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE +#define DEFAULT_PADDING_SIZE (2*DEFAULT_CACHE_LINE_SIZE) // According to the ARMv8 ARM, "Concurrent modification and execution // of instructions can lead to the resulting instruction performing diff --git a/src/hotspot/cpu/aarch64/icache_aarch64.cpp b/src/hotspot/cpu/aarch64/icache_aarch64.cpp index 311f3a7de1f73..a942406f45ee9 100644 --- a/src/hotspot/cpu/aarch64/icache_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/icache_aarch64.cpp @@ -31,4 +31,4 @@ void ICacheStubGenerator::generate_icache_flush( *flush_icache_stub = nullptr; } -void ICache::initialize() {} +void ICache::initialize(int phase) {} diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 9a4b8e243a978..cf347768de3e1 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -675,6 +675,9 @@ void MacroAssembler::set_last_Java_frame(Register last_java_sp, } static inline bool target_needs_far_branch(address addr) { + if (AOTCodeCache::is_on_for_dump()) { + return true; + } // codecache size <= 128M if (!MacroAssembler::far_branches()) { return false; @@ -859,6 +862,9 @@ void MacroAssembler::call_VM_helper(Register oop_result, address entry_point, in // Check the entry target is always reachable from any branch. static bool is_always_within_branch_range(Address entry) { + if (AOTCodeCache::is_on_for_dump()) { + return false; + } const address target = entry.target(); if (!CodeCache::contains(target)) { @@ -1003,9 +1009,6 @@ void MacroAssembler::c2bool(Register x) { address MacroAssembler::ic_call(address entry, jint method_index) { RelocationHolder rh = virtual_call_Relocation::spec(pc(), method_index); - // address const_ptr = long_constant((jlong)Universe::non_oop_word()); - // uintptr_t offset; - // ldr_constant(rscratch2, const_ptr); movptr(rscratch2, (intptr_t)Universe::non_oop_word()); return trampoline_call(Address(entry, rh)); } @@ -2041,7 +2044,7 @@ void MacroAssembler::clinit_barrier(Register klass, Register scratch, Label* L_f // Fast path check: class is fully initialized lea(scratch, Address(klass, InstanceKlass::init_state_offset())); ldarb(scratch, scratch); - subs(zr, scratch, InstanceKlass::fully_initialized); + cmp(scratch, InstanceKlass::fully_initialized); br(Assembler::EQ, *L_fast_path); // Fast path check: current thread is initializer thread @@ -2157,7 +2160,7 @@ void MacroAssembler::call_VM_leaf_base(address entry_point, stp(rscratch1, rmethod, Address(pre(sp, -2 * wordSize))); - mov(rscratch1, entry_point); + mov(rscratch1, RuntimeAddress(entry_point)); blr(rscratch1); if (retaddr) bind(*retaddr); @@ -3234,9 +3237,13 @@ void MacroAssembler::resolve_global_jobject(Register value, Register tmp1, Regis } void MacroAssembler::stop(const char* msg) { - BLOCK_COMMENT(msg); + // Skip AOT caching C strings in scratch buffer. + const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg); + BLOCK_COMMENT(str); + // load msg into r0 so we can access it from the signal handler + // ExternalAddress enables saving and restoring via the code cache + lea(c_rarg0, ExternalAddress((address) str)); dcps1(0xdeae); - emit_int64((uintptr_t)msg); } void MacroAssembler::unimplemented(const char* what) { @@ -5520,9 +5527,8 @@ void MacroAssembler::movoop(Register dst, jobject obj) { mov(dst, Address((address)obj, rspec)); } else { address dummy = address(uintptr_t(pc()) & -wordSize); // A nearby aligned address - ldr_constant(dst, Address(dummy, rspec)); + ldr(dst, Address(dummy, rspec)); } - } // Move a metadata address into a register. diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 11d1985e50bf4..17ee72a00c0e0 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -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. * Copyright (c) 2014, 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #define CPU_AARCH64_MACROASSEMBLER_AARCH64_HPP #include "asm/assembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "code/vmreg.hpp" #include "metaprogramming/enableIf.hpp" #include "oops/compressedOops.hpp" @@ -1315,6 +1316,10 @@ class MacroAssembler: public Assembler { // Check if branches to the non nmethod section require a far jump static bool codestub_branch_needs_far_jump() { + if (AOTCodeCache::is_on_for_dump()) { + // To calculate far_codestub_branch_size correctly. + return true; + } return CodeCache::max_distance_to_non_nmethod() > branch_range; } @@ -1472,16 +1477,6 @@ class MacroAssembler: public Assembler { public: - void ldr_constant(Register dest, const Address &const_addr) { - if (NearCpool) { - ldr(dest, const_addr); - } else { - uint64_t offset; - adrp(dest, InternalAddress(const_addr.target()), offset); - ldr(dest, Address(dest, offset)); - } - } - address read_polling_page(Register r, relocInfo::relocType rtype); void get_polling_page(Register dest, relocInfo::relocType rtype); @@ -1611,11 +1606,15 @@ class MacroAssembler: public Assembler { void aes_round(FloatRegister input, FloatRegister subkey); // ChaCha20 functions support block - void cc20_quarter_round(FloatRegister aVec, FloatRegister bVec, - FloatRegister cVec, FloatRegister dVec, FloatRegister scratch, - FloatRegister tbl); - void cc20_shift_lane_org(FloatRegister bVec, FloatRegister cVec, - FloatRegister dVec, bool colToDiag); + void cc20_qr_add4(FloatRegister (&addFirst)[4], + FloatRegister (&addSecond)[4]); + void cc20_qr_xor4(FloatRegister (&firstElem)[4], + FloatRegister (&secondElem)[4], FloatRegister (&result)[4]); + void cc20_qr_lrot4(FloatRegister (&sourceReg)[4], + FloatRegister (&destReg)[4], int bits, FloatRegister table); + void cc20_set_qr_registers(FloatRegister (&vectorSet)[4], + const FloatRegister (&stateVectors)[16], int idx1, int idx2, + int idx3, int idx4); // Place an ISB after code may have been modified due to a safepoint. void safepoint_isb(); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64_chacha.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64_chacha.cpp index 1f7bb8f46f64f..083e81af5d969 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64_chacha.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64_chacha.cpp @@ -28,60 +28,119 @@ #include "runtime/stubRoutines.hpp" /** - * Perform the quarter round calculations on values contained within - * four SIMD registers. + * Perform the vectorized add for a group of 4 quarter round operations. + * In the ChaCha20 quarter round, there are two add ops: a += b and c += d. + * Each parameter is a set of 4 registers representing the 4 registers + * for the each addend in the add operation for each of the quarter rounds. + * (e.g. for "a" it would consist of v0/v1/v2/v3). The result of the add + * is placed into the vectors in the "addFirst" array. * - * @param aVec the SIMD register containing only the "a" values - * @param bVec the SIMD register containing only the "b" values - * @param cVec the SIMD register containing only the "c" values - * @param dVec the SIMD register containing only the "d" values - * @param scratch scratch SIMD register used for 12 and 7 bit left rotations - * @param table the SIMD register used as a table for 8 bit left rotations + * @param addFirst array of SIMD registers representing the first addend. + * @param addSecond array of SIMD registers representing the second addend. */ -void MacroAssembler::cc20_quarter_round(FloatRegister aVec, FloatRegister bVec, - FloatRegister cVec, FloatRegister dVec, FloatRegister scratch, - FloatRegister table) { +void MacroAssembler::cc20_qr_add4(FloatRegister (&addFirst)[4], + FloatRegister (&addSecond)[4]) { + for (int i = 0; i < 4; i++) { + addv(addFirst[i], T4S, addFirst[i], addSecond[i]); + } +} + + +/** + * Perform the vectorized XOR for a group of 4 quarter round operations. + * In the ChaCha20 quarter round, there are two XOR ops: d ^= a and b ^= c + * Each parameter is a set of 4 registers representing the 4 registers + * for the each element in the xor operation for each of the quarter rounds. + * (e.g. for "a" it would consist of v0/v1/v2/v3) + * Note: because the b ^= c ops precede a non-byte-aligned left-rotation, + * there is a third parameter which can take a set of scratch registers + * for the result, which facilitates doing the subsequent operations for + * the left rotation. + * + * @param firstElem array of SIMD registers representing the first element. + * @param secondElem array of SIMD registers representing the second element. + * @param result array of SIMD registers representing the destination. + * May be the same as firstElem or secondElem, or a separate array. + */ +void MacroAssembler::cc20_qr_xor4(FloatRegister (&firstElem)[4], + FloatRegister (&secondElem)[4], FloatRegister (&result)[4]) { + for (int i = 0; i < 4; i++) { + eor(result[i], T16B, firstElem[i], secondElem[i]); + } +} + +/** + * Perform the vectorized left-rotation on 32-bit lanes for a group of + * 4 quarter round operations. + * Each parameter is a set of 4 registers representing the 4 registers + * for the each element in the source and destination for each of the quarter + * rounds (e.g. for "d" it would consist of v12/v13/v14/v15 on columns and + * v15/v12/v13/v14 on diagonal alignments). + * + * @param sourceReg array of SIMD registers representing the source + * @param destReg array of SIMD registers representing the destination + * @param bits the distance of the rotation in bits, must be 16/12/8/7 per + * the ChaCha20 specification. + */ +void MacroAssembler::cc20_qr_lrot4(FloatRegister (&sourceReg)[4], + FloatRegister (&destReg)[4], int bits, FloatRegister table) { + switch (bits) { + case 16: // reg <<<= 16, in-place swap of half-words + for (int i = 0; i < 4; i++) { + rev32(destReg[i], T8H, sourceReg[i]); + } + break; - // a += b, d ^= a, d <<<= 16 - addv(aVec, T4S, aVec, bVec); - eor(dVec, T16B, dVec, aVec); - rev32(dVec, T8H, dVec); + case 7: // reg <<<= (12 || 7) + case 12: // r-shift src -> dest, l-shift src & ins to dest + for (int i = 0; i < 4; i++) { + ushr(destReg[i], T4S, sourceReg[i], 32 - bits); + } - // c += d, b ^= c, b <<<= 12 - addv(cVec, T4S, cVec, dVec); - eor(scratch, T16B, bVec, cVec); - ushr(bVec, T4S, scratch, 20); - sli(bVec, T4S, scratch, 12); + for (int i = 0; i < 4; i++) { + sli(destReg[i], T4S, sourceReg[i], bits); + } + break; - // a += b, d ^= a, d <<<= 8 - addv(aVec, T4S, aVec, bVec); - eor(dVec, T16B, dVec, aVec); - tbl(dVec, T16B, dVec, 1, table); + case 8: // reg <<<= 8, simulate left rotation with table reorg + for (int i = 0; i < 4; i++) { + tbl(destReg[i], T16B, sourceReg[i], 1, table); + } + break; - // c += d, b ^= c, b <<<= 7 - addv(cVec, T4S, cVec, dVec); - eor(scratch, T16B, bVec, cVec); - ushr(bVec, T4S, scratch, 25); - sli(bVec, T4S, scratch, 7); + default: + // The caller shouldn't be sending bit rotation values outside + // of the 16/12/8/7 as defined in the specification. + ShouldNotReachHere(); + } } /** - * Shift the b, c, and d vectors between columnar and diagonal representations. - * Note that the "a" vector does not shift. + * Set the FloatRegisters for a 4-vector register set. These will be used + * during various quarter round transformations (adds, xors and left-rotations). + * This method itself does not result in the output of any assembly + * instructions. It just organizes the vectors so they can be in columnar or + * diagonal alignments. * - * @param bVec the SIMD register containing only the "b" values - * @param cVec the SIMD register containing only the "c" values - * @param dVec the SIMD register containing only the "d" values - * @param colToDiag true if moving columnar to diagonal, false if - * moving diagonal back to columnar. + * @param vectorSet a 4-vector array to be altered into a new alignment + * @param stateVectors the 16-vector array that represents the current + * working state. The indices of this array match up with the + * organization of the ChaCha20 state per RFC 7539 (e.g. stateVectors[12] + * would contain the vector that holds the 32-bit counter, etc.) + * @param idx1 the index of the stateVectors array to be assigned to the + * first vectorSet element. + * @param idx2 the index of the stateVectors array to be assigned to the + * second vectorSet element. + * @param idx3 the index of the stateVectors array to be assigned to the + * third vectorSet element. + * @param idx4 the index of the stateVectors array to be assigned to the + * fourth vectorSet element. */ -void MacroAssembler::cc20_shift_lane_org(FloatRegister bVec, FloatRegister cVec, - FloatRegister dVec, bool colToDiag) { - int bShift = colToDiag ? 4 : 12; - int cShift = 8; - int dShift = colToDiag ? 12 : 4; - - ext(bVec, T16B, bVec, bVec, bShift); - ext(cVec, T16B, cVec, cVec, cShift); - ext(dVec, T16B, dVec, dVec, dShift); +void MacroAssembler::cc20_set_qr_registers(FloatRegister (&vectorSet)[4], + const FloatRegister (&stateVectors)[16], int idx1, int idx2, + int idx3, int idx4) { + vectorSet[0] = stateVectors[idx1]; + vectorSet[1] = stateVectors[idx2]; + vectorSet[2] = stateVectors[idx3]; + vectorSet[3] = stateVectors[idx4]; } diff --git a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp index a6cd055775870..0fbc2ef141e8b 100644 --- a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -200,4 +200,8 @@ return false; } + // Is FEAT_FP16 supported for this CPU? + static bool is_feat_fp16_supported() { + return (VM_Version::supports_fphp() && VM_Version::supports_asimdhp()); + } #endif // CPU_AARCH64_MATCHER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp index 588b8898d2d2a..cdf67e3423f66 100644 --- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp @@ -93,14 +93,60 @@ void MethodHandles::verify_klass(MacroAssembler* _masm, void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) { } +void MethodHandles::verify_method(MacroAssembler* _masm, Register method, vmIntrinsics::ID iid) { + BLOCK_COMMENT("verify_method {"); + __ verify_method_ptr(method); + if (VerifyMethodHandles) { + Label L_ok; + assert_different_registers(method, rscratch1, rscratch2); + const Register method_holder = rscratch1; + __ load_method_holder(method_holder, method); + + switch (iid) { + case vmIntrinsicID::_invokeBasic: + // Require compiled LambdaForm class to be fully initialized. + __ lea(rscratch2, Address(method_holder, InstanceKlass::init_state_offset())); + __ ldarb(rscratch2, rscratch2); + __ cmp(rscratch2, InstanceKlass::fully_initialized); + __ br(Assembler::EQ, L_ok); + break; + + case vmIntrinsicID::_linkToStatic: + __ clinit_barrier(method_holder, rscratch2, &L_ok); + break; + + case vmIntrinsicID::_linkToVirtual: + case vmIntrinsicID::_linkToSpecial: + case vmIntrinsicID::_linkToInterface: + // Class initialization check is too strong here. Just ensure that class initialization has been initiated. + __ lea(rscratch2, Address(method_holder, InstanceKlass::init_state_offset())); + __ ldarb(rscratch2, rscratch2); + __ cmp(rscratch2, InstanceKlass::being_initialized); + __ br(Assembler::GE, L_ok); + + // init_state check failed, but it may be an abstract interface method + __ ldrh(rscratch2, Address(method, Method::access_flags_offset())); + __ tbnz(rscratch2, exact_log2(JVM_ACC_ABSTRACT), L_ok); + break; + + default: + fatal("unexpected intrinsic %d: %s", vmIntrinsics::as_int(iid), vmIntrinsics::name_at(iid)); + } + + // Method holder init state check failed for a concrete method. + __ stop("Method holder klass is not initialized"); + __ bind(L_ok); + } + BLOCK_COMMENT("} verify_method"); +} #endif //ASSERT void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, - bool for_compiler_entry) { + bool for_compiler_entry, vmIntrinsics::ID iid) { assert(method == rmethod, "interpreter calling convention"); Label L_no_such_method; __ cbz(rmethod, L_no_such_method); - __ verify_method_ptr(method); + verify_method(_masm, method, iid); if (!for_compiler_entry && JvmtiExport::can_post_interpreter_events()) { Label run_compiled_code; @@ -160,7 +206,7 @@ void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm, __ BIND(L); } - jump_from_method_handle(_masm, method_temp, temp2, for_compiler_entry); + jump_from_method_handle(_masm, method_temp, temp2, for_compiler_entry, vmIntrinsics::_invokeBasic); BLOCK_COMMENT("} jump_to_lambda_form"); } @@ -447,8 +493,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, // After figuring out which concrete method to call, jump into it. // Note that this works in the interpreter with no data motion. // But the compiled version will require that r2_recv be shifted out. - __ verify_method_ptr(rmethod); - jump_from_method_handle(_masm, rmethod, temp1, for_compiler_entry); + jump_from_method_handle(_masm, rmethod, temp1, for_compiler_entry, iid); if (iid == vmIntrinsics::_linkToInterface) { __ bind(L_incompatible_class_change_error); __ far_jump(RuntimeAddress(SharedRuntime::throw_IncompatibleClassChangeError_entry())); diff --git a/src/hotspot/cpu/aarch64/methodHandles_aarch64.hpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.hpp index bd36f3e84c29a..e82f4d6237ea1 100644 --- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.hpp @@ -39,6 +39,8 @@ enum /* platform_dependent_constants */ { Register obj, vmClassID klass_id, const char* error_message = "wrong klass") NOT_DEBUG_RETURN; + static void verify_method(MacroAssembler* _masm, Register method, vmIntrinsics::ID iid) NOT_DEBUG_RETURN; + static void verify_method_handle(MacroAssembler* _masm, Register mh_reg) { verify_klass(_masm, mh_reg, VM_CLASS_ID(java_lang_invoke_MethodHandle), "reference is a MH"); @@ -49,7 +51,7 @@ enum /* platform_dependent_constants */ { // Similar to InterpreterMacroAssembler::jump_from_interpreted. // Takes care of special dispatch from single stepping too. static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, - bool for_compiler_entry); + bool for_compiler_entry, vmIntrinsics::ID iid); static void jump_to_lambda_form(MacroAssembler* _masm, Register recv, Register method_temp, diff --git a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp index 83e43c3ebd235..2361d584f4252 100644 --- a/src/hotspot/cpu/aarch64/runtime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp @@ -65,6 +65,9 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // Setup code generation tools const char* name = OptoRuntime::stub_name(OptoStubId::uncommon_trap_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } MacroAssembler* masm = new MacroAssembler(&buffer); assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); @@ -285,6 +288,9 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { // Setup code generation tools const char* name = OptoRuntime::stub_name(OptoStubId::exception_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } MacroAssembler* masm = new MacroAssembler(&buffer); // TODO check various assumptions made here diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 967984b882166..0c3dfabc93e88 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -557,40 +557,6 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, // If this happens, control eventually transfers back to the compiled // caller, but with an uncorrected stack, causing delayed havoc. - if (VerifyAdapterCalls && - (Interpreter::code() != nullptr || StubRoutines::final_stubs_code() != nullptr)) { -#if 0 - // So, let's test for cascading c2i/i2c adapters right now. - // assert(Interpreter::contains($return_addr) || - // StubRoutines::contains($return_addr), - // "i2c adapter must return to an interpreter frame"); - __ block_comment("verify_i2c { "); - Label L_ok; - if (Interpreter::code() != nullptr) { - range_check(masm, rax, r11, - Interpreter::code()->code_start(), Interpreter::code()->code_end(), - L_ok); - } - if (StubRoutines::initial_stubs_code() != nullptr) { - range_check(masm, rax, r11, - StubRoutines::initial_stubs_code()->code_begin(), - StubRoutines::initial_stubs_code()->code_end(), - L_ok); - } - if (StubRoutines::final_stubs_code() != nullptr) { - range_check(masm, rax, r11, - StubRoutines::final_stubs_code()->code_begin(), - StubRoutines::final_stubs_code()->code_end(), - L_ok); - } - const char* msg = "i2c adapter must return to an interpreter frame"; - __ block_comment(msg); - __ stop(msg); - __ bind(L_ok); - __ block_comment("} verify_i2ce "); -#endif - } - // Cut-out for having no stack args. int comp_words_on_stack = align_up(comp_args_on_stack*VMRegImpl::stack_slot_size, wordSize)>>LogBytesPerWord; if (comp_args_on_stack) { @@ -711,12 +677,12 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, } // --------------------------------------------------------------- -AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, - int total_args_passed, - int comp_args_on_stack, - const BasicType *sig_bt, - const VMRegPair *regs, - AdapterFingerPrint* fingerprint) { +void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + AdapterHandlerEntry* handler) { address i2c_entry = __ pc(); gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); @@ -777,7 +743,8 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); - return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry); + handler->set_entry_points(i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry); + return; } static int c_calling_convention_priv(const BasicType *sig_bt, diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 467505ed33784..f5567dcc03ac5 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -4405,89 +4405,44 @@ class StubGenerator: public StubCodeGenerator { return start; } - /** - * Arguments: - * - * Inputs: - * c_rarg0 - int crc - * c_rarg1 - byte* buf - * c_rarg2 - int length - * - * Output: - * rax - int crc result - */ - address generate_updateBytesCRC32() { - assert(UseCRC32Intrinsics, "what are we doing here?"); - - __ align(CodeEntryAlignment); - StubGenStubId stub_id = StubGenStubId::updateBytesCRC32_id; - StubCodeMark mark(this, stub_id); - - address start = __ pc(); - - const Register crc = c_rarg0; // crc - const Register buf = c_rarg1; // source java byte array address - const Register len = c_rarg2; // length - const Register table0 = c_rarg3; // crc_table address - const Register table1 = c_rarg4; - const Register table2 = c_rarg5; - const Register table3 = c_rarg6; - const Register tmp3 = c_rarg7; - - BLOCK_COMMENT("Entry:"); - __ enter(); // required for proper stackwalking of RuntimeStub frame - - __ kernel_crc32(crc, buf, len, - table0, table1, table2, table3, rscratch1, rscratch2, tmp3); - - __ leave(); // required for proper stackwalking of RuntimeStub frame - __ ret(lr); - - return start; - } - - // ChaCha20 block function. This version parallelizes 4 quarter - // round operations at a time. It uses 16 SIMD registers to - // produce 4 blocks of key stream. + // ChaCha20 block function. This version parallelizes the 32-bit + // state elements on each of 16 vectors, producing 4 blocks of + // keystream at a time. // // state (int[16]) = c_rarg0 // keystream (byte[256]) = c_rarg1 - // return - number of bytes of keystream (always 256) - // - // In this approach, we load the 512-bit start state sequentially into - // 4 128-bit vectors. We then make 4 4-vector copies of that starting - // state, with each successive set of 4 vectors having a +1 added into - // the first 32-bit lane of the 4th vector in that group (the counter). - // By doing this, we can perform the block function on 4 512-bit blocks - // within one run of this intrinsic. - // The alignment of the data across the 4-vector group is such that at - // the start it is already aligned for the first round of each two-round - // loop iteration. In other words, the corresponding lanes of each vector - // will contain the values needed for that quarter round operation (e.g. - // elements 0/4/8/12, 1/5/9/13, 2/6/10/14, etc.). - // In between each full round, a lane shift must occur. Within a loop - // iteration, between the first and second rounds, the 2nd, 3rd, and 4th - // vectors are rotated left 32, 64 and 96 bits, respectively. The result - // is effectively a diagonal orientation in columnar form. After the - // second full round, those registers are left-rotated again, this time - // 96, 64, and 32 bits - returning the vectors to their columnar organization. - // After all 10 iterations, the original state is added to each 4-vector - // working state along with the add mask, and the 4 vector groups are - // sequentially written to the memory dedicated for the output key stream. + // return - number of bytes of produced keystream (always 256) // - // For a more detailed explanation, see Goll and Gueron, "Vectorization of - // ChaCha Stream Cipher", 2014 11th Int. Conf. on Information Technology: - // New Generations, Las Vegas, NV, USA, April 2014, DOI: 10.1109/ITNG.2014.33 - address generate_chacha20Block_qrpar() { - Label L_Q_twoRounds, L_Q_cc20_const; + // This implementation takes each 32-bit integer from the state + // array and broadcasts it across all 4 32-bit lanes of a vector register + // (e.g. state[0] is replicated on all 4 lanes of v4, state[1] to all 4 lanes + // of v5, etc.). Once all 16 elements have been broadcast onto 16 vectors, + // the quarter round schedule is implemented as outlined in RFC 7539 section + // 2.3. However, instead of sequentially processing the 3 quarter round + // operations represented by one QUARTERROUND function, we instead stack all + // the adds, xors and left-rotations from the first 4 quarter rounds together + // and then do the same for the second set of 4 quarter rounds. This removes + // some latency that would otherwise be incurred by waiting for an add to + // complete before performing an xor (which depends on the result of the + // add), etc. An adjustment happens between the first and second groups of 4 + // quarter rounds, but this is done only in the inputs to the macro functions + // that generate the assembly instructions - these adjustments themselves are + // not part of the resulting assembly. + // The 4 registers v0-v3 are used during the quarter round operations as + // scratch registers. Once the 20 rounds are complete, these 4 scratch + // registers become the vectors involved in adding the start state back onto + // the post-QR working state. After the adds are complete, each of the 16 + // vectors write their first lane back to the keystream buffer, followed + // by the second lane from all vectors and so on. + address generate_chacha20Block_blockpar() { + Label L_twoRounds, L_cc20_const; // The constant data is broken into two 128-bit segments to be loaded - // onto SIMD registers. The first 128 bits are a counter add overlay - // that adds +1/+0/+0/+0 to the vectors holding replicated state[12]. + // onto FloatRegisters. The first 128 bits are a counter add overlay + // that adds +0/+1/+2/+3 to the vector holding replicated state[12]. // The second 128-bits is a table constant used for 8-bit left rotations. - // on 32-bit lanes within a SIMD register. - __ BIND(L_Q_cc20_const); - __ emit_int64(0x0000000000000001UL); - __ emit_int64(0x0000000000000000UL); + __ BIND(L_cc20_const); + __ emit_int64(0x0000000100000000UL); + __ emit_int64(0x0000000300000002UL); __ emit_int64(0x0605040702010003UL); __ emit_int64(0x0E0D0C0F0A09080BUL); @@ -4497,144 +4452,142 @@ class StubGenerator: public StubCodeGenerator { address start = __ pc(); __ enter(); + int i, j; const Register state = c_rarg0; const Register keystream = c_rarg1; const Register loopCtr = r10; const Register tmpAddr = r11; + const FloatRegister ctrAddOverlay = v28; + const FloatRegister lrot8Tbl = v29; + + // Organize SIMD registers in an array that facilitates + // putting repetitive opcodes into loop structures. It is + // important that each grouping of 4 registers is monotonically + // increasing to support the requirements of multi-register + // instructions (e.g. ld4r, st4, etc.) + const FloatRegister workSt[16] = { + v4, v5, v6, v7, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27 + }; - const FloatRegister aState = v0; - const FloatRegister bState = v1; - const FloatRegister cState = v2; - const FloatRegister dState = v3; - const FloatRegister a1Vec = v4; - const FloatRegister b1Vec = v5; - const FloatRegister c1Vec = v6; - const FloatRegister d1Vec = v7; - // Skip the callee-saved registers v8 - v15 - const FloatRegister a2Vec = v16; - const FloatRegister b2Vec = v17; - const FloatRegister c2Vec = v18; - const FloatRegister d2Vec = v19; - const FloatRegister a3Vec = v20; - const FloatRegister b3Vec = v21; - const FloatRegister c3Vec = v22; - const FloatRegister d3Vec = v23; - const FloatRegister a4Vec = v24; - const FloatRegister b4Vec = v25; - const FloatRegister c4Vec = v26; - const FloatRegister d4Vec = v27; - const FloatRegister scratch = v28; - const FloatRegister addMask = v29; - const FloatRegister lrot8Tbl = v30; - - // Load the initial state in the first 4 quadword registers, - // then copy the initial state into the next 4 quadword registers - // that will be used for the working state. - __ ld1(aState, bState, cState, dState, __ T16B, Address(state)); - - // Load the index register for 2 constant 128-bit data fields. - // The first represents the +1/+0/+0/+0 add mask. The second is - // the 8-bit left rotation. - __ adr(tmpAddr, L_Q_cc20_const); - __ ldpq(addMask, lrot8Tbl, Address(tmpAddr)); - - __ mov(a1Vec, __ T16B, aState); - __ mov(b1Vec, __ T16B, bState); - __ mov(c1Vec, __ T16B, cState); - __ mov(d1Vec, __ T16B, dState); - - __ mov(a2Vec, __ T16B, aState); - __ mov(b2Vec, __ T16B, bState); - __ mov(c2Vec, __ T16B, cState); - __ addv(d2Vec, __ T4S, d1Vec, addMask); - - __ mov(a3Vec, __ T16B, aState); - __ mov(b3Vec, __ T16B, bState); - __ mov(c3Vec, __ T16B, cState); - __ addv(d3Vec, __ T4S, d2Vec, addMask); - - __ mov(a4Vec, __ T16B, aState); - __ mov(b4Vec, __ T16B, bState); - __ mov(c4Vec, __ T16B, cState); - __ addv(d4Vec, __ T4S, d3Vec, addMask); - - // Set up the 10 iteration loop + // Pull in constant data. The first 16 bytes are the add overlay + // which is applied to the vector holding the counter (state[12]). + // The second 16 bytes is the index register for the 8-bit left + // rotation tbl instruction. + __ adr(tmpAddr, L_cc20_const); + __ ldpq(ctrAddOverlay, lrot8Tbl, Address(tmpAddr)); + + // Load from memory and interlace across 16 SIMD registers, + // With each word from memory being broadcast to all lanes of + // each successive SIMD register. + // Addr(0) -> All lanes in workSt[i] + // Addr(4) -> All lanes workSt[i + 1], etc. + __ mov(tmpAddr, state); + for (i = 0; i < 16; i += 4) { + __ ld4r(workSt[i], workSt[i + 1], workSt[i + 2], workSt[i + 3], __ T4S, + __ post(tmpAddr, 16)); + } + __ addv(workSt[12], __ T4S, workSt[12], ctrAddOverlay); // Add ctr overlay + + // Before entering the loop, create 5 4-register arrays. These + // will hold the 4 registers that represent the a/b/c/d fields + // in the quarter round operation. For instance the "b" field + // for the first 4 quarter round operations is the set of v16/v17/v18/v19, + // but in the second 4 quarter rounds it gets adjusted to v17/v18/v19/v16 + // since it is part of a diagonal organization. The aSet and scratch + // register sets are defined at declaration time because they do not change + // organization at any point during the 20-round processing. + FloatRegister aSet[4] = { v4, v5, v6, v7 }; + FloatRegister bSet[4]; + FloatRegister cSet[4]; + FloatRegister dSet[4]; + FloatRegister scratch[4] = { v0, v1, v2, v3 }; + + // Set up the 10 iteration loop and perform all 8 quarter round ops __ mov(loopCtr, 10); - __ BIND(L_Q_twoRounds); - - // The first set of operations on the vectors covers the first 4 quarter - // round operations: - // Qround(state, 0, 4, 8,12) - // Qround(state, 1, 5, 9,13) - // Qround(state, 2, 6,10,14) - // Qround(state, 3, 7,11,15) - __ cc20_quarter_round(a1Vec, b1Vec, c1Vec, d1Vec, scratch, lrot8Tbl); - __ cc20_quarter_round(a2Vec, b2Vec, c2Vec, d2Vec, scratch, lrot8Tbl); - __ cc20_quarter_round(a3Vec, b3Vec, c3Vec, d3Vec, scratch, lrot8Tbl); - __ cc20_quarter_round(a4Vec, b4Vec, c4Vec, d4Vec, scratch, lrot8Tbl); - - // Shuffle the b1Vec/c1Vec/d1Vec to reorganize the state vectors to - // diagonals. The a1Vec does not need to change orientation. - __ cc20_shift_lane_org(b1Vec, c1Vec, d1Vec, true); - __ cc20_shift_lane_org(b2Vec, c2Vec, d2Vec, true); - __ cc20_shift_lane_org(b3Vec, c3Vec, d3Vec, true); - __ cc20_shift_lane_org(b4Vec, c4Vec, d4Vec, true); - - // The second set of operations on the vectors covers the second 4 quarter - // round operations, now acting on the diagonals: - // Qround(state, 0, 5,10,15) - // Qround(state, 1, 6,11,12) - // Qround(state, 2, 7, 8,13) - // Qround(state, 3, 4, 9,14) - __ cc20_quarter_round(a1Vec, b1Vec, c1Vec, d1Vec, scratch, lrot8Tbl); - __ cc20_quarter_round(a2Vec, b2Vec, c2Vec, d2Vec, scratch, lrot8Tbl); - __ cc20_quarter_round(a3Vec, b3Vec, c3Vec, d3Vec, scratch, lrot8Tbl); - __ cc20_quarter_round(a4Vec, b4Vec, c4Vec, d4Vec, scratch, lrot8Tbl); - - // Before we start the next iteration, we need to perform shuffles - // on the b/c/d vectors to move them back to columnar organizations - // from their current diagonal orientation. - __ cc20_shift_lane_org(b1Vec, c1Vec, d1Vec, false); - __ cc20_shift_lane_org(b2Vec, c2Vec, d2Vec, false); - __ cc20_shift_lane_org(b3Vec, c3Vec, d3Vec, false); - __ cc20_shift_lane_org(b4Vec, c4Vec, d4Vec, false); + __ BIND(L_twoRounds); + + // Set to columnar organization and do the following 4 quarter-rounds: + // QUARTERROUND(0, 4, 8, 12) + // QUARTERROUND(1, 5, 9, 13) + // QUARTERROUND(2, 6, 10, 14) + // QUARTERROUND(3, 7, 11, 15) + __ cc20_set_qr_registers(bSet, workSt, 4, 5, 6, 7); + __ cc20_set_qr_registers(cSet, workSt, 8, 9, 10, 11); + __ cc20_set_qr_registers(dSet, workSt, 12, 13, 14, 15); + + __ cc20_qr_add4(aSet, bSet); // a += b + __ cc20_qr_xor4(dSet, aSet, dSet); // d ^= a + __ cc20_qr_lrot4(dSet, dSet, 16, lrot8Tbl); // d <<<= 16 + + __ cc20_qr_add4(cSet, dSet); // c += d + __ cc20_qr_xor4(bSet, cSet, scratch); // b ^= c (scratch) + __ cc20_qr_lrot4(scratch, bSet, 12, lrot8Tbl); // b <<<= 12 + + __ cc20_qr_add4(aSet, bSet); // a += b + __ cc20_qr_xor4(dSet, aSet, dSet); // d ^= a + __ cc20_qr_lrot4(dSet, dSet, 8, lrot8Tbl); // d <<<= 8 + + __ cc20_qr_add4(cSet, dSet); // c += d + __ cc20_qr_xor4(bSet, cSet, scratch); // b ^= c (scratch) + __ cc20_qr_lrot4(scratch, bSet, 7, lrot8Tbl); // b <<<= 12 + + // Set to diagonal organization and do the next 4 quarter-rounds: + // QUARTERROUND(0, 5, 10, 15) + // QUARTERROUND(1, 6, 11, 12) + // QUARTERROUND(2, 7, 8, 13) + // QUARTERROUND(3, 4, 9, 14) + __ cc20_set_qr_registers(bSet, workSt, 5, 6, 7, 4); + __ cc20_set_qr_registers(cSet, workSt, 10, 11, 8, 9); + __ cc20_set_qr_registers(dSet, workSt, 15, 12, 13, 14); + + __ cc20_qr_add4(aSet, bSet); // a += b + __ cc20_qr_xor4(dSet, aSet, dSet); // d ^= a + __ cc20_qr_lrot4(dSet, dSet, 16, lrot8Tbl); // d <<<= 16 + + __ cc20_qr_add4(cSet, dSet); // c += d + __ cc20_qr_xor4(bSet, cSet, scratch); // b ^= c (scratch) + __ cc20_qr_lrot4(scratch, bSet, 12, lrot8Tbl); // b <<<= 12 + + __ cc20_qr_add4(aSet, bSet); // a += b + __ cc20_qr_xor4(dSet, aSet, dSet); // d ^= a + __ cc20_qr_lrot4(dSet, dSet, 8, lrot8Tbl); // d <<<= 8 + + __ cc20_qr_add4(cSet, dSet); // c += d + __ cc20_qr_xor4(bSet, cSet, scratch); // b ^= c (scratch) + __ cc20_qr_lrot4(scratch, bSet, 7, lrot8Tbl); // b <<<= 12 // Decrement and iterate __ sub(loopCtr, loopCtr, 1); - __ cbnz(loopCtr, L_Q_twoRounds); - - // Once the counter reaches zero, we fall out of the loop - // and need to add the initial state back into the working state - // represented by the a/b/c/d1Vec registers. This is destructive - // on the dState register but we no longer will need it. - __ addv(a1Vec, __ T4S, a1Vec, aState); - __ addv(b1Vec, __ T4S, b1Vec, bState); - __ addv(c1Vec, __ T4S, c1Vec, cState); - __ addv(d1Vec, __ T4S, d1Vec, dState); - - __ addv(a2Vec, __ T4S, a2Vec, aState); - __ addv(b2Vec, __ T4S, b2Vec, bState); - __ addv(c2Vec, __ T4S, c2Vec, cState); - __ addv(dState, __ T4S, dState, addMask); - __ addv(d2Vec, __ T4S, d2Vec, dState); - - __ addv(a3Vec, __ T4S, a3Vec, aState); - __ addv(b3Vec, __ T4S, b3Vec, bState); - __ addv(c3Vec, __ T4S, c3Vec, cState); - __ addv(dState, __ T4S, dState, addMask); - __ addv(d3Vec, __ T4S, d3Vec, dState); - - __ addv(a4Vec, __ T4S, a4Vec, aState); - __ addv(b4Vec, __ T4S, b4Vec, bState); - __ addv(c4Vec, __ T4S, c4Vec, cState); - __ addv(dState, __ T4S, dState, addMask); - __ addv(d4Vec, __ T4S, d4Vec, dState); - - // Write the final state back to the result buffer - __ st1(a1Vec, b1Vec, c1Vec, d1Vec, __ T16B, __ post(keystream, 64)); - __ st1(a2Vec, b2Vec, c2Vec, d2Vec, __ T16B, __ post(keystream, 64)); - __ st1(a3Vec, b3Vec, c3Vec, d3Vec, __ T16B, __ post(keystream, 64)); - __ st1(a4Vec, b4Vec, c4Vec, d4Vec, __ T16B, __ post(keystream, 64)); + __ cbnz(loopCtr, L_twoRounds); + + __ mov(tmpAddr, state); + + // Add the starting state back to the post-loop keystream + // state. We read/interlace the state array from memory into + // 4 registers similar to what we did in the beginning. Then + // add the counter overlay onto workSt[12] at the end. + for (i = 0; i < 16; i += 4) { + __ ld4r(v0, v1, v2, v3, __ T4S, __ post(tmpAddr, 16)); + __ addv(workSt[i], __ T4S, workSt[i], v0); + __ addv(workSt[i + 1], __ T4S, workSt[i + 1], v1); + __ addv(workSt[i + 2], __ T4S, workSt[i + 2], v2); + __ addv(workSt[i + 3], __ T4S, workSt[i + 3], v3); + } + __ addv(workSt[12], __ T4S, workSt[12], ctrAddOverlay); // Add ctr overlay + + // Write working state into the keystream buffer. This is accomplished + // by taking the lane "i" from each of the four vectors and writing + // it to consecutive 4-byte offsets, then post-incrementing by 16 and + // repeating with the next 4 vectors until all 16 vectors have been used. + // Then move to the next lane and repeat the process until all lanes have + // been written. + for (i = 0; i < 4; i++) { + for (j = 0; j < 16; j += 4) { + __ st4(workSt[j], workSt[j + 1], workSt[j + 2], workSt[j + 3], __ S, i, + __ post(keystream, 16)); + } + } __ mov(r0, 256); // Return length of output keystream __ leave(); @@ -7008,6 +6961,47 @@ class StubGenerator: public StubCodeGenerator { return start; } + /** + * Arguments: + * + * Inputs: + * c_rarg0 - int crc + * c_rarg1 - byte* buf + * c_rarg2 - int length + * + * Output: + * rax - int crc result + */ + address generate_updateBytesCRC32() { + assert(UseCRC32Intrinsics, "what are we doing here?"); + + __ align(CodeEntryAlignment); + StubGenStubId stub_id = StubGenStubId::updateBytesCRC32_id; + StubCodeMark mark(this, stub_id); + + address start = __ pc(); + + const Register crc = c_rarg0; // crc + const Register buf = c_rarg1; // source java byte array address + const Register len = c_rarg2; // length + const Register table0 = c_rarg3; // crc_table address + const Register table1 = c_rarg4; + const Register table2 = c_rarg5; + const Register table3 = c_rarg6; + const Register tmp3 = c_rarg7; + + BLOCK_COMMENT("Entry:"); + __ enter(); // required for proper stackwalking of RuntimeStub frame + + __ kernel_crc32(crc, buf, len, + table0, table1, table2, table3, rscratch1, rscratch2, tmp3); + + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(lr); + + return start; + } + /** * Arguments: * @@ -11172,79 +11166,6 @@ class StubGenerator: public StubCodeGenerator { // } }; - void generate_vector_math_stubs() { - // Get native vector math stub routine addresses - void* libsleef = nullptr; - char ebuf[1024]; - char dll_name[JVM_MAXPATHLEN]; - if (os::dll_locate_lib(dll_name, sizeof(dll_name), Arguments::get_dll_dir(), "sleef")) { - libsleef = os::dll_load(dll_name, ebuf, sizeof ebuf); - } - if (libsleef == nullptr) { - log_info(library)("Failed to load native vector math library, %s!", ebuf); - return; - } - // Method naming convention - // All the methods are named as _ - // Where: - // is the operation name, e.g. sin - // is optional to indicate float/double - // "f/d" for vector float/double operation - // is the number of elements in the vector - // "2/4" for neon, and "x" for sve - // is the precision level - // "u10/u05" represents 1.0/0.5 ULP error bounds - // We use "u10" for all operations by default - // But for those functions do not have u10 support, we use "u05" instead - // indicates neon/sve - // "sve/advsimd" for sve/neon implementations - // e.g. sinfx_u10sve is the method for computing vector float sin using SVE instructions - // cosd2_u10advsimd is the method for computing 2 elements vector double cos using NEON instructions - // - log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, JNI_LIB_PREFIX "sleef" JNI_LIB_SUFFIX, p2i(libsleef)); - - // Math vector stubs implemented with SVE for scalable vector size. - if (UseSVE > 0) { - for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { - int vop = VectorSupport::VECTOR_OP_MATH_START + op; - // Skip "tanh" because there is performance regression - if (vop == VectorSupport::VECTOR_OP_TANH) { - continue; - } - - // The native library does not support u10 level of "hypot". - const char* ulf = (vop == VectorSupport::VECTOR_OP_HYPOT) ? "u05" : "u10"; - - snprintf(ebuf, sizeof(ebuf), "%sfx_%ssve", VectorSupport::mathname[op], ulf); - StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_SCALABLE][op] = (address)os::dll_lookup(libsleef, ebuf); - - snprintf(ebuf, sizeof(ebuf), "%sdx_%ssve", VectorSupport::mathname[op], ulf); - StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_SCALABLE][op] = (address)os::dll_lookup(libsleef, ebuf); - } - } - - // Math vector stubs implemented with NEON for 64/128 bits vector size. - for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { - int vop = VectorSupport::VECTOR_OP_MATH_START + op; - // Skip "tanh" because there is performance regression - if (vop == VectorSupport::VECTOR_OP_TANH) { - continue; - } - - // The native library does not support u10 level of "hypot". - const char* ulf = (vop == VectorSupport::VECTOR_OP_HYPOT) ? "u05" : "u10"; - - snprintf(ebuf, sizeof(ebuf), "%sf4_%sadvsimd", VectorSupport::mathname[op], ulf); - StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_64][op] = (address)os::dll_lookup(libsleef, ebuf); - - snprintf(ebuf, sizeof(ebuf), "%sf4_%sadvsimd", VectorSupport::mathname[op], ulf); - StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_128][op] = (address)os::dll_lookup(libsleef, ebuf); - - snprintf(ebuf, sizeof(ebuf), "%sd2_%sadvsimd", VectorSupport::mathname[op], ulf); - StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_128][op] = (address)os::dll_lookup(libsleef, ebuf); - } - } - // Initialization void generate_initial_stubs() { // Generate initial stubs and initializes the entry points @@ -11398,12 +11319,10 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_montgomerySquare = g.generate_multiply(); } - generate_vector_math_stubs(); - #endif // COMPILER2 if (UseChaCha20Intrinsics) { - StubRoutines::_chacha20Block = generate_chacha20Block_qrpar(); + StubRoutines::_chacha20Block = generate_chacha20Block_blockpar(); } if (UseKyberIntrinsics) { diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index b2d3455348791..6ed7a6be58552 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -642,6 +642,7 @@ void VM_Version::initialize() { if (_model2) { os::snprintf_checked(buf + buf_used_len, sizeof(buf) - buf_used_len, "(0x%03x)", _model2); } + size_t features_offset = strnlen(buf, sizeof(buf)); #define ADD_FEATURE_IF_SUPPORTED(id, name, bit) \ do { \ if (VM_Version::supports_##name()) strcat(buf, ", " #name); \ @@ -649,7 +650,11 @@ void VM_Version::initialize() { CPU_FEATURE_FLAGS(ADD_FEATURE_IF_SUPPORTED) #undef ADD_FEATURE_IF_SUPPORTED - _features_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf); + + _features_string = extract_features_string(_cpu_info_string, + strnlen(_cpu_info_string, sizeof(buf)), + features_offset); } #if defined(LINUX) @@ -716,7 +721,7 @@ void VM_Version::initialize_cpu_information(void) { int desc_len = snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "AArch64 "); get_compatible_board(_cpu_desc + desc_len, CPU_DETAILED_DESC_BUF_SIZE - desc_len); desc_len = (int)strlen(_cpu_desc); - snprintf(_cpu_desc + desc_len, CPU_DETAILED_DESC_BUF_SIZE - desc_len, " %s", _features_string); + snprintf(_cpu_desc + desc_len, CPU_DETAILED_DESC_BUF_SIZE - desc_len, " %s", _cpu_info_string); _initialized = true; } diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 04cf9c9c2a07c..373f8da540589 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -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. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -125,6 +125,8 @@ enum Ampere_CPU_Model { decl(SHA2, sha256, 6) \ decl(CRC32, crc32, 7) \ decl(LSE, lse, 8) \ + decl(FPHP, fphp, 9) \ + decl(ASIMDHP, asimdhp, 10) \ decl(DCPOP, dcpop, 16) \ decl(SHA3, sha3, 17) \ decl(SHA512, sha512, 21) \ diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index f3b97d23ad306..4a0b557968caa 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1238,11 +1238,11 @@ encode %{ enc_class save_last_PC %{ // preserve mark address mark = __ inst_mark(); - debug_only(int off0 = __ offset()); + DEBUG_ONLY(int off0 = __ offset()); int ret_addr_offset = as_MachCall()->ret_addr_offset(); __ adr(LR, mark + ret_addr_offset); __ str(LR, Address(Rthread, JavaThread::last_Java_pc_offset())); - debug_only(int off1 = __ offset()); + DEBUG_ONLY(int off1 = __ offset()); assert(off1 - off0 == 2 * Assembler::InstructionSize, "correct size prediction"); // restore mark __ set_inst_mark(mark); @@ -1251,11 +1251,11 @@ encode %{ enc_class preserve_SP %{ // preserve mark address mark = __ inst_mark(); - debug_only(int off0 = __ offset()); + DEBUG_ONLY(int off0 = __ offset()); // FP is preserved across all calls, even compiled calls. // Use it to preserve SP in places where the callee might change the SP. __ mov(Rmh_SP_save, SP); - debug_only(int off1 = __ offset()); + DEBUG_ONLY(int off1 = __ offset()); assert(off1 - off0 == 4, "correct size prediction"); // restore mark __ set_inst_mark(mark); diff --git a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp index bca6c7ca30cb8..5683bc59d5c07 100644 --- a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp +++ b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp @@ -59,7 +59,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ call(Runtime1::entry_for(C1StubId::predicate_failed_trap_id), relocInfo::runtime_call_type); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); return; } // Pass the array index on stack because all registers must be preserved @@ -91,7 +91,7 @@ void PredicateFailedStub::emit_code(LIR_Assembler* ce) { __ call(Runtime1::entry_for(C1StubId::predicate_failed_trap_id), relocInfo::runtime_call_type); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } void DivByZeroStub::emit_code(LIR_Assembler* ce) { diff --git a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp index 466dcc8fe66c1..049477cda7658 100644 --- a/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/gc/g1/g1BarrierSetAssembler_arm.cpp @@ -26,7 +26,6 @@ #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BarrierSetAssembler.hpp" #include "gc/g1/g1BarrierSetRuntime.hpp" -#include "gc/g1/g1ThreadLocalData.hpp" #include "gc/g1/g1CardTable.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1ThreadLocalData.hpp" diff --git a/src/hotspot/cpu/arm/gc/shared/barrierSetNMethod_arm.cpp b/src/hotspot/cpu/arm/gc/shared/barrierSetNMethod_arm.cpp index 224a499ff5420..52d71ca65c29d 100644 --- a/src/hotspot/cpu/arm/gc/shared/barrierSetNMethod_arm.cpp +++ b/src/hotspot/cpu/arm/gc/shared/barrierSetNMethod_arm.cpp @@ -29,8 +29,8 @@ #include "memory/resourceArea.hpp" #include "runtime/frame.inline.hpp" #include "runtime/javaThread.hpp" -#include "runtime/sharedRuntime.hpp" #include "runtime/registerMap.hpp" +#include "runtime/sharedRuntime.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" @@ -72,7 +72,7 @@ void NativeNMethodBarrier::verify() const { static NativeNMethodBarrier* native_nmethod_barrier(nmethod* nm) { address barrier_address = nm->code_begin() + nm->frame_complete_offset() - entry_barrier_bytes; NativeNMethodBarrier* barrier = reinterpret_cast(barrier_address); - debug_only(barrier->verify()); + DEBUG_ONLY(barrier->verify()); return barrier; } diff --git a/src/hotspot/cpu/arm/runtime_arm.cpp b/src/hotspot/cpu/arm/runtime_arm.cpp index 20c1bc199d3e7..615a63eac19af 100644 --- a/src/hotspot/cpu/arm/runtime_arm.cpp +++ b/src/hotspot/cpu/arm/runtime_arm.cpp @@ -54,6 +54,9 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // Measured 8/7/03 at 660 in 32bit debug build CodeBuffer buffer(name, 2000, 512); #endif + if (buffer.blob() == nullptr) { + return nullptr; + } // bypassed when code generation useless MacroAssembler* masm = new MacroAssembler(&buffer); const Register Rublock = R6; @@ -209,6 +212,9 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { // Measured 8/7/03 at 256 in 32bit debug build const char* name = OptoRuntime::stub_name(OptoStubId::exception_id); CodeBuffer buffer(name, 600, 512); + if (buffer.blob() == nullptr) { + return nullptr; + } MacroAssembler* masm = new MacroAssembler(&buffer); int framesize_in_words = 2; // FP + LR diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index 6dde82daaf975..8ba847e7e3288 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -612,12 +612,12 @@ static void gen_c2i_adapter(MacroAssembler *masm, } -AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, - int total_args_passed, - int comp_args_on_stack, - const BasicType *sig_bt, - const VMRegPair *regs, - AdapterFingerPrint* fingerprint) { +void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + AdapterHandlerEntry* handler) { address i2c_entry = __ pc(); gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); @@ -637,7 +637,8 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm address c2i_entry = __ pc(); gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); - return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry); + handler->set_entry_points(i2c_entry, c2i_entry, c2i_unverified_entry, nullptr); + return; } diff --git a/src/hotspot/cpu/arm/vm_version_arm_32.cpp b/src/hotspot/cpu/arm/vm_version_arm_32.cpp index 148786a55da41..d094193603567 100644 --- a/src/hotspot/cpu/arm/vm_version_arm_32.cpp +++ b/src/hotspot/cpu/arm/vm_version_arm_32.cpp @@ -295,7 +295,7 @@ void VM_Version::initialize() { (has_multiprocessing_extensions() ? ", mp_ext" : "")); // buf is started with ", " or is empty - _features_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf); if (has_simd()) { if (FLAG_IS_DEFAULT(UsePopCountInstruction)) { @@ -363,6 +363,6 @@ void VM_Version::initialize_cpu_information(void) { _no_of_threads = _no_of_cores; _no_of_sockets = _no_of_cores; snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE - 1, "ARM%d", _arm_arch); - snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "%s", _features_string); + snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "%s", _cpu_info_string); _initialized = true; } diff --git a/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp b/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp index d4f5faa29a869..a390a6eeed410 100644 --- a/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp @@ -74,7 +74,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ bctrl(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ illtrap()); + DEBUG_ONLY(__ illtrap()); return; } @@ -98,7 +98,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ bctrl(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ illtrap()); + DEBUG_ONLY(__ illtrap()); } @@ -115,7 +115,7 @@ void PredicateFailedStub::emit_code(LIR_Assembler* ce) { __ bctrl(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ illtrap()); + DEBUG_ONLY(__ illtrap()); } @@ -156,7 +156,7 @@ void DivByZeroStub::emit_code(LIR_Assembler* ce) { __ bctrl(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ illtrap()); + DEBUG_ONLY(__ illtrap()); } @@ -179,7 +179,7 @@ void ImplicitNullCheckStub::emit_code(LIR_Assembler* ce) { __ bctrl(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ illtrap()); + DEBUG_ONLY(__ illtrap()); } @@ -193,7 +193,7 @@ void SimpleExceptionStub::emit_code(LIR_Assembler* ce) { __ mtctr(R0); __ bctrl(); ce->add_call_info_here(_info); - debug_only( __ illtrap(); ) + DEBUG_ONLY( __ illtrap(); ) } @@ -441,7 +441,7 @@ void DeoptimizeStub::emit_code(LIR_Assembler* ce) { __ load_const_optimized(R0, _trap_request); // Pass trap request in R0. __ bctrl(); ce->add_call_info_here(_info); - debug_only(__ illtrap()); + DEBUG_ONLY(__ illtrap()); } diff --git a/src/hotspot/cpu/ppc/c1_FrameMap_ppc.cpp b/src/hotspot/cpu/ppc/c1_FrameMap_ppc.cpp index e4684613e2589..8ce324a570bd9 100644 --- a/src/hotspot/cpu/ppc/c1_FrameMap_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_FrameMap_ppc.cpp @@ -189,7 +189,7 @@ LIR_Opr FrameMap::_caller_save_fpu_regs[] = {}; FloatRegister FrameMap::nr2floatreg (int rnr) { assert(_init_done, "tables not initialized"); - debug_only(fpu_range_check(rnr);) + DEBUG_ONLY(fpu_range_check(rnr);) return _fpu_regs[rnr]; } diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetNMethod_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetNMethod_ppc.cpp index 19084ed27c7c0..d3bb9cc3c04da 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetNMethod_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetNMethod_ppc.cpp @@ -23,8 +23,8 @@ */ #include "code/codeBlob.hpp" -#include "code/nmethod.hpp" #include "code/nativeInst.hpp" +#include "code/nmethod.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/barrierSetNMethod.hpp" @@ -108,7 +108,7 @@ static NativeNMethodBarrier* get_nmethod_barrier(nmethod* nm) { } auto barrier = reinterpret_cast(barrier_address); - debug_only(barrier->verify()); + DEBUG_ONLY(barrier->verify()); return barrier; } diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp index 48422bc66212e..5b24259103f53 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp @@ -26,9 +26,9 @@ #include "asm/macroAssembler.inline.hpp" #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" +#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" -#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #define __ masm->masm()-> diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 842201e158489..ec5b98bd4c516 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -24,8 +24,10 @@ */ #include "asm/macroAssembler.inline.hpp" -#include "gc/shared/gcArguments.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcArguments.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahForwarding.hpp" @@ -34,8 +36,6 @@ #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "interpreter/interpreter.hpp" #include "macroAssembler_ppc.hpp" #include "runtime/javaThread.hpp" diff --git a/src/hotspot/cpu/ppc/gc/z/zAddress_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zAddress_ppc.cpp index 2e3eed8ec60d9..f3a7a948f7021 100644 --- a/src/hotspot/cpu/ppc/gc/z/zAddress_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/z/zAddress_ppc.cpp @@ -21,8 +21,8 @@ * questions. */ -#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcLogPrecious.hpp" #include "gc/z/zAddress.inline.hpp" #include "gc/z/zGlobals.hpp" #include "runtime/globals.hpp" diff --git a/src/hotspot/cpu/ppc/runtime_ppc.cpp b/src/hotspot/cpu/ppc/runtime_ppc.cpp index 94e8c08ebf5fc..6d9a1dfcb1ea8 100644 --- a/src/hotspot/cpu/ppc/runtime_ppc.cpp +++ b/src/hotspot/cpu/ppc/runtime_ppc.cpp @@ -73,6 +73,9 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { // Setup code generation tools. const char* name = OptoRuntime::stub_name(OptoStubId::exception_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer); address start = __ pc(); diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 5a94d46943425..5a33a14f79e0b 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -1143,12 +1143,12 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, __ bctr(); } -AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, - int total_args_passed, - int comp_args_on_stack, - const BasicType *sig_bt, - const VMRegPair *regs, - AdapterFingerPrint* fingerprint) { +void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + AdapterHandlerEntry* handler) { address i2c_entry; address c2i_unverified_entry; address c2i_entry; @@ -1223,8 +1223,8 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, call_interpreter, ientry); - return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, - c2i_no_clinit_check_entry); + handler->set_entry_points(i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry); + return; } // An oop arg. Must pass a handle not the oop itself. @@ -3106,6 +3106,9 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // Setup code generation tools. const char* name = OptoRuntime::stub_name(OptoStubId::uncommon_trap_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer); address start = __ pc(); diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index 939c3d3094a1a..4a0ced42ed4e8 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -546,6 +546,177 @@ class StubGenerator: public StubCodeGenerator { return start; } + // Computes the Galois/Counter Mode (GCM) product and reduction. + // + // This function performs polynomial multiplication of the subkey H with + // the current GHASH state using vectorized polynomial multiplication (`vpmsumd`). + // The subkey H is divided into lower, middle, and higher halves. + // The multiplication results are reduced using `vConstC2` to stay within GF(2^128). + // The final computed value is stored back into `vState`. + static void computeGCMProduct(MacroAssembler* _masm, + VectorRegister vLowerH, VectorRegister vH, VectorRegister vHigherH, + VectorRegister vConstC2, VectorRegister vZero, VectorRegister vState, + VectorRegister vLowProduct, VectorRegister vMidProduct, VectorRegister vHighProduct, + VectorRegister vReducedLow, VectorRegister vTmp8, VectorRegister vTmp9, + VectorRegister vCombinedResult, VectorRegister vSwappedH) { + __ vxor(vH, vH, vState); + __ vpmsumd(vLowProduct, vLowerH, vH); // L : Lower Half of subkey H + __ vpmsumd(vMidProduct, vSwappedH, vH); // M : Combined halves of subkey H + __ vpmsumd(vHighProduct, vHigherH, vH); // H : Higher Half of subkey H + __ vpmsumd(vReducedLow, vLowProduct, vConstC2); // Reduction + __ vsldoi(vTmp8, vMidProduct, vZero, 8); // mL : Extract the lower 64 bits of M + __ vsldoi(vTmp9, vZero, vMidProduct, 8); // mH : Extract the higher 64 bits of M + __ vxor(vLowProduct, vLowProduct, vTmp8); // LL + mL : Partial result for lower half + __ vxor(vHighProduct, vHighProduct, vTmp9); // HH + mH : Partial result for upper half + __ vsldoi(vLowProduct, vLowProduct, vLowProduct, 8); // Swap + __ vxor(vLowProduct, vLowProduct, vReducedLow); + __ vsldoi(vCombinedResult, vLowProduct, vLowProduct, 8); // Swap + __ vpmsumd(vLowProduct, vLowProduct, vConstC2); // Reduction using constant + __ vxor(vCombinedResult, vCombinedResult, vHighProduct); // Combine reduced Low & High products + __ vxor(vState, vLowProduct, vCombinedResult); + } + + // Generate stub for ghash process blocks. + // + // Arguments for generated stub: + // state: R3_ARG1 (long[] state) + // subkeyH: R4_ARG2 (long[] subH) + // data: R5_ARG3 (byte[] data) + // blocks: R6_ARG4 (number of 16-byte blocks to process) + // + // The polynomials are processed in bit-reflected order for efficiency reasons. + // This optimization leverages the structure of the Galois field arithmetic + // to minimize the number of bit manipulations required during multiplication. + // For an explanation of how this works, refer : + // Vinodh Gopal, Erdinc Ozturk, Wajdi Feghali, Jim Guilford, Gil Wolrich, + // Martin Dixon. "Optimized Galois-Counter-Mode Implementation on Intel® + // Architecture Processor" + // http://web.archive.org/web/20130609111954/http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/communications-ia-galois-counter-mode-paper.pdf + // + // + address generate_ghash_processBlocks() { + StubCodeMark mark(this, "StubRoutines", "ghash"); + address start = __ function_entry(); + + // Registers for parameters + Register state = R3_ARG1; // long[] state + Register subkeyH = R4_ARG2; // long[] subH + Register data = R5_ARG3; // byte[] data + Register blocks = R6_ARG4; + Register temp1 = R8; + // Vector Registers + VectorRegister vZero = VR0; + VectorRegister vH = VR1; + VectorRegister vLowerH = VR2; + VectorRegister vHigherH = VR3; + VectorRegister vLowProduct = VR4; + VectorRegister vMidProduct = VR5; + VectorRegister vHighProduct = VR6; + VectorRegister vReducedLow = VR7; + VectorRegister vTmp8 = VR8; + VectorRegister vTmp9 = VR9; + VectorRegister vTmp10 = VR10; + VectorRegister vSwappedH = VR11; + VectorRegister vTmp12 = VR12; + VectorRegister loadOrder = VR13; + VectorRegister vHigh = VR14; + VectorRegister vLow = VR15; + VectorRegister vState = VR16; + VectorRegister vPerm = VR17; + VectorRegister vCombinedResult = VR18; + VectorRegister vConstC2 = VR19; + + __ li(temp1, 0xc2); + __ sldi(temp1, temp1, 56); + __ vspltisb(vZero, 0); + __ mtvrd(vConstC2, temp1); + __ lxvd2x(vH->to_vsr(), subkeyH); + __ lxvd2x(vState->to_vsr(), state); + // Operations to obtain lower and higher bytes of subkey H. + __ vspltisb(vReducedLow, 1); + __ vspltisb(vTmp10, 7); + __ vsldoi(vTmp8, vZero, vReducedLow, 1); // 0x1 + __ vor(vTmp8, vConstC2, vTmp8); // 0xC2...1 + __ vsplt(vTmp9, 0, vH); // MSB of H + __ vsl(vH, vH, vReducedLow); // Carry = H<<7 + __ vsrab(vTmp9, vTmp9, vTmp10); + __ vand(vTmp9, vTmp9, vTmp8); // Carry + __ vxor(vTmp10, vH, vTmp9); + __ vsldoi(vConstC2, vZero, vConstC2, 8); + __ vsldoi(vSwappedH, vTmp10, vTmp10, 8); // swap Lower and Higher Halves of subkey H + __ vsldoi(vLowerH, vZero, vSwappedH, 8); // H.L + __ vsldoi(vHigherH, vSwappedH, vZero, 8); // H.H +#ifdef ASSERT + __ cmpwi(CR0, blocks, 0); // Compare 'blocks' (R6_ARG4) with zero + __ asm_assert_ne("blocks should NOT be zero"); +#endif + __ clrldi(blocks, blocks, 32); + __ mtctr(blocks); + __ lvsl(loadOrder, temp1); +#ifdef VM_LITTLE_ENDIAN + __ vspltisb(vTmp12, 0xf); + __ vxor(loadOrder, loadOrder, vTmp12); +#define LE_swap_bytes(x) __ vec_perm(x, x, x, loadOrder) +#else +#define LE_swap_bytes(x) +#endif + + // This code performs Karatsuba multiplication in Galois fields to compute the GHASH operation. + // + // The Karatsuba method breaks the multiplication of two 128-bit numbers into smaller parts, + // performing three 128-bit multiplications and combining the results efficiently. + // + // (C1:C0) = A1*B1, (D1:D0) = A0*B0, (E1:E0) = (A0+A1)(B0+B1) + // (A1:A0)(B1:B0) = C1:(C0+C1+D1+E1):(D1+C0+D0+E0):D0 + // + // Inputs: + // - vH: The data vector (state), containing both B0 (lower half) and B1 (higher half). + // - vLowerH: Lower half of the subkey H (A0). + // - vHigherH: Higher half of the subkey H (A1). + // - vConstC2: Constant used for reduction (for final processing). + // + // References: + // Shay Gueron, Michael E. Kounavis. + // "Intel® Carry-Less Multiplication Instruction and its Usage for Computing the GCM Mode" + // https://web.archive.org/web/20110609115824/https://software.intel.com/file/24918 + // + Label L_aligned_loop, L_store, L_unaligned_loop, L_initialize_unaligned_loop; + __ andi(temp1, data, 15); + __ cmpwi(CR0, temp1, 0); + __ bne(CR0, L_initialize_unaligned_loop); + + __ bind(L_aligned_loop); + __ lvx(vH, temp1, data); + LE_swap_bytes(vH); + computeGCMProduct(_masm, vLowerH, vH, vHigherH, vConstC2, vZero, vState, + vLowProduct, vMidProduct, vHighProduct, vReducedLow, vTmp8, vTmp9, vCombinedResult, vSwappedH); + __ addi(data, data, 16); + __ bdnz(L_aligned_loop); + __ b(L_store); + + __ bind(L_initialize_unaligned_loop); + __ li(temp1, 0); + __ lvsl(vPerm, temp1, data); + __ lvx(vHigh, temp1, data); +#ifdef VM_LITTLE_ENDIAN + __ vspltisb(vTmp12, -1); + __ vxor(vPerm, vPerm, vTmp12); +#endif + __ bind(L_unaligned_loop); + __ addi(data, data, 16); + __ lvx(vLow, temp1, data); + __ vec_perm(vH, vHigh, vLow, vPerm); + computeGCMProduct(_masm, vLowerH, vH, vHigherH, vConstC2, vZero, vState, + vLowProduct, vMidProduct, vHighProduct, vReducedLow, vTmp8, vTmp9, vCombinedResult, vSwappedH); + __ vmr(vHigh, vLow); + __ bdnz(L_unaligned_loop); + + __ bind(L_store); + __ stxvd2x(vState->to_vsr(), state); + __ blr(); + + return start; + } // -XX:+OptimizeFill : convert fill/copy loops into intrinsic // // The code is implemented(ported from sparc) as we believe it benefits JVM98, however @@ -5028,6 +5199,10 @@ void generate_lookup_secondary_supers_table_stub() { StubRoutines::_data_cache_writeback_sync = generate_data_cache_writeback_sync(); } + if (UseGHASHIntrinsics) { + StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); + } + if (UseAESIntrinsics) { StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(); StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(); diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index 8ec69bffe15ea..c8c53543d14ac 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -219,7 +219,7 @@ void VM_Version::initialize() { (has_brw() ? " brw" : "") // Make sure number of %s matches num_features! ); - _features_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf); if (Verbose) { print_features(); } @@ -308,8 +308,14 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); } - if (UseGHASHIntrinsics) { - warning("GHASH intrinsics are not available on this CPU"); + if (VM_Version::has_vsx()) { + if (FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { + UseGHASHIntrinsics = true; + } + } else if (UseGHASHIntrinsics) { + if (!FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { + warning("GHASH intrinsics are not available on this CPU"); + } FLAG_SET_DEFAULT(UseGHASHIntrinsics, false); } @@ -519,7 +525,7 @@ void VM_Version::print_platform_virtualization_info(outputStream* st) { } void VM_Version::print_features() { - tty->print_cr("Version: %s L1_data_cache_line_size=%d", features_string(), L1_data_cache_line_size()); + tty->print_cr("Version: %s L1_data_cache_line_size=%d", cpu_info_string(), L1_data_cache_line_size()); if (Verbose) { if (ContendedPaddingWidth > 0) { @@ -726,6 +732,6 @@ void VM_Version::initialize_cpu_information(void) { _no_of_threads = _no_of_cores; _no_of_sockets = _no_of_cores; snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE, "PowerPC POWER%lu", PowerArchitecturePPC64); - snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "PPC %s", features_string()); + snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "PPC %s", cpu_info_string()); _initialized = true; } diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index e036cb6b1ec60..4773043e1ba07 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -2323,6 +2323,7 @@ enum Nf { } // Vector Bit-manipulation used in Cryptography (Zvbb) Extension + INSN(vandn_vx, 0b1010111, 0b100, 0b000001); INSN(vrol_vx, 0b1010111, 0b100, 0b010101); INSN(vror_vx, 0b1010111, 0b100, 0b010100); diff --git a/src/hotspot/cpu/riscv/c1_CodeStubs_riscv.cpp b/src/hotspot/cpu/riscv/c1_CodeStubs_riscv.cpp index d55521823ecdc..ea299181ca7af 100644 --- a/src/hotspot/cpu/riscv/c1_CodeStubs_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_CodeStubs_riscv.cpp @@ -70,7 +70,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ far_call(RuntimeAddress(a)); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); return; } @@ -92,7 +92,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ rt_call(Runtime1::entry_for(stub_id), ra); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } PredicateFailedStub::PredicateFailedStub(CodeEmitInfo* info) { @@ -105,7 +105,7 @@ void PredicateFailedStub::emit_code(LIR_Assembler* ce) { __ far_call(RuntimeAddress(a)); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } void DivByZeroStub::emit_code(LIR_Assembler* ce) { @@ -258,7 +258,7 @@ void ImplicitNullCheckStub::emit_code(LIR_Assembler* ce) { __ far_call(RuntimeAddress(a)); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } void SimpleExceptionStub::emit_code(LIR_Assembler* ce) { @@ -272,7 +272,7 @@ void SimpleExceptionStub::emit_code(LIR_Assembler* ce) { } __ far_call(RuntimeAddress(Runtime1::entry_for(_stub))); ce->add_call_info_here(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } void ArrayCopyStub::emit_code(LIR_Assembler* ce) { diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 39a49f8f1eb85..77b4e26cc924b 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -2156,6 +2156,36 @@ void C2_MacroAssembler::enc_cmove(int cmpFlag, Register op1, Register op2, Regis } } +void C2_MacroAssembler::enc_cmove_cmp_fp(int cmpFlag, FloatRegister op1, FloatRegister op2, Register dst, Register src, bool is_single) { + int op_select = cmpFlag & (~unsigned_branch_mask); + + switch (op_select) { + case BoolTest::eq: + cmov_cmp_fp_eq(op1, op2, dst, src, is_single); + break; + case BoolTest::ne: + cmov_cmp_fp_ne(op1, op2, dst, src, is_single); + break; + case BoolTest::le: + cmov_cmp_fp_le(op1, op2, dst, src, is_single); + break; + case BoolTest::ge: + assert(false, "Should go to BoolTest::le case"); + ShouldNotReachHere(); + break; + case BoolTest::lt: + cmov_cmp_fp_lt(op1, op2, dst, src, is_single); + break; + case BoolTest::gt: + assert(false, "Should go to BoolTest::lt case"); + ShouldNotReachHere(); + break; + default: + assert(false, "unsupported compare condition"); + ShouldNotReachHere(); + } +} + // Set dst to NaN if any NaN input. void C2_MacroAssembler::minmax_fp(FloatRegister dst, FloatRegister src1, FloatRegister src2, FLOAT_TYPE ft, bool is_min) { @@ -3080,7 +3110,9 @@ void C2_MacroAssembler::compare_integral_v(VectorRegister vd, VectorRegister src assert(is_integral_type(bt), "unsupported element type"); assert(vm == Assembler::v0_t ? vd != v0 : true, "should be different registers"); vsetvli_helper(bt, vector_length); - vmclr_m(vd); + if (vm == Assembler::v0_t) { + vmclr_m(vd); + } switch (cond) { case BoolTest::eq: vmseq_vv(vd, src1, src2, vm); break; case BoolTest::ne: vmsne_vv(vd, src1, src2, vm); break; @@ -3103,7 +3135,9 @@ void C2_MacroAssembler::compare_fp_v(VectorRegister vd, VectorRegister src1, Vec assert(is_floating_point_type(bt), "unsupported element type"); assert(vm == Assembler::v0_t ? vd != v0 : true, "should be different registers"); vsetvli_helper(bt, vector_length); - vmclr_m(vd); + if (vm == Assembler::v0_t) { + vmclr_m(vd); + } switch (cond) { case BoolTest::eq: vmfeq_vv(vd, src1, src2, vm); break; case BoolTest::ne: vmfne_vv(vd, src1, src2, vm); break; diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp index a650174d90f08..73fceea38051e 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp @@ -129,6 +129,10 @@ Register op1, Register op2, Register dst, Register src); + void enc_cmove_cmp_fp(int cmpFlag, + FloatRegister op1, FloatRegister op2, + Register dst, Register src, bool is_single); + void spill(Register r, bool is64, int offset) { is64 ? sd(r, Address(sp, offset)) : sw(r, Address(sp, offset)); diff --git a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp index de3c1b17c8eab..79bdc4917c9ed 100644 --- a/src/hotspot/cpu/riscv/c2_globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_globals_riscv.hpp @@ -43,7 +43,7 @@ define_pd_global(bool, TieredCompilation, COMPILER1_PRESENT(true) NOT define_pd_global(intx, CompileThreshold, 10000); define_pd_global(intx, OnStackReplacePercentage, 140); -define_pd_global(intx, ConditionalMoveLimit, 0); +define_pd_global(intx, ConditionalMoveLimit, 3); define_pd_global(intx, FreqInlineSize, 325); define_pd_global(intx, MinJumpTableSize, 10); define_pd_global(intx, InteriorEntryAlignment, 16); diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetNMethod_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetNMethod_riscv.cpp index 39da77181c674..f24e4f789bc50 100644 --- a/src/hotspot/cpu/riscv/gc/shared/barrierSetNMethod_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetNMethod_riscv.cpp @@ -31,8 +31,8 @@ #include "memory/resourceArea.hpp" #include "runtime/frame.inline.hpp" #include "runtime/javaThread.hpp" -#include "runtime/sharedRuntime.hpp" #include "runtime/registerMap.hpp" +#include "runtime/sharedRuntime.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" #if INCLUDE_JVMCI diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp index 2a96bd32cf8d7..11c4e5dc81b6c 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp @@ -26,9 +26,9 @@ #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" -#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #define __ masm->masm()-> diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 3021351cca84f..4c1056e75a551 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -23,6 +23,8 @@ * */ +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahForwarding.hpp" @@ -30,10 +32,8 @@ #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" -#include "interpreter/interpreter.hpp" #include "interpreter/interp_masm.hpp" +#include "interpreter/interpreter.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" #ifdef COMPILER1 diff --git a/src/hotspot/cpu/riscv/gc/z/zAddress_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zAddress_riscv.cpp index 1f2f0146f04a2..5f783e6fb8ba5 100644 --- a/src/hotspot/cpu/riscv/gc/z/zAddress_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/z/zAddress_riscv.cpp @@ -22,8 +22,8 @@ * questions. */ -#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcLogPrecious.hpp" #include "gc/z/zAddress.hpp" #include "gc/z/zBarrierSetAssembler.hpp" #include "gc/z/zGlobals.hpp" diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index c6393be071401..825722ad29bc0 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -1267,6 +1267,130 @@ void MacroAssembler::cmov_gtu(Register cmp1, Register cmp2, Register dst, Regist bind(no_set); } +// ----------- cmove, compare float ----------- + +// Move src to dst only if cmp1 == cmp2, +// otherwise leave dst unchanged, including the case where one of them is NaN. +// Clarification: +// java code : cmp1 != cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 eq cmp2), dst, src +void MacroAssembler::cmov_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) { + if (UseZicond) { + if (is_single) { + feq_s(t0, cmp1, cmp2); + } else { + feq_d(t0, cmp1, cmp2); + } + czero_nez(dst, dst, t0); + czero_eqz(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + if (is_single) { + // jump if cmp1 != cmp2, including the case of NaN + // not jump (i.e. move src to dst) if cmp1 == cmp2 + float_bne(cmp1, cmp2, no_set); + } else { + double_bne(cmp1, cmp2, no_set); + } + mv(dst, src); + bind(no_set); +} + +// Keep dst unchanged only if cmp1 == cmp2, +// otherwise move src to dst, including the case where one of them is NaN. +// Clarification: +// java code : cmp1 == cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 ne cmp2), dst, src +void MacroAssembler::cmov_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) { + if (UseZicond) { + if (is_single) { + feq_s(t0, cmp1, cmp2); + } else { + feq_d(t0, cmp1, cmp2); + } + czero_eqz(dst, dst, t0); + czero_nez(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + if (is_single) { + // jump if cmp1 == cmp2 + // not jump (i.e. move src to dst) if cmp1 != cmp2, including the case of NaN + float_beq(cmp1, cmp2, no_set); + } else { + double_beq(cmp1, cmp2, no_set); + } + mv(dst, src); + bind(no_set); +} + +// When cmp1 <= cmp2 or any of them is NaN then dst = src, otherwise, dst = dst +// Clarification +// scenario 1: +// java code : cmp2 < cmp1 ? dst : src +// transformed to : CMove dst, (cmp1 le cmp2), dst, src +// scenario 2: +// java code : cmp1 > cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 le cmp2), dst, src +void MacroAssembler::cmov_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) { + if (UseZicond) { + if (is_single) { + flt_s(t0, cmp2, cmp1); + } else { + flt_d(t0, cmp2, cmp1); + } + czero_eqz(dst, dst, t0); + czero_nez(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + if (is_single) { + // jump if cmp1 > cmp2 + // not jump (i.e. move src to dst) if cmp1 <= cmp2 or either is NaN + float_bgt(cmp1, cmp2, no_set); + } else { + double_bgt(cmp1, cmp2, no_set); + } + mv(dst, src); + bind(no_set); +} + +// When cmp1 < cmp2 or any of them is NaN then dst = src, otherwise, dst = dst +// Clarification +// scenario 1: +// java code : cmp2 <= cmp1 ? dst : src +// transformed to : CMove dst, (cmp1 lt cmp2), dst, src +// scenario 2: +// java code : cmp1 >= cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 lt cmp2), dst, src +void MacroAssembler::cmov_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single) { + if (UseZicond) { + if (is_single) { + fle_s(t0, cmp2, cmp1); + } else { + fle_d(t0, cmp2, cmp1); + } + czero_eqz(dst, dst, t0); + czero_nez(t0 , src, t0); + orr(dst, dst, t0); + return; + } + Label no_set; + if (is_single) { + // jump if cmp1 >= cmp2 + // not jump (i.e. move src to dst) if cmp1 < cmp2 or either is NaN + float_bge(cmp1, cmp2, no_set); + } else { + double_bge(cmp1, cmp2, no_set); + } + mv(dst, src); + bind(no_set); +} + // Float compare branch instructions #define INSN(NAME, FLOATCMP, BRANCH) \ @@ -1682,7 +1806,7 @@ void MacroAssembler::vector_update_crc32(Register crc, Register buf, Register le for (int i = 0; i < N; i++) { vmv_x_s(tmp2, vcrc); // in vmv_x_s, the value is sign-extended to SEW bits, but we need zero-extended here. - zext_w(tmp2, tmp2); + zext(tmp2, tmp2, 32); vslidedown_vi(vcrc, vcrc, 1); xorr(crc, crc, tmp2); for (int j = 0; j < W; j++) { diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index b390fb236c273..c47200579c785 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -657,6 +657,11 @@ class MacroAssembler: public Assembler { void cmov_gt(Register cmp1, Register cmp2, Register dst, Register src); void cmov_gtu(Register cmp1, Register cmp2, Register dst, Register src); + void cmov_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); + void cmov_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); + void cmov_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); + void cmov_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); + public: // We try to follow risc-v asm menomics. // But as we don't layout a reachable GOT, diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp index 295e92bbc1b78..d8f5fa57816f3 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp @@ -300,7 +300,7 @@ class NativeGeneralJump: public NativeJump { inline NativeGeneralJump* nativeGeneralJump_at(address addr) { assert_cond(addr != nullptr); NativeGeneralJump* jump = (NativeGeneralJump*)(addr); - debug_only(jump->verify();) + DEBUG_ONLY(jump->verify();) return jump; } diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 1eb1464e7d938..f6fb2e195d3d8 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1596,7 +1596,8 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r __ unspill(as_VectorRegister(Matcher::_regEncode[dst_lo]), ra_->reg2offset(src_lo)); } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_vector) { // vpr to vpr - __ vmv1r_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); + __ vsetvli_helper(T_BYTE, MaxVectorSize); + __ vmv_v_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); } else { ShouldNotReachHere(); } @@ -1614,7 +1615,8 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r __ unspill_vmask(as_VectorRegister(Matcher::_regEncode[dst_lo]), ra_->reg2offset(src_lo)); } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_vector) { // vmask to vmask - __ vmv1r_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); + __ vsetvli_helper(T_BYTE, MaxVectorSize >> 3); + __ vmv_v_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); } else { ShouldNotReachHere(); } @@ -1914,9 +1916,10 @@ bool Matcher::match_rule_supported(int opcode) { case Op_FmaF: case Op_FmaD: + return UseFMA; case Op_FmaVF: case Op_FmaVD: - return UseFMA; + return UseRVV && UseFMA; case Op_ConvHF2F: case Op_ConvF2HF: @@ -1933,6 +1936,12 @@ bool Matcher::match_rule_supported(int opcode) { case Op_SubHF: case Op_SqrtHF: return UseZfh; + + case Op_CMoveF: + case Op_CMoveD: + case Op_CMoveP: + case Op_CMoveN: + return false; } return true; // Per default match rules are supported. @@ -1944,11 +1953,11 @@ const RegMask* Matcher::predicate_reg_mask(void) { // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { - return EnableVectorSupport && UseVectorStubs; + return EnableVectorSupport; } OptoRegPair Matcher::vector_return_value(uint ideal_reg) { - assert(EnableVectorSupport && UseVectorStubs, "sanity"); + assert(EnableVectorSupport, "sanity"); assert(ideal_reg == Op_VecA, "sanity"); // check more info at https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc int lo = V8_num; @@ -9938,12 +9947,15 @@ instruct far_cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP0 zero, la // ============================================================================ // Conditional Move Instructions + +// --------- CMoveI --------- + instruct cmovI_cmpI(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOp cop) %{ match(Set dst (CMoveI (Binary cop (CmpI op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpI\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpI\n\t" %} ins_encode %{ @@ -9960,7 +9972,7 @@ instruct cmovI_cmpU(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpU\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpU\n\t" %} ins_encode %{ @@ -9977,7 +9989,7 @@ instruct cmovI_cmpL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOp cop) % ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpL\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpL\n\t" %} ins_encode %{ @@ -9994,7 +10006,7 @@ instruct cmovI_cmpUL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpUL\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpUL\n\t" %} ins_encode %{ @@ -10006,12 +10018,46 @@ instruct cmovI_cmpUL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOpU cop) ins_pipe(pipe_class_compare); %} +instruct cmovI_cmpF(iRegINoSp dst, iRegI src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveI (Binary cop (CmpF op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpF\n\t" + %} + + ins_encode %{ + __ enc_cmove_cmp_fp($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovI_cmpD(iRegINoSp dst, iRegI src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveI (Binary cop (CmpD op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpD\n\t" + %} + + ins_encode %{ + __ enc_cmove_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + instruct cmovI_cmpN(iRegINoSp dst, iRegI src, iRegN op1, iRegN op2, cmpOpU cop) %{ match(Set dst (CMoveI (Binary cop (CmpN op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpN\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpN\n\t" %} ins_encode %{ @@ -10028,7 +10074,7 @@ instruct cmovI_cmpP(iRegINoSp dst, iRegI src, iRegP op1, iRegP op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpP\n\t" + "CMoveI $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpP\n\t" %} ins_encode %{ @@ -10040,12 +10086,14 @@ instruct cmovI_cmpP(iRegINoSp dst, iRegI src, iRegP op1, iRegP op2, cmpOpU cop) ins_pipe(pipe_class_compare); %} +// --------- CMoveL --------- + instruct cmovL_cmpL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOp cop) %{ match(Set dst (CMoveL (Binary cop (CmpL op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpL\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpL\n\t" %} ins_encode %{ @@ -10062,7 +10110,7 @@ instruct cmovL_cmpUL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpUL\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpUL\n\t" %} ins_encode %{ @@ -10079,7 +10127,7 @@ instruct cmovL_cmpI(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOp cop) % ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpI\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpI\n\t" %} ins_encode %{ @@ -10096,7 +10144,7 @@ instruct cmovL_cmpU(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpU\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpU\n\t" %} ins_encode %{ @@ -10108,12 +10156,46 @@ instruct cmovL_cmpU(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOpU cop) ins_pipe(pipe_class_compare); %} +instruct cmovL_cmpF(iRegLNoSp dst, iRegL src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveL (Binary cop (CmpF op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpF\n\t" + %} + + ins_encode %{ + __ enc_cmove_cmp_fp($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovL_cmpD(iRegLNoSp dst, iRegL src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveL (Binary cop (CmpD op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpD\n\t" + %} + + ins_encode %{ + __ enc_cmove_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_Register($dst$$reg), as_Register($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + instruct cmovL_cmpN(iRegLNoSp dst, iRegL src, iRegN op1, iRegN op2, cmpOpU cop) %{ match(Set dst (CMoveL (Binary cop (CmpN op1 op2)) (Binary dst src))); ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpN\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpN\n\t" %} ins_encode %{ @@ -10130,7 +10212,7 @@ instruct cmovL_cmpP(iRegLNoSp dst, iRegL src, iRegP op1, iRegP op2, cmpOpU cop) ins_cost(ALU_COST + BRANCH_COST); format %{ - "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpP\n\t" + "CMoveL $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpP\n\t" %} ins_encode %{ diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index 7c1ca4f8960ab..58c788747976e 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -89,6 +89,8 @@ source %{ return UseZvbb; case Op_LoadVectorGather: case Op_LoadVectorGatherMasked: + case Op_StoreVectorScatter: + case Op_StoreVectorScatterMasked: if (is_subword_type(bt)) { return false; } @@ -413,11 +415,11 @@ instruct vadd_fp_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-immediate add (unpredicated) -instruct vadd_immI(vReg dst, vReg src1, immI5 con) %{ +instruct vadd_vi(vReg dst, vReg src1, immI5 con) %{ match(Set dst (AddVB src1 (Replicate con))); match(Set dst (AddVS src1 (Replicate con))); match(Set dst (AddVI src1 (Replicate con))); - format %{ "vadd_immI $dst, $src1, $con" %} + format %{ "vadd_vi $dst, $src1, $con" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -428,9 +430,9 @@ instruct vadd_immI(vReg dst, vReg src1, immI5 con) %{ ins_pipe(pipe_slow); %} -instruct vadd_immL(vReg dst, vReg src1, immL5 con) %{ +instruct vaddL_vi(vReg dst, vReg src1, immL5 con) %{ match(Set dst (AddVL src1 (Replicate con))); - format %{ "vadd_immL $dst, $src1, $con" %} + format %{ "vaddL_vi $dst, $src1, $con" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vadd_vi(as_VectorRegister($dst$$reg), @@ -442,11 +444,11 @@ instruct vadd_immL(vReg dst, vReg src1, immL5 con) %{ // vector-scalar add (unpredicated) -instruct vadd_regI(vReg dst, vReg src1, iRegIorL2I src2) %{ +instruct vadd_vx(vReg dst, vReg src1, iRegIorL2I src2) %{ match(Set dst (AddVB src1 (Replicate src2))); match(Set dst (AddVS src1 (Replicate src2))); match(Set dst (AddVI src1 (Replicate src2))); - format %{ "vadd_regI $dst, $src1, $src2" %} + format %{ "vadd_vx $dst, $src1, $src2" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -457,9 +459,9 @@ instruct vadd_regI(vReg dst, vReg src1, iRegIorL2I src2) %{ ins_pipe(pipe_slow); %} -instruct vadd_regL(vReg dst, vReg src1, iRegL src2) %{ +instruct vaddL_vx(vReg dst, vReg src1, iRegL src2) %{ match(Set dst (AddVL src1 (Replicate src2))); - format %{ "vadd_regL $dst, $src1, $src2" %} + format %{ "vaddL_vx $dst, $src1, $src2" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vadd_vx(as_VectorRegister($dst$$reg), @@ -471,11 +473,11 @@ instruct vadd_regL(vReg dst, vReg src1, iRegL src2) %{ // vector-immediate add (predicated) -instruct vadd_immI_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ +instruct vadd_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ match(Set dst_src (AddVB (Binary dst_src (Replicate con)) v0)); match(Set dst_src (AddVS (Binary dst_src (Replicate con)) v0)); match(Set dst_src (AddVI (Binary dst_src (Replicate con)) v0)); - format %{ "vadd_immI_masked $dst_src, $dst_src, $con" %} + format %{ "vadd_vi_masked $dst_src, $dst_src, $con, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -486,9 +488,9 @@ instruct vadd_immI_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vadd_immL_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ +instruct vaddL_vi_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ match(Set dst_src (AddVL (Binary dst_src (Replicate con)) v0)); - format %{ "vadd_immL_masked $dst_src, $dst_src, $con" %} + format %{ "vaddL_vi_masked $dst_src, $dst_src, $con, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vadd_vi(as_VectorRegister($dst_src$$reg), @@ -500,11 +502,11 @@ instruct vadd_immL_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ // vector-scalar add (predicated) -instruct vadd_regI_masked(vReg dst_src, iRegIorL2I src2, vRegMask_V0 v0) %{ +instruct vadd_vx_masked(vReg dst_src, iRegIorL2I src2, vRegMask_V0 v0) %{ match(Set dst_src (AddVB (Binary dst_src (Replicate src2)) v0)); match(Set dst_src (AddVS (Binary dst_src (Replicate src2)) v0)); match(Set dst_src (AddVI (Binary dst_src (Replicate src2)) v0)); - format %{ "vadd_regI_masked $dst_src, $dst_src, $src2" %} + format %{ "vadd_vx_masked $dst_src, $dst_src, $src2, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -515,9 +517,9 @@ instruct vadd_regI_masked(vReg dst_src, iRegIorL2I src2, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vadd_regL_masked(vReg dst_src, iRegL src2, vRegMask_V0 v0) %{ +instruct vaddL_vx_masked(vReg dst_src, iRegL src2, vRegMask_V0 v0) %{ match(Set dst_src (AddVL (Binary dst_src (Replicate src2)) v0)); - format %{ "vadd_regL_masked $dst_src, $dst_src, $src2" %} + format %{ "vaddL_vx_masked $dst_src, $dst_src, $src2, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vadd_vx(as_VectorRegister($dst_src$$reg), @@ -593,11 +595,11 @@ instruct vsub_fp_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-scalar sub (unpredicated) -instruct vsub_regI(vReg dst, vReg src1, iRegIorL2I src2) %{ +instruct vsub_vx(vReg dst, vReg src1, iRegIorL2I src2) %{ match(Set dst (SubVB src1 (Replicate src2))); match(Set dst (SubVS src1 (Replicate src2))); match(Set dst (SubVI src1 (Replicate src2))); - format %{ "vsub_regI $dst, $src1, $src2" %} + format %{ "vsub_vx $dst, $src1, $src2" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -608,9 +610,9 @@ instruct vsub_regI(vReg dst, vReg src1, iRegIorL2I src2) %{ ins_pipe(pipe_slow); %} -instruct vsub_regL(vReg dst, vReg src1, iRegL src2) %{ +instruct vsubL_vx(vReg dst, vReg src1, iRegL src2) %{ match(Set dst (SubVL src1 (Replicate src2))); - format %{ "vsub_regL $dst, $src1, $src2" %} + format %{ "vsubL_vx $dst, $src1, $src2" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vsub_vx(as_VectorRegister($dst$$reg), @@ -622,11 +624,11 @@ instruct vsub_regL(vReg dst, vReg src1, iRegL src2) %{ // vector-scalar sub (predicated) -instruct vsub_regI_masked(vReg dst_src, iRegIorL2I src2, vRegMask_V0 v0) %{ +instruct vsub_vx_masked(vReg dst_src, iRegIorL2I src2, vRegMask_V0 v0) %{ match(Set dst_src (SubVB (Binary dst_src (Replicate src2)) v0)); match(Set dst_src (SubVS (Binary dst_src (Replicate src2)) v0)); match(Set dst_src (SubVI (Binary dst_src (Replicate src2)) v0)); - format %{ "vsub_regI_masked $dst_src, $dst_src, $src2" %} + format %{ "vsub_vx_masked $dst_src, $dst_src, $src2, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -637,9 +639,9 @@ instruct vsub_regI_masked(vReg dst_src, iRegIorL2I src2, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vsub_regL_masked(vReg dst_src, iRegL src2, vRegMask_V0 v0) %{ +instruct vsubL_vx_masked(vReg dst_src, iRegL src2, vRegMask_V0 v0) %{ match(Set dst_src (SubVL (Binary dst_src (Replicate src2)) v0)); - format %{ "vsub_regL_masked $dst_src, $dst_src, $src2" %} + format %{ "vsubL_vx_masked $dst_src, $dst_src, $src2, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vsub_vx(as_VectorRegister($dst_src$$reg), @@ -683,30 +685,30 @@ instruct vand_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-immediate and (unpredicated) -instruct vand_immI(vReg dst_src, immI5 con) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); - match(Set dst_src (AndV dst_src (Replicate con))); - format %{ "vand_immI $dst_src, $dst_src, $con" %} +instruct vand_vi(vReg dst, vReg src1, immI5 con) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); + match(Set dst (AndV src1 (Replicate con))); + format %{ "vand_vi $dst, $src1, $con" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vand_vi(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), + __ vand_vi(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), $con$$constant); %} ins_pipe(pipe_slow); %} -instruct vand_immL(vReg dst_src, immL5 con) %{ +instruct vandL_vi(vReg dst, vReg src1, immL5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); - match(Set dst_src (AndV dst_src (Replicate con))); - format %{ "vand_immL $dst_src, $dst_src, $con" %} + match(Set dst (AndV src1 (Replicate con))); + format %{ "vandL_vi $dst, $src1, $con" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); - __ vand_vi(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), + __ vand_vi(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), $con$$constant); %} ins_pipe(pipe_slow); @@ -714,43 +716,43 @@ instruct vand_immL(vReg dst_src, immL5 con) %{ // vector-scalar and (unpredicated) -instruct vand_regI(vReg dst_src, iRegIorL2I src) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); - match(Set dst_src (AndV dst_src (Replicate src))); - format %{ "vand_regI $dst_src, $dst_src, $src" %} +instruct vand_vx(vReg dst, vReg src1, iRegIorL2I src2) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); + match(Set dst (AndV src1 (Replicate src2))); + format %{ "vand_vx $dst, $src1, $src2" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vand_vx(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), - as_Register($src$$reg)); + __ vand_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); %} ins_pipe(pipe_slow); %} -instruct vand_regL(vReg dst_src, iRegL src) %{ +instruct vandL_vx(vReg dst, vReg src1, iRegL src2) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); - match(Set dst_src (AndV dst_src (Replicate src))); - format %{ "vand_regL $dst_src, $dst_src, $src" %} + match(Set dst (AndV src1 (Replicate src2))); + format %{ "vandL_vx $dst, $src1, $src2" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); - __ vand_vx(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), - as_Register($src$$reg)); + __ vand_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); %} ins_pipe(pipe_slow); %} // vector-immediate and (predicated) -instruct vand_immI_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); +instruct vand_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); match(Set dst_src (AndV (Binary dst_src (Replicate con)) v0)); - format %{ "vand_immI_masked $dst_src, $dst_src, $con" %} + format %{ "vand_vi_masked $dst_src, $dst_src, $con, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -761,10 +763,10 @@ instruct vand_immI_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vand_immL_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ +instruct vandL_vi_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src (AndV (Binary dst_src (Replicate con)) v0)); - format %{ "vand_immL_masked $dst_src, $dst_src, $con" %} + format %{ "vandL_vi_masked $dst_src, $dst_src, $con, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vand_vi(as_VectorRegister($dst_src$$reg), @@ -776,12 +778,12 @@ instruct vand_immL_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ // vector-scalar and (predicated) -instruct vand_regI_masked(vReg dst_src, iRegIorL2I src, vRegMask_V0 v0) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); +instruct vand_vx_masked(vReg dst_src, iRegIorL2I src, vRegMask_V0 v0) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); match(Set dst_src (AndV (Binary dst_src (Replicate src)) v0)); - format %{ "vand_regI_masked $dst_src, $dst_src, $src" %} + format %{ "vand_vx_masked $dst_src, $dst_src, $src, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -792,10 +794,10 @@ instruct vand_regI_masked(vReg dst_src, iRegIorL2I src, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vand_regL_masked(vReg dst_src, iRegL src, vRegMask_V0 v0) %{ +instruct vandL_vx_masked(vReg dst_src, iRegL src, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src (AndV (Binary dst_src (Replicate src)) v0)); - format %{ "vand_regL_masked $dst_src, $dst_src, $src" %} + format %{ "vandL_vx_masked $dst_src, $dst_src, $src, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vand_vx(as_VectorRegister($dst_src$$reg), @@ -839,30 +841,30 @@ instruct vor_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-immediate or (unpredicated) -instruct vor_immI(vReg dst_src, immI5 con) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); - match(Set dst_src (OrV dst_src (Replicate con))); - format %{ "vor_immI $dst_src, $dst_src, $con" %} +instruct vor_vi(vReg dst, vReg src1, immI5 con) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); + match(Set dst (OrV src1 (Replicate con))); + format %{ "vor_vi $dst, $src1, $con" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vor_vi(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), + __ vor_vi(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), $con$$constant); %} ins_pipe(pipe_slow); %} -instruct vor_immL(vReg dst_src, immL5 con) %{ +instruct vorL_vi(vReg dst, vReg src1, immL5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); - match(Set dst_src (OrV dst_src (Replicate con))); - format %{ "vor_immL $dst_src, $dst_src, $con" %} + match(Set dst (OrV src1 (Replicate con))); + format %{ "vorL_vi $dst, $src1, $con" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); - __ vor_vi(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), + __ vor_vi(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), $con$$constant); %} ins_pipe(pipe_slow); @@ -870,43 +872,43 @@ instruct vor_immL(vReg dst_src, immL5 con) %{ // vector-scalar or (unpredicated) -instruct vor_regI(vReg dst_src, iRegIorL2I src) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); - match(Set dst_src (OrV dst_src (Replicate src))); - format %{ "vor_regI $dst_src, $dst_src, $src" %} +instruct vor_vx(vReg dst, vReg src1, iRegIorL2I src2) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); + match(Set dst (OrV src1 (Replicate src2))); + format %{ "vor_vx $dst, $src1, $src2" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vor_vx(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), - as_Register($src$$reg)); + __ vor_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); %} ins_pipe(pipe_slow); %} -instruct vor_regL(vReg dst_src, iRegL src) %{ +instruct vorL_vx(vReg dst, vReg src1, iRegL src2) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); - match(Set dst_src (OrV dst_src (Replicate src))); - format %{ "vor_regL $dst_src, $dst_src, $src" %} + match(Set dst (OrV src1 (Replicate src2))); + format %{ "vorL_vx $dst, $src1, $src2" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); - __ vor_vx(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), - as_Register($src$$reg)); + __ vor_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); %} ins_pipe(pipe_slow); %} // vector-immediate or (predicated) -instruct vor_immI_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); +instruct vor_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); match(Set dst_src (OrV (Binary dst_src (Replicate con)) v0)); - format %{ "vor_immI_masked $dst_src, $dst_src, $con" %} + format %{ "vor_vi_masked $dst_src, $dst_src, $con, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -917,10 +919,10 @@ instruct vor_immI_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vor_immL_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ +instruct vorL_vi_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src (OrV (Binary dst_src (Replicate con)) v0)); - format %{ "vor_immL_masked $dst_src, $dst_src, $con" %} + format %{ "vorL_vi_masked $dst_src, $dst_src, $con, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vor_vi(as_VectorRegister($dst_src$$reg), @@ -932,12 +934,12 @@ instruct vor_immL_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ // vector-scalar or (predicated) -instruct vor_regI_masked(vReg dst_src, iRegIorL2I src, vRegMask_V0 v0) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); +instruct vor_vx_masked(vReg dst_src, iRegIorL2I src, vRegMask_V0 v0) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); match(Set dst_src (OrV (Binary dst_src (Replicate src)) v0)); - format %{ "vor_regI_masked $dst_src, $dst_src, $src" %} + format %{ "vor_vx_masked $dst_src, $dst_src, $src, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -948,10 +950,10 @@ instruct vor_regI_masked(vReg dst_src, iRegIorL2I src, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vor_regL_masked(vReg dst_src, iRegL src, vRegMask_V0 v0) %{ +instruct vorL_vx_masked(vReg dst_src, iRegL src, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src (OrV (Binary dst_src (Replicate src)) v0)); - format %{ "vor_regL_masked $dst_src, $dst_src, $src" %} + format %{ "vorL_vx_masked $dst_src, $dst_src, $src, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vor_vx(as_VectorRegister($dst_src$$reg), @@ -995,30 +997,30 @@ instruct vxor_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-immediate xor (unpredicated) -instruct vxor_immI(vReg dst_src, immI5 con) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); - match(Set dst_src (XorV dst_src (Replicate con))); - format %{ "vxor_immI $dst_src, $dst_src, $con" %} +instruct vxor_vi(vReg dst, vReg src1, immI5 con) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); + match(Set dst (XorV src1 (Replicate con))); + format %{ "vxor_vi $dst, $src1, $con" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vxor_vi(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), + __ vxor_vi(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), $con$$constant); %} ins_pipe(pipe_slow); %} -instruct vxor_immL(vReg dst_src, immL5 con) %{ +instruct vxorL_vi(vReg dst, vReg src1, immL5 con) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); - match(Set dst_src (XorV dst_src (Replicate con))); - format %{ "vxor_immL $dst_src, $dst_src, $con" %} + match(Set dst (XorV src1 (Replicate con))); + format %{ "vxorL_vi $dst, $src1, $con" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); - __ vxor_vi(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), + __ vxor_vi(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), $con$$constant); %} ins_pipe(pipe_slow); @@ -1026,43 +1028,43 @@ instruct vxor_immL(vReg dst_src, immL5 con) %{ // vector-scalar xor (unpredicated) -instruct vxor_regI(vReg dst_src, iRegIorL2I src) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); - match(Set dst_src (XorV dst_src (Replicate src))); - format %{ "vxor_regI $dst_src, $dst_src, $src" %} +instruct vxor_vx(vReg dst, vReg src1, iRegIorL2I src2) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); + match(Set dst (XorV src1 (Replicate src2))); + format %{ "vxor_vx $dst, $src1, $src2" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); - __ vxor_vx(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), - as_Register($src$$reg)); + __ vxor_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); %} ins_pipe(pipe_slow); %} -instruct vxor_regL(vReg dst_src, iRegL src) %{ +instruct vxorL_vx(vReg dst, vReg src1, iRegL src2) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); - match(Set dst_src (XorV dst_src (Replicate src))); - format %{ "vxor_regL $dst_src, $dst_src, $src" %} + match(Set dst (XorV src1 (Replicate src2))); + format %{ "vxorL_vx $dst, $src1, $src2" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); - __ vxor_vx(as_VectorRegister($dst_src$$reg), - as_VectorRegister($dst_src$$reg), - as_Register($src$$reg)); + __ vxor_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); %} ins_pipe(pipe_slow); %} // vector-immediate xor (predicated) -instruct vxor_immI_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); +instruct vxor_vi_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); match(Set dst_src (XorV (Binary dst_src (Replicate con)) v0)); - format %{ "vxor_immI_masked $dst_src, $dst_src, $con" %} + format %{ "vxor_vi_masked $dst_src, $dst_src, $con, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -1073,10 +1075,10 @@ instruct vxor_immI_masked(vReg dst_src, immI5 con, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vxor_immL_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ +instruct vxorL_vi_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src (XorV (Binary dst_src (Replicate con)) v0)); - format %{ "vxor_immL_masked $dst_src, $dst_src, $con" %} + format %{ "vxorL_vi_masked $dst_src, $dst_src, $con, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vxor_vi(as_VectorRegister($dst_src$$reg), @@ -1088,12 +1090,12 @@ instruct vxor_immL_masked(vReg dst_src, immL5 con, vRegMask_V0 v0) %{ // vector-scalar xor (predicated) -instruct vxor_regI_masked(vReg dst_src, iRegIorL2I src, vRegMask_V0 v0) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); +instruct vxor_vx_masked(vReg dst_src, iRegIorL2I src, vRegMask_V0 v0) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); match(Set dst_src (XorV (Binary dst_src (Replicate src)) v0)); - format %{ "vxor_regI_masked $dst_src, $dst_src, $src" %} + format %{ "vxor_vx_masked $dst_src, $dst_src, $src, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -1104,10 +1106,10 @@ instruct vxor_regI_masked(vReg dst_src, iRegIorL2I src, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vxor_regL_masked(vReg dst_src, iRegL src, vRegMask_V0 v0) %{ +instruct vxorL_vx_masked(vReg dst_src, iRegL src, vRegMask_V0 v0) %{ predicate(Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src (XorV (Binary dst_src (Replicate src)) v0)); - format %{ "vxor_regL_masked $dst_src, $dst_src, $src" %} + format %{ "vxorL_vx_masked $dst_src, $dst_src, $src, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vxor_vx(as_VectorRegister($dst_src$$reg), @@ -1121,16 +1123,38 @@ instruct vxor_regL_masked(vReg dst_src, iRegL src, vRegMask_V0 v0) %{ // vector and not +instruct vand_notB(vReg dst, vReg src1, vReg src2, immI_M1 m1) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_BYTE); + match(Set dst (AndV src1 (XorV src2 (Replicate m1)))); + format %{ "vand_notB $dst, $src1, $src2" %} + ins_encode %{ + __ vsetvli_helper(T_BYTE, Matcher::vector_length(this)); + __ vandn_vv(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vand_notS(vReg dst, vReg src1, vReg src2, immI_M1 m1) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_SHORT); + match(Set dst (AndV src1 (XorV src2 (Replicate m1)))); + format %{ "vand_notS $dst, $src1, $src2" %} + ins_encode %{ + __ vsetvli_helper(T_SHORT, Matcher::vector_length(this)); + __ vandn_vv(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + instruct vand_notI(vReg dst, vReg src1, vReg src2, immI_M1 m1) %{ - predicate(UseZvbb); - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_INT); match(Set dst (AndV src1 (XorV src2 (Replicate m1)))); format %{ "vand_notI $dst, $src1, $src2" %} ins_encode %{ - BasicType bt = Matcher::vector_element_basic_type(this); - __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vsetvli_helper(T_INT, Matcher::vector_length(this)); __ vandn_vv(as_VectorRegister($dst$$reg), as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg)); @@ -1139,8 +1163,7 @@ instruct vand_notI(vReg dst, vReg src1, vReg src2, immI_M1 m1) %{ %} instruct vand_notL(vReg dst, vReg src1, vReg src2, immL_M1 m1) %{ - predicate(UseZvbb); - predicate(Matcher::vector_element_basic_type(n) == T_LONG); + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst (AndV src1 (XorV src2 (Replicate m1)))); format %{ "vand_notL $dst, $src1, $src2" %} ins_encode %{ @@ -1152,16 +1175,40 @@ instruct vand_notL(vReg dst, vReg src1, vReg src2, immL_M1 m1) %{ ins_pipe(pipe_slow); %} +instruct vand_notB_masked(vReg dst_src1, vReg src2, immI_M1 m1, vRegMask_V0 v0) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_BYTE); + match(Set dst_src1 (AndV (Binary dst_src1 (XorV src2 (Replicate m1))) v0)); + format %{ "vand_notB_masked $dst_src1, $dst_src1, $src2, $v0" %} + ins_encode %{ + __ vsetvli_helper(T_BYTE, Matcher::vector_length(this)); + __ vandn_vv(as_VectorRegister($dst_src1$$reg), + as_VectorRegister($dst_src1$$reg), + as_VectorRegister($src2$$reg), + Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + +instruct vand_notS_masked(vReg dst_src1, vReg src2, immI_M1 m1, vRegMask_V0 v0) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_SHORT); + match(Set dst_src1 (AndV (Binary dst_src1 (XorV src2 (Replicate m1))) v0)); + format %{ "vand_notS_masked $dst_src1, $dst_src1, $src2, $v0" %} + ins_encode %{ + __ vsetvli_helper(T_SHORT, Matcher::vector_length(this)); + __ vandn_vv(as_VectorRegister($dst_src1$$reg), + as_VectorRegister($dst_src1$$reg), + as_VectorRegister($src2$$reg), + Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + instruct vand_notI_masked(vReg dst_src1, vReg src2, immI_M1 m1, vRegMask_V0 v0) %{ - predicate(UseZvbb); - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_INT); match(Set dst_src1 (AndV (Binary dst_src1 (XorV src2 (Replicate m1))) v0)); format %{ "vand_notI_masked $dst_src1, $dst_src1, $src2, $v0" %} ins_encode %{ - BasicType bt = Matcher::vector_element_basic_type(this); - __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vsetvli_helper(T_INT, Matcher::vector_length(this)); __ vandn_vv(as_VectorRegister($dst_src1$$reg), as_VectorRegister($dst_src1$$reg), as_VectorRegister($src2$$reg), @@ -1171,8 +1218,7 @@ instruct vand_notI_masked(vReg dst_src1, vReg src2, immI_M1 m1, vRegMask_V0 v0) %} instruct vand_notL_masked(vReg dst_src1, vReg src2, immL_M1 m1, vRegMask_V0 v0) %{ - predicate(UseZvbb); - predicate(Matcher::vector_element_basic_type(n) == T_LONG); + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst_src1 (AndV (Binary dst_src1 (XorV src2 (Replicate m1))) v0)); format %{ "vand_notL_masked $dst_src1, $dst_src1, $src2, $v0" %} ins_encode %{ @@ -1185,16 +1231,124 @@ instruct vand_notL_masked(vReg dst_src1, vReg src2, immL_M1 m1, vRegMask_V0 v0) ins_pipe(pipe_slow); %} +instruct vand_notB_vx(vReg dst, vReg src1, iRegIorL2I src2, immI_M1 m1) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_BYTE); + match(Set dst (AndV src1 (Replicate (XorI src2 m1)))); + format %{ "vand_notB_vx $dst, $src1, $src2" %} + ins_encode %{ + __ vsetvli_helper(T_BYTE, Matcher::vector_length(this)); + __ vandn_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vand_notS_vx(vReg dst, vReg src1, iRegIorL2I src2, immI_M1 m1) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_SHORT); + match(Set dst (AndV src1 (Replicate (XorI src2 m1)))); + format %{ "vand_notS_vx $dst, $src1, $src2" %} + ins_encode %{ + __ vsetvli_helper(T_SHORT, Matcher::vector_length(this)); + __ vandn_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vand_notI_vx(vReg dst, vReg src1, iRegIorL2I src2, immI_M1 m1) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_INT); + match(Set dst (AndV src1 (Replicate (XorI src2 m1)))); + format %{ "vand_notI_vx $dst, $src1, $src2" %} + ins_encode %{ + __ vsetvli_helper(T_INT, Matcher::vector_length(this)); + __ vandn_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vand_notL_vx(vReg dst, vReg src1, iRegL src2, immL_M1 m1) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_LONG); + match(Set dst (AndV src1 (Replicate (XorL src2 m1)))); + format %{ "vand_notL_vx $dst, $src1, $src2" %} + ins_encode %{ + __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); + __ vandn_vx(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), + as_Register($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vand_notB_vx_masked(vReg dst_src1, iRegIorL2I src2, immI_M1 m1, vRegMask_V0 v0) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_BYTE); + match(Set dst_src1 (AndV (Binary dst_src1 (Replicate (XorI src2 m1))) v0)); + format %{ "vand_notB_vx_masked $dst_src1, $dst_src1, $src2, $v0" %} + ins_encode %{ + __ vsetvli_helper(T_BYTE, Matcher::vector_length(this)); + __ vandn_vx(as_VectorRegister($dst_src1$$reg), + as_VectorRegister($dst_src1$$reg), + as_Register($src2$$reg), + Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + +instruct vand_notS_vx_masked(vReg dst_src1, iRegIorL2I src2, immI_M1 m1, vRegMask_V0 v0) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_SHORT); + match(Set dst_src1 (AndV (Binary dst_src1 (Replicate (XorI src2 m1))) v0)); + format %{ "vand_notS_vx_masked $dst_src1, $dst_src1, $src2, $v0" %} + ins_encode %{ + __ vsetvli_helper(T_SHORT, Matcher::vector_length(this)); + __ vandn_vx(as_VectorRegister($dst_src1$$reg), + as_VectorRegister($dst_src1$$reg), + as_Register($src2$$reg), + Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + +instruct vand_notI_vx_masked(vReg dst_src1, iRegIorL2I src2, immI_M1 m1, vRegMask_V0 v0) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_INT); + match(Set dst_src1 (AndV (Binary dst_src1 (Replicate (XorI src2 m1))) v0)); + format %{ "vand_notI_vx_masked $dst_src1, $dst_src1, $src2, $v0" %} + ins_encode %{ + __ vsetvli_helper(T_INT, Matcher::vector_length(this)); + __ vandn_vx(as_VectorRegister($dst_src1$$reg), + as_VectorRegister($dst_src1$$reg), + as_Register($src2$$reg), + Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + +instruct vand_notL_vx_masked(vReg dst_src1, iRegL src2, immL_M1 m1, vRegMask_V0 v0) %{ + predicate(UseZvbb && Matcher::vector_element_basic_type(n) == T_LONG); + match(Set dst_src1 (AndV (Binary dst_src1 (Replicate (XorL src2 m1))) v0)); + format %{ "vand_notL_vx_masked $dst_src1, $dst_src1, $src2, $v0" %} + ins_encode %{ + __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); + __ vandn_vx(as_VectorRegister($dst_src1$$reg), + as_VectorRegister($dst_src1$$reg), + as_Register($src2$$reg), + Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + // ------------------------------ Vector not ----------------------------------- // vector not -instruct vnotI(vReg dst, vReg src, immI_M1 m1) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); +instruct vnot(vReg dst, vReg src, immI_M1 m1) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); match(Set dst (XorV src (Replicate m1))); - format %{ "vnotI $dst, $src" %} + format %{ "vnot $dst, $src" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -1220,12 +1374,12 @@ instruct vnotL(vReg dst, vReg src, immL_M1 m1) %{ // vector not - predicated -instruct vnotI_masked(vReg dst_src, immI_M1 m1, vRegMask_V0 v0) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); +instruct vnot_masked(vReg dst_src, immI_M1 m1, vRegMask_V0 v0) %{ + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); match(Set dst_src (XorV (Binary dst_src (Replicate m1)) v0)); - format %{ "vnotI_masked $dst_src, $dst_src, $v0" %} + format %{ "vnot_masked $dst_src, $dst_src, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -1347,6 +1501,66 @@ instruct vmin_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} +// vector unsigned integer max/min + +instruct vmaxu(vReg dst, vReg src1, vReg src2) %{ + match(Set dst (UMaxV src1 src2)); + ins_cost(VEC_COST); + format %{ "vmaxu $dst, $src1, $src2" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vmaxu_vv(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +instruct vminu(vReg dst, vReg src1, vReg src2) %{ + match(Set dst (UMinV src1 src2)); + ins_cost(VEC_COST); + format %{ "vminu $dst, $src1, $src2" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vminu_vv(as_VectorRegister($dst$$reg), + as_VectorRegister($src1$$reg), as_VectorRegister($src2$$reg)); + %} + ins_pipe(pipe_slow); +%} + +// vector unsigned integer max/min - predicated + +instruct vmaxu_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ + match(Set dst_src1 (UMaxV (Binary dst_src1 src2) v0)); + ins_cost(VEC_COST); + format %{ "vmaxu_masked $dst_src1, $dst_src1, $src2, $v0" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vmaxu_vv(as_VectorRegister($dst_src1$$reg), as_VectorRegister($dst_src1$$reg), + as_VectorRegister($src2$$reg), Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + +instruct vminu_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ + match(Set dst_src1 (UMinV (Binary dst_src1 src2) v0)); + ins_cost(VEC_COST); + format %{ "vminu_masked $dst_src1, $dst_src1, $src2, $v0" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this); + assert(is_integral_type(bt), "unsupported type"); + __ vsetvli_helper(bt, Matcher::vector_length(this)); + __ vminu_vv(as_VectorRegister($dst_src1$$reg), as_VectorRegister($dst_src1$$reg), + as_VectorRegister($src2$$reg), Assembler::v0_t); + %} + ins_pipe(pipe_slow); +%} + // vector float-point max/min instruct vmax_fp(vReg dst, vReg src1, vReg src2, vRegMask_V0 v0) %{ @@ -1735,11 +1949,11 @@ instruct vmul_fp_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{ // vector-scalar mul (unpredicated) -instruct vmul_regI(vReg dst, vReg src1, iRegIorL2I src2) %{ +instruct vmul_vx(vReg dst, vReg src1, iRegIorL2I src2) %{ match(Set dst (MulVB src1 (Replicate src2))); match(Set dst (MulVS src1 (Replicate src2))); match(Set dst (MulVI src1 (Replicate src2))); - format %{ "vmul_regI $dst, $src1, $src2" %} + format %{ "vmul_vx $dst, $src1, $src2" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -1750,9 +1964,9 @@ instruct vmul_regI(vReg dst, vReg src1, iRegIorL2I src2) %{ ins_pipe(pipe_slow); %} -instruct vmul_regL(vReg dst, vReg src1, iRegL src2) %{ +instruct vmulL_vx(vReg dst, vReg src1, iRegL src2) %{ match(Set dst (MulVL src1 (Replicate src2))); - format %{ "vmul_regL $dst, $src1, $src2" %} + format %{ "vmulL_vx $dst, $src1, $src2" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vmul_vx(as_VectorRegister($dst$$reg), @@ -1764,11 +1978,11 @@ instruct vmul_regL(vReg dst, vReg src1, iRegL src2) %{ // vector-scalar mul (predicated) -instruct vmul_regI_masked(vReg dst_src, iRegIorL2I src2, vRegMask_V0 v0) %{ +instruct vmul_vx_masked(vReg dst_src, iRegIorL2I src2, vRegMask_V0 v0) %{ match(Set dst_src (MulVB (Binary dst_src (Replicate src2)) v0)); match(Set dst_src (MulVS (Binary dst_src (Replicate src2)) v0)); match(Set dst_src (MulVI (Binary dst_src (Replicate src2)) v0)); - format %{ "vmul_regI_masked $dst_src, $dst_src, $src2" %} + format %{ "vmul_vx_masked $dst_src, $dst_src, $src2, $v0" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -1779,9 +1993,9 @@ instruct vmul_regI_masked(vReg dst_src, iRegIorL2I src2, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vmul_regL_masked(vReg dst_src, iRegL src2, vRegMask_V0 v0) %{ +instruct vmulL_vx_masked(vReg dst_src, iRegL src2, vRegMask_V0 v0) %{ match(Set dst_src (MulVL (Binary dst_src (Replicate src2)) v0)); - format %{ "vmul_regL_masked $dst_src, $dst_src, $src2" %} + format %{ "vmulL_vx_masked $dst_src, $dst_src, $src2, $v0" %} ins_encode %{ __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); __ vmul_vx(as_VectorRegister($dst_src$$reg), @@ -1855,14 +2069,14 @@ instruct vfneg_masked(vReg dst_src, vRegMask_V0 v0) %{ // vector and reduction -instruct reduce_andI(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ +instruct reduce_and(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (AndReductionV src1 src2)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "reduce_andI $dst, $src1, $src2\t# KILL $tmp" %} + format %{ "reduce_and $dst, $src1, $src2\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -1889,14 +2103,14 @@ instruct reduce_andL(iRegLNoSp dst, iRegL src1, vReg src2, vReg tmp) %{ // vector and reduction - predicated -instruct reduce_andI_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ +instruct reduce_and_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (AndReductionV (Binary src1 src2) v0)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "reduce_andI_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} + format %{ "reduce_and_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -1925,14 +2139,14 @@ instruct reduce_andL_masked(iRegLNoSp dst, iRegL src1, vReg src2, vRegMask_V0 v0 // vector or reduction -instruct reduce_orI(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ +instruct reduce_or(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (OrReductionV src1 src2)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "reduce_orI $dst, $src1, $src2\t# KILL $tmp" %} + format %{ "reduce_or $dst, $src1, $src2\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -1959,14 +2173,14 @@ instruct reduce_orL(iRegLNoSp dst, iRegL src1, vReg src2, vReg tmp) %{ // vector or reduction - predicated -instruct reduce_orI_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ +instruct reduce_or_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (OrReductionV (Binary src1 src2) v0)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "reduce_orI_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} + format %{ "reduce_or_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -1995,14 +2209,14 @@ instruct reduce_orL_masked(iRegLNoSp dst, iRegL src1, vReg src2, vRegMask_V0 v0, // vector xor reduction -instruct reduce_xorI(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ +instruct reduce_xor(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (XorReductionV src1 src2)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "reduce_xorI $dst, $src1, $src2\t# KILL $tmp" %} + format %{ "reduce_xor $dst, $src1, $src2\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -2029,14 +2243,14 @@ instruct reduce_xorL(iRegLNoSp dst, iRegL src1, vReg src2, vReg tmp) %{ // vector xor reduction - predicated -instruct reduce_xorI_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ +instruct reduce_xor_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (XorReductionV (Binary src1 src2) v0)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "reduce_xorI_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} + format %{ "reduce_xor_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -2065,14 +2279,14 @@ instruct reduce_xorL_masked(iRegLNoSp dst, iRegL src1, vReg src2, vRegMask_V0 v0 // vector add reduction -instruct reduce_addI(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ +instruct reduce_add(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (AddReductionVI src1 src2)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "reduce_addI $dst, $src1, $src2\t# KILL $tmp" %} + format %{ "reduce_add $dst, $src1, $src2\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -2171,14 +2385,14 @@ instruct reduce_addD_unordered(fRegD dst, fRegD src1, vReg src2, vReg tmp) %{ // vector add reduction - predicated -instruct reduce_addI_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ +instruct reduce_add_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (AddReductionVI (Binary src1 src2) v0)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "reduce_addI_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} + format %{ "reduce_add_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -2237,14 +2451,14 @@ instruct reduce_addD_masked(fRegD dst, fRegD src1, vReg src2, vRegMask_V0 v0, vR // vector integer max reduction -instruct vreduce_maxI(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ +instruct vreduce_max(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (MaxReductionV src1 src2)); ins_cost(VEC_COST); effect(TEMP tmp); - format %{ "vreduce_maxI $dst, $src1, $src2\t# KILL $tmp" %} + format %{ "vreduce_max $dst, $src1, $src2\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -2271,14 +2485,14 @@ instruct vreduce_maxL(iRegLNoSp dst, iRegL src1, vReg src2, vReg tmp) %{ // vector integer max reduction - predicated -instruct vreduce_maxI_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ +instruct vreduce_max_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (MaxReductionV (Binary src1 src2) v0)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "vreduce_maxI_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} + format %{ "vreduce_max_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -2307,14 +2521,14 @@ instruct vreduce_maxL_masked(iRegLNoSp dst, iRegL src1, vReg src2, vRegMask_V0 v // vector integer min reduction -instruct vreduce_minI(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ +instruct vreduce_min(iRegINoSp dst, iRegIorL2I src1, vReg src2, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (MinReductionV src1 src2)); ins_cost(VEC_COST); effect(TEMP tmp); - format %{ "vreduce_minI $dst, $src1, $src2\t# KILL $tmp" %} + format %{ "vreduce_min $dst, $src1, $src2\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -2341,14 +2555,14 @@ instruct vreduce_minL(iRegLNoSp dst, iRegL src1, vReg src2, vReg tmp) %{ // vector integer min reduction - predicated -instruct vreduce_minI_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ +instruct vreduce_min_masked(iRegINoSp dst, iRegIorL2I src1, vReg src2, vRegMask_V0 v0, vReg tmp) %{ predicate(Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || Matcher::vector_element_basic_type(n->in(2)) == T_INT); match(Set dst (MinReductionV (Binary src1 src2) v0)); effect(TEMP tmp); ins_cost(VEC_COST); - format %{ "vreduce_minI_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} + format %{ "vreduce_min_masked $dst, $src1, $src2, $v0\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this, $src2); __ reduce_integral_v($dst$$Register, $src1$$Register, @@ -3056,10 +3270,10 @@ instruct vlsrL_masked(vReg dst_src, vReg shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vasrB_imm(vReg dst, vReg src, immI shift) %{ +instruct vasrB_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (RShiftVB src (RShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vasrB_imm $dst, $src, $shift" %} + format %{ "vasrB_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_BYTE, Matcher::vector_length(this)); @@ -3074,10 +3288,10 @@ instruct vasrB_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vasrS_imm(vReg dst, vReg src, immI shift) %{ +instruct vasrS_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (RShiftVS src (RShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vasrS_imm $dst, $src, $shift" %} + format %{ "vasrS_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_SHORT, Matcher::vector_length(this)); @@ -3092,10 +3306,10 @@ instruct vasrS_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vasrI_imm(vReg dst, vReg src, immI shift) %{ +instruct vasrI_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (RShiftVI src (RShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vasrI_imm $dst, $src, $shift" %} + format %{ "vasrI_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_INT, Matcher::vector_length(this)); @@ -3109,11 +3323,11 @@ instruct vasrI_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vasrL_imm(vReg dst, vReg src, immI shift) %{ +instruct vasrL_vi(vReg dst, vReg src, immI shift) %{ predicate((n->in(2)->in(1)->get_int() & 0x3f) < 32); match(Set dst (RShiftVL src (RShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vasrL_imm $dst, $src, $shift" %} + format %{ "vasrL_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); @@ -3127,10 +3341,10 @@ instruct vasrL_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vasrB_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vasrB_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (RShiftVB (Binary dst_src (RShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vasrB_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vasrB_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; if (con == 0) { @@ -3144,10 +3358,10 @@ instruct vasrB_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vasrS_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vasrS_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (RShiftVS (Binary dst_src (RShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vasrS_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vasrS_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; if (con == 0) { @@ -3161,10 +3375,10 @@ instruct vasrS_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vasrI_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vasrI_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (RShiftVI (Binary dst_src (RShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vasrI_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vasrI_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; if (con == 0) { @@ -3177,11 +3391,11 @@ instruct vasrI_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vasrL_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vasrL_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ predicate((n->in(1)->in(2)->in(1)->get_int() & 0x3f) < 32); match(Set dst_src (RShiftVL (Binary dst_src (RShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vasrL_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vasrL_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; if (con == 0) { @@ -3194,10 +3408,10 @@ instruct vasrL_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vlsrB_imm(vReg dst, vReg src, immI shift) %{ +instruct vlsrB_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (URShiftVB src (RShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vlsrB_imm $dst, $src, $shift" %} + format %{ "vlsrB_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_BYTE, Matcher::vector_length(this)); @@ -3216,10 +3430,10 @@ instruct vlsrB_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vlsrS_imm(vReg dst, vReg src, immI shift) %{ +instruct vlsrS_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (URShiftVS src (RShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vlsrS_imm $dst, $src, $shift" %} + format %{ "vlsrS_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_SHORT, Matcher::vector_length(this)); @@ -3238,10 +3452,10 @@ instruct vlsrS_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vlsrI_imm(vReg dst, vReg src, immI shift) %{ +instruct vlsrI_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (URShiftVI src (RShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vlsrI_imm $dst, $src, $shift" %} + format %{ "vlsrI_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_INT, Matcher::vector_length(this)); @@ -3255,11 +3469,11 @@ instruct vlsrI_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vlsrL_imm(vReg dst, vReg src, immI shift) %{ +instruct vlsrL_vi(vReg dst, vReg src, immI shift) %{ predicate((n->in(2)->in(1)->get_int() & 0x3f) < 32); match(Set dst (URShiftVL src (RShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vlsrL_imm $dst, $src, $shift" %} + format %{ "vlsrL_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); @@ -3273,10 +3487,10 @@ instruct vlsrL_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vlsrB_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vlsrB_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (URShiftVB (Binary dst_src (RShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vlsrB_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vlsrB_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; if (con == 0) { @@ -3294,10 +3508,10 @@ instruct vlsrB_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vlsrS_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vlsrS_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (URShiftVS (Binary dst_src (RShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vlsrS_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vlsrS_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; if (con == 0) { @@ -3315,10 +3529,10 @@ instruct vlsrS_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vlsrI_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vlsrI_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (URShiftVI (Binary dst_src (RShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vlsrI_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vlsrI_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; if (con == 0) { @@ -3331,11 +3545,11 @@ instruct vlsrI_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vlsrL_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vlsrL_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ predicate((n->in(1)->in(2)->in(1)->get_int() & 0x3f) < 32); match(Set dst_src (URShiftVL (Binary dst_src (RShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vlsrL_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vlsrL_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; if (con == 0) { @@ -3348,10 +3562,10 @@ instruct vlsrL_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vlslB_imm(vReg dst, vReg src, immI shift) %{ +instruct vlslB_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (LShiftVB src (LShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vlslB_imm $dst, $src, $shift" %} + format %{ "vlslB_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_BYTE, Matcher::vector_length(this)); @@ -3365,10 +3579,10 @@ instruct vlslB_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vlslS_imm(vReg dst, vReg src, immI shift) %{ +instruct vlslS_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (LShiftVS src (LShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vlslS_imm $dst, $src, $shift" %} + format %{ "vlslS_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_SHORT, Matcher::vector_length(this)); @@ -3382,10 +3596,10 @@ instruct vlslS_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vlslI_imm(vReg dst, vReg src, immI shift) %{ +instruct vlslI_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (LShiftVI src (LShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vlslI_imm $dst, $src, $shift" %} + format %{ "vlslI_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_INT, Matcher::vector_length(this)); @@ -3394,11 +3608,11 @@ instruct vlslI_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vlslL_imm(vReg dst, vReg src, immI shift) %{ +instruct vlslL_vi(vReg dst, vReg src, immI shift) %{ predicate((n->in(2)->in(1)->get_int() & 0x3f) < 32); match(Set dst (LShiftVL src (LShiftCntV shift))); ins_cost(VEC_COST); - format %{ "vlslL_imm $dst, $src, $shift" %} + format %{ "vlslL_vi $dst, $src, $shift" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); @@ -3407,10 +3621,10 @@ instruct vlslL_imm(vReg dst, vReg src, immI shift) %{ ins_pipe(pipe_slow); %} -instruct vlslB_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vlslB_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (LShiftVB (Binary dst_src (LShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vlslB_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vlslB_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_BYTE, Matcher::vector_length(this)); @@ -3425,10 +3639,10 @@ instruct vlslB_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vlslS_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vlslS_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (LShiftVS (Binary dst_src (LShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vlslS_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vlslS_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_SHORT, Matcher::vector_length(this)); @@ -3443,10 +3657,10 @@ instruct vlslS_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vlslI_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vlslI_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (LShiftVI (Binary dst_src (LShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vlslI_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vlslI_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_INT, Matcher::vector_length(this)); @@ -3456,11 +3670,11 @@ instruct vlslI_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ ins_pipe(pipe_slow); %} -instruct vlslL_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vlslL_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ predicate((n->in(1)->in(2)->in(1)->get_int() & 0x3f) < 32); match(Set dst_src (LShiftVL (Binary dst_src (LShiftCntV shift)) v0)); ins_cost(VEC_COST); - format %{ "vlslL_imm_masked $dst_src, $dst_src, $shift, $v0" %} + format %{ "vlslL_vi_masked $dst_src, $dst_src, $shift, $v0" %} ins_encode %{ uint32_t con = (unsigned)$shift$$constant & 0x1f; __ vsetvli_helper(T_LONG, Matcher::vector_length(this)); @@ -3500,9 +3714,9 @@ instruct vrotate_right(vReg dst, vReg src, vReg shift) %{ %} // Only the low log2(SEW) bits of shift value are used, all other bits are ignored. -instruct vrotate_right_reg(vReg dst, vReg src, iRegIorL2I shift) %{ +instruct vrotate_right_vx(vReg dst, vReg src, iRegIorL2I shift) %{ match(Set dst (RotateRightV src (Replicate shift))); - format %{ "vrotate_right_reg $dst, $src, $shift\t" %} + format %{ "vrotate_right_vx $dst, $src, $shift\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -3512,9 +3726,9 @@ instruct vrotate_right_reg(vReg dst, vReg src, iRegIorL2I shift) %{ ins_pipe(pipe_slow); %} -instruct vrotate_right_imm(vReg dst, vReg src, immI shift) %{ +instruct vrotate_right_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (RotateRightV src shift)); - format %{ "vrotate_right_imm $dst, $src, $shift\t" %} + format %{ "vrotate_right_vi $dst, $src, $shift\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); uint32_t bits = type2aelembytes(bt) * 8; @@ -3532,7 +3746,7 @@ instruct vrotate_right_imm(vReg dst, vReg src, immI shift) %{ instruct vrotate_right_masked(vReg dst_src, vReg shift, vRegMask_V0 v0) %{ match(Set dst_src (RotateRightV (Binary dst_src shift) v0)); - format %{ "vrotate_right_masked $dst_src, $dst_src, $shift, v0.t\t" %} + format %{ "vrotate_right_masked $dst_src, $dst_src, $shift, $v0\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -3543,9 +3757,9 @@ instruct vrotate_right_masked(vReg dst_src, vReg shift, vRegMask_V0 v0) %{ %} // Only the low log2(SEW) bits of shift value are used, all other bits are ignored. -instruct vrotate_right_reg_masked(vReg dst_src, iRegIorL2I shift, vRegMask_V0 v0) %{ +instruct vrotate_right_vx_masked(vReg dst_src, iRegIorL2I shift, vRegMask_V0 v0) %{ match(Set dst_src (RotateRightV (Binary dst_src (Replicate shift)) v0)); - format %{ "vrotate_right_reg_masked $dst_src, $dst_src, $shift, v0.t\t" %} + format %{ "vrotate_right_vx_masked $dst_src, $dst_src, $shift, $v0\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -3555,9 +3769,9 @@ instruct vrotate_right_reg_masked(vReg dst_src, iRegIorL2I shift, vRegMask_V0 v0 ins_pipe(pipe_slow); %} -instruct vrotate_right_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vrotate_right_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (RotateRightV (Binary dst_src shift) v0)); - format %{ "vrotate_right_imm_masked $dst_src, $dst_src, $shift, v0.t\t" %} + format %{ "vrotate_right_vi_masked $dst_src, $dst_src, $shift, $v0\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); uint32_t bits = type2aelembytes(bt) * 8; @@ -3587,9 +3801,9 @@ instruct vrotate_left(vReg dst, vReg src, vReg shift) %{ %} // Only the low log2(SEW) bits of shift value are used, all other bits are ignored. -instruct vrotate_left_reg(vReg dst, vReg src, iRegIorL2I shift) %{ +instruct vrotate_left_vx(vReg dst, vReg src, iRegIorL2I shift) %{ match(Set dst (RotateLeftV src (Replicate shift))); - format %{ "vrotate_left_reg $dst, $src, $shift\t" %} + format %{ "vrotate_left_vx $dst, $src, $shift\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -3599,9 +3813,9 @@ instruct vrotate_left_reg(vReg dst, vReg src, iRegIorL2I shift) %{ ins_pipe(pipe_slow); %} -instruct vrotate_left_imm(vReg dst, vReg src, immI shift) %{ +instruct vrotate_left_vi(vReg dst, vReg src, immI shift) %{ match(Set dst (RotateLeftV src shift)); - format %{ "vrotate_left_imm $dst, $src, $shift\t" %} + format %{ "vrotate_left_vi $dst, $src, $shift\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); uint32_t bits = type2aelembytes(bt) * 8; @@ -3620,7 +3834,7 @@ instruct vrotate_left_imm(vReg dst, vReg src, immI shift) %{ instruct vrotate_left_masked(vReg dst_src, vReg shift, vRegMask_V0 v0) %{ match(Set dst_src (RotateLeftV (Binary dst_src shift) v0)); - format %{ "vrotate_left_masked $dst_src, $dst_src, $shift, v0.t\t" %} + format %{ "vrotate_left_masked $dst_src, $dst_src, $shift, $v0\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -3631,9 +3845,9 @@ instruct vrotate_left_masked(vReg dst_src, vReg shift, vRegMask_V0 v0) %{ %} // Only the low log2(SEW) bits of shift value are used, all other bits are ignored. -instruct vrotate_left_reg_masked(vReg dst_src, iRegIorL2I shift, vRegMask_V0 v0) %{ +instruct vrotate_left_vx_masked(vReg dst_src, iRegIorL2I shift, vRegMask_V0 v0) %{ match(Set dst_src (RotateLeftV (Binary dst_src (Replicate shift)) v0)); - format %{ "vrotate_left_reg_masked $dst_src, $dst_src, $shift, v0.t\t" %} + format %{ "vrotate_left_vx_masked $dst_src, $dst_src, $shift, $v0\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -3643,9 +3857,9 @@ instruct vrotate_left_reg_masked(vReg dst_src, iRegIorL2I shift, vRegMask_V0 v0) ins_pipe(pipe_slow); %} -instruct vrotate_left_imm_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ +instruct vrotate_left_vi_masked(vReg dst_src, immI shift, vRegMask_V0 v0) %{ match(Set dst_src (RotateLeftV (Binary dst_src shift) v0)); - format %{ "vrotate_left_imm_masked $dst_src, $dst_src, $shift, v0.t\t" %} + format %{ "vrotate_left_vi_masked $dst_src, $dst_src, $shift, $v0\t" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); uint32_t bits = type2aelembytes(bt) * 8; @@ -4241,8 +4455,8 @@ instruct vcvtStoB(vReg dst, vReg src) %{ %} instruct vcvtStoX(vReg dst, vReg src) %{ - predicate((Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_LONG)); + predicate(Matcher::vector_element_basic_type(n) == T_INT || + Matcher::vector_element_basic_type(n) == T_LONG); match(Set dst (VectorCastS2X src)); effect(TEMP_DEF dst); format %{ "vcvtStoX $dst, $src" %} @@ -4255,8 +4469,8 @@ instruct vcvtStoX(vReg dst, vReg src) %{ %} instruct vcvtStoX_fp(vReg dst, vReg src) %{ - predicate((Matcher::vector_element_basic_type(n) == T_FLOAT || - Matcher::vector_element_basic_type(n) == T_DOUBLE)); + predicate(Matcher::vector_element_basic_type(n) == T_FLOAT || + Matcher::vector_element_basic_type(n) == T_DOUBLE); match(Set dst (VectorCastS2X src)); effect(TEMP_DEF dst); format %{ "vcvtStoX_fp $dst, $src" %} @@ -4353,9 +4567,9 @@ instruct vcvtItoD(vReg dst, vReg src) %{ // VectorCastL2X instruct vcvtLtoI(vReg dst, vReg src) %{ - predicate(Matcher::vector_element_basic_type(n) == T_INT || - Matcher::vector_element_basic_type(n) == T_BYTE || - Matcher::vector_element_basic_type(n) == T_SHORT); + predicate(Matcher::vector_element_basic_type(n) == T_BYTE || + Matcher::vector_element_basic_type(n) == T_SHORT || + Matcher::vector_element_basic_type(n) == T_INT); match(Set dst (VectorCastL2X src)); format %{ "vcvtLtoI $dst, $src" %} ins_encode %{ @@ -5091,14 +5305,14 @@ instruct populateindex(vReg dst, iRegIorL2I src1, iRegIorL2I src2, vReg tmp) %{ // BYTE, SHORT, INT -instruct insertI_index_lt32(vReg dst, vReg src, iRegIorL2I val, immI idx, vRegMask_V0 v0) %{ +instruct insert_index_lt32(vReg dst, vReg src, iRegIorL2I val, immI idx, vRegMask_V0 v0) %{ predicate(n->in(2)->get_int() < 32 && (Matcher::vector_element_basic_type(n) == T_BYTE || Matcher::vector_element_basic_type(n) == T_SHORT || Matcher::vector_element_basic_type(n) == T_INT)); match(Set dst (VectorInsert (Binary src val) idx)); effect(TEMP v0); - format %{ "insertI_index_lt32 $dst, $src, $val, $idx" %} + format %{ "insert_index_lt32 $dst, $src, $val, $idx" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); @@ -5110,14 +5324,14 @@ instruct insertI_index_lt32(vReg dst, vReg src, iRegIorL2I val, immI idx, vRegMa ins_pipe(pipe_slow); %} -instruct insertI_index(vReg dst, vReg src, iRegIorL2I val, iRegIorL2I idx, vReg tmp, vRegMask_V0 v0) %{ +instruct insert_index(vReg dst, vReg src, iRegIorL2I val, iRegIorL2I idx, vReg tmp, vRegMask_V0 v0) %{ predicate(n->in(2)->get_int() >= 32 && (Matcher::vector_element_basic_type(n) == T_BYTE || Matcher::vector_element_basic_type(n) == T_SHORT || Matcher::vector_element_basic_type(n) == T_INT)); match(Set dst (VectorInsert (Binary src val) idx)); effect(TEMP tmp, TEMP v0); - format %{ "insertI_index $dst, $src, $val, $idx\t# KILL $tmp" %} + format %{ "insert_index $dst, $src, $val, $idx\t# KILL $tmp" %} ins_encode %{ BasicType bt = Matcher::vector_element_basic_type(this); __ vsetvli_helper(bt, Matcher::vector_length(this)); diff --git a/src/hotspot/cpu/riscv/runtime_riscv.cpp b/src/hotspot/cpu/riscv/runtime_riscv.cpp index 44a8e35e285b9..7c8ca853bc40b 100644 --- a/src/hotspot/cpu/riscv/runtime_riscv.cpp +++ b/src/hotspot/cpu/riscv/runtime_riscv.cpp @@ -63,6 +63,9 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // Setup code generation tools const char* name = OptoRuntime::stub_name(OptoStubId::uncommon_trap_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } MacroAssembler* masm = new MacroAssembler(&buffer); assert_cond(masm != nullptr); @@ -282,6 +285,9 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { // Setup code generation tools const char* name = OptoRuntime::stub_name(OptoStubId::exception_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } MacroAssembler* masm = new MacroAssembler(&buffer); assert_cond(masm != nullptr); diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 1079084149042..5f53485a97fe1 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -596,12 +596,13 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, } // --------------------------------------------------------------- -AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, - int total_args_passed, - int comp_args_on_stack, - const BasicType *sig_bt, - const VMRegPair *regs, - AdapterFingerPrint* fingerprint) { + +void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + AdapterHandlerEntry* handler) { address i2c_entry = __ pc(); gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); @@ -658,7 +659,8 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); - return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry); + handler->set_entry_points(i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry); + return; } int SharedRuntime::vector_calling_convention(VMRegPair *regs, diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 4527a32926f52..fb4539267ae98 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -6458,58 +6458,6 @@ static const int64_t right_3_bits = right_n_bits(3); return start; } - void generate_vector_math_stubs() { - if (!UseRVV) { - log_info(library)("vector is not supported, skip loading vector math (sleef) library!"); - return; - } - - // Get native vector math stub routine addresses - void* libsleef = nullptr; - char ebuf[1024]; - char dll_name[JVM_MAXPATHLEN]; - if (os::dll_locate_lib(dll_name, sizeof(dll_name), Arguments::get_dll_dir(), "sleef")) { - libsleef = os::dll_load(dll_name, ebuf, sizeof ebuf); - } - if (libsleef == nullptr) { - log_info(library)("Failed to load native vector math (sleef) library, %s!", ebuf); - return; - } - - // Method naming convention - // All the methods are named as _ - // - // Where: - // is the operation name, e.g. sin, cos - // is to indicate float/double - // "fx/dx" for vector float/double operation - // is the precision level - // "u10/u05" represents 1.0/0.5 ULP error bounds - // We use "u10" for all operations by default - // But for those functions do not have u10 support, we use "u05" instead - // rvv, indicates riscv vector extension - // - // e.g. sinfx_u10rvv is the method for computing vector float sin using rvv instructions - // - log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, JNI_LIB_PREFIX "sleef" JNI_LIB_SUFFIX, p2i(libsleef)); - - for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { - int vop = VectorSupport::VECTOR_OP_MATH_START + op; - if (vop == VectorSupport::VECTOR_OP_TANH) { // skip tanh because of performance regression - continue; - } - - // The native library does not support u10 level of "hypot". - const char* ulf = (vop == VectorSupport::VECTOR_OP_HYPOT) ? "u05" : "u10"; - - snprintf(ebuf, sizeof(ebuf), "%sfx_%srvv", VectorSupport::mathname[op], ulf); - StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_SCALABLE][op] = (address)os::dll_lookup(libsleef, ebuf); - - snprintf(ebuf, sizeof(ebuf), "%sdx_%srvv", VectorSupport::mathname[op], ulf); - StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_SCALABLE][op] = (address)os::dll_lookup(libsleef, ebuf); - } - } - #endif // COMPILER2 /** @@ -6741,8 +6689,6 @@ static const int64_t right_3_bits = right_n_bits(3); generate_string_indexof_stubs(); - generate_vector_math_stubs(); - #endif // COMPILER2 } diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index a0de9d767bfb2..28c32ed33c824 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -248,14 +248,6 @@ void VM_Version::common_initialize() { #ifdef COMPILER2 void VM_Version::c2_initialize() { - if (UseCMoveUnconditionally) { - FLAG_SET_DEFAULT(UseCMoveUnconditionally, false); - } - - if (ConditionalMoveLimit > 0) { - FLAG_SET_DEFAULT(ConditionalMoveLimit, 0); - } - if (!UseRVV) { FLAG_SET_DEFAULT(MaxVectorSize, 0); } else { @@ -476,7 +468,7 @@ void VM_Version::initialize_cpu_information(void) { _no_of_threads = _no_of_cores; _no_of_sockets = _no_of_cores; snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE - 1, "RISCV64"); - snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "RISCV64 %s", features_string()); + snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "RISCV64 %s", cpu_info_string()); _initialized = true; } diff --git a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp index c858a4b8cb14b..430928a66ed85 100644 --- a/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp +++ b/src/hotspot/cpu/s390/c1_CodeStubs_s390.cpp @@ -52,7 +52,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { CHECK_BAILOUT(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); return; } @@ -74,7 +74,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { CHECK_BAILOUT(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } PredicateFailedStub::PredicateFailedStub(CodeEmitInfo* info) { @@ -88,7 +88,7 @@ void PredicateFailedStub::emit_code(LIR_Assembler* ce) { CHECK_BAILOUT(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } void CounterOverflowStub::emit_code(LIR_Assembler* ce) { @@ -116,7 +116,7 @@ void DivByZeroStub::emit_code(LIR_Assembler* ce) { ce->emit_call_c(Runtime1::entry_for (C1StubId::throw_div0_exception_id)); CHECK_BAILOUT(); ce->add_call_info_here(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } void ImplicitNullCheckStub::emit_code(LIR_Assembler* ce) { @@ -134,7 +134,7 @@ void ImplicitNullCheckStub::emit_code(LIR_Assembler* ce) { CHECK_BAILOUT(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } // Note: pass object in Z_R1_scratch @@ -147,7 +147,7 @@ void SimpleExceptionStub::emit_code(LIR_Assembler* ce) { ce->emit_call_c(a); CHECK_BAILOUT(); ce->add_call_info_here(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } NewInstanceStub::NewInstanceStub(LIR_Opr klass_reg, LIR_Opr result, ciInstanceKlass* klass, CodeEmitInfo* info, C1StubId stub_id) { diff --git a/src/hotspot/cpu/s390/c1_FrameMap_s390.cpp b/src/hotspot/cpu/s390/c1_FrameMap_s390.cpp index 9fa6da8341ff8..ddba445154a2a 100644 --- a/src/hotspot/cpu/s390/c1_FrameMap_s390.cpp +++ b/src/hotspot/cpu/s390/c1_FrameMap_s390.cpp @@ -144,13 +144,13 @@ LIR_Opr FrameMap::_caller_save_fpu_regs[] = {}; // c1 rnr -> FloatRegister FloatRegister FrameMap::nr2floatreg (int rnr) { assert(_init_done, "tables not initialized"); - debug_only(fpu_range_check(rnr);) + DEBUG_ONLY(fpu_range_check(rnr);) return _fpu_rnr2reg[rnr]; } void FrameMap::map_float_register(int rnr, FloatRegister reg) { - debug_only(fpu_range_check(rnr);) - debug_only(fpu_range_check(reg->encoding());) + DEBUG_ONLY(fpu_range_check(rnr);) + DEBUG_ONLY(fpu_range_check(reg->encoding());) _fpu_rnr2reg[rnr] = reg; // mapping c1 regnr. -> FloatRegister _fpu_reg2rnr[reg->encoding()] = rnr; // mapping assembler encoding -> c1 regnr. } diff --git a/src/hotspot/cpu/s390/c1_FrameMap_s390.hpp b/src/hotspot/cpu/s390/c1_FrameMap_s390.hpp index 66ccc8de876c4..721995f41fe0a 100644 --- a/src/hotspot/cpu/s390/c1_FrameMap_s390.hpp +++ b/src/hotspot/cpu/s390/c1_FrameMap_s390.hpp @@ -107,7 +107,7 @@ static int fpu_reg2rnr (FloatRegister reg) { assert(_init_done, "tables not initialized"); int c1rnr = _fpu_reg2rnr[reg->encoding()]; - debug_only(fpu_range_check(c1rnr);) + DEBUG_ONLY(fpu_range_check(c1rnr);) return c1rnr; } diff --git a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp index 2054c3db36c50..dea3317270e71 100644 --- a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp @@ -24,16 +24,16 @@ */ #include "asm/macroAssembler.inline.hpp" -#include "registerSaver_s390.hpp" -#include "gc/g1/g1CardTable.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BarrierSetAssembler.hpp" #include "gc/g1/g1BarrierSetRuntime.hpp" +#include "gc/g1/g1CardTable.hpp" #include "gc/g1/g1DirtyCardQueue.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1SATBMarkQueueSet.hpp" #include "gc/g1/g1ThreadLocalData.hpp" #include "interpreter/interp_masm.hpp" +#include "registerSaver_s390.hpp" #include "runtime/jniHandles.hpp" #include "runtime/sharedRuntime.hpp" #include "utilities/macros.hpp" diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetNMethod_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetNMethod_s390.cpp index 85dcc0a4e73f3..88b3199e4e166 100644 --- a/src/hotspot/cpu/s390/gc/shared/barrierSetNMethod_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/barrierSetNMethod_s390.cpp @@ -40,7 +40,7 @@ class NativeMethodBarrier: public NativeInstruction { address get_patchable_data_address() const { address inst_addr = get_barrier_start_address() + PATCHABLE_INSTRUCTION_OFFSET; - debug_only(Assembler::is_z_cfi(*((long*)inst_addr))); + DEBUG_ONLY(Assembler::is_z_cfi(*((long*)inst_addr))); return inst_addr + 2; } @@ -91,7 +91,7 @@ static NativeMethodBarrier* get_nmethod_barrier(nmethod* nm) { address barrier_address = nm->code_begin() + nm->frame_complete_offset() - NativeMethodBarrier::BARRIER_TOTAL_LENGTH; auto barrier = reinterpret_cast(barrier_address); - debug_only(barrier->verify()); + DEBUG_ONLY(barrier->verify()); return barrier; } diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index 4ba99eb9e8830..09995334330c4 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -444,7 +444,7 @@ void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, // Useful if consumed previously by access via stackTop(). void InterpreterMacroAssembler::popx(int len) { add2reg(Z_esp, len*Interpreter::stackElementSize); - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); } // Get Address object of stack top. No checks. No pop. @@ -458,38 +458,38 @@ void InterpreterMacroAssembler::pop_i(Register r) { z_l(r, Interpreter::expr_offset_in_bytes(0), Z_esp); add2reg(Z_esp, Interpreter::stackElementSize); assert_different_registers(r, Z_R1_scratch); - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); } void InterpreterMacroAssembler::pop_ptr(Register r) { z_lg(r, Interpreter::expr_offset_in_bytes(0), Z_esp); add2reg(Z_esp, Interpreter::stackElementSize); assert_different_registers(r, Z_R1_scratch); - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); } void InterpreterMacroAssembler::pop_l(Register r) { z_lg(r, Interpreter::expr_offset_in_bytes(0), Z_esp); add2reg(Z_esp, 2*Interpreter::stackElementSize); assert_different_registers(r, Z_R1_scratch); - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); } void InterpreterMacroAssembler::pop_f(FloatRegister f) { mem2freg_opt(f, Address(Z_esp, Interpreter::expr_offset_in_bytes(0)), false); add2reg(Z_esp, Interpreter::stackElementSize); - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); } void InterpreterMacroAssembler::pop_d(FloatRegister f) { mem2freg_opt(f, Address(Z_esp, Interpreter::expr_offset_in_bytes(0)), true); add2reg(Z_esp, 2*Interpreter::stackElementSize); - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); } void InterpreterMacroAssembler::push_i(Register r) { assert_different_registers(r, Z_R1_scratch); - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); z_st(r, Address(Z_esp)); add2reg(Z_esp, -Interpreter::stackElementSize); } @@ -501,7 +501,7 @@ void InterpreterMacroAssembler::push_ptr(Register r) { void InterpreterMacroAssembler::push_l(Register r) { assert_different_registers(r, Z_R1_scratch); - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); int offset = -Interpreter::stackElementSize; z_stg(r, Address(Z_esp, offset)); clear_mem(Address(Z_esp), Interpreter::stackElementSize); @@ -509,13 +509,13 @@ void InterpreterMacroAssembler::push_l(Register r) { } void InterpreterMacroAssembler::push_f(FloatRegister f) { - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); freg2mem_opt(f, Address(Z_esp), false); add2reg(Z_esp, -Interpreter::stackElementSize); } void InterpreterMacroAssembler::push_d(FloatRegister d) { - debug_only(verify_esp(Z_esp, Z_R1_scratch)); + DEBUG_ONLY(verify_esp(Z_esp, Z_R1_scratch)); int offset = -Interpreter::stackElementSize; freg2mem_opt(d, Address(Z_esp, offset)); add2reg(Z_esp, 2 * offset); diff --git a/src/hotspot/cpu/s390/runtime_s390.cpp b/src/hotspot/cpu/s390/runtime_s390.cpp index 4eedb3877d2ab..8f96ff55ccb4c 100644 --- a/src/hotspot/cpu/s390/runtime_s390.cpp +++ b/src/hotspot/cpu/s390/runtime_s390.cpp @@ -72,6 +72,9 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { // Setup code generation tools const char* name = OptoRuntime::stub_name(OptoStubId::exception_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } MacroAssembler* masm = new MacroAssembler(&buffer); Register handle_exception = Z_ARG5; diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index f4487ccabec14..bd5bbf4c7e5e6 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -2352,12 +2352,12 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, __ z_br(Z_R1_scratch); } -AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, - int total_args_passed, - int comp_args_on_stack, - const BasicType *sig_bt, - const VMRegPair *regs, - AdapterFingerPrint* fingerprint) { +void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + AdapterHandlerEntry* handler) { __ align(CodeEntryAlignment); address i2c_entry = __ pc(); gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); @@ -2411,7 +2411,8 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); - return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry); + handler->set_entry_points(i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry); + return; } // This function returns the adjust size (in number of words) to a c2i adapter @@ -2768,6 +2769,9 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // Setup code generation tools const char* name = OptoRuntime::stub_name(OptoStubId::uncommon_trap_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer); Register unroll_block_reg = Z_tmp_1; diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index 157b945e6e1a4..8261fbd083aae 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -90,7 +90,7 @@ static const char* z_features[] = {" ", void VM_Version::initialize() { determine_features(); // Get processor capabilities. - set_features_string(); // Set a descriptive feature indication. + set_cpu_info_string(); // Set a descriptive feature indication. if (Verbose || PrintAssembly || PrintStubCode) { print_features_internal("CPU Version as detected internally:", PrintAssembly || PrintStubCode); @@ -388,9 +388,9 @@ int VM_Version::get_model_index() { } -void VM_Version::set_features_string() { - // A note on the _features_string format: - // There are jtreg tests checking the _features_string for various properties. +void VM_Version::set_cpu_info_string() { + // A note on the _cpu_info_string format: + // There are jtreg tests checking the _cpu_info_string for various properties. // For some strange reason, these tests require the string to contain // only _lowercase_ characters. Keep that in mind when being surprised // about the unusual notation of features - and when adding new ones. @@ -412,29 +412,29 @@ void VM_Version::set_features_string() { _model_string = "unknown model"; strcpy(buf, "z/Architecture (ambiguous detection)"); } - _features_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf); if (has_Crypto_AES()) { - assert(strlen(_features_string) + 3*8 < sizeof(buf), "increase buffer size"); + assert(strlen(_cpu_info_string) + 3*8 < sizeof(buf), "increase buffer size"); jio_snprintf(buf, sizeof(buf), "%s%s%s%s", - _features_string, + _cpu_info_string, has_Crypto_AES128() ? ", aes128" : "", has_Crypto_AES192() ? ", aes192" : "", has_Crypto_AES256() ? ", aes256" : ""); - os::free((void *)_features_string); - _features_string = os::strdup(buf); + os::free((void *)_cpu_info_string); + _cpu_info_string = os::strdup(buf); } if (has_Crypto_SHA()) { - assert(strlen(_features_string) + 6 + 2*8 + 7 < sizeof(buf), "increase buffer size"); + assert(strlen(_cpu_info_string) + 6 + 2*8 + 7 < sizeof(buf), "increase buffer size"); jio_snprintf(buf, sizeof(buf), "%s%s%s%s%s", - _features_string, + _cpu_info_string, has_Crypto_SHA1() ? ", sha1" : "", has_Crypto_SHA256() ? ", sha256" : "", has_Crypto_SHA512() ? ", sha512" : "", has_Crypto_GHASH() ? ", ghash" : ""); - os::free((void *)_features_string); - _features_string = os::strdup(buf); + os::free((void *)_cpu_info_string); + _cpu_info_string = os::strdup(buf); } } @@ -464,7 +464,7 @@ bool VM_Version::test_feature_bit(unsigned long* featureBuffer, int featureNum, } void VM_Version::print_features_internal(const char* text, bool print_anyway) { - tty->print_cr("%s %s", text, features_string()); + tty->print_cr("%s %s", text, cpu_info_string()); tty->cr(); if (Verbose || print_anyway) { @@ -906,7 +906,7 @@ void VM_Version::set_features_from(const char* march) { err = true; } if (!err) { - set_features_string(); + set_cpu_info_string(); if (prt || PrintAssembly) { print_features_internal("CPU Version as set by cmdline option:", prt); } @@ -1542,6 +1542,6 @@ void VM_Version::initialize_cpu_information(void) { _no_of_threads = _no_of_cores; _no_of_sockets = _no_of_cores; snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE, "s390 %s", VM_Version::get_model_string()); - snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "s390 %s", features_string()); + snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "s390 %s", cpu_info_string()); _initialized = true; } diff --git a/src/hotspot/cpu/s390/vm_version_s390.hpp b/src/hotspot/cpu/s390/vm_version_s390.hpp index 49e6f5686f60a..6c6eb76bf7b03 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.hpp +++ b/src/hotspot/cpu/s390/vm_version_s390.hpp @@ -148,7 +148,7 @@ class VM_Version: public Abstract_VM_Version { static bool test_feature_bit(unsigned long* featureBuffer, int featureNum, unsigned int bufLen); static int get_model_index(); - static void set_features_string(); + static void set_cpu_info_string(); static void print_features_internal(const char* text, bool print_anyway=false); static void determine_features(); static long call_getFeatures(unsigned long* buffer, int buflen, int functionCode); diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 6853af9e74642..7a4d7c6d6f340 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -801,7 +801,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { address ip = inst; bool is_64bit = false; - debug_only(bool has_disp32 = false); + DEBUG_ONLY(bool has_disp32 = false); int tail_size = 0; // other random bytes (#32, #16, etc.) at end of insn again_after_prefix: @@ -859,7 +859,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0x8A: // movb r, a case 0x8B: // movl r, a case 0x8F: // popl a - debug_only(has_disp32 = true); + DEBUG_ONLY(has_disp32 = true); break; case 0x68: // pushq #32 @@ -898,10 +898,10 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0x8B: // movw r, a case 0x89: // movw a, r - debug_only(has_disp32 = true); + DEBUG_ONLY(has_disp32 = true); break; case 0xC7: // movw a, #16 - debug_only(has_disp32 = true); + DEBUG_ONLY(has_disp32 = true); tail_size = 2; // the imm16 break; case 0x0F: // several SSE/SSE2 variants @@ -923,7 +923,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0x69: // imul r, a, #32 case 0xC7: // movl a, #32(oop?) tail_size = 4; - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! break; case 0x0F: // movx..., etc. @@ -932,11 +932,11 @@ address Assembler::locate_operand(address inst, WhichOperand which) { tail_size = 1; case 0x38: // ptest, pmovzxbw ip++; // skip opcode - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! break; case 0x70: // pshufd r, r/a, #8 - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! case 0x73: // psrldq r, #8 tail_size = 1; break; @@ -961,7 +961,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0xAE: // ldmxcsr, stmxcsr, fxrstor, fxsave, clflush case 0xD6: // movq case 0xFE: // paddd - debug_only(has_disp32 = true); + DEBUG_ONLY(has_disp32 = true); break; case 0xAD: // shrd r, a, %cl @@ -976,18 +976,18 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0xC1: // xaddl case 0xC7: // cmpxchg8 case REP16(0x90): // setcc a - debug_only(has_disp32 = true); + DEBUG_ONLY(has_disp32 = true); // fall out of the switch to decode the address break; case 0xC4: // pinsrw r, a, #8 - debug_only(has_disp32 = true); + DEBUG_ONLY(has_disp32 = true); case 0xC5: // pextrw r, r, #8 tail_size = 1; // the imm8 break; case 0xAC: // shrd r, a, #8 - debug_only(has_disp32 = true); + DEBUG_ONLY(has_disp32 = true); tail_size = 1; // the imm8 break; @@ -1004,12 +1004,12 @@ address Assembler::locate_operand(address inst, WhichOperand which) { // also: orl, adcl, sbbl, andl, subl, xorl, cmpl // on 32bit in the case of cmpl, the imm might be an oop tail_size = 4; - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! break; case 0x83: // addl a, #8; addl r, #8 // also: orl, adcl, sbbl, andl, subl, xorl, cmpl - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! tail_size = 1; break; @@ -1026,7 +1026,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0x9B: switch (0xFF & *ip++) { case 0xD9: // fnstcw a - debug_only(has_disp32 = true); + DEBUG_ONLY(has_disp32 = true); break; default: ShouldNotReachHere(); @@ -1045,7 +1045,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0x87: // xchg r, a case REP4(0x38): // cmp... case 0x85: // test r, a - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! break; case 0xA8: // testb rax, #8 @@ -1057,7 +1057,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0xC6: // movb a, #8 case 0x80: // cmpb a, #8 case 0x6B: // imul r, a, #8 - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! tail_size = 1; // the imm8 break; @@ -1109,7 +1109,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { break; } ip++; // skip opcode - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! break; case 0x62: // EVEX_4bytes @@ -1135,7 +1135,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { break; } ip++; // skip opcode - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! break; case 0xD1: // sal a, 1; sar a, 1; shl a, 1; shr a, 1 @@ -1147,7 +1147,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0xD8: // fadd_s a; fsubr_s a; fmul_s a; fdivr_s a; fcomp_s a case 0xDC: // fadd_d a; fsubr_d a; fmul_d a; fdivr_d a; fcomp_d a case 0xDE: // faddp_d a; fsubrp_d a; fmulp_d a; fdivrp_d a; fcompp_d a - debug_only(has_disp32 = true); + DEBUG_ONLY(has_disp32 = true); break; case 0xE8: // call rdisp32 @@ -1184,7 +1184,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { default: ip++; } - debug_only(has_disp32 = true); // has both kinds of operands! + DEBUG_ONLY(has_disp32 = true); // has both kinds of operands! break; default: @@ -2010,6 +2010,11 @@ void Assembler::cpuid() { emit_int16(0x0F, (unsigned char)0xA2); } +void Assembler::serialize() { + assert(VM_Version::supports_serialize(), ""); + emit_int24(0x0F, 0x01, 0xE8); +} + // Opcode / Instruction Op / En 64 - Bit Mode Compat / Leg Mode Description Implemented // F2 0F 38 F0 / r CRC32 r32, r / m8 RM Valid Valid Accumulate CRC32 on r / m8. v // F2 REX 0F 38 F0 / r CRC32 r32, r / m8* RM Valid N.E. Accumulate CRC32 on r / m8. - diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 6395be02f273c..719334701a5a6 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1232,6 +1232,9 @@ class Assembler : public AbstractAssembler { // Identify processor type and features void cpuid(); + // Serialize instruction stream + void serialize(); + // CRC32C void crc32(Register crc, Register v, int8_t sizeInBytes); void crc32(Register crc, Address adr, int8_t sizeInBytes); diff --git a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp index 73262b213655e..7c0d3ff624d6a 100644 --- a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp +++ b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp @@ -68,7 +68,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ call(RuntimeAddress(a)); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); return; } @@ -88,7 +88,7 @@ void RangeCheckStub::emit_code(LIR_Assembler* ce) { __ call(RuntimeAddress(Runtime1::entry_for(stub_id))); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } PredicateFailedStub::PredicateFailedStub(CodeEmitInfo* info) { @@ -101,7 +101,7 @@ void PredicateFailedStub::emit_code(LIR_Assembler* ce) { __ call(RuntimeAddress(a)); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } void DivByZeroStub::emit_code(LIR_Assembler* ce) { @@ -111,7 +111,7 @@ void DivByZeroStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); __ call(RuntimeAddress(Runtime1::entry_for(C1StubId::throw_div0_exception_id))); ce->add_call_info_here(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } @@ -399,7 +399,7 @@ void ImplicitNullCheckStub::emit_code(LIR_Assembler* ce) { __ call(RuntimeAddress(a)); ce->add_call_info_here(_info); ce->verify_oop_map(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } @@ -413,7 +413,7 @@ void SimpleExceptionStub::emit_code(LIR_Assembler* ce) { } __ call(RuntimeAddress(Runtime1::entry_for(_stub))); ce->add_call_info_here(_info); - debug_only(__ should_not_reach_here()); + DEBUG_ONLY(__ should_not_reach_here()); } diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 574bc081fceab..19e25cde2ec6f 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -787,6 +787,119 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, // C2 uses the value of ZF to determine the continuation. } +static void abort_verify_int_in_range(uint idx, jint val, jint lo, jint hi) { + fatal("Invalid CastII, idx: %u, val: %d, lo: %d, hi: %d", idx, val, lo, hi); +} + +static void reconstruct_frame_pointer_helper(MacroAssembler* masm, Register dst) { + const int framesize = Compile::current()->output()->frame_size_in_bytes(); + masm->movptr(dst, rsp); + if (framesize > 2 * wordSize) { + masm->addptr(dst, framesize - 2 * wordSize); + } +} + +void C2_MacroAssembler::reconstruct_frame_pointer(Register rtmp) { + if (PreserveFramePointer) { + // frame pointer is valid +#ifdef ASSERT + // Verify frame pointer value in rbp. + reconstruct_frame_pointer_helper(this, rtmp); + Label L_success; + cmpq(rbp, rtmp); + jccb(Assembler::equal, L_success); + STOP("frame pointer mismatch"); + bind(L_success); +#endif // ASSERT + } else { + reconstruct_frame_pointer_helper(this, rbp); + } +} + +void C2_MacroAssembler::verify_int_in_range(uint idx, const TypeInt* t, Register val) { + jint lo = t->_lo; + jint hi = t->_hi; + assert(lo < hi, "type should not be empty or constant, idx: %u, lo: %d, hi: %d", idx, lo, hi); + if (t == TypeInt::INT) { + return; + } + + BLOCK_COMMENT("CastII {"); + Label fail; + Label succeed; + if (hi == max_jint) { + cmpl(val, lo); + jccb(Assembler::greaterEqual, succeed); + } else { + if (lo != min_jint) { + cmpl(val, lo); + jccb(Assembler::less, fail); + } + cmpl(val, hi); + jccb(Assembler::lessEqual, succeed); + } + + bind(fail); + movl(c_rarg0, idx); + movl(c_rarg1, val); + movl(c_rarg2, lo); + movl(c_rarg3, hi); + reconstruct_frame_pointer(rscratch1); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, abort_verify_int_in_range))); + hlt(); + bind(succeed); + BLOCK_COMMENT("} // CastII"); +} + +static void abort_verify_long_in_range(uint idx, jlong val, jlong lo, jlong hi) { + fatal("Invalid CastLL, idx: %u, val: " JLONG_FORMAT ", lo: " JLONG_FORMAT ", hi: " JLONG_FORMAT, idx, val, lo, hi); +} + +void C2_MacroAssembler::verify_long_in_range(uint idx, const TypeLong* t, Register val, Register tmp) { + jlong lo = t->_lo; + jlong hi = t->_hi; + assert(lo < hi, "type should not be empty or constant, idx: %u, lo: " JLONG_FORMAT ", hi: " JLONG_FORMAT, idx, lo, hi); + if (t == TypeLong::LONG) { + return; + } + + BLOCK_COMMENT("CastLL {"); + Label fail; + Label succeed; + + auto cmp_val = [&](jlong bound) { + if (is_simm32(bound)) { + cmpq(val, checked_cast(bound)); + } else { + mov64(tmp, bound); + cmpq(val, tmp); + } + }; + + if (hi == max_jlong) { + cmp_val(lo); + jccb(Assembler::greaterEqual, succeed); + } else { + if (lo != min_jlong) { + cmp_val(lo); + jccb(Assembler::less, fail); + } + cmp_val(hi); + jccb(Assembler::lessEqual, succeed); + } + + bind(fail); + movl(c_rarg0, idx); + movq(c_rarg1, val); + mov64(c_rarg2, lo); + mov64(c_rarg3, hi); + reconstruct_frame_pointer(rscratch1); + call(RuntimeAddress(CAST_FROM_FN_PTR(address, abort_verify_long_in_range))); + hlt(); + bind(succeed); + BLOCK_COMMENT("} // CastLL"); +} + //------------------------------------------------------------------------------------------- // Generic instructions support for use in .ad files C2 code generation diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index dd2880d88c381..713eb73d68f38 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -44,6 +44,9 @@ Register t, Register thread); void fast_unlock_lightweight(Register obj, Register reg_rax, Register t, Register thread); + void verify_int_in_range(uint idx, const TypeInt* t, Register val); + void verify_long_in_range(uint idx, const TypeLong* t, Register val, Register tmp); + // Generic instructions support for use in .ad files C2 code generation void vabsnegd(int opcode, XMMRegister dst, XMMRegister src); void vabsnegd(int opcode, XMMRegister dst, XMMRegister src, int vector_len); @@ -574,4 +577,7 @@ void scalar_max_min_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2); + + void reconstruct_frame_pointer(Register rtmp); + #endif // CPU_X86_C2_MACROASSEMBLER_X86_HPP diff --git a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp index 298e5640b27d1..66fb4cbb8c78d 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp @@ -26,9 +26,9 @@ #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_MacroAssembler.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" -#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #define __ masm->masm()-> diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 45e4c46161ff1..deb8111adade8 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -23,6 +23,8 @@ * */ +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahForwarding.hpp" @@ -30,8 +32,6 @@ #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "interpreter/interpreter.hpp" #include "runtime/javaThread.hpp" #include "runtime/sharedRuntime.hpp" diff --git a/src/hotspot/cpu/x86/globalDefinitions_x86.hpp b/src/hotspot/cpu/x86/globalDefinitions_x86.hpp index 873cfbdcea0ec..3c1474ae8611a 100644 --- a/src/hotspot/cpu/x86/globalDefinitions_x86.hpp +++ b/src/hotspot/cpu/x86/globalDefinitions_x86.hpp @@ -34,9 +34,7 @@ const bool CCallingConventionRequiresIntsAsLongs = false; #define SUPPORTS_NATIVE_CX8 -#ifdef _LP64 #define SUPPORT_MONITOR_COUNT -#endif #define CPU_MULTI_COPY_ATOMIC @@ -44,15 +42,11 @@ const bool CCallingConventionRequiresIntsAsLongs = false; #define DEFAULT_CACHE_LINE_SIZE 64 // The default padding size for data structures to avoid false sharing. -#ifdef _LP64 // The common wisdom is that adjacent cache line prefetchers on some hardware // may pull two cache lines on access, so we have to pessimistically assume twice // the cache line size for padding. TODO: Check if this is still true for modern // hardware. If not, DEFAULT_CACHE_LINE_SIZE might as well suffice. #define DEFAULT_PADDING_SIZE (DEFAULT_CACHE_LINE_SIZE*2) -#else -#define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE -#endif #if defined(LINUX) || defined(__APPLE__) #define SUPPORT_RESERVED_STACK_AREA diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp index 54888a9f849d9..a1d4a71874f55 100644 --- a/src/hotspot/cpu/x86/globals_x86.hpp +++ b/src/hotspot/cpu/x86/globals_x86.hpp @@ -61,29 +61,19 @@ define_pd_global(intx, InlineSmallCode, 1000); #define MIN_STACK_RED_PAGES DEFAULT_STACK_RED_PAGES #define MIN_STACK_RESERVED_PAGES (0) -#ifdef _LP64 // Java_java_net_SocketOutputStream_socketWrite0() uses a 64k buffer on the -// stack if compiled for unix and LP64. To pass stack overflow tests we need -// 20 shadow pages. +// stack if compiled for unix. To pass stack overflow tests we need 20 shadow pages. #define DEFAULT_STACK_SHADOW_PAGES (NOT_WIN64(20) WIN64_ONLY(8) DEBUG_ONLY(+4)) // For those clients that do not use write socket, we allow // the min range value to be below that of the default #define MIN_STACK_SHADOW_PAGES (NOT_WIN64(10) WIN64_ONLY(8) DEBUG_ONLY(+4)) -#else -#define DEFAULT_STACK_SHADOW_PAGES (4 DEBUG_ONLY(+5)) -#define MIN_STACK_SHADOW_PAGES DEFAULT_STACK_SHADOW_PAGES -#endif // _LP64 define_pd_global(intx, StackYellowPages, DEFAULT_STACK_YELLOW_PAGES); define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); -#ifdef _LP64 define_pd_global(bool, VMContinuations, true); -#else -define_pd_global(bool, VMContinuations, false); -#endif define_pd_global(bool, RewriteBytecodes, true); define_pd_global(bool, RewriteFrequentPairs, true); @@ -191,6 +181,15 @@ define_pd_global(intx, InitArrayShortSize, 8*BytesPerLong); product(bool, IntelJccErratumMitigation, true, DIAGNOSTIC, \ "Turn off JVM mitigations related to Intel micro code " \ "mitigations for the Intel JCC erratum") \ + \ + product(int, X86ICacheSync, -1, DIAGNOSTIC, \ + "Select the X86 ICache sync mechanism: -1 = auto-select; " \ + "0 = none (dangerous); 1 = CLFLUSH loop; 2 = CLFLUSHOPT loop; "\ + "3 = CLWB loop; 4 = single CPUID; 5 = single SERIALIZE. " \ + "Explicitly selected mechanism will fail at startup if " \ + "hardware does not support it.") \ + range(-1, 5) \ + \ // end of ARCH_FLAGS #endif // CPU_X86_GLOBALS_X86_HPP diff --git a/src/hotspot/cpu/x86/icache_x86.cpp b/src/hotspot/cpu/x86/icache_x86.cpp index 45679332ecaca..889cfb32931e6 100644 --- a/src/hotspot/cpu/x86/icache_x86.cpp +++ b/src/hotspot/cpu/x86/icache_x86.cpp @@ -23,15 +23,63 @@ */ #include "asm/macroAssembler.hpp" +#include "runtime/flags/flagSetting.hpp" +#include "runtime/globals_extension.hpp" #include "runtime/icache.hpp" #define __ _masm-> +void x86_generate_icache_fence(MacroAssembler* _masm) { + switch (X86ICacheSync) { + case 0: + break; + case 1: + __ mfence(); + break; + case 2: + case 3: + __ sfence(); + break; + case 4: + __ push(rax); + __ push(rbx); + __ push(rcx); + __ push(rdx); + __ xorptr(rax, rax); + __ cpuid(); + __ pop(rdx); + __ pop(rcx); + __ pop(rbx); + __ pop(rax); + break; + case 5: + __ serialize(); + break; + default: + ShouldNotReachHere(); + } +} + +void x86_generate_icache_flush_insn(MacroAssembler* _masm, Register addr) { + switch (X86ICacheSync) { + case 1: + __ clflush(Address(addr, 0)); + break; + case 2: + __ clflushopt(Address(addr, 0)); + break; + case 3: + __ clwb(Address(addr, 0)); + break; + default: + ShouldNotReachHere(); + } +} + void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub) { - StubCodeMark mark(this, "ICache", "flush_icache_stub"); + StubCodeMark mark(this, "ICache", _stub_name); address start = __ pc(); -#ifdef AMD64 const Register addr = c_rarg0; const Register lines = c_rarg1; @@ -40,26 +88,22 @@ void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flu Label flush_line, done; __ testl(lines, lines); - __ jcc(Assembler::zero, done); + __ jccb(Assembler::zero, done); - // Force ordering wrt cflush. - // Other fence and sync instructions won't do the job. - __ mfence(); + x86_generate_icache_fence(_masm); - __ bind(flush_line); - __ clflush(Address(addr, 0)); - __ addptr(addr, ICache::line_size); - __ decrementl(lines); - __ jcc(Assembler::notZero, flush_line); + if (1 <= X86ICacheSync && X86ICacheSync <= 3) { + __ bind(flush_line); + x86_generate_icache_flush_insn(_masm, addr); + __ addptr(addr, ICache::line_size); + __ decrementl(lines); + __ jccb(Assembler::notZero, flush_line); - __ mfence(); + x86_generate_icache_fence(_masm); + } __ bind(done); -#else - const Address magic(rsp, 3*wordSize); - __ lock(); __ addl(Address(rsp, 0), 0); -#endif // AMD64 __ movptr(rax, magic); // Handshake with caller to make sure it happened! __ ret(0); @@ -67,4 +111,22 @@ void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flu *flush_icache_stub = (ICache::flush_icache_stub_t)start; } +void ICache::initialize(int phase) { + switch (phase) { + case 1: { + // Initial phase, we assume only CLFLUSH is available. + IntFlagSetting fs(X86ICacheSync, 1); + AbstractICache::initialize(phase); + break; + } + case 2: { + // Final phase, generate the stub again. + AbstractICache::initialize(phase); + break; + } + default: + ShouldNotReachHere(); + } +} + #undef __ diff --git a/src/hotspot/cpu/x86/icache_x86.hpp b/src/hotspot/cpu/x86/icache_x86.hpp index 48286a7e3b385..805022fbb3225 100644 --- a/src/hotspot/cpu/x86/icache_x86.hpp +++ b/src/hotspot/cpu/x86/icache_x86.hpp @@ -40,21 +40,13 @@ class ICache : public AbstractICache { public: -#ifdef AMD64 enum { stub_size = 64, // Size of the icache flush stub in bytes line_size = 64, // Icache line size in bytes log2_line_size = 6 // log2(line_size) }; - // Use default implementation -#else - enum { - stub_size = 16, // Size of the icache flush stub in bytes - line_size = BytesPerWord, // conservative - log2_line_size = LogBytesPerWord // log2(line_size) - }; -#endif // AMD64 + static void initialize(int phase); }; #endif // CPU_X86_ICACHE_X86_HPP diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 4d3bd12ed6697..35e461b601f0f 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -24,6 +24,7 @@ #include "asm/assembler.hpp" #include "asm/assembler.inline.hpp" +#include "code/aotCodeCache.hpp" #include "code/compiledIC.hpp" #include "compiler/compiler_globals.hpp" #include "compiler/disassembler.hpp" @@ -366,7 +367,9 @@ void MacroAssembler::stop(const char* msg) { lea(c_rarg1, InternalAddress(rip)); movq(c_rarg2, rsp); // pass pointer to regs array } - lea(c_rarg0, ExternalAddress((address) msg)); + // Skip AOT caching C strings in scratch buffer. + const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg); + lea(c_rarg0, ExternalAddress((address) str)); andq(rsp, -16); // align stack as required by ABI call(RuntimeAddress(CAST_FROM_FN_PTR(address, MacroAssembler::debug64))); hlt(); diff --git a/src/hotspot/cpu/x86/methodHandles_x86.cpp b/src/hotspot/cpu/x86/methodHandles_x86.cpp index ee4dc26ae404e..f3683e7d09cc2 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.cpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.cpp @@ -122,17 +122,64 @@ void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Registe __ bind(L); } -#endif //ASSERT +void MethodHandles::verify_method(MacroAssembler* _masm, Register method, Register temp, vmIntrinsics::ID iid) { + BLOCK_COMMENT("verify_method {"); + __ verify_method_ptr(method); + if (VerifyMethodHandles) { + Label L_ok; + assert_different_registers(method, temp); + + const Register method_holder = temp; + __ load_method_holder(method_holder, method); + __ push(method_holder); // keep holder around for diagnostic purposes + + switch (iid) { + case vmIntrinsicID::_invokeBasic: + // Require compiled LambdaForm class to be fully initialized. + __ cmpb(Address(method_holder, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); + __ jccb(Assembler::equal, L_ok); + break; + + case vmIntrinsicID::_linkToStatic: + __ clinit_barrier(method_holder, &L_ok); + break; + + case vmIntrinsicID::_linkToVirtual: + case vmIntrinsicID::_linkToSpecial: + case vmIntrinsicID::_linkToInterface: + // Class initialization check is too strong here. Just ensure that initialization has been initiated. + __ cmpb(Address(method_holder, InstanceKlass::init_state_offset()), InstanceKlass::being_initialized); + __ jcc(Assembler::greaterEqual, L_ok); + + // init_state check failed, but it may be an abstract interface method + __ load_unsigned_short(temp, Address(method, Method::access_flags_offset())); + __ testl(temp, JVM_ACC_ABSTRACT); + __ jccb(Assembler::notZero, L_ok); + break; + + default: + fatal("unexpected intrinsic %d: %s", vmIntrinsics::as_int(iid), vmIntrinsics::name_at(iid)); + } + + // clinit check failed for a concrete method + __ STOP("Method holder klass is not initialized"); + + __ BIND(L_ok); + __ pop(method_holder); // restore stack layout + } + BLOCK_COMMENT("} verify_method"); +} +#endif // ASSERT void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, - bool for_compiler_entry) { + bool for_compiler_entry, vmIntrinsics::ID iid) { assert(method == rbx, "interpreter calling convention"); Label L_no_such_method; __ testptr(rbx, rbx); __ jcc(Assembler::zero, L_no_such_method); - __ verify_method_ptr(method); + verify_method(_masm, method, temp, iid); if (!for_compiler_entry && JvmtiExport::can_post_interpreter_events()) { Label run_compiled_code; @@ -193,7 +240,7 @@ void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm, __ BIND(L); } - jump_from_method_handle(_masm, method_temp, temp2, for_compiler_entry); + jump_from_method_handle(_masm, method_temp, temp2, for_compiler_entry, vmIntrinsics::_invokeBasic); BLOCK_COMMENT("} jump_to_lambda_form"); } @@ -485,8 +532,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, // After figuring out which concrete method to call, jump into it. // Note that this works in the interpreter with no data motion. // But the compiled version will require that rcx_recv be shifted out. - __ verify_method_ptr(rbx_method); - jump_from_method_handle(_masm, rbx_method, temp1, for_compiler_entry); + jump_from_method_handle(_masm, rbx_method, temp1, for_compiler_entry, iid); if (iid == vmIntrinsics::_linkToInterface) { __ bind(L_incompatible_class_change_error); diff --git a/src/hotspot/cpu/x86/methodHandles_x86.hpp b/src/hotspot/cpu/x86/methodHandles_x86.hpp index 9ffe5e198acd8..6ba9b5f6a4fa4 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.hpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.hpp @@ -38,6 +38,8 @@ enum /* platform_dependent_constants */ { Register obj, vmClassID klass_id, const char* error_message = "wrong klass") NOT_DEBUG_RETURN; + static void verify_method(MacroAssembler* _masm, Register method, Register temp, vmIntrinsics::ID iid) NOT_DEBUG_RETURN; + static void verify_method_handle(MacroAssembler* _masm, Register mh_reg) { verify_klass(_masm, mh_reg, VM_CLASS_ID(MethodHandle_klass), "reference is a MH"); @@ -48,7 +50,7 @@ enum /* platform_dependent_constants */ { // Similar to InterpreterMacroAssembler::jump_from_interpreted. // Takes care of special dispatch from single stepping too. static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp, - bool for_compiler_entry); + bool for_compiler_entry, vmIntrinsics::ID iid); static void jump_to_lambda_form(MacroAssembler* _masm, Register recv, Register method_temp, diff --git a/src/hotspot/cpu/x86/nativeInst_x86.cpp b/src/hotspot/cpu/x86/nativeInst_x86.cpp index 4ee741077dc06..c3345be2172f1 100644 --- a/src/hotspot/cpu/x86/nativeInst_x86.cpp +++ b/src/hotspot/cpu/x86/nativeInst_x86.cpp @@ -67,9 +67,7 @@ void NativeCall::print() { // Inserts a native call instruction at a given pc void NativeCall::insert(address code_pos, address entry) { intptr_t disp = (intptr_t)entry - ((intptr_t)code_pos + 1 + 4); -#ifdef AMD64 guarantee(disp == (intptr_t)(jint)disp, "must be 32-bit offset"); -#endif // AMD64 *code_pos = instruction_code; *((int32_t *)(code_pos+1)) = (int32_t) disp; ICache::invalidate_range(code_pos, instruction_size); @@ -140,7 +138,7 @@ bool NativeCall::is_displacement_aligned() { // Used in the runtime linkage of calls; see class CompiledIC. // (Cf. 4506997 and 4479829, where threads witnessed garbage displacements.) void NativeCall::set_destination_mt_safe(address dest) { - debug_only(verify()); + DEBUG_ONLY(verify()); // Make sure patching code is locked. No two threads can patch at the same // time but one may be executing this code. assert(CodeCache_lock->is_locked() || SafepointSynchronize::is_at_safepoint() || @@ -157,7 +155,6 @@ void NativeCall::set_destination_mt_safe(address dest) { void NativeMovConstReg::verify() { -#ifdef AMD64 // make sure code pattern is actually a mov reg64, imm64 instruction bool valid_rex_prefix = ubyte_at(0) == Assembler::REX_W || ubyte_at(0) == Assembler::REX_WB; bool valid_rex2_prefix = ubyte_at(0) == Assembler::REX2 && @@ -169,12 +166,6 @@ void NativeMovConstReg::verify() { print(); fatal("not a REX.W[B] mov reg64, imm64"); } -#else - // make sure code pattern is actually a mov reg, imm32 instruction - u_char test_byte = *(u_char*)instruction_address(); - u_char test_byte_2 = test_byte & ( 0xff ^ register_mask); - if (test_byte_2 != instruction_code) fatal("not a mov reg, imm32"); -#endif // AMD64 } @@ -192,12 +183,10 @@ int NativeMovRegMem::instruction_start() const { // See comment in Assembler::locate_operand() about VEX prefixes. if (instr_0 == instruction_VEX_prefix_2bytes) { assert((UseAVX > 0), "shouldn't have VEX prefix"); - NOT_LP64(assert((0xC0 & ubyte_at(1)) == 0xC0, "shouldn't have LDS and LES instructions")); return 2; } if (instr_0 == instruction_VEX_prefix_3bytes) { assert((UseAVX > 0), "shouldn't have VEX prefix"); - NOT_LP64(assert((0xC0 & ubyte_at(1)) == 0xC0, "shouldn't have LDS and LES instructions")); return 3; } if (instr_0 == instruction_EVEX_prefix_4bytes) { @@ -313,8 +302,7 @@ void NativeMovRegMem::print() { void NativeLoadAddress::verify() { // make sure code pattern is actually a mov [reg+offset], reg instruction u_char test_byte = *(u_char*)instruction_address(); - if ( ! ((test_byte == lea_instruction_code) - LP64_ONLY(|| (test_byte == mov64_instruction_code) ))) { + if ((test_byte != lea_instruction_code) && (test_byte != mov64_instruction_code)) { fatal ("not a lea reg, [reg+offs] instruction"); } } @@ -340,9 +328,7 @@ void NativeJump::verify() { void NativeJump::insert(address code_pos, address entry) { intptr_t disp = (intptr_t)entry - ((intptr_t)code_pos + 1 + 4); -#ifdef AMD64 guarantee(disp == (intptr_t)(int32_t)disp, "must be 32-bit offset"); -#endif // AMD64 *code_pos = instruction_code; *((int32_t*)(code_pos + 1)) = (int32_t)disp; @@ -355,11 +341,7 @@ void NativeJump::check_verified_entry_alignment(address entry, address verified_ // in use. The patching in that instance must happen only when certain // alignment restrictions are true. These guarantees check those // conditions. -#ifdef AMD64 const int linesize = 64; -#else - const int linesize = 32; -#endif // AMD64 // Must be wordSize aligned guarantee(((uintptr_t) verified_entry & (wordSize -1)) == 0, @@ -386,7 +368,6 @@ void NativeJump::check_verified_entry_alignment(address entry, address verified_ // void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) { // complete jump instruction (to be inserted) is in code_buffer; -#ifdef _LP64 union { jlong cb_long; unsigned char code_buffer[8]; @@ -402,43 +383,6 @@ void NativeJump::patch_verified_entry(address entry, address verified_entry, add Atomic::store((jlong *) verified_entry, u.cb_long); ICache::invalidate_range(verified_entry, 8); - -#else - unsigned char code_buffer[5]; - code_buffer[0] = instruction_code; - intptr_t disp = (intptr_t)dest - ((intptr_t)verified_entry + 1 + 4); - *(int32_t*)(code_buffer + 1) = (int32_t)disp; - - check_verified_entry_alignment(entry, verified_entry); - - // Can't call nativeJump_at() because it's asserts jump exists - NativeJump* n_jump = (NativeJump*) verified_entry; - - //First patch dummy jmp in place - - unsigned char patch[4]; - assert(sizeof(patch)==sizeof(int32_t), "sanity check"); - patch[0] = 0xEB; // jmp rel8 - patch[1] = 0xFE; // jmp to self - patch[2] = 0xEB; - patch[3] = 0xFE; - - // First patch dummy jmp in place - *(int32_t*)verified_entry = *(int32_t *)patch; - - n_jump->wrote(0); - - // Patch 5th byte (from jump instruction) - verified_entry[4] = code_buffer[4]; - - n_jump->wrote(4); - - // Patch bytes 0-3 (from jump instruction) - *(int32_t*)verified_entry = *(int32_t *)code_buffer; - // Invalidate. Opteron requires a flush after every write. - n_jump->wrote(0); -#endif // _LP64 - } void NativeIllegalInstruction::insert(address code_pos) { @@ -455,9 +399,7 @@ void NativeGeneralJump::verify() { void NativeGeneralJump::insert_unconditional(address code_pos, address entry) { intptr_t disp = (intptr_t)entry - ((intptr_t)code_pos + 1 + 4); -#ifdef AMD64 guarantee(disp == (intptr_t)(int32_t)disp, "must be 32-bit offset"); -#endif // AMD64 *code_pos = unconditional_long_jump; *((int32_t *)(code_pos+1)) = (int32_t) disp; diff --git a/src/hotspot/cpu/x86/nativeInst_x86.hpp b/src/hotspot/cpu/x86/nativeInst_x86.hpp index d02387aa9ffbb..b2448cb99fdb0 100644 --- a/src/hotspot/cpu/x86/nativeInst_x86.hpp +++ b/src/hotspot/cpu/x86/nativeInst_x86.hpp @@ -126,10 +126,8 @@ class NativeCall: public NativeInstruction { address return_address() const { return addr_at(return_address_offset); } address destination() const; void set_destination(address dest) { -#ifdef AMD64 intptr_t disp = dest - return_address(); guarantee(disp == (intptr_t)(jint)disp, "must be 32-bit offset"); -#endif // AMD64 set_int_at(displacement_offset, (int)(dest - return_address())); } // Returns whether the 4-byte displacement operand is 4-byte aligned. @@ -211,15 +209,9 @@ class NativeCallReg: public NativeInstruction { // Instruction format for implied addressing mode immediate operand move to register instruction: // [REX/REX2] [OPCODE] [IMM32] class NativeMovConstReg: public NativeInstruction { -#ifdef AMD64 static const bool has_rex = true; static const int rex_size = 1; static const int rex2_size = 2; -#else - static const bool has_rex = false; - static const int rex_size = 0; - static const int rex2_size = 0; -#endif // AMD64 public: enum Intel_specific_constants { instruction_code = 0xB8, @@ -390,13 +382,8 @@ inline NativeMovRegMem* nativeMovRegMem_at (address address) { // leal reg, [reg + offset] class NativeLoadAddress: public NativeMovRegMem { -#ifdef AMD64 static const bool has_rex = true; static const int rex_size = 1; -#else - static const bool has_rex = false; - static const int rex_size = 0; -#endif // AMD64 public: enum Intel_specific_constants { instruction_prefix_wide = Assembler::REX_W, @@ -447,9 +434,7 @@ class NativeJump: public NativeInstruction { if (dest == (address) -1) { val = -5; // jump to self } -#ifdef AMD64 assert((labs(val) & 0xFFFFFFFF00000000) == 0 || dest == (address)-1, "must be 32bit offset or -1"); -#endif // AMD64 set_int_at(data_offset, (jint)val); } @@ -503,7 +488,7 @@ class NativeGeneralJump: public NativeInstruction { inline NativeGeneralJump* nativeGeneralJump_at(address address) { NativeGeneralJump* jump = (NativeGeneralJump*)(address); - debug_only(jump->verify();) + DEBUG_ONLY(jump->verify();) return jump; } @@ -572,19 +557,14 @@ inline bool NativeInstruction::is_jump_reg() { inline bool NativeInstruction::is_cond_jump() { return (int_at(0) & 0xF0FF) == 0x800F /* long jump */ || (ubyte_at(0) & 0xF0) == 0x70; /* short jump */ } inline bool NativeInstruction::is_safepoint_poll() { -#ifdef AMD64 const bool has_rex_prefix = ubyte_at(0) == NativeTstRegMem::instruction_rex_b_prefix; const int test_offset = has_rex2_prefix() ? 2 : (has_rex_prefix ? 1 : 0); -#else - const int test_offset = 0; -#endif const bool is_test_opcode = ubyte_at(test_offset) == NativeTstRegMem::instruction_code_memXregl; const bool is_rax_target = (ubyte_at(test_offset + 1) & NativeTstRegMem::modrm_mask) == NativeTstRegMem::modrm_reg; return is_test_opcode && is_rax_target; } inline bool NativeInstruction::is_mov_literal64() { -#ifdef AMD64 bool valid_rex_prefix = ubyte_at(0) == Assembler::REX_W || ubyte_at(0) == Assembler::REX_WB; bool valid_rex2_prefix = ubyte_at(0) == Assembler::REX2 && (ubyte_at(1) == Assembler::REX2BIT_W || @@ -593,9 +573,6 @@ inline bool NativeInstruction::is_mov_literal64() { int opcode = has_rex2_prefix() ? ubyte_at(2) : ubyte_at(1); return ((valid_rex_prefix || valid_rex2_prefix) && (opcode & (0xff ^ NativeMovConstReg::register_mask)) == 0xB8); -#else - return false; -#endif // AMD64 } class NativePostCallNop: public NativeInstruction { diff --git a/src/hotspot/cpu/x86/runtime_x86_64.cpp b/src/hotspot/cpu/x86/runtime_x86_64.cpp index a063c7aeb37a9..027a523b33d72 100644 --- a/src/hotspot/cpu/x86/runtime_x86_64.cpp +++ b/src/hotspot/cpu/x86/runtime_x86_64.cpp @@ -61,6 +61,9 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() { // Setup code generation tools const char* name = OptoRuntime::stub_name(OptoStubId::uncommon_trap_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } MacroAssembler* masm = new MacroAssembler(&buffer); assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned"); @@ -267,6 +270,9 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() { // Setup code generation tools const char* name = OptoRuntime::stub_name(OptoStubId::exception_id); CodeBuffer buffer(name, 2048, 1024); + if (buffer.blob() == nullptr) { + return nullptr; + } MacroAssembler* masm = new MacroAssembler(&buffer); diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 621340964ac4b..7811d59d12d11 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -675,7 +675,6 @@ static void patch_callers_callsite(MacroAssembler *masm) { __ bind(L); } - static void gen_c2i_adapter(MacroAssembler *masm, int total_args_passed, int comp_args_on_stack, @@ -826,19 +825,6 @@ static void gen_c2i_adapter(MacroAssembler *masm, __ jmp(rcx); } -static void range_check(MacroAssembler* masm, Register pc_reg, Register temp_reg, - address code_start, address code_end, - Label& L_ok) { - Label L_fail; - __ lea(temp_reg, AddressLiteral(code_start, relocInfo::none)); - __ cmpptr(pc_reg, temp_reg); - __ jcc(Assembler::belowEqual, L_fail); - __ lea(temp_reg, AddressLiteral(code_end, relocInfo::none)); - __ cmpptr(pc_reg, temp_reg); - __ jcc(Assembler::below, L_ok); - __ bind(L_fail); -} - void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, int total_args_passed, int comp_args_on_stack, @@ -871,41 +857,6 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, // If this happens, control eventually transfers back to the compiled // caller, but with an uncorrected stack, causing delayed havoc. - if (VerifyAdapterCalls && - (Interpreter::code() != nullptr || StubRoutines::final_stubs_code() != nullptr)) { - // So, let's test for cascading c2i/i2c adapters right now. - // assert(Interpreter::contains($return_addr) || - // StubRoutines::contains($return_addr), - // "i2c adapter must return to an interpreter frame"); - __ block_comment("verify_i2c { "); - // Pick up the return address - __ movptr(rax, Address(rsp, 0)); - Label L_ok; - if (Interpreter::code() != nullptr) { - range_check(masm, rax, r11, - Interpreter::code()->code_start(), - Interpreter::code()->code_end(), - L_ok); - } - if (StubRoutines::initial_stubs_code() != nullptr) { - range_check(masm, rax, r11, - StubRoutines::initial_stubs_code()->code_begin(), - StubRoutines::initial_stubs_code()->code_end(), - L_ok); - } - if (StubRoutines::final_stubs_code() != nullptr) { - range_check(masm, rax, r11, - StubRoutines::final_stubs_code()->code_begin(), - StubRoutines::final_stubs_code()->code_end(), - L_ok); - } - const char* msg = "i2c adapter must return to an interpreter frame"; - __ block_comment(msg); - __ stop(msg); - __ bind(L_ok); - __ block_comment("} verify_i2ce "); - } - // Must preserve original SP for loading incoming arguments because // we need to align the outgoing SP for compiled code. __ movptr(r11, rsp); @@ -1050,12 +1001,12 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, } // --------------------------------------------------------------- -AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, - int total_args_passed, - int comp_args_on_stack, - const BasicType *sig_bt, - const VMRegPair *regs, - AdapterFingerPrint* fingerprint) { +void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + AdapterHandlerEntry* handler) { address i2c_entry = __ pc(); gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); @@ -1117,7 +1068,8 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); - return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry); + handler->set_entry_points(i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry); + return; } int SharedRuntime::c_calling_convention(const BasicType *sig_bt, diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index b88a2bd1f8e6b..1a16416787d6e 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -4333,70 +4333,6 @@ void StubGenerator::generate_compiler_stubs() { } } - // Get svml stub routine addresses - void *libjsvml = nullptr; - char ebuf[1024]; - char dll_name[JVM_MAXPATHLEN]; - if (os::dll_locate_lib(dll_name, sizeof(dll_name), Arguments::get_dll_dir(), "jsvml")) { - libjsvml = os::dll_load(dll_name, ebuf, sizeof ebuf); - } - if (libjsvml != nullptr) { - // SVML method naming convention - // All the methods are named as __jsvml_op_ha_ - // Where: - // ha stands for high accuracy - // is optional to indicate float/double - // Set to f for vector float operation - // Omitted for vector double operation - // is the number of elements in the vector - // 1, 2, 4, 8, 16 - // e.g. 128 bit float vector has 4 float elements - // indicates the avx/sse level: - // z0 is AVX512, l9 is AVX2, e9 is AVX1 and ex is for SSE2 - // e.g. __jsvml_expf16_ha_z0 is the method for computing 16 element vector float exp using AVX 512 insns - // __jsvml_exp8_ha_z0 is the method for computing 8 element vector double exp using AVX 512 insns - - log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, JNI_LIB_PREFIX "jsvml" JNI_LIB_SUFFIX, p2i(libjsvml)); - if (UseAVX > 2) { - for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { - int vop = VectorSupport::VECTOR_OP_MATH_START + op; - if ((!VM_Version::supports_avx512dq()) && - (vop == VectorSupport::VECTOR_OP_LOG || vop == VectorSupport::VECTOR_OP_LOG10 || vop == VectorSupport::VECTOR_OP_POW)) { - continue; - } - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf16_ha_z0", VectorSupport::mathname[op]); - StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_512][op] = (address)os::dll_lookup(libjsvml, ebuf); - - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s8_ha_z0", VectorSupport::mathname[op]); - StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_512][op] = (address)os::dll_lookup(libjsvml, ebuf); - } - } - const char* avx_sse_str = (UseAVX >= 2) ? "l9" : ((UseAVX == 1) ? "e9" : "ex"); - for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { - int vop = VectorSupport::VECTOR_OP_MATH_START + op; - if (vop == VectorSupport::VECTOR_OP_POW) { - continue; - } - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::mathname[op], avx_sse_str); - StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_64][op] = (address)os::dll_lookup(libjsvml, ebuf); - - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::mathname[op], avx_sse_str); - StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_128][op] = (address)os::dll_lookup(libjsvml, ebuf); - - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf8_ha_%s", VectorSupport::mathname[op], avx_sse_str); - StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_256][op] = (address)os::dll_lookup(libjsvml, ebuf); - - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s1_ha_%s", VectorSupport::mathname[op], avx_sse_str); - StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_64][op] = (address)os::dll_lookup(libjsvml, ebuf); - - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s2_ha_%s", VectorSupport::mathname[op], avx_sse_str); - StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_128][op] = (address)os::dll_lookup(libjsvml, ebuf); - - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s4_ha_%s", VectorSupport::mathname[op], avx_sse_str); - StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_256][op] = (address)os::dll_lookup(libjsvml, ebuf); - } - } - #endif // COMPILER2 #endif // COMPILER2_OR_JVMCI } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp index d13809bfcd911..52ce2731b1fde 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_tanh.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2024, Intel Corporation. All rights reserved. +* Copyright (c) 2024, 2025, Intel Corporation. All rights reserved. * Intel Math Library (LIBM) Source Code * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -46,7 +46,7 @@ // for |x| in [23/64,3*2^7) // e^{-2*|x|}=2^{-k-f}*2^{-r} ~ 2^{-k}*(Tn+Dn)*(1+p)=(T0+D0)*(1+p) // -// For |x| in [2^{-4},2^5): +// For |x| in [2^{-4},22): // 2^{-r}-1 ~ p=c1*r+c2*r^2+..+c5*r^5 // Let R=1/(1+T0+p*T0), truncated to 35 significant bits // R=1/(1+T0+D0+p*(T0+D0))*(1+eps), |eps|<2^{-33} @@ -66,11 +66,11 @@ // // For |x|<2^{-64}: x is returned // -// For |x|>=2^32: return +/-1 +// For |x|>=22: return +/-1 // // Special cases: // tanh(NaN) = quiet NaN, and raise invalid exception -// tanh(INF) = that INF +// tanh(+/-INF) = +/-1 // tanh(+/-0) = +/-0 // /******************************************************************************/ @@ -324,6 +324,12 @@ address StubGenerator::generate_libmTanh() { __ enter(); // required for proper stackwalking of RuntimeStub frame __ bind(B1_2); + __ pextrw(rcx, xmm0, 3); + __ movl(rdx, 32768); + __ andl(rdx, rcx); + __ andl(rcx, 32767); + __ cmpl(rcx, 16438); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_2_0_1); // Branch only if |x| >= 22 __ movsd(xmm3, ExternalAddress(HALFMASK), r11 /*rscratch*/); __ xorpd(xmm4, xmm4); __ movsd(xmm1, ExternalAddress(L2E), r11 /*rscratch*/); @@ -331,16 +337,12 @@ address StubGenerator::generate_libmTanh() { __ movl(rax, 32768); __ pinsrw(xmm4, rax, 3); __ movsd(xmm6, ExternalAddress(Shifter), r11 /*rscratch*/); - __ pextrw(rcx, xmm0, 3); __ andpd(xmm3, xmm0); __ andnpd(xmm4, xmm0); __ pshufd(xmm5, xmm4, 68); - __ movl(rdx, 32768); - __ andl(rdx, rcx); - __ andl(rcx, 32767); __ subl(rcx, 16304); - __ cmpl(rcx, 144); - __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_1); + __ cmpl(rcx, 134); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_0_0_1); // Branch only if |x| is not in [2^{-4},22) __ subsd(xmm4, xmm3); __ mulsd(xmm3, xmm1); __ mulsd(xmm2, xmm5); @@ -427,8 +429,8 @@ address StubGenerator::generate_libmTanh() { __ bind(L_2TAG_PACKET_0_0_1); __ addl(rcx, 960); - __ cmpl(rcx, 1104); - __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_1_0_1); + __ cmpl(rcx, 1094); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_1_0_1); // Branch only if |x| not in [2^{-64}, 2^{-4}) __ movdqu(xmm2, ExternalAddress(pv), r11 /*rscratch*/); __ pshufd(xmm1, xmm0, 68); __ movdqu(xmm3, ExternalAddress(pv + 16), r11 /*rscratch*/); @@ -449,11 +451,8 @@ address StubGenerator::generate_libmTanh() { __ jmp(B1_4); __ bind(L_2TAG_PACKET_1_0_1); - __ addl(rcx, 15344); - __ cmpl(rcx, 16448); - __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_2_0_1); __ cmpl(rcx, 16); - __ jcc(Assembler::below, L_2TAG_PACKET_3_0_1); + __ jcc(Assembler::below, L_2TAG_PACKET_3_0_1); // Branch only if |x| is denormalized __ xorpd(xmm2, xmm2); __ movl(rax, 17392); __ pinsrw(xmm2, rax, 3); @@ -468,7 +467,7 @@ address StubGenerator::generate_libmTanh() { __ bind(L_2TAG_PACKET_2_0_1); __ cmpl(rcx, 32752); - __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_4_0_1); + __ jcc(Assembler::aboveEqual, L_2TAG_PACKET_4_0_1); // Branch only if |x| is INF or NaN __ xorpd(xmm2, xmm2); __ movl(rcx, 15344); __ pinsrw(xmm2, rcx, 3); @@ -489,7 +488,7 @@ address StubGenerator::generate_libmTanh() { __ movdl(rcx, xmm2); __ orl(rcx, rax); __ cmpl(rcx, 0); - __ jcc(Assembler::equal, L_2TAG_PACKET_5_0_1); + __ jcc(Assembler::equal, L_2TAG_PACKET_5_0_1); // Branch only if |x| is not NaN __ addsd(xmm0, xmm0); __ bind(B1_4); diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 32e6e33d13380..5306f0becefdd 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -72,8 +72,6 @@ static get_cpu_info_stub_t get_cpu_info_stub = nullptr; static detect_virt_stub_t detect_virt_stub = nullptr; static clear_apx_test_state_t clear_apx_test_state_stub = nullptr; -#ifdef _LP64 - bool VM_Version::supports_clflush() { // clflush should always be available on x86_64 // if not we are in real trouble because we rely on it @@ -87,7 +85,6 @@ bool VM_Version::supports_clflush() { assert ((!Universe::is_fully_initialized() || (_features & CPU_FLUSH) != 0), "clflush should be available"); return true; } -#endif #define CPUID_STANDARD_FN 0x0 #define CPUID_STANDARD_FN_1 0x1 @@ -107,7 +104,6 @@ class VM_Version_StubGenerator: public StubCodeGenerator { VM_Version_StubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} -#if defined(_LP64) address clear_apx_test_state() { # define __ _masm-> address start = __ pc(); @@ -126,7 +122,6 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ ret(0); return start; } -#endif address generate_get_cpu_info() { // Flags to test CPU type. @@ -151,14 +146,10 @@ class VM_Version_StubGenerator: public StubCodeGenerator { // // void get_cpu_info(VM_Version::CpuidInfo* cpuid_info); // - // LP64: rcx and rdx are first and second argument registers on windows + // rcx and rdx are first and second argument registers on windows __ push(rbp); -#ifdef _LP64 __ mov(rbp, c_rarg0); // cpuid_info address -#else - __ movptr(rbp, Address(rsp, 8)); // cpuid_info address -#endif __ push(rbx); __ push(rsi); __ pushf(); // preserve rbx, and flags @@ -418,7 +409,6 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movl(Address(rsi, 8), rcx); __ movl(Address(rsi,12), rdx); -#if defined(_LP64) // // Check if OS has enabled XGETBV instruction to access XCR0 // (OSXSAVE feature flag) and CPU supports APX @@ -453,7 +443,6 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movq(Address(rsi, 8), r31); UseAPX = save_apx; -#endif #endif __ bind(vector_save_restore); // @@ -527,10 +516,8 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movdl(xmm0, rcx); __ vpbroadcastd(xmm0, xmm0, Assembler::AVX_512bit); __ evmovdqul(xmm7, xmm0, Assembler::AVX_512bit); -#ifdef _LP64 __ evmovdqul(xmm8, xmm0, Assembler::AVX_512bit); __ evmovdqul(xmm31, xmm0, Assembler::AVX_512bit); -#endif VM_Version::clean_cpuFeatures(); __ jmp(save_restore_except); } @@ -556,10 +543,8 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ pshufd(xmm0, xmm0, 0x00); __ vinsertf128_high(xmm0, xmm0); __ vmovdqu(xmm7, xmm0); -#ifdef _LP64 __ vmovdqu(xmm8, xmm0); __ vmovdqu(xmm15, xmm0); -#endif VM_Version::clean_cpuFeatures(); __ bind(save_restore_except); @@ -600,10 +585,8 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ lea(rsi, Address(rbp, in_bytes(VM_Version::zmm_save_offset()))); __ evmovdqul(Address(rsi, 0), xmm0, Assembler::AVX_512bit); __ evmovdqul(Address(rsi, 64), xmm7, Assembler::AVX_512bit); -#ifdef _LP64 __ evmovdqul(Address(rsi, 128), xmm8, Assembler::AVX_512bit); __ evmovdqul(Address(rsi, 192), xmm31, Assembler::AVX_512bit); -#endif #ifdef _WINDOWS __ evmovdqul(xmm31, Address(rsp, 0), Assembler::AVX_512bit); @@ -628,10 +611,8 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ lea(rsi, Address(rbp, in_bytes(VM_Version::ymm_save_offset()))); __ vmovdqu(Address(rsi, 0), xmm0); __ vmovdqu(Address(rsi, 32), xmm7); -#ifdef _LP64 __ vmovdqu(Address(rsi, 64), xmm8); __ vmovdqu(Address(rsi, 96), xmm15); -#endif #ifdef _WINDOWS __ vmovdqu(xmm15, Address(rsp, 0)); @@ -687,13 +668,8 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ push(rbx); __ push(rsi); // for Windows -#ifdef _LP64 __ mov(rax, c_rarg0); // CPUID leaf __ mov(rsi, c_rarg1); // register array address (eax, ebx, ecx, edx) -#else - __ movptr(rax, Address(rsp, 16)); // CPUID leaf - __ movptr(rsi, Address(rsp, 20)); // register array address -#endif __ cpuid(); @@ -734,14 +710,10 @@ class VM_Version_StubGenerator: public StubCodeGenerator { // // void getCPUIDBrandString(VM_Version::CpuidInfo* cpuid_info); // - // LP64: rcx and rdx are first and second argument registers on windows + // rcx and rdx are first and second argument registers on windows __ push(rbp); -#ifdef _LP64 __ mov(rbp, c_rarg0); // cpuid_info address -#else - __ movptr(rbp, Address(rsp, 8)); // cpuid_info address -#endif __ push(rbx); __ push(rsi); __ pushf(); // preserve rbx, and flags @@ -889,19 +861,16 @@ void VM_Version::get_processor_features() { // xchg and xadd instructions _supports_atomic_getset4 = true; _supports_atomic_getadd4 = true; - LP64_ONLY(_supports_atomic_getset8 = true); - LP64_ONLY(_supports_atomic_getadd8 = true); + _supports_atomic_getset8 = true; + _supports_atomic_getadd8 = true; -#ifdef _LP64 // OS should support SSE for x64 and hardware should support at least SSE2. if (!VM_Version::supports_sse2()) { vm_exit_during_initialization("Unknown x64 processor: SSE2 not supported"); } // in 64 bit the use of SSE2 is the minimum if (UseSSE < 2) UseSSE = 2; -#endif -#ifdef AMD64 // flush_icache_stub have to be generated first. // That is why Icache line size is hard coded in ICache class, // see icache_x86.hpp. It is also the reason why we can't use @@ -913,9 +882,7 @@ void VM_Version::get_processor_features() { guarantee(_cpuid_info.std_cpuid1_edx.bits.clflush != 0, "clflush is not supported"); // clflush_size is size in quadwords (8 bytes). guarantee(_cpuid_info.std_cpuid1_ebx.bits.clflush_size == 8, "such clflush size is not supported"); -#endif -#ifdef _LP64 // assigning this field effectively enables Unsafe.writebackMemory() // by initing UnsafeConstant.DATA_CACHE_LINE_FLUSH_SIZE to non-zero // that is only implemented on x86_64 and only if the OS plays ball @@ -924,7 +891,6 @@ void VM_Version::get_processor_features() { // let if default to zero thereby disabling writeback _data_cache_line_flush_size = _cpuid_info.std_cpuid1_ebx.bits.clflush_size * 8; } -#endif // Check if processor has Intel Ecore if (FLAG_IS_DEFAULT(EnableX86ECoreOpts) && is_intel() && cpu_family() == 6 && @@ -1089,16 +1055,44 @@ void VM_Version::get_processor_features() { _has_intel_jcc_erratum = IntelJccErratumMitigation; } + assert(supports_cpuid(), "Always present"); + assert(supports_clflush(), "Always present"); + if (X86ICacheSync == -1) { + // Auto-detect, choosing the best performant one that still flushes + // the cache. We could switch to CPUID/SERIALIZE ("4"/"5") going forward. + if (supports_clwb()) { + FLAG_SET_ERGO(X86ICacheSync, 3); + } else if (supports_clflushopt()) { + FLAG_SET_ERGO(X86ICacheSync, 2); + } else { + FLAG_SET_ERGO(X86ICacheSync, 1); + } + } else { + if ((X86ICacheSync == 2) && !supports_clflushopt()) { + vm_exit_during_initialization("CPU does not support CLFLUSHOPT, unable to use X86ICacheSync=2"); + } + if ((X86ICacheSync == 3) && !supports_clwb()) { + vm_exit_during_initialization("CPU does not support CLWB, unable to use X86ICacheSync=3"); + } + if ((X86ICacheSync == 5) && !supports_serialize()) { + vm_exit_during_initialization("CPU does not support SERIALIZE, unable to use X86ICacheSync=5"); + } + } + char buf[1024]; - int res = jio_snprintf( + int cpu_info_size = jio_snprintf( buf, sizeof(buf), "(%u cores per cpu, %u threads per core) family %d model %d stepping %d microcode 0x%x", cores_per_cpu(), threads_per_core(), cpu_family(), _model, _stepping, os::cpu_microcode_revision()); - assert(res > 0, "not enough temporary space allocated"); - insert_features_names(buf + res, sizeof(buf) - res, _features_names); + assert(cpu_info_size > 0, "not enough temporary space allocated"); + insert_features_names(buf + cpu_info_size, sizeof(buf) - cpu_info_size, _features_names); - _features_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf); + + _features_string = extract_features_string(_cpu_info_string, + strnlen(_cpu_info_string, sizeof(buf)), + cpu_info_size); // Use AES instructions if available. if (supports_aes()) { @@ -1182,7 +1176,6 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseCRC32Intrinsics, false); } -#ifdef _LP64 if (supports_avx2()) { if (FLAG_IS_DEFAULT(UseAdler32Intrinsics)) { UseAdler32Intrinsics = true; @@ -1193,12 +1186,6 @@ void VM_Version::get_processor_features() { } FLAG_SET_DEFAULT(UseAdler32Intrinsics, false); } -#else - if (UseAdler32Intrinsics) { - warning("Adler32Intrinsics not available on this CPU."); - FLAG_SET_DEFAULT(UseAdler32Intrinsics, false); - } -#endif if (supports_sse4_2() && supports_clmul()) { if (FLAG_IS_DEFAULT(UseCRC32CIntrinsics)) { @@ -1222,7 +1209,6 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseGHASHIntrinsics, false); } -#ifdef _LP64 // ChaCha20 Intrinsics // As long as the system supports AVX as a baseline we can do a // SIMD-enabled block function. StubGenerator makes the determination @@ -1238,24 +1224,14 @@ void VM_Version::get_processor_features() { } FLAG_SET_DEFAULT(UseChaCha20Intrinsics, false); } -#else - // No support currently for ChaCha20 intrinsics on 32-bit platforms - if (UseChaCha20Intrinsics) { - warning("ChaCha20 intrinsics are not available on this CPU."); - FLAG_SET_DEFAULT(UseChaCha20Intrinsics, false); - } -#endif // _LP64 // Dilithium Intrinsics // Currently we only have them for AVX512 -#ifdef _LP64 if (supports_evex() && supports_avx512bw()) { if (FLAG_IS_DEFAULT(UseDilithiumIntrinsics)) { UseDilithiumIntrinsics = true; } - } else -#endif - if (UseDilithiumIntrinsics) { + } else if (UseDilithiumIntrinsics) { warning("Intrinsics for ML-DSA are not available on this CPU."); FLAG_SET_DEFAULT(UseDilithiumIntrinsics, false); } @@ -1284,7 +1260,7 @@ void VM_Version::get_processor_features() { UseMD5Intrinsics = true; } - if (supports_sha() LP64_ONLY(|| (supports_avx2() && supports_bmi2()))) { + if (supports_sha() || (supports_avx2() && supports_bmi2())) { if (FLAG_IS_DEFAULT(UseSHA)) { UseSHA = true; } @@ -1311,27 +1287,20 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseSHA256Intrinsics, false); } -#ifdef _LP64 - // These are only supported on 64-bit if (UseSHA && supports_avx2() && (supports_bmi2() || supports_sha512())) { if (FLAG_IS_DEFAULT(UseSHA512Intrinsics)) { FLAG_SET_DEFAULT(UseSHA512Intrinsics, true); } - } else -#endif - if (UseSHA512Intrinsics) { + } else if (UseSHA512Intrinsics) { warning("Intrinsics for SHA-384 and SHA-512 crypto hash functions not available on this CPU."); FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } -#ifdef _LP64 if (supports_evex() && supports_avx512bw()) { if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) { UseSHA3Intrinsics = true; } - } else -#endif - if (UseSHA3Intrinsics) { + } else if (UseSHA3Intrinsics) { warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } @@ -1353,11 +1322,7 @@ void VM_Version::get_processor_features() { max_vector_size = 64; } -#ifdef _LP64 int min_vector_size = 4; // We require MaxVectorSize to be at least 4 on 64bit -#else - int min_vector_size = 0; -#endif if (!FLAG_IS_DEFAULT(MaxVectorSize)) { if (MaxVectorSize < min_vector_size) { @@ -1381,7 +1346,7 @@ void VM_Version::get_processor_features() { if (MaxVectorSize > 0) { if (supports_avx() && PrintMiscellaneous && Verbose && TraceNewVectors) { tty->print_cr("State of YMM registers after signal handle:"); - int nreg = 2 LP64_ONLY(+2); + int nreg = 4; const char* ymm_name[4] = {"0", "7", "8", "15"}; for (int i = 0; i < nreg; i++) { tty->print("YMM%s:", ymm_name[i]); @@ -1394,31 +1359,24 @@ void VM_Version::get_processor_features() { } #endif // COMPILER2 && ASSERT -#ifdef _LP64 if ((supports_avx512ifma() && supports_avx512vlbw()) || supports_avxifma()) { if (FLAG_IS_DEFAULT(UsePoly1305Intrinsics)) { FLAG_SET_DEFAULT(UsePoly1305Intrinsics, true); } - } else -#endif - if (UsePoly1305Intrinsics) { + } else if (UsePoly1305Intrinsics) { warning("Intrinsics for Poly1305 crypto hash functions not available on this CPU."); FLAG_SET_DEFAULT(UsePoly1305Intrinsics, false); } -#ifdef _LP64 if ((supports_avx512ifma() && supports_avx512vlbw()) || supports_avxifma()) { if (FLAG_IS_DEFAULT(UseIntPolyIntrinsics)) { FLAG_SET_DEFAULT(UseIntPolyIntrinsics, true); } - } else -#endif - if (UseIntPolyIntrinsics) { + } else if (UseIntPolyIntrinsics) { warning("Intrinsics for Polynomial crypto functions not available on this CPU."); FLAG_SET_DEFAULT(UseIntPolyIntrinsics, false); } -#ifdef _LP64 if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { UseMultiplyToLenIntrinsic = true; } @@ -1434,38 +1392,6 @@ void VM_Version::get_processor_features() { if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) { UseMontgomerySquareIntrinsic = true; } -#else - if (UseMultiplyToLenIntrinsic) { - if (!FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { - warning("multiplyToLen intrinsic is not available in 32-bit VM"); - } - FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, false); - } - if (UseMontgomeryMultiplyIntrinsic) { - if (!FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) { - warning("montgomeryMultiply intrinsic is not available in 32-bit VM"); - } - FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, false); - } - if (UseMontgomerySquareIntrinsic) { - if (!FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) { - warning("montgomerySquare intrinsic is not available in 32-bit VM"); - } - FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, false); - } - if (UseSquareToLenIntrinsic) { - if (!FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) { - warning("squareToLen intrinsic is not available in 32-bit VM"); - } - FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, false); - } - if (UseMulAddIntrinsic) { - if (!FLAG_IS_DEFAULT(UseMulAddIntrinsic)) { - warning("mulAdd intrinsic is not available in 32-bit VM"); - } - FLAG_SET_DEFAULT(UseMulAddIntrinsic, false); - } -#endif // _LP64 #endif // COMPILER2_OR_JVMCI // On new cpus instructions which update whole XMM register should be used @@ -1742,7 +1668,6 @@ void VM_Version::get_processor_features() { } #endif -#ifdef _LP64 if (UseSSE42Intrinsics) { if (FLAG_IS_DEFAULT(UseVectorizedMismatchIntrinsic)) { UseVectorizedMismatchIntrinsic = true; @@ -1759,20 +1684,6 @@ void VM_Version::get_processor_features() { warning("vectorizedHashCode intrinsics are not available on this CPU"); FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, false); } -#else - if (UseVectorizedMismatchIntrinsic) { - if (!FLAG_IS_DEFAULT(UseVectorizedMismatchIntrinsic)) { - warning("vectorizedMismatch intrinsic is not available in 32-bit VM"); - } - FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false); - } - if (UseVectorizedHashCodeIntrinsic) { - if (!FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) { - warning("vectorizedHashCode intrinsic is not available in 32-bit VM"); - } - FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, false); - } -#endif // _LP64 // Use count leading zeros count instruction if available. if (supports_lzcnt()) { @@ -1921,7 +1832,6 @@ void VM_Version::get_processor_features() { #endif } -#ifdef _LP64 // Prefetch settings // Prefetch interval for gc copy/scan == 9 dcache lines. Derived from @@ -1940,7 +1850,6 @@ void VM_Version::get_processor_features() { if (FLAG_IS_DEFAULT(PrefetchScanIntervalInBytes)) { FLAG_SET_DEFAULT(PrefetchScanIntervalInBytes, 576); } -#endif if (FLAG_IS_DEFAULT(ContendedPaddingWidth) && (cache_line_size > ContendedPaddingWidth)) @@ -2171,11 +2080,9 @@ int VM_Version::avx3_threshold() { FLAG_IS_DEFAULT(AVX3Threshold)) ? 0 : AVX3Threshold; } -#if defined(_LP64) void VM_Version::clear_apx_test_state() { clear_apx_test_state_stub(); } -#endif static bool _vm_version_initialized = false; @@ -2193,14 +2100,11 @@ void VM_Version::initialize() { g.generate_get_cpu_info()); detect_virt_stub = CAST_TO_FN_PTR(detect_virt_stub_t, g.generate_detect_virt()); - -#if defined(_LP64) clear_apx_test_state_stub = CAST_TO_FN_PTR(clear_apx_test_state_t, g.clear_apx_test_state()); -#endif get_processor_features(); - LP64_ONLY(Assembler::precompute_instructions();) + Assembler::precompute_instructions(); if (VM_Version::supports_hv()) { // Supports hypervisor check_virtualizations(); @@ -2967,12 +2871,10 @@ uint64_t VM_Version::CpuidInfo::feature_flags() const { result |= CPU_CMOV; if (std_cpuid1_edx.bits.clflush != 0) result |= CPU_FLUSH; -#ifdef _LP64 // clflush should always be available on x86_64 // if not we are in real trouble because we rely on it // to flush the code cache. assert ((result & CPU_FLUSH) != 0, "clflush should be available"); -#endif if (std_cpuid1_edx.bits.fxsr != 0 || (is_amd_family() && ext_cpuid1_edx.bits.fxsr != 0)) result |= CPU_FXSR; @@ -3144,7 +3046,7 @@ uint64_t VM_Version::CpuidInfo::feature_flags() const { bool VM_Version::os_supports_avx_vectors() { bool retVal = false; - int nreg = 2 LP64_ONLY(+2); + int nreg = 4; if (supports_evex()) { // Verify that OS save/restore all bits of EVEX registers // during signal processing. @@ -3300,11 +3202,7 @@ int VM_Version::allocate_prefetch_distance(bool use_watermark_prefetch) { if (supports_sse4_2() && supports_ht()) { // Nehalem based cpus return 192; } else if (use_watermark_prefetch) { // watermark prefetching on Core -#ifdef _LP64 return 384; -#else - return 320; -#endif } } if (supports_sse2()) { diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index cc5c6c1c63992..7eb627c871417 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -642,7 +642,7 @@ class VM_Version : public Abstract_VM_Version { static void set_cpuinfo_cont_addr_apx(address pc) { _cpuinfo_cont_addr_apx = pc; } static address cpuinfo_cont_addr_apx() { return _cpuinfo_cont_addr_apx; } - LP64_ONLY(static void clear_apx_test_state()); + static void clear_apx_test_state(); static void clean_cpuFeatures() { _features = 0; } static void set_avx_cpuFeatures() { _features |= (CPU_SSE | CPU_SSE2 | CPU_AVX | CPU_VZEROUPPER ); } @@ -839,12 +839,12 @@ class VM_Version : public Abstract_VM_Version { // x86_64 supports fast class initialization checks static bool supports_fast_class_init_checks() { - return LP64_ONLY(true) NOT_LP64(false); // not implemented on x86_32 + return true; } // x86_64 supports secondary supers table constexpr static bool supports_secondary_supers_table() { - return LP64_ONLY(true) NOT_LP64(false); // not implemented on x86_32 + return true; } constexpr static bool supports_stack_watermark_barrier() { @@ -879,11 +879,7 @@ class VM_Version : public Abstract_VM_Version { // synchronize with other memory ops. so, it needs preceding // and trailing StoreStore fences. -#ifdef _LP64 static bool supports_clflush(); // Can't inline due to header file conflict -#else - static bool supports_clflush() { return ((_features & CPU_FLUSH) != 0); } -#endif // _LP64 // Note: CPU_FLUSHOPT and CPU_CLWB bits should always be zero for 32-bit static bool supports_clflushopt() { return ((_features & CPU_FLUSHOPT) != 0); } diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 078150c61fbc5..25cee7a3094cd 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -422,6 +422,18 @@ source_hpp %{ #include "peephole_x86_64.hpp" +bool castLL_is_imm32(const Node* n); + +%} + +source %{ + +bool castLL_is_imm32(const Node* n) { + assert(n->is_CastLL(), "must be a CastLL"); + const TypeLong* t = n->bottom_type()->is_long(); + return (t->_lo == min_jlong || Assembler::is_simm32(t->_lo)) && (t->_hi == max_jlong || Assembler::is_simm32(t->_hi)); +} + %} // Register masks @@ -1584,14 +1596,11 @@ uint MachUEPNode::size(PhaseRegAlloc* ra_) const //============================================================================= bool Matcher::supports_vector_calling_convention(void) { - if (EnableVectorSupport && UseVectorStubs) { - return true; - } - return false; + return EnableVectorSupport; } OptoRegPair Matcher::vector_return_value(uint ideal_reg) { - assert(EnableVectorSupport && UseVectorStubs, "sanity"); + assert(EnableVectorSupport, "sanity"); int lo = XMM0_num; int hi = XMM0b_num; if (ideal_reg == Op_VecX) hi = XMM0d_num; @@ -1838,14 +1847,14 @@ encode %{ %} enc_class clear_avx %{ - debug_only(int off0 = __ offset()); + DEBUG_ONLY(int off0 = __ offset()); if (generate_vzeroupper(Compile::current())) { // Clear upper bits of YMM registers to avoid AVX <-> SSE transition penalty // Clear upper bits of YMM registers when current compiled code uses // wide vectors to avoid AVX <-> SSE transition penalty during call. __ vzeroupper(); } - debug_only(int off1 = __ offset()); + DEBUG_ONLY(int off1 = __ offset()); assert(off1 - off0 == clear_avx_size(), "correct size prediction"); %} @@ -7605,6 +7614,7 @@ instruct castPP(rRegP dst) instruct castII(rRegI dst) %{ + predicate(VerifyConstraintCasts == 0); match(Set dst (CastII dst)); size(0); @@ -7614,8 +7624,22 @@ instruct castII(rRegI dst) ins_pipe(empty); %} +instruct castII_checked(rRegI dst, rFlagsReg cr) +%{ + predicate(VerifyConstraintCasts > 0); + match(Set dst (CastII dst)); + + effect(KILL cr); + format %{ "# cast_checked_II $dst" %} + ins_encode %{ + __ verify_int_in_range(_idx, bottom_type()->is_int(), $dst$$Register); + %} + ins_pipe(pipe_slow); +%} + instruct castLL(rRegL dst) %{ + predicate(VerifyConstraintCasts == 0); match(Set dst (CastLL dst)); size(0); @@ -7625,6 +7649,32 @@ instruct castLL(rRegL dst) ins_pipe(empty); %} +instruct castLL_checked_L32(rRegL dst, rFlagsReg cr) +%{ + predicate(VerifyConstraintCasts > 0 && castLL_is_imm32(n)); + match(Set dst (CastLL dst)); + + effect(KILL cr); + format %{ "# cast_checked_LL $dst" %} + ins_encode %{ + __ verify_long_in_range(_idx, bottom_type()->is_long(), $dst$$Register, noreg); + %} + ins_pipe(pipe_slow); +%} + +instruct castLL_checked(rRegL dst, rRegL tmp, rFlagsReg cr) +%{ + predicate(VerifyConstraintCasts > 0 && !castLL_is_imm32(n)); + match(Set dst (CastLL dst)); + + effect(KILL cr, TEMP tmp); + format %{ "# cast_checked_LL $dst\tusing $tmp as TEMP" %} + ins_encode %{ + __ verify_long_in_range(_idx, bottom_type()->is_long(), $dst$$Register, $tmp$$Register); + %} + ins_pipe(pipe_slow); +%} + instruct castFF(regF dst) %{ match(Set dst (CastFF dst)); diff --git a/src/hotspot/cpu/zero/icache_zero.hpp b/src/hotspot/cpu/zero/icache_zero.hpp index b40e07d5e3b3c..781021a2b20ef 100644 --- a/src/hotspot/cpu/zero/icache_zero.hpp +++ b/src/hotspot/cpu/zero/icache_zero.hpp @@ -33,7 +33,7 @@ class ICache : public AbstractICache { public: - static void initialize() {} + static void initialize(int phase) {} static void invalidate_word(address addr) {} static void invalidate_range(address start, int nbytes) {} }; diff --git a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp index f141135ff9571..60a873ab31f01 100644 --- a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp +++ b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp @@ -50,18 +50,17 @@ int SharedRuntime::java_calling_convention(const BasicType *sig_bt, return 0; } -AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters( - MacroAssembler *masm, - int total_args_passed, - int comp_args_on_stack, - const BasicType *sig_bt, - const VMRegPair *regs, - AdapterFingerPrint *fingerprint) { - return AdapterHandlerLibrary::new_entry( - fingerprint, - CAST_FROM_FN_PTR(address,zero_null_code_stub), - CAST_FROM_FN_PTR(address,zero_null_code_stub), - CAST_FROM_FN_PTR(address,zero_null_code_stub)); +void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + AdapterHandlerEntry* handler) { + handler->set_entry_points(CAST_FROM_FN_PTR(address,zero_null_code_stub), + CAST_FROM_FN_PTR(address,zero_null_code_stub), + CAST_FROM_FN_PTR(address,zero_null_code_stub), + nullptr); + return; } nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, diff --git a/src/hotspot/cpu/zero/vm_version_zero.cpp b/src/hotspot/cpu/zero/vm_version_zero.cpp index e38561e19c571..3ce9227c1939c 100644 --- a/src/hotspot/cpu/zero/vm_version_zero.cpp +++ b/src/hotspot/cpu/zero/vm_version_zero.cpp @@ -151,6 +151,6 @@ void VM_Version::initialize_cpu_information(void) { _no_of_threads = _no_of_cores; _no_of_sockets = _no_of_cores; snprintf(_cpu_name, CPU_TYPE_DESC_BUF_SIZE - 1, "Zero VM"); - snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "%s", _features_string); + snprintf(_cpu_desc, CPU_DETAILED_DESC_BUF_SIZE, "%s", _cpu_info_string); _initialized = true; } diff --git a/src/hotspot/os/aix/libodm_aix.cpp b/src/hotspot/os/aix/libodm_aix.cpp index 9fe0fb7abd842..854fd5e2b79ba 100644 --- a/src/hotspot/os/aix/libodm_aix.cpp +++ b/src/hotspot/os/aix/libodm_aix.cpp @@ -30,6 +30,7 @@ #include #include "runtime/arguments.hpp" #include "runtime/os.hpp" +#include "utilities/permitForbiddenFunctions.hpp" dynamicOdm::dynamicOdm() { @@ -59,7 +60,7 @@ dynamicOdm::~dynamicOdm() { } -void odmWrapper::clean_data() { if (_data) { free(_data); _data = nullptr; } } +void odmWrapper::clean_data() { if (_data) { permit_forbidden_function::free(_data); _data = nullptr; } } int odmWrapper::class_offset(const char *field, bool is_aix_5) diff --git a/src/hotspot/os/aix/loadlib_aix.cpp b/src/hotspot/os/aix/loadlib_aix.cpp index 90a7271ad6d38..e7dbd775e3707 100644 --- a/src/hotspot/os/aix/loadlib_aix.cpp +++ b/src/hotspot/os/aix/loadlib_aix.cpp @@ -38,6 +38,7 @@ #include "logging/log.hpp" #include "utilities/debug.hpp" #include "utilities/ostream.hpp" +#include "utilities/permitForbiddenFunctions.hpp" // For loadquery() #include @@ -58,7 +59,7 @@ class StringList { // Enlarge list. If oom, leave old list intact and return false. bool enlarge() { int cap2 = _cap + 64; - char** l2 = (char**) ::realloc(_list, sizeof(char*) * cap2); + char** l2 = (char**) permit_forbidden_function::realloc(_list, sizeof(char*) * cap2); if (!l2) { return false; } @@ -76,7 +77,7 @@ class StringList { } } assert0(_cap > _num); - char* s2 = ::strdup(s); + char* s2 = permit_forbidden_function::strdup(s); if (!s2) { return nullptr; } @@ -170,7 +171,7 @@ static void free_entry_list(loaded_module_t** start) { loaded_module_t* lm = *start; while (lm) { loaded_module_t* const lm2 = lm->next; - ::free(lm); + permit_forbidden_function::free(lm); lm = lm2; } *start = nullptr; @@ -193,7 +194,7 @@ static bool reload_table() { uint8_t* buffer = nullptr; size_t buflen = 1024; for (;;) { - buffer = (uint8_t*) ::realloc(buffer, buflen); + buffer = (uint8_t*) permit_forbidden_function::realloc(buffer, buflen); if (loadquery(L_GETINFO, buffer, buflen) == -1) { if (errno == ENOMEM) { buflen *= 2; @@ -229,7 +230,7 @@ static bool reload_table() { for (;;) { - loaded_module_t* lm = (loaded_module_t*) ::malloc(sizeof(loaded_module_t)); + loaded_module_t* lm = (loaded_module_t*) permit_forbidden_function::malloc(sizeof(loaded_module_t)); if (!lm) { log_warning(os)("OOM."); goto cleanup; @@ -250,7 +251,7 @@ static bool reload_table() { if (!lm->path) { log_warning(os)("OOM."); - free(lm); + permit_forbidden_function::free(lm); goto cleanup; } @@ -272,7 +273,7 @@ static bool reload_table() { lm->member = g_stringlist.add(p_mbr_name); if (!lm->member) { log_warning(os)("OOM."); - free(lm); + permit_forbidden_function::free(lm); goto cleanup; } } else { @@ -320,7 +321,7 @@ static bool reload_table() { free_entry_list(&new_list); } - ::free(buffer); + permit_forbidden_function::free(buffer); return rc; diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index d913139ffdf89..1bbaf29125d7a 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -73,6 +73,7 @@ #include "utilities/defaultStream.hpp" #include "utilities/events.hpp" #include "utilities/growableArray.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include "utilities/vmError.hpp" #if INCLUDE_JFR #include "jfr/support/jfrNativeLibraryLoadEvent.hpp" @@ -131,8 +132,6 @@ extern "C" int getargs(procsinfo*, int, char*, int); #define MAX_PATH (2 * K) -// for timer info max values which include all bits -#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) // for multipage initialization error analysis (in 'g_multipage_error') #define ERROR_MP_OS_TOO_OLD 100 #define ERROR_MP_EXTSHM_ACTIVE 101 @@ -369,9 +368,9 @@ static void query_multipage_support() { // or by environment variable LDR_CNTRL (suboption DATAPSIZE). If none is given, // default should be 4K. { - void* p = ::malloc(16*M); + void* p = permit_forbidden_function::malloc(16*M); g_multipage_support.datapsize = os::Aix::query_pagesize(p); - ::free(p); + permit_forbidden_function::free(p); } // Query default shm page size (LDR_CNTRL SHMPSIZE). @@ -905,7 +904,7 @@ jlong os::javaTimeNanos() { } void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; + info_ptr->max_value = all_bits_jlong; // mread_real_time() is monotonic (see 'os::javaTimeNanos()') info_ptr->may_skip_backward = false; info_ptr->may_skip_forward = false; @@ -1398,7 +1397,7 @@ static struct { } vmem; static void vmembk_add(char* addr, size_t size, size_t pagesize, int type) { - vmembk_t* p = (vmembk_t*) ::malloc(sizeof(vmembk_t)); + vmembk_t* p = (vmembk_t*) permit_forbidden_function::malloc(sizeof(vmembk_t)); assert0(p); if (p) { MiscUtils::AutoCritSect lck(&vmem.cs); @@ -1427,7 +1426,7 @@ static void vmembk_remove(vmembk_t* p0) { for (vmembk_t** pp = &(vmem.first); *pp; pp = &((*pp)->next)) { if (*pp == p0) { *pp = p0->next; - ::free(p0); + permit_forbidden_function::free(p0); return; } } @@ -2570,14 +2569,14 @@ jlong os::thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { } void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + 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 info_ptr->may_skip_forward = false; // elapsed time not wall time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned } void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + 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 info_ptr->may_skip_forward = false; // elapsed time not wall time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned diff --git a/src/hotspot/os/aix/os_perf_aix.cpp b/src/hotspot/os/aix/os_perf_aix.cpp index 0b008a197ded7..8444002b87194 100644 --- a/src/hotspot/os/aix/os_perf_aix.cpp +++ b/src/hotspot/os/aix/os_perf_aix.cpp @@ -72,7 +72,7 @@ enum { * Get info for requested PID from /proc//psinfo file */ static bool read_psinfo(const u_longlong_t& pid, psinfo_t& psinfo) { - static size_t BUF_LENGTH = 32 + sizeof(u_longlong_t); + const size_t BUF_LENGTH = 32 + sizeof(u_longlong_t); FILE* fp; char buf[BUF_LENGTH]; @@ -118,7 +118,6 @@ static OSReturn get_lcpu_ticks(perfstat_id_t* lcpu_name, cpu_tick_store_t* ptick * Return CPU load caused by the currently executing process (the jvm). */ static OSReturn get_jvm_load(double* jvm_uload, double* jvm_sload) { - static clock_t ticks_per_sec = sysconf(_SC_CLK_TCK); static u_longlong_t last_timebase = 0; perfstat_process_t jvm_stats; @@ -204,8 +203,6 @@ static bool populate_lcpu_names(int ncpus, perfstat_id_t* lcpu_names) { * (Context Switches / Tick) * (Tick / s) = Context Switches per second */ static OSReturn perf_context_switch_rate(double* rate) { - static clock_t ticks_per_sec = sysconf(_SC_CLK_TCK); - u_longlong_t ticks; perfstat_cpu_total_t cpu_stats; @@ -214,7 +211,7 @@ static OSReturn perf_context_switch_rate(double* rate) { } ticks = cpu_stats.user + cpu_stats.sys + cpu_stats.idle + cpu_stats.wait; - *rate = (cpu_stats.pswitch / ticks) * ticks_per_sec; + *rate = (cpu_stats.pswitch / ticks) * os::Posix::clock_tics_per_second(); return OS_OK; } diff --git a/src/hotspot/os/aix/porting_aix.cpp b/src/hotspot/os/aix/porting_aix.cpp index cbc45d3e12234..2235d3da6864c 100644 --- a/src/hotspot/os/aix/porting_aix.cpp +++ b/src/hotspot/os/aix/porting_aix.cpp @@ -39,6 +39,7 @@ #include "runtime/os.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include #include #include @@ -250,7 +251,7 @@ bool AixSymbols::get_function_name ( p_name[namelen-1] = '\0'; } if (demangled_name != nullptr) { - ALLOW_C_FUNCTION(::free, ::free(demangled_name)); + permit_forbidden_function::free(demangled_name); } } } else { @@ -1081,7 +1082,7 @@ void* Aix_dlopen(const char* filename, int Flags, int *eno, const char** error_r if (g_handletable_used == max_handletable) { // No place in array anymore; increase array. unsigned new_max = MAX2(max_handletable * 2, init_num_handles); - struct handletableentry* new_tab = (struct handletableentry*)::realloc(p_handletable, new_max * sizeof(struct handletableentry)); + struct handletableentry* new_tab = (struct handletableentry*) permit_forbidden_function::realloc(p_handletable, new_max * sizeof(struct handletableentry)); assert(new_tab != nullptr, "no more memory for handletable"); if (new_tab == nullptr) { *error_report = "dlopen: no more memory for handletable"; diff --git a/src/hotspot/os/bsd/decoder_machO.cpp b/src/hotspot/os/bsd/decoder_machO.cpp index 173e030a7b51c..15592d172fed4 100644 --- a/src/hotspot/os/bsd/decoder_machO.cpp +++ b/src/hotspot/os/bsd/decoder_machO.cpp @@ -27,6 +27,8 @@ #include "decoder_machO.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include #include @@ -42,9 +44,9 @@ bool MachODecoder::demangle(const char* symbol, char *buf, int buflen) { // may use different malloc/realloc mechanism that allocates 'buf'. if ((result = abi::__cxa_demangle(symbol, nullptr, nullptr, &status)) != nullptr) { jio_snprintf(buf, buflen, "%s", result); - // call c library's free - ::free(result); - return true; + // call c library's free + permit_forbidden_function::free(result); + return true; } return false; } diff --git a/src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp b/src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp index 86549e878cb74..861fda7a71d7a 100644 --- a/src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp +++ b/src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp @@ -77,7 +77,7 @@ ZPhysicalMemoryBacking::ZPhysicalMemoryBacking(size_t max_capacity) _initialized(false) { // Reserve address space for backing memory - _base = (uintptr_t)os::reserve_memory(max_capacity, false, mtJavaHeap); + _base = (uintptr_t)os::reserve_memory(max_capacity, mtJavaHeap); if (_base == 0) { // Failed ZInitialize::error("Failed to reserve address space for backing memory"); diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 193c7cb068952..9a9954b3eb95a 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -114,9 +114,6 @@ #define MAX_PATH (2 * K) -// for timer info max values which include all bits -#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) - //////////////////////////////////////////////////////////////////////////////// // global variables julong os::Bsd::_physical_memory = 0; @@ -815,7 +812,7 @@ jlong os::javaTimeNanos() { } void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; + info_ptr->max_value = all_bits_jlong; info_ptr->may_skip_backward = false; // not subject to resetting or drifting info_ptr->may_skip_forward = false; // not subject to resetting or drifting info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time @@ -2423,14 +2420,14 @@ jlong os::thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + 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 info_ptr->may_skip_forward = false; // elapsed time not wall time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned } void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + 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 info_ptr->may_skip_forward = false; // elapsed time not wall time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned diff --git a/src/hotspot/os/linux/decoder_linux.cpp b/src/hotspot/os/linux/decoder_linux.cpp index 26b435f69e11f..bfe5c53786491 100644 --- a/src/hotspot/os/linux/decoder_linux.cpp +++ b/src/hotspot/os/linux/decoder_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, 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 @@ -27,6 +27,7 @@ #include "utilities/decoder_elf.hpp" #include "utilities/elfFile.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include @@ -46,9 +47,9 @@ bool ElfDecoder::demangle(const char* symbol, char *buf, int buflen) { // may use different malloc/realloc mechanism that allocates 'buf'. if ((result = abi::__cxa_demangle(symbol, nullptr, nullptr, &status)) != nullptr) { jio_snprintf(buf, buflen, "%s", result); - // call c library's free - ALLOW_C_FUNCTION(::free, ::free(result);) - return true; + // call c library's free + permit_forbidden_function::free(result); + return true; } return false; } diff --git a/src/hotspot/os/linux/gc/z/zMountPoint_linux.cpp b/src/hotspot/os/linux/gc/z/zMountPoint_linux.cpp index 60ce39179ff14..92c5cb713acef 100644 --- a/src/hotspot/os/linux/gc/z/zMountPoint_linux.cpp +++ b/src/hotspot/os/linux/gc/z/zMountPoint_linux.cpp @@ -28,6 +28,7 @@ #include "runtime/globals.hpp" #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include #include @@ -62,11 +63,11 @@ char* ZMountPoint::get_mountpoint(const char* line, const char* filesystem) cons strcmp(line_filesystem, filesystem) != 0 || access(line_mountpoint, R_OK|W_OK|X_OK) != 0) { // Not a matching or accessible filesystem - ALLOW_C_FUNCTION(::free, ::free(line_mountpoint);) + permit_forbidden_function::free(line_mountpoint); line_mountpoint = nullptr; } - ALLOW_C_FUNCTION(::free, ::free(line_filesystem);) + permit_forbidden_function::free(line_filesystem); return line_mountpoint; } @@ -90,14 +91,14 @@ void ZMountPoint::get_mountpoints(const char* filesystem, ZArray* mountpo } // readline will return malloced memory. Need raw ::free, not os::free. - ALLOW_C_FUNCTION(::free, ::free(line);) + permit_forbidden_function::free(line); fclose(fd); } void ZMountPoint::free_mountpoints(ZArray* mountpoints) const { ZArrayIterator iter(mountpoints); for (char* mountpoint; iter.next(&mountpoint);) { - ALLOW_C_FUNCTION(::free, ::free(mountpoint);) // *not* os::free + permit_forbidden_function::free(mountpoint); // *not* os::free } mountpoints->clear(); } diff --git a/src/hotspot/os/linux/gc/z/zSyscall_linux.hpp b/src/hotspot/os/linux/gc/z/zSyscall_linux.hpp index 1e1becf5a140f..e5978c8d93a1f 100644 --- a/src/hotspot/os/linux/gc/z/zSyscall_linux.hpp +++ b/src/hotspot/os/linux/gc/z/zSyscall_linux.hpp @@ -35,6 +35,10 @@ #define MPOL_F_ADDR (1<<1) #endif +#ifndef MAP_FIXED_NOREPLACE +#define MAP_FIXED_NOREPLACE 0x100000 +#endif + class ZSyscall : public AllStatic { public: static int memfd_create(const char* name, unsigned int flags); diff --git a/src/hotspot/os/linux/mallocInfoDcmd.cpp b/src/hotspot/os/linux/mallocInfoDcmd.cpp index ad98d5edece19..1a2368b1e50bd 100644 --- a/src/hotspot/os/linux/mallocInfoDcmd.cpp +++ b/src/hotspot/os/linux/mallocInfoDcmd.cpp @@ -26,6 +26,7 @@ #include "os_linux.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include @@ -35,7 +36,7 @@ void MallocInfoDcmd::execute(DCmdSource source, TRAPS) { #ifdef __GLIBC__ char* buf; size_t size; - ALLOW_C_FUNCTION(::open_memstream, FILE* stream = ::open_memstream(&buf, &size);) + FILE* stream = ::open_memstream(&buf, &size); if (stream == nullptr) { _output->print_cr("Error: Could not call malloc_info(3)"); return; @@ -43,7 +44,7 @@ void MallocInfoDcmd::execute(DCmdSource source, TRAPS) { int err = os::Linux::malloc_info(stream); if (err == 0) { - ALLOW_C_FUNCTION(::fflush, fflush(stream);) + fflush(stream); _output->print_raw(buf); _output->cr(); } else if (err == -1) { @@ -53,8 +54,8 @@ void MallocInfoDcmd::execute(DCmdSource source, TRAPS) { } else { ShouldNotReachHere(); } - ALLOW_C_FUNCTION(::fclose, ::fclose(stream);) - ALLOW_C_FUNCTION(::free, ::free(buf);) + ::fclose(stream); + permit_forbidden_function::free(buf); #else _output->print_cr(malloc_info_unavailable); #endif // __GLIBC__ diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 1afece719cbb9..9bd45b0fec883 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -139,9 +139,6 @@ #define MAX_PATH (2 * K) -// for timer info max values which include all bits -#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) - #ifdef MUSL_LIBC // dlvsym is not a part of POSIX // and musl libc doesn't implement it. @@ -213,8 +210,6 @@ typedef int (*malloc_info_func_t)(int options, FILE *stream); static malloc_info_func_t g_malloc_info = nullptr; #endif // __GLIBC__ -static int clock_tics_per_sec = 100; - // If the VM might have been created on the primordial thread, we need to resolve the // primordial thread stack bounds and check if the current thread might be the // primordial thread in places. If we know that the primordial thread is never used, @@ -1667,7 +1662,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { } ThreadInVMfromNative tiv(jt); - debug_only(VMNativeEntryWrapper vew;) + DEBUG_ONLY(VMNativeEntryWrapper vew;) VM_LinuxDllLoad op(filename, ebuf, ebuflen); VMThread::execute(&op); @@ -4381,8 +4376,6 @@ static void check_pax(void) { // this is called _before_ most of the global arguments have been parsed void os::init(void) { char dummy; // used to get a guess on initial stack address - - clock_tics_per_sec = checked_cast(sysconf(_SC_CLK_TCK)); int sys_pg_size = checked_cast(sysconf(_SC_PAGESIZE)); if (sys_pg_size < 0) { fatal("os_linux.cpp: os::init: sysconf failed (%s)", @@ -4575,7 +4568,7 @@ static void workaround_expand_exec_shield_cs_limit() { */ char* hint = (char*)(os::Linux::initial_thread_stack_bottom() - (StackOverflow::stack_guard_zone_size() + page_size)); - char* codebuf = os::attempt_reserve_memory_at(hint, page_size, false, mtThread); + char* codebuf = os::attempt_reserve_memory_at(hint, page_size, mtThread); if (codebuf == nullptr) { // JDK-8197429: There may be a stack gap of one megabyte between @@ -4583,7 +4576,7 @@ static void workaround_expand_exec_shield_cs_limit() { // Linux kernel workaround for CVE-2017-1000364. If we failed to // map our codebuf, try again at an address one megabyte lower. hint -= 1 * M; - codebuf = os::attempt_reserve_memory_at(hint, page_size, false, mtThread); + codebuf = os::attempt_reserve_memory_at(hint, page_size, mtThread); } if ((codebuf == nullptr) || (!os::commit_memory(codebuf, page_size, true))) { @@ -5135,21 +5128,21 @@ static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { &user_time, &sys_time); if (count != 13) return -1; if (user_sys_cpu_time) { - return ((jlong)sys_time + (jlong)user_time) * (1000000000 / clock_tics_per_sec); + return ((jlong)sys_time + (jlong)user_time) * (1000000000 / os::Posix::clock_tics_per_second()); } else { - return (jlong)user_time * (1000000000 / clock_tics_per_sec); + return (jlong)user_time * (1000000000 / os::Posix::clock_tics_per_second()); } } void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + 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 info_ptr->may_skip_forward = false; // elapsed time not wall time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned } void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + 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 info_ptr->may_skip_forward = false; // elapsed time not wall time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned diff --git a/src/hotspot/os/posix/forbiddenFunctions_posix.hpp b/src/hotspot/os/posix/forbiddenFunctions_posix.hpp new file mode 100644 index 0000000000000..9f9881202c78a --- /dev/null +++ b/src/hotspot/os/posix/forbiddenFunctions_posix.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 OS_POSIX_FORBIDDENFUNCTIONS_POSIX_HPP +#define OS_POSIX_FORBIDDENFUNCTIONS_POSIX_HPP + +#include "utilities/compilerWarnings.hpp" + +// For types used in the signatures. +#include + +// Workaround for noreturn functions: _exit - see the clang +// definition of FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE. +#ifdef __clang__ +#include +#endif + +// If needed, add os::strndup and use that instead. +FORBID_C_FUNCTION(char* strndup(const char*, size_t), "don't use"); + +// These are unimplementable for Windows, and they aren't useful for a +// POSIX implementation of NMT either. +// https://stackoverflow.com/questions/62962839/stdaligned-alloc-missing-from-visual-studio-2019 +FORBID_C_FUNCTION(int posix_memalign(void**, size_t, size_t), "don't use"); +FORBID_C_FUNCTION(void* aligned_alloc(size_t, size_t), "don't use"); + +// realpath with a null second argument mallocs a string for the result. +// With a non-null second argument, there is a risk of buffer overrun. +PRAGMA_DIAG_PUSH +FORBIDDEN_FUNCTION_IGNORE_CLANG_FORTIFY_WARNING +FORBID_C_FUNCTION(char* realpath(const char*, char*), "use os::realpath"); +PRAGMA_DIAG_POP + +// Returns a malloc'ed string. +FORBID_C_FUNCTION(char* get_current_dir_name(), "use os::get_current_directory"); + +// Problematic API that should never be used. +FORBID_C_FUNCTION(char* getwd(char*), "use os::get_current_directory"); + +// BSD utility that is subtly different from realloc. +FORBID_C_FUNCTION(void* reallocf(void*, size_t), "use os::realloc"); + +#endif // OS_POSIX_FORBIDDENFUNCTIONS_POSIX_HPP diff --git a/src/hotspot/os/posix/gc/z/zVirtualMemoryManager_posix.cpp b/src/hotspot/os/posix/gc/z/zVirtualMemoryManager_posix.cpp index 4eea35c8a2e9d..89bde0557ec36 100644 --- a/src/hotspot/os/posix/gc/z/zVirtualMemoryManager_posix.cpp +++ b/src/hotspot/os/posix/gc/z/zVirtualMemoryManager_posix.cpp @@ -24,6 +24,9 @@ #include "gc/z/zAddress.inline.hpp" #include "gc/z/zVirtualMemoryManager.hpp" #include "logging/log.hpp" +#ifdef LINUX +#include "gc/z/zSyscall_linux.hpp" +#endif #include @@ -32,7 +35,9 @@ void ZVirtualMemoryReserver::pd_register_callbacks(ZVirtualMemoryRegistry* regis } bool ZVirtualMemoryReserver::pd_reserve(zaddress_unsafe addr, size_t size) { - void* const res = mmap((void*)untype(addr), size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0); + const int flags = MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE LINUX_ONLY(|MAP_FIXED_NOREPLACE); + + void* const res = mmap((void*)untype(addr), size, PROT_NONE, flags, -1, 0); if (res == MAP_FAILED) { // Failed to reserve memory return false; diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index a36d6b87641e5..1da0eb219b647 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -49,6 +49,7 @@ #include "utilities/formatBuffer.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include "utilities/vmError.hpp" #if INCLUDE_JFR #include "jfr/support/jfrNativeLibraryLoadEvent.hpp" @@ -491,9 +492,9 @@ static char* chop_extra_memory(size_t size, size_t alignment, char* extra_base, // Multiple threads can race in this code, and can remap over each other with MAP_FIXED, // so on posix, unmap the section at the start and at the end of the chunk that we mapped // rather than unmapping and remapping the whole chunk to get requested alignment. -char* os::reserve_memory_aligned(size_t size, size_t alignment, bool exec) { +char* os::reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag, bool exec) { size_t extra_size = calculate_aligned_extra_size(size, alignment); - char* extra_base = os::reserve_memory(extra_size, exec); + char* extra_base = os::reserve_memory(extra_size, mem_tag, exec); if (extra_base == nullptr) { return nullptr; } @@ -930,11 +931,11 @@ ssize_t os::connect(int fd, struct sockaddr* him, socklen_t len) { } void os::exit(int num) { - ALLOW_C_FUNCTION(::exit, ::exit(num);) + permit_forbidden_function::exit(num); } void os::_exit(int num) { - ALLOW_C_FUNCTION(::_exit, ::_exit(num);) + permit_forbidden_function::_exit(num); } void os::naked_yield() { @@ -991,7 +992,7 @@ char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) { // This assumes platform realpath() is implemented according to POSIX.1-2008. // POSIX.1-2008 allows to specify null for the output buffer, in which case // output buffer is dynamically allocated and must be ::free()'d by the caller. - ALLOW_C_FUNCTION(::realpath, char* p = ::realpath(filename, nullptr);) + char* p = permit_forbidden_function::realpath(filename, nullptr); if (p != nullptr) { if (strlen(p) < outbuflen) { strcpy(outbuf, p); @@ -999,7 +1000,7 @@ char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) { } else { errno = ENAMETOOLONG; } - ALLOW_C_FUNCTION(::free, ::free(p);) // *not* os::free + permit_forbidden_function::free(p); // *not* os::free } else { // Fallback for platforms struggling with modern Posix standards (AIX 5.3, 6.1). If realpath // returns EINVAL, this may indicate that realpath is not POSIX.1-2008 compatible and @@ -1008,7 +1009,7 @@ char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) { // a memory overwrite. if (errno == EINVAL) { outbuf[outbuflen - 1] = '\0'; - ALLOW_C_FUNCTION(::realpath, p = ::realpath(filename, outbuf);) + p = permit_forbidden_function::realpath(filename, outbuf); if (p != nullptr) { guarantee(outbuf[outbuflen - 1] == '\0', "realpath buffer overwrite detected."); result = p; @@ -1325,6 +1326,10 @@ void os::Posix::init_2(void) { _use_clock_monotonic_condattr ? "CLOCK_MONOTONIC" : "the default clock"); } +int os::Posix::clock_tics_per_second() { + return clock_tics_per_sec; +} + // Utility to convert the given timeout to an absolute timespec // (based on the appropriate clock) to use with pthread_cond_timewait, // and sem_timedwait(). @@ -1472,12 +1477,9 @@ jlong os::javaTimeNanos() { return result; } -// for timer info max values which include all bits -#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) - void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { // CLOCK_MONOTONIC - amount of time since some arbitrary point in the past - info_ptr->max_value = ALL_64_BITS; + info_ptr->max_value = all_bits_jlong; info_ptr->may_skip_backward = false; // not subject to resetting or drifting info_ptr->may_skip_forward = false; // not subject to resetting or drifting info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time diff --git a/src/hotspot/os/posix/os_posix.hpp b/src/hotspot/os/posix/os_posix.hpp index 248a30d04adb7..5c3b1f35bd128 100644 --- a/src/hotspot/os/posix/os_posix.hpp +++ b/src/hotspot/os/posix/os_posix.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, 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 @@ -91,6 +91,9 @@ class os::Posix { static void to_RTC_abstime(timespec* abstime, int64_t millis); + // clock ticks per second of the system + static int clock_tics_per_second(); + static bool handle_stack_overflow(JavaThread* thread, address addr, address pc, const void* ucVoid, address* stub); diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index 4d6fc1e4b8c9e..cbbecea3a6a0a 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -64,7 +64,7 @@ static char* backing_store_file_name = nullptr; // name of the backing store static char* create_standard_memory(size_t size) { // allocate an aligned chuck of memory - char* mapAddress = os::reserve_memory(size); + char* mapAddress = os::reserve_memory(size, mtInternal); if (mapAddress == nullptr) { return nullptr; diff --git a/src/hotspot/os/posix/permitForbiddenFunctions_posix.hpp b/src/hotspot/os/posix/permitForbiddenFunctions_posix.hpp new file mode 100644 index 0000000000000..3ff8c383a31c9 --- /dev/null +++ b/src/hotspot/os/posix/permitForbiddenFunctions_posix.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 OS_POSIX_PERMITFORBIDDENFUNCTIONS_POSIX_HPP +#define OS_POSIX_PERMITFORBIDDENFUNCTIONS_POSIX_HPP + +#include "utilities/compilerWarnings.hpp" +#include "utilities/globalDefinitions.hpp" + +// Provide wrappers for some functions otherwise forbidden from use in HotSpot. +// See forbiddenFunctions.hpp for details. + +namespace permit_forbidden_function { +BEGIN_ALLOW_FORBIDDEN_FUNCTIONS + +// Used by the POSIX implementation of os::realpath. +inline char* realpath(const char* path, char* resolved_path) { + return ::realpath(path, resolved_path); +} + +END_ALLOW_FORBIDDEN_FUNCTIONS +} // namespace permit_forbidden_function + +#endif // OS_POSIX_PERMITFORBIDDENFUNCTIONS_POSIX_HPP diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 555ac832aae7f..e900d5695aedd 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -147,7 +147,7 @@ class SavedSignalHandlers { }; -debug_only(static bool signal_sets_initialized = false); +DEBUG_ONLY(static bool signal_sets_initialized = false); static sigset_t unblocked_sigs, vm_sigs, preinstalled_sigs; // Our own signal handlers should never ever get replaced by a third party one. @@ -1547,7 +1547,7 @@ static void signal_sets_init() { if (!ReduceSignalUsage) { sigaddset(&vm_sigs, BREAK_SIGNAL); } - debug_only(signal_sets_initialized = true); + DEBUG_ONLY(signal_sets_initialized = true); } // These are signals that are unblocked while a thread is running Java. diff --git a/src/hotspot/os/windows/forbiddenFunctions_windows.hpp b/src/hotspot/os/windows/forbiddenFunctions_windows.hpp new file mode 100644 index 0000000000000..81599522ed4c1 --- /dev/null +++ b/src/hotspot/os/windows/forbiddenFunctions_windows.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 OS_WINDOWS_FORBIDDENFUNCTIONS_WINDOWS_HPP +#define OS_WINDOWS_FORBIDDENFUNCTIONS_WINDOWS_HPP + +#include "utilities/compilerWarnings.hpp" + +#include // for size_t + +// _fullpath with a null first argument mallocs a string for the result. +FORBID_IMPORTED_C_FUNCTION(char* _fullpath(char*, const char*, size_t), "use os::realpath"); + +// _snprintf does NOT null terminate if the output would exceed the buffer size. +FORBID_C_FUNCTION(int _snprintf(char*, size_t, const char*, ...), "use os::snprintf"); + +#endif // OS_WINDOWS_FORBIDDENFUNCTIONS_WINDOWS_HPP diff --git a/src/hotspot/os/windows/gc/z/zSyscall_windows.hpp b/src/hotspot/os/windows/gc/z/zSyscall_windows.hpp index d6174de7d884c..3d20fa5a9245a 100644 --- a/src/hotspot/os/windows/gc/z/zSyscall_windows.hpp +++ b/src/hotspot/os/windows/gc/z/zSyscall_windows.hpp @@ -27,7 +27,6 @@ #include "utilities/globalDefinitions.hpp" #include -#include class ZSyscall { private: diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 84e89334feb63..c6107b95259ad 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -76,6 +76,7 @@ #include "utilities/defaultStream.hpp" #include "utilities/events.hpp" #include "utilities/macros.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include "utilities/population_count.hpp" #include "utilities/vmError.hpp" #include "windbghelp.hpp" @@ -111,9 +112,6 @@ #include #include -// for timer info max values which include all bits -#define ALL_64_BITS CONST64(-1) - // For DLL loading/load error detection // Values of PE COFF #define IMAGE_FILE_PTR_TO_SIGNATURE 0x3c @@ -1224,16 +1222,16 @@ void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { if (freq < NANOSECS_PER_SEC) { // the performance counter is 64 bits and we will // be multiplying it -- so no wrap in 64 bits - info_ptr->max_value = ALL_64_BITS; + info_ptr->max_value = all_bits_jlong; } else if (freq > NANOSECS_PER_SEC) { // use the max value the counter can reach to // determine the max value which could be returned - julong max_counter = (julong)ALL_64_BITS; + julong max_counter = (julong)all_bits_jlong; info_ptr->max_value = (jlong)(max_counter / (freq / NANOSECS_PER_SEC)); } else { // the performance counter is 64 bits and we will // be using it directly -- so no wrap in 64 bits - info_ptr->max_value = ALL_64_BITS; + info_ptr->max_value = all_bits_jlong; } // using a counter, so no skipping @@ -3019,7 +3017,7 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, PAGE_READWRITE); // If reservation failed, return null if (p_buf == nullptr) return nullptr; - MemTracker::record_virtual_memory_reserve((address)p_buf, size_of_reserve, CALLER_PC); + MemTracker::record_virtual_memory_reserve((address)p_buf, size_of_reserve, CALLER_PC, mtNone); os::release_memory(p_buf, bytes + chunk_size); // we still need to round up to a page boundary (in case we are using large pages) @@ -3080,7 +3078,7 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, // need to create a dummy 'reserve' record to match // the release. MemTracker::record_virtual_memory_reserve((address)p_buf, - bytes_to_release, CALLER_PC); + bytes_to_release, CALLER_PC, mtNone); os::release_memory(p_buf, bytes_to_release); } #ifdef ASSERT @@ -3098,9 +3096,9 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, // Although the memory is allocated individually, it is returned as one. // NMT records it as one block. if ((flags & MEM_COMMIT) != 0) { - MemTracker::record_virtual_memory_reserve_and_commit((address)p_buf, bytes, CALLER_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)p_buf, bytes, CALLER_PC, mtNone); } else { - MemTracker::record_virtual_memory_reserve((address)p_buf, bytes, CALLER_PC); + MemTracker::record_virtual_memory_reserve((address)p_buf, bytes, CALLER_PC, mtNone); } // made it this far, success @@ -3240,7 +3238,7 @@ char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, in // Multiple threads can race in this code but it's not possible to unmap small sections of // virtual space to get requested alignment, like posix-like os's. // Windows prevents multiple thread from remapping over each other so this loop is thread-safe. -static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int file_desc, MemTag mem_tag = mtNone) { +static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int file_desc, MemTag mem_tag) { assert(is_aligned(alignment, os::vm_allocation_granularity()), "Alignment must be a multiple of allocation granularity (page size)"); assert(is_aligned(size, os::vm_allocation_granularity()), @@ -3254,7 +3252,7 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi for (int attempt = 0; attempt < max_attempts && aligned_base == nullptr; attempt ++) { char* extra_base = file_desc != -1 ? os::map_memory_to_file(extra_size, file_desc, mem_tag) : - os::reserve_memory(extra_size, false, mem_tag); + os::reserve_memory(extra_size, mem_tag); if (extra_base == nullptr) { return nullptr; } @@ -3271,7 +3269,7 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi // Attempt to map, into the just vacated space, the slightly smaller aligned area. // Which may fail, hence the loop. aligned_base = file_desc != -1 ? os::attempt_map_memory_to_file_at(aligned_base, size, file_desc, mem_tag) : - os::attempt_reserve_memory_at(aligned_base, size, false, mem_tag); + os::attempt_reserve_memory_at(aligned_base, size, mem_tag); } assert(aligned_base != nullptr, @@ -3280,9 +3278,9 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi return aligned_base; } -char* os::reserve_memory_aligned(size_t size, size_t alignment, bool exec) { +char* os::reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag, bool exec) { // exec can be ignored - return map_or_reserve_memory_aligned(size, alignment, -1 /* file_desc */); + return map_or_reserve_memory_aligned(size, alignment, -1/* file_desc */, mem_tag); } char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MemTag mem_tag) { @@ -4358,9 +4356,9 @@ static void exit_process_or_thread(Ept what, int exit_code) { if (what == EPT_THREAD) { _endthreadex((unsigned)exit_code); } else if (what == EPT_PROCESS) { - ALLOW_C_FUNCTION(::exit, ::exit(exit_code);) + permit_forbidden_function::exit(exit_code); } else { // EPT_PROCESS_DIE - ALLOW_C_FUNCTION(::_exit, ::_exit(exit_code);) + permit_forbidden_function::_exit(exit_code); } // Should not reach here @@ -4812,14 +4810,14 @@ jlong os::thread_cpu_time(Thread* thread, bool user_sys_cpu_time) { } void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // the max value -- all 64 bits + info_ptr->max_value = all_bits_jlong; // the max value -- all 64 bits info_ptr->may_skip_backward = false; // GetThreadTimes returns absolute time info_ptr->may_skip_forward = false; // GetThreadTimes returns absolute time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned } void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // the max value -- all 64 bits + info_ptr->max_value = all_bits_jlong; // the max value -- all 64 bits info_ptr->may_skip_backward = false; // GetThreadTimes returns absolute time info_ptr->may_skip_forward = false; // GetThreadTimes returns absolute time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned @@ -5131,7 +5129,7 @@ char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) { } char* result = nullptr; - ALLOW_C_FUNCTION(::_fullpath, char* p = ::_fullpath(nullptr, filename, 0);) + char* p = permit_forbidden_function::_fullpath(nullptr, filename, 0); if (p != nullptr) { if (strlen(p) < outbuflen) { strcpy(outbuf, p); @@ -5139,7 +5137,7 @@ char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) { } else { errno = ENAMETOOLONG; } - ALLOW_C_FUNCTION(::free, ::free(p);) // *not* os::free + permit_forbidden_function::free(p); // *not* os::free } return result; } @@ -5187,7 +5185,7 @@ char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, } // Record virtual memory allocation - MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, CALLER_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, CALLER_PC, mtNone); DWORD bytes_read; OVERLAPPED overlapped; diff --git a/src/hotspot/os/windows/perfMemory_windows.cpp b/src/hotspot/os/windows/perfMemory_windows.cpp index dda0acde79346..322b844f413a2 100644 --- a/src/hotspot/os/windows/perfMemory_windows.cpp +++ b/src/hotspot/os/windows/perfMemory_windows.cpp @@ -54,7 +54,7 @@ typedef BOOL (WINAPI *SetSecurityDescriptorControlFnPtr)( static char* create_standard_memory(size_t size) { // allocate an aligned chuck of memory - char* mapAddress = os::reserve_memory(size); + char* mapAddress = os::reserve_memory(size, mtInternal); if (mapAddress == nullptr) { return nullptr; diff --git a/src/hotspot/os/windows/permitForbiddenFunctions_windows.hpp b/src/hotspot/os/windows/permitForbiddenFunctions_windows.hpp new file mode 100644 index 0000000000000..99e77464fbd3b --- /dev/null +++ b/src/hotspot/os/windows/permitForbiddenFunctions_windows.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 OS_WINDOWS_PERMITFORBIDDENFUNCTIONS_WINDOWS_HPP +#define OS_WINDOWS_PERMITFORBIDDENFUNCTIONS_WINDOWS_HPP + +#include "utilities/compilerWarnings.hpp" +#include "utilities/globalDefinitions.hpp" + +// Provide wrappers for some functions otherwise forbidden from use in HotSpot. +// See forbiddenFunctions.hpp for details. + +namespace permit_forbidden_function { +BEGIN_ALLOW_FORBIDDEN_FUNCTIONS + +// Used by the Windows implementation of os::realpath. +inline char* _fullpath(char* absPath, const char* relPath, size_t maxLength) { + return ::_fullpath(absPath, relPath, maxLength); +} + +END_ALLOW_FORBIDDEN_FUNCTIONS +} // namespace permit_forbidden_function + +#endif // OS_WINDOWS_PERMITFORBIDDENFUNCTIONS_WINDOWS_HPP diff --git a/src/hotspot/os/windows/symbolengine.cpp b/src/hotspot/os/windows/symbolengine.cpp index 83cb930f7bfec..8f6f6cf09c6e0 100644 --- a/src/hotspot/os/windows/symbolengine.cpp +++ b/src/hotspot/os/windows/symbolengine.cpp @@ -26,6 +26,7 @@ #include "symbolengine.hpp" #include "utilities/debug.hpp" #include "utilities/ostream.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include "windbghelp.hpp" #include @@ -102,7 +103,7 @@ class SimpleBufferWithFallback { virtual void initialize () { assert(_p == nullptr && _capacity == 0, "Only call once."); const size_t bytes = OPTIMAL_CAPACITY * sizeof(T); - T* q = (T*) ::malloc(bytes); + T* q = (T*) permit_forbidden_function::malloc(bytes); if (q != nullptr) { _p = q; _capacity = OPTIMAL_CAPACITY; @@ -118,7 +119,7 @@ class SimpleBufferWithFallback { // case, where two buffers need to be of identical capacity. void reset_to_fallback_capacity() { if (_p != _fallback_buffer) { - ::free(_p); + permit_forbidden_function::free(_p); } _p = _fallback_buffer; _capacity = (int)(sizeof(_fallback_buffer) / sizeof(T)); diff --git a/src/hotspot/os_cpu/bsd_aarch64/icache_bsd_aarch64.hpp b/src/hotspot/os_cpu/bsd_aarch64/icache_bsd_aarch64.hpp index 7e9ca43efcf3c..8b942730ae248 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/icache_bsd_aarch64.hpp +++ b/src/hotspot/os_cpu/bsd_aarch64/icache_bsd_aarch64.hpp @@ -33,7 +33,7 @@ class ICache : public AbstractICache { public: - static void initialize(); + static void initialize(int phase); static void invalidate_word(address addr) { __clear_cache((char *)addr, (char *)(addr + 4)); } diff --git a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp index 7b35317882da8..aeba308d3a2e2 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp +++ b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp @@ -271,11 +271,8 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, stub = SharedRuntime::handle_unsafe_access(thread, next_pc); } } else if (sig == SIGILL && nativeInstruction_at(pc)->is_stop()) { - // Pull a pointer to the error message out of the instruction - // stream. - const uint64_t *detail_msg_ptr - = (uint64_t*)(pc + NativeInstruction::instruction_size); - const char *detail_msg = (const char *)*detail_msg_ptr; + // A pointer to the message will have been placed in r0 + const char *detail_msg = (const char *)(uc->uc_mcontext->DU3_PREFIX(ss,x[0])); const char *msg = "stop"; if (TraceTraps) { tty->print_cr("trap: %s: (SIGILL)", msg); diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp index 75ca68e43fb48..957be26670229 100644 --- a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp @@ -32,7 +32,7 @@ class ICache : public AbstractICache { public: - static void initialize(); + static void initialize(int phase); static void invalidate_word(address addr) { __builtin___clear_cache((char *)addr, (char *)(addr + 4)); } diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp index 7728c62682c3b..4335733d1fed4 100644 --- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp @@ -248,11 +248,8 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, stub = SharedRuntime::handle_unsafe_access(thread, next_pc); } } else if (sig == SIGILL && nativeInstruction_at(pc)->is_stop()) { - // Pull a pointer to the error message out of the instruction - // stream. - const uint64_t *detail_msg_ptr - = (uint64_t*)(pc + NativeInstruction::instruction_size); - const char *detail_msg = (const char *)*detail_msg_ptr; + // A pointer to the message will have been placed in r0 + const char *detail_msg = (const char *)(uc->uc_mcontext.regs[0]); const char *msg = "stop"; if (TraceTraps) { tty->print_cr("trap: %s: (SIGILL)", msg); diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index dabc69403f3d5..9725c6cd6c073 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp @@ -75,6 +75,14 @@ #define HWCAP_PACA (1 << 30) #endif +#ifndef HWCAP_FPHP +#define HWCAP_FPHP (1<<9) +#endif + +#ifndef HWCAP_ASIMDHP +#define HWCAP_ASIMDHP (1<<10) +#endif + #ifndef HWCAP2_SVE2 #define HWCAP2_SVE2 (1 << 1) #endif @@ -119,6 +127,8 @@ void VM_Version::get_os_cpu_info() { static_assert(CPU_SHA512 == HWCAP_SHA512, "Flag CPU_SHA512 must follow Linux HWCAP"); static_assert(CPU_SVE == HWCAP_SVE, "Flag CPU_SVE must follow Linux HWCAP"); static_assert(CPU_PACA == HWCAP_PACA, "Flag CPU_PACA must follow Linux HWCAP"); + static_assert(CPU_FPHP == HWCAP_FPHP, "Flag CPU_FPHP must follow Linux HWCAP"); + static_assert(CPU_ASIMDHP == HWCAP_ASIMDHP, "Flag CPU_ASIMDHP must follow Linux HWCAP"); _features = auxv & ( HWCAP_FP | HWCAP_ASIMD | @@ -133,7 +143,9 @@ void VM_Version::get_os_cpu_info() { HWCAP_SHA3 | HWCAP_SHA512 | HWCAP_SVE | - HWCAP_PACA); + HWCAP_PACA | + HWCAP_FPHP | + HWCAP_ASIMDHP); if (auxv2 & HWCAP2_SVE2) _features |= CPU_SVE2; if (auxv2 & HWCAP2_SVEBITPERM) _features |= CPU_SVEBITPERM; diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index 06d6aaf109f86..d19128cafc2d2 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -233,10 +233,13 @@ void RiscvHwprobe::add_features_from_query_result() { if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZACAS)) { VM_Version::ext_Zacas.enable_feature(); } -#endif + // Currently tests shows that cmove using Zicond instructions will bring + // performance regression, but to get a test coverage all the time, will + // still prefer to enabling it in debug version. if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICOND)) { VM_Version::ext_Zicond.enable_feature(); } +#endif if (is_valid(RISCV_HWPROBE_KEY_CPUPERF_0)) { VM_Version::unaligned_access.enable_feature( query[RISCV_HWPROBE_KEY_CPUPERF_0].value & RISCV_HWPROBE_MISALIGNED_MASK); diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index b6095c279cbf0..506c78cacca18 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -129,6 +129,9 @@ void VM_Version::setup_cpu_available_features() { snprintf(buf, sizeof(buf)/2, "%s ", uarch); } os::free((void*) uarch); + + int features_offset = strnlen(buf, sizeof(buf)); + strcat(buf, "rv64"); int i = 0; while (_feature_list[i] != nullptr) { @@ -191,7 +194,9 @@ void VM_Version::setup_cpu_available_features() { } } - _features_string = os::strdup(buf); + _cpu_info_string = os::strdup(buf); + + _features_string = _cpu_info_string + features_offset; } void VM_Version::os_aux_features() { diff --git a/src/hotspot/os_cpu/windows_aarch64/icache_windows_aarch64.hpp b/src/hotspot/os_cpu/windows_aarch64/icache_windows_aarch64.hpp index bf36b77d98e29..e12caebf0e263 100644 --- a/src/hotspot/os_cpu/windows_aarch64/icache_windows_aarch64.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/icache_windows_aarch64.hpp @@ -32,7 +32,7 @@ class ICache : public AbstractICache { public: - static void initialize(); + static void initialize(int phase); static void invalidate_word(address addr) { invalidate_range(addr, 4); } diff --git a/src/hotspot/share/adlc/output_h.cpp b/src/hotspot/share/adlc/output_h.cpp index a4ab29008f0af..dd149064a5a8f 100644 --- a/src/hotspot/share/adlc/output_h.cpp +++ b/src/hotspot/share/adlc/output_h.cpp @@ -870,7 +870,8 @@ void ArchDesc::declare_pipe_classes(FILE *fp_hpp) { fprintf(fp_hpp, " }\n\n"); fprintf(fp_hpp, " void step(uint cycles) {\n"); fprintf(fp_hpp, " _used = 0;\n"); - fprintf(fp_hpp, " _mask <<= cycles;\n"); + fprintf(fp_hpp, " uint max_shift = 8 * sizeof(_mask) - 1;\n"); + fprintf(fp_hpp, " _mask <<= (cycles < max_shift) ? cycles : max_shift;\n"); fprintf(fp_hpp, " }\n\n"); fprintf(fp_hpp, " friend class Pipeline_Use;\n"); fprintf(fp_hpp, "};\n\n"); diff --git a/src/hotspot/share/asm/assembler.hpp b/src/hotspot/share/asm/assembler.hpp index 9abd3eb71716e..961b5fab700a7 100644 --- a/src/hotspot/share/asm/assembler.hpp +++ b/src/hotspot/share/asm/assembler.hpp @@ -73,7 +73,7 @@ class Label; */ class Label { private: - enum { PatchCacheSize = 4 debug_only( +4 ) }; + enum { PatchCacheSize = 4 DEBUG_ONLY( +4 ) }; // _loc encodes both the binding state (via its sign) // and the binding locator (via its value) of a label. diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index 917569e2be6fd..be7a39380d922 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -92,7 +92,7 @@ CodeBuffer::CodeBuffer(CodeBlob* blob) DEBUG_ONLY(: Scrubber(this, sizeof(*this) // Provide code buffer with meaningful name initialize_misc(blob->name()); initialize(blob->content_begin(), blob->content_size()); - debug_only(verify_section_allocation();) + DEBUG_ONLY(verify_section_allocation();) } void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) { @@ -120,7 +120,7 @@ void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) { _insts.initialize_locs(locs_size / sizeof(relocInfo)); } - debug_only(verify_section_allocation();) + DEBUG_ONLY(verify_section_allocation();) } @@ -494,7 +494,7 @@ void CodeBuffer::compute_final_layout(CodeBuffer* dest) const { prev_cs = cs; } - debug_only(dest_cs->_start = nullptr); // defeat double-initialization assert + DEBUG_ONLY(dest_cs->_start = nullptr); // defeat double-initialization assert dest_cs->initialize(buf+buf_offset, csize); dest_cs->set_end(buf+buf_offset+csize); assert(dest_cs->is_allocated(), "must always be allocated"); @@ -505,7 +505,7 @@ void CodeBuffer::compute_final_layout(CodeBuffer* dest) const { // Done calculating sections; did it come out to the right end? assert(buf_offset == total_content_size(), "sanity"); - debug_only(dest->verify_section_allocation();) + DEBUG_ONLY(dest->verify_section_allocation();) } // Append an oop reference that keeps the class alive. @@ -722,7 +722,7 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) { #ifndef PRODUCT if (PrintNMethods && (WizardMode || Verbose)) { tty->print("done with CodeBuffer:"); - ((CodeBuffer*)this)->print(); + ((CodeBuffer*)this)->print_on(tty); } #endif //PRODUCT @@ -861,7 +861,7 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { #ifndef PRODUCT if (PrintNMethods && (WizardMode || Verbose)) { tty->print("expanding CodeBuffer:"); - this->print(); + this->print_on(tty); } if (StressCodeBuffers && blob() != nullptr) { @@ -939,17 +939,17 @@ void CodeBuffer::expand(CodeSection* which_cs, csize_t amount) { cb.set_blob(nullptr); // Zap the old code buffer contents, to avoid mistakenly using them. - debug_only(Copy::fill_to_bytes(bxp->_total_start, bxp->_total_size, + DEBUG_ONLY(Copy::fill_to_bytes(bxp->_total_start, bxp->_total_size, badCodeHeapFreeVal);) // Make certain that the new sections are all snugly inside the new blob. - debug_only(verify_section_allocation();) + DEBUG_ONLY(verify_section_allocation();) #ifndef PRODUCT _decode_begin = nullptr; // sanity if (PrintNMethods && (WizardMode || Verbose)) { tty->print("expanded CodeBuffer:"); - this->print(); + this->print_on(tty); } #endif //PRODUCT } @@ -1042,6 +1042,9 @@ void CodeBuffer::shared_stub_to_interp_for(ciMethod* callee, csize_t call_offset #ifndef PRODUCT void CodeBuffer::block_comment(ptrdiff_t offset, const char* comment) { + if (insts()->scratch_emit()) { + return; + } if (_collect_comments) { const char* str = _asm_remarks.insert(offset, comment); postcond(str != comment); @@ -1049,6 +1052,9 @@ void CodeBuffer::block_comment(ptrdiff_t offset, const char* comment) { } const char* CodeBuffer::code_string(const char* str) { + if (insts()->scratch_emit()) { + return str; + } const char* tmp = _dbg_strings.insert(str); postcond(tmp != str); return tmp; @@ -1060,24 +1066,24 @@ void CodeBuffer::decode() { _decode_begin = insts_end(); } -void CodeSection::print(const char* name) { +void CodeSection::print_on(outputStream* st, const char* name) { csize_t locs_size = locs_end() - locs_start(); - tty->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)", + st->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)", name, p2i(start()), p2i(end()), p2i(limit()), size(), capacity()); - tty->print_cr(" %7s.locs = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d) point=%d", + st->print_cr(" %7s.locs = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d) point=%d", name, p2i(locs_start()), p2i(locs_end()), p2i(locs_limit()), locs_size, locs_capacity(), locs_point_off()); if (PrintRelocations && (locs_size != 0)) { RelocIterator iter(this); - iter.print(); + iter.print_on(st); } } -void CodeBuffer::print() { - tty->print_cr("CodeBuffer:"); +void CodeBuffer::print_on(outputStream* st) { + st->print_cr("CodeBuffer:%s", name()); for (int n = 0; n < (int)SECT_LIMIT; n++) { // print each section CodeSection* cs = code_section(n); - cs->print(code_section_name(n)); + cs->print_on(st, code_section_name(n)); } } diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp index f855d41b18108..95cd4b7f912af 100644 --- a/src/hotspot/share/asm/codeBuffer.hpp +++ b/src/hotspot/share/asm/codeBuffer.hpp @@ -89,6 +89,7 @@ class CodeOffsets: public StackObj { // They are filled concurrently, and concatenated at the end. class CodeSection { friend class CodeBuffer; + friend class AOTCodeReader; public: typedef int csize_t; // code size type; would be size_t except for history @@ -121,8 +122,8 @@ class CodeSection { _locs_own = false; _scratch_emit = false; _skipped_instructions_size = 0; - debug_only(_index = -1); - debug_only(_outer = (CodeBuffer*)badAddress); + DEBUG_ONLY(_index = -1); + DEBUG_ONLY(_outer = (CodeBuffer*)badAddress); } void initialize_outer(CodeBuffer* outer, int8_t index) { @@ -283,7 +284,7 @@ class CodeSection { #ifndef PRODUCT void decode(); - void print(const char* name); + void print_on(outputStream* st, const char* name); #endif //PRODUCT }; @@ -386,6 +387,7 @@ typedef GrowableArray SharedStubToInterpRequests; class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { friend class CodeSection; friend class StubCodeGenerator; + friend class AOTCodeReader; private: // CodeBuffers must be allocated on the stack except for a single @@ -535,7 +537,7 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { assert(code_start != nullptr, "sanity"); initialize_misc("static buffer"); initialize(code_start, code_size); - debug_only(verify_section_allocation();) + DEBUG_ONLY(verify_section_allocation();) } // (2) CodeBuffer referring to pre-allocated CodeBlob. @@ -742,7 +744,7 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { // Printing / Decoding // decodes from decode_begin() to code_end() and sets decode_begin to end void decode(); - void print(); + void print_on(outputStream* st); #endif // Directly disassemble code buffer. void decode(address start, address end); diff --git a/src/hotspot/share/c1/c1_FrameMap.hpp b/src/hotspot/share/c1/c1_FrameMap.hpp index 4e4fde0cb4a02..f10c4d3f226b3 100644 --- a/src/hotspot/share/c1/c1_FrameMap.hpp +++ b/src/hotspot/share/c1/c1_FrameMap.hpp @@ -109,19 +109,19 @@ class FrameMap : public CompilationResourceObj { static Register cpu_rnr2reg (int rnr) { assert(_init_done, "tables not initialized"); - debug_only(cpu_range_check(rnr);) + DEBUG_ONLY(cpu_range_check(rnr);) return _cpu_rnr2reg[rnr]; } static int cpu_reg2rnr (Register reg) { assert(_init_done, "tables not initialized"); - debug_only(cpu_range_check(reg->encoding());) + DEBUG_ONLY(cpu_range_check(reg->encoding());) return _cpu_reg2rnr[reg->encoding()]; } static void map_register(int rnr, Register reg) { - debug_only(cpu_range_check(rnr);) - debug_only(cpu_range_check(reg->encoding());) + DEBUG_ONLY(cpu_range_check(rnr);) + DEBUG_ONLY(cpu_range_check(reg->encoding());) _cpu_rnr2reg[rnr] = reg; _cpu_reg2rnr[reg->encoding()] = rnr; } diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index 9d4b35024ed05..0f87a90a417a7 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -1363,7 +1363,7 @@ int Runtime1::move_klass_patching(JavaThread* current) { // // NOTE: we are still in Java // - debug_only(NoHandleMark nhm;) + DEBUG_ONLY(NoHandleMark nhm;) { // Enter VM mode ResetNoHandleMark rnhm; @@ -1380,7 +1380,7 @@ int Runtime1::move_mirror_patching(JavaThread* current) { // // NOTE: we are still in Java // - debug_only(NoHandleMark nhm;) + DEBUG_ONLY(NoHandleMark nhm;) { // Enter VM mode ResetNoHandleMark rnhm; @@ -1397,7 +1397,7 @@ int Runtime1::move_appendix_patching(JavaThread* current) { // // NOTE: we are still in Java // - debug_only(NoHandleMark nhm;) + DEBUG_ONLY(NoHandleMark nhm;) { // Enter VM mode ResetNoHandleMark rnhm; diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index 95d242c2089cd..d87b501150b59 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -25,6 +25,7 @@ #include "cds/aotClassLinker.hpp" #include "cds/aotArtifactFinder.hpp" #include "cds/aotClassInitializer.hpp" +#include "cds/aotReferenceObjSupport.hpp" #include "cds/dumpTimeClassInfo.inline.hpp" #include "cds/heapShared.hpp" #include "cds/lambdaProxyClassDictionary.hpp" @@ -73,6 +74,7 @@ void AOTArtifactFinder::find_artifacts() { // Note, if a class is not excluded, it does NOT mean it will be automatically included // into the AOT cache -- that will be decided by the code below. SystemDictionaryShared::finish_exclusion_checks(); + AOTReferenceObjSupport::init_keep_alive_objs_table(); start_scanning_for_oops(); @@ -207,11 +209,36 @@ void AOTArtifactFinder::add_aot_inited_class(InstanceKlass* ik) { } } +void AOTArtifactFinder::append_to_all_cached_classes(Klass* k) { + precond(!SystemDictionaryShared::should_be_excluded(k)); + _all_cached_classes->append(k); +} + void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { + if (CDSConfig::is_dumping_dynamic_archive() && ik->is_shared()) { + // This class is already included in the base archive. No need to cache + // it again in the dynamic archive. + return; + } + bool created; _seen_classes->put_if_absent(ik, &created); if (created) { - _all_cached_classes->append(ik); + append_to_all_cached_classes(ik); + + // All super types must be added. + InstanceKlass* s = ik->java_super(); + if (s != nullptr) { + add_cached_instance_class(s); + } + + Array* interfaces = ik->local_interfaces(); + int len = interfaces->length(); + for (int i = 0; i < len; i++) { + InstanceKlass* intf = interfaces->at(i); + add_cached_instance_class(intf); + } + if (CDSConfig::is_dumping_final_static_archive() && ik->is_shared_unregistered_class()) { // The following are not appliable to unregistered classes return; @@ -229,7 +256,7 @@ void AOTArtifactFinder::add_cached_type_array_class(TypeArrayKlass* tak) { bool created; _seen_classes->put_if_absent(tak, &created); if (created) { - _all_cached_classes->append(tak); + append_to_all_cached_classes(tak); scan_oops_in_array_class(tak); } } diff --git a/src/hotspot/share/cds/aotArtifactFinder.hpp b/src/hotspot/share/cds/aotArtifactFinder.hpp index d890d874af9ef..5d293f20af073 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.hpp +++ b/src/hotspot/share/cds/aotArtifactFinder.hpp @@ -79,6 +79,7 @@ class AOTArtifactFinder : AllStatic { static void scan_oops_in_array_class(ArrayKlass* ak); static void add_cached_type_array_class(TypeArrayKlass* tak); static void add_cached_instance_class(InstanceKlass* ik); + static void append_to_all_cached_classes(Klass* k); public: static void initialize(); static void find_artifacts(); diff --git a/src/hotspot/share/cds/aotCacheAccess.cpp b/src/hotspot/share/cds/aotCacheAccess.cpp new file mode 100644 index 0000000000000..b6c4a201da502 --- /dev/null +++ b/src/hotspot/share/cds/aotCacheAccess.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "cds/aotCacheAccess.hpp" +#include "cds/archiveBuilder.hpp" +#include "cds/cdsConfig.hpp" +#include "cds/filemap.hpp" +#include "cds/heapShared.hpp" +#include "cds/metaspaceShared.hpp" +#include "classfile/stringTable.hpp" +#include "logging/log.hpp" +#include "logging/logStream.hpp" +#include "memory/resourceArea.hpp" +#include "memory/universe.hpp" +#include "memory/virtualspace.hpp" +#include "oops/instanceKlass.hpp" + +void* AOTCacheAccess::allocate_aot_code_region(size_t size) { + assert(CDSConfig::is_dumping_final_static_archive(), "must be"); + return (void*)ArchiveBuilder::ac_region_alloc(size); +} + +size_t AOTCacheAccess::get_aot_code_region_size() { + assert(CDSConfig::is_using_archive(), "must be"); + FileMapInfo* mapinfo = FileMapInfo::current_info(); + assert(mapinfo != nullptr, "must be"); + return mapinfo->region_at(MetaspaceShared::ac)->used_aligned(); +} + +bool AOTCacheAccess::map_aot_code_region(ReservedSpace rs) { + FileMapInfo* static_mapinfo = FileMapInfo::current_info(); + assert(UseSharedSpaces && static_mapinfo != nullptr, "must be"); + return static_mapinfo->map_aot_code_region(rs); +} diff --git a/src/hotspot/share/cds/aotCacheAccess.hpp b/src/hotspot/share/cds/aotCacheAccess.hpp new file mode 100644 index 0000000000000..31fb9da534dbf --- /dev/null +++ b/src/hotspot/share/cds/aotCacheAccess.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_CDS_AOTCACHEACCESS_HPP +#define SHARE_CDS_AOTCACHEACCESS_HPP + +#include "cds/archiveBuilder.hpp" +#include "cds/archiveUtils.hpp" +#include "memory/allStatic.hpp" +#include "oops/oopsHierarchy.hpp" +#include "utilities/globalDefinitions.hpp" + +class ReservedSpace; + +// AOT Cache API for AOT compiler + +class AOTCacheAccess : AllStatic { +public: + static void* allocate_aot_code_region(size_t size) NOT_CDS_RETURN_(nullptr); + + static size_t get_aot_code_region_size() NOT_CDS_RETURN_(0); + + static bool map_aot_code_region(ReservedSpace rs) NOT_CDS_RETURN_(false); +}; + +#endif // SHARE_CDS_AOTCACHEACCESS_HPP diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 297f8109eb4ba..be7e1f31d8a52 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -338,7 +338,8 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { bool AOTClassInitializer::is_runtime_setup_required(InstanceKlass* ik) { return ik == vmClasses::Class_klass() || ik == vmClasses::internal_Unsafe_klass() || - ik == vmClasses::ConcurrentHashMap_klass(); + ik == vmClasses::ConcurrentHashMap_klass() || + ik == vmClasses::Reference_klass(); } void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass* ik) { diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.cpp b/src/hotspot/share/cds/aotReferenceObjSupport.cpp new file mode 100644 index 0000000000000..e948a6cefa39d --- /dev/null +++ b/src/hotspot/share/cds/aotReferenceObjSupport.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "cds/aotReferenceObjSupport.hpp" +#include "cds/heapShared.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/vmSymbols.hpp" +#include "logging/log.hpp" +#include "memory/resourceArea.hpp" +#include "memory/universe.hpp" +#include "oops/oop.inline.hpp" +#include "oops/oopHandle.inline.hpp" +#include "runtime/fieldDescriptor.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "utilities/resourceHash.hpp" + +// Handling of java.lang.ref.Reference objects in the AOT cache +// ============================================================ +// +// When AOTArtifactFinder finds an oop which is a instance of java.lang.ref.Reference: +// +// - We check if the oop is eligible to be stored in the AOT cache. If not, the AOT cache +// creation fails -- see AOTReferenceObjSupport::check_if_ref_obj() +// +// - Otherwise, we store the oop into the AOT cache, but we unconditionally reset its +// "next" and "discovered" fields to null. Otherwise, if AOTArtifactFinder follows these +// fields, it may found unrelated objects that we don't intend to cache. +// +// Eligibility +// =========== +// +// [1] A reference that does not require special clean up (i.e., Reference::queue == ReferenceQueue.NULL_QUEUE) +// is eligible. +// +// [2] A reference that REQUIRE specials clean up (i.e., Reference::queue != ReferenceQueue.NULL_QUEUE) +// is eligible ONLY if its referent is not null. +// +// As of this version, the only oops in group [2] that can be found by AOTArtifactFinder are +// the keys used by ReferencedKeyMap in the implementation of MethodType::internTable. +// stabilize_cached_reference_objects() ensures that all keys found by AOTArtifactFinder are eligible. +// +// The purpose of the error check in check_if_ref_obj() is to guard against changes in the JDK core +// libs that might introduce new types of oops in group [2] into the AOT cache. +// +// Reasons for the eligibility restrictions +// ======================================== +// +// Reference handling is complex. In this version, we implement only enough functionality to support +// the use of Weak/Soft references used by java.lang.invoke. +// +// We intend to evolve the implementation in the future by +// -- implementing more assemblySetup() operations for other use cases, and/or +// -- relaxing the eligibility restrictions. +// +// +// null referents for group [1] +// ============================ +// +// Any cached reference R1 of group [1] is allowed to have a null referent. +// This can happen in the following situations: +// (a) R1.clear() was called by Java code during the assembly phase. +// (b) The referent has been collected, and R1 is in the "pending" state. +// In case (b), the "next" and "discovered" fields of the cached copy of R1 will +// be set to null. During the production run: +// - It would appear to the Java program as if immediately during VM start-up, the referent +// was collected and ReferenceThread completed processing of R1. +// - It would appear to the GC as if immediately during VM start-up, the Java program called +// R1.clear(). + +#if INCLUDE_CDS_JAVA_HEAP + +class KeepAliveObjectsTable : public ResourceHashtable {}; + +static KeepAliveObjectsTable* _keep_alive_objs_table; +static OopHandle _keep_alive_objs_array; +static OopHandle _null_queue; + +bool AOTReferenceObjSupport::is_enabled() { + // For simplicity, AOTReferenceObjSupport is enabled only when dumping method handles. + // Otherwise we won't see Reference objects in the AOT cache. Let's be conservative now. + return CDSConfig::is_dumping_method_handles(); +} + +void AOTReferenceObjSupport::initialize(TRAPS) { + if (!AOTReferenceObjSupport::is_enabled()) { + return; + } + + TempNewSymbol class_name = SymbolTable::new_symbol("java/lang/ref/ReferenceQueue"); + Klass* k = SystemDictionary::resolve_or_fail(class_name, true, CHECK); + InstanceKlass* ik = InstanceKlass::cast(k); + ik->initialize(CHECK); + + TempNewSymbol field_name = SymbolTable::new_symbol("NULL_QUEUE"); + fieldDescriptor fd; + bool found = ik->find_local_field(field_name, vmSymbols::referencequeue_signature(), &fd); + precond(found); + precond(fd.is_static()); + + _null_queue = OopHandle(Universe::vm_global(), ik->java_mirror()->obj_field(fd.offset())); +} + +// Ensure that all group [2] references found by AOTArtifactFinder are eligible. +void AOTReferenceObjSupport::stabilize_cached_reference_objects(TRAPS) { + if (AOTReferenceObjSupport::is_enabled()) { + // This assert means that the MethodType and MethodTypeForm tables won't be + // updated concurrently, so we can remove GC'ed entries ... + assert(CDSConfig::allow_only_single_java_thread(), "Required"); + + { + TempNewSymbol method_name = SymbolTable::new_symbol("assemblySetup"); + JavaValue result(T_VOID); + JavaCalls::call_static(&result, vmClasses::MethodType_klass(), + method_name, + vmSymbols::void_method_signature(), + CHECK); + } + + { + Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS(); + Klass* cds_klass = SystemDictionary::resolve_or_fail(cds_name, true /*throw error*/, CHECK); + TempNewSymbol method_name = SymbolTable::new_symbol("getKeepAliveObjects"); + TempNewSymbol method_sig = SymbolTable::new_symbol("()[Ljava/lang/Object;"); + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, cds_klass, method_name, method_sig, CHECK); + + _keep_alive_objs_array = OopHandle(Universe::vm_global(), result.get_oop()); + } + } +} + +void AOTReferenceObjSupport::init_keep_alive_objs_table() { + assert_at_safepoint(); // _keep_alive_objs_table uses raw oops + oop a = _keep_alive_objs_array.resolve(); + if (a != nullptr) { + precond(a->is_objArray()); + precond(AOTReferenceObjSupport::is_enabled()); + objArrayOop array = objArrayOop(a); + + _keep_alive_objs_table = new (mtClass)KeepAliveObjectsTable(); + for (int i = 0; i < array->length(); i++) { + oop obj = array->obj_at(i); + _keep_alive_objs_table->put(obj, true); // The array may have duplicated entries but that's OK. + } + } +} + +// Returns true IFF obj is an instance of java.lang.ref.Reference. If so, perform extra eligibility checks. +bool AOTReferenceObjSupport::check_if_ref_obj(oop obj) { + // We have a single Java thread. This means java.lang.ref.Reference$ReferenceHandler thread + // is not running. Otherwise the checks for next/discovered may not work. + precond(CDSConfig::allow_only_single_java_thread()); + assert_at_safepoint(); // _keep_alive_objs_table uses raw oops + + if (obj->klass()->is_subclass_of(vmClasses::Reference_klass())) { + precond(AOTReferenceObjSupport::is_enabled()); + precond(JavaClasses::is_supported_for_archiving(obj)); + precond(_keep_alive_objs_table != nullptr); + + // GC needs to know about this load, It will keep referent alive until the current safepoint ends. + oop referent = HeapAccess::oop_load_at(obj, java_lang_ref_Reference::referent_offset()); + + oop queue = obj->obj_field(java_lang_ref_Reference::queue_offset()); + oop next = java_lang_ref_Reference::next(obj); + oop discovered = java_lang_ref_Reference::discovered(obj); + bool needs_special_cleanup = (queue != _null_queue.resolve()); + + // If you see the errors below, you probably modified the implementation of java.lang.invoke. + // Please check the comments at the top of this file. + if (needs_special_cleanup && (referent == nullptr || !_keep_alive_objs_table->contains(referent))) { + ResourceMark rm; + + log_error(cds, heap)("Cannot archive reference object " PTR_FORMAT " of class %s", + p2i(obj), obj->klass()->external_name()); + log_error(cds, heap)("referent = " PTR_FORMAT + ", queue = " PTR_FORMAT + ", next = " PTR_FORMAT + ", discovered = " PTR_FORMAT, + p2i(referent), p2i(queue), p2i(next), p2i(discovered)); + log_error(cds, heap)("This object requires special clean up as its queue is not ReferenceQueue::N" "ULL (" + PTR_FORMAT ")", p2i(_null_queue.resolve())); + log_error(cds, heap)("%s", (referent == nullptr) ? + "referent cannot be null" : "referent is not registered with CDS.keepAlive()"); + HeapShared::debug_trace(); + MetaspaceShared::unrecoverable_writing_error(); + } + + if (log_is_enabled(Info, cds, ref)) { + ResourceMark rm; + log_info(cds, ref)("Reference obj:" + " r=" PTR_FORMAT + " q=" PTR_FORMAT + " n=" PTR_FORMAT + " d=" PTR_FORMAT + " %s", + p2i(referent), + p2i(queue), + p2i(next), + p2i(discovered), + obj->klass()->external_name()); + } + return true; + } else { + return false; + } +} + +bool AOTReferenceObjSupport::skip_field(int field_offset) { + return (field_offset == java_lang_ref_Reference::next_offset() || + field_offset == java_lang_ref_Reference::discovered_offset()); +} + +#endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.hpp b/src/hotspot/share/cds/aotReferenceObjSupport.hpp new file mode 100644 index 0000000000000..3d645c2df17c8 --- /dev/null +++ b/src/hotspot/share/cds/aotReferenceObjSupport.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_CDS_AOTREFERENCEOBJSUPPORT_HPP +#define SHARE_CDS_AOTREFERENCEOBJSUPPORT_HPP + +#include "memory/allStatic.hpp" +#include "oops/oopsHierarchy.hpp" +#include "utilities/exceptions.hpp" + +// Support for ahead-of-time allocated instances of java.lang.ref.Reference + +class AOTReferenceObjSupport : AllStatic { + +public: + static void initialize(TRAPS); + static void stabilize_cached_reference_objects(TRAPS); + static void init_keep_alive_objs_table() NOT_CDS_JAVA_HEAP_RETURN; + static bool check_if_ref_obj(oop obj); + static bool skip_field(int field_offset); + static bool is_enabled(); +}; + +#endif // SHARE_CDS_AOTREFERENCEOBJSUPPORT_HPP diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index c309de17b4cad..3de4cdec187f6 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -42,6 +42,7 @@ #include "classfile/symbolTable.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" +#include "code/aotCodeCache.hpp" #include "interpreter/abstractInterpreter.hpp" #include "jvm.h" #include "logging/log.hpp" @@ -163,6 +164,7 @@ ArchiveBuilder::ArchiveBuilder() : _pz_region("pz", MAX_SHARED_DELTA), // protection zone -- used only during dumping; does NOT exist in cds archive. _rw_region("rw", MAX_SHARED_DELTA), _ro_region("ro", MAX_SHARED_DELTA), + _ac_region("ac", MAX_SHARED_DELTA), _ptrmap(mtClassShared), _rw_ptrmap(mtClassShared), _ro_ptrmap(mtClassShared), @@ -306,10 +308,12 @@ void ArchiveBuilder::sort_klasses() { } address ArchiveBuilder::reserve_buffer() { - size_t buffer_size = LP64_ONLY(CompressedClassSpaceSize) NOT_LP64(256 * M); + // AOTCodeCache::max_aot_code_size() accounts for aot code region. + size_t buffer_size = LP64_ONLY(CompressedClassSpaceSize) NOT_LP64(256 * M) + AOTCodeCache::max_aot_code_size(); ReservedSpace rs = MemoryReserver::reserve(buffer_size, MetaspaceShared::core_region_alignment(), - os::vm_page_size()); + os::vm_page_size(), + mtClassShared); if (!rs.is_reserved()) { log_error(cds)("Failed to reserve %zu bytes of output buffer.", buffer_size); MetaspaceShared::unrecoverable_writing_error(); @@ -531,6 +535,13 @@ ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref } else if (ref->msotype() == MetaspaceObj::MethodDataType || ref->msotype() == MetaspaceObj::MethodCountersType) { return set_to_null; + } else if (ref->msotype() == MetaspaceObj::AdapterHandlerEntryType) { + if (AOTCodeCache::is_dumping_adapters()) { + AdapterHandlerEntry* entry = (AdapterHandlerEntry*)ref->obj(); + return AdapterHandlerLibrary::is_abstract_method_adapter(entry) ? set_to_null : make_a_copy; + } else { + return set_to_null; + } } else { if (ref->msotype() == MetaspaceObj::ClassType) { Klass* klass = (Klass*)ref->obj(); @@ -704,6 +715,11 @@ void ArchiveBuilder::mark_and_relocate_to_buffered_addr(address* ptr_location) { ArchivePtrMarker::mark_pointer(ptr_location); } +bool ArchiveBuilder::has_been_archived(address src_addr) const { + SourceObjInfo* p = _src_obj_table.get(src_addr); + return (p != nullptr); +} + bool ArchiveBuilder::has_been_buffered(address src_addr) const { if (RegeneratedClasses::has_been_regenerated(src_addr) || _src_obj_table.get(src_addr) == nullptr || @@ -972,6 +988,15 @@ address ArchiveBuilder::offset_to_buffered_address(u4 offset) const { return buffered_addr; } +void ArchiveBuilder::start_ac_region() { + ro_region()->pack(); + start_dump_region(&_ac_region); +} + +void ArchiveBuilder::end_ac_region() { + _ac_region.pack(); +} + #if INCLUDE_CDS_JAVA_HEAP narrowKlass ArchiveBuilder::get_requested_narrow_klass(Klass* k) { assert(CDSConfig::is_dumping_heap(), "sanity"); @@ -1081,8 +1106,9 @@ int ArchiveBuilder::precomputed_narrow_klass_shift() { #endif // _LP64 void ArchiveBuilder::relocate_to_requested() { - ro_region()->pack(); - + if (!ro_region()->is_packed()) { + ro_region()->pack(); + } size_t my_archive_size = buffer_top() - buffer_bottom(); if (CDSConfig::is_dumping_static_archive()) { @@ -1201,7 +1227,7 @@ class ArchiveBuilder::CDSMapLogger : AllStatic { #undef _LOG_PREFIX // Log information about a region, whose address at dump time is [base .. top). At - // runtime, this region will be mapped to requested_base. requested_base is 0 if this + // runtime, this region will be mapped to requested_base. requested_base is nullptr if this // region will be mapped at os-selected addresses (such as the bitmap region), or will // be accessed with os::read (the header). // @@ -1210,7 +1236,11 @@ class ArchiveBuilder::CDSMapLogger : AllStatic { static void log_region(const char* name, address base, address top, address requested_base) { size_t size = top - base; base = requested_base; - top = requested_base + size; + if (requested_base == nullptr) { + top = (address)size; + } else { + top = requested_base + size; + } log_info(cds, map)("[%-18s " PTR_FORMAT " - " PTR_FORMAT " %9zu bytes]", name, p2i(base), p2i(top), size); } @@ -1522,6 +1552,7 @@ void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, ArchiveHeapInfo* heap_i write_region(mapinfo, MetaspaceShared::rw, &_rw_region, /*read_only=*/false,/*allow_exec=*/false); write_region(mapinfo, MetaspaceShared::ro, &_ro_region, /*read_only=*/true, /*allow_exec=*/false); + write_region(mapinfo, MetaspaceShared::ac, &_ac_region, /*read_only=*/false,/*allow_exec=*/false); // Split pointer map into read-write and read-only bitmaps ArchivePtrMarker::initialize_rw_ro_maps(&_rw_ptrmap, &_ro_ptrmap); @@ -1574,6 +1605,7 @@ void ArchiveBuilder::print_region_stats(FileMapInfo *mapinfo, ArchiveHeapInfo* h _rw_region.print(total_reserved); _ro_region.print(total_reserved); + _ac_region.print(total_reserved); print_bitmap_region_stats(bitmap_used, total_reserved); diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp index 5913ae29c7878..531c43e1cbfba 100644 --- a/src/hotspot/share/cds/archiveBuilder.hpp +++ b/src/hotspot/share/cds/archiveBuilder.hpp @@ -217,6 +217,7 @@ class ArchiveBuilder : public StackObj { DumpRegion _rw_region; DumpRegion _ro_region; + DumpRegion _ac_region; // AOT code // Combined bitmap to track pointers in both RW and RO regions. This is updated // as objects are copied into RW and RO. @@ -372,6 +373,7 @@ class ArchiveBuilder : public StackObj { DumpRegion* pz_region() { return &_pz_region; } DumpRegion* rw_region() { return &_rw_region; } DumpRegion* ro_region() { return &_ro_region; } + DumpRegion* ac_region() { return &_ac_region; } static char* rw_region_alloc(size_t num_bytes) { return current()->rw_region()->allocate(num_bytes); @@ -379,6 +381,12 @@ class ArchiveBuilder : public StackObj { static char* ro_region_alloc(size_t num_bytes) { return current()->ro_region()->allocate(num_bytes); } + static char* ac_region_alloc(size_t num_bytes) { + return current()->ac_region()->allocate(num_bytes); + } + + void start_ac_region(); + void end_ac_region(); template static Array* new_ro_array(int length) { @@ -426,6 +434,8 @@ class ArchiveBuilder : public StackObj { mark_and_relocate_to_buffered_addr((address*)ptr_location); } + bool has_been_archived(address src_addr) const; + bool has_been_buffered(address src_addr) const; template bool has_been_buffered(T src_addr) const { return has_been_buffered((address)src_addr); diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index 5684066105f1f..2b6d403d83528 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.cpp +++ b/src/hotspot/share/cds/archiveHeapWriter.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotReferenceObjSupport.hpp" #include "cds/archiveHeapWriter.hpp" #include "cds/cdsConfig.hpp" #include "cds/filemap.hpp" @@ -607,18 +608,27 @@ class ArchiveHeapWriter::EmbeddedOopRelocator: public BasicOopIterateClosure { oop _src_obj; address _buffered_obj; CHeapBitMap* _oopmap; - + bool _is_java_lang_ref; public: EmbeddedOopRelocator(oop src_obj, address buffered_obj, CHeapBitMap* oopmap) : - _src_obj(src_obj), _buffered_obj(buffered_obj), _oopmap(oopmap) {} + _src_obj(src_obj), _buffered_obj(buffered_obj), _oopmap(oopmap) + { + _is_java_lang_ref = AOTReferenceObjSupport::check_if_ref_obj(src_obj); + } void do_oop(narrowOop *p) { EmbeddedOopRelocator::do_oop_work(p); } void do_oop( oop *p) { EmbeddedOopRelocator::do_oop_work(p); } private: template void do_oop_work(T *p) { - size_t field_offset = pointer_delta(p, _src_obj, sizeof(char)); - ArchiveHeapWriter::relocate_field_in_buffer((T*)(_buffered_obj + field_offset), _oopmap); + int field_offset = pointer_delta_as_int((char*)p, cast_from_oop(_src_obj)); + T* field_addr = (T*)(_buffered_obj + field_offset); + if (_is_java_lang_ref && AOTReferenceObjSupport::skip_field(field_offset)) { + // Do not copy these fields. Set them to null + *field_addr = (T)0x0; + } else { + ArchiveHeapWriter::relocate_field_in_buffer(field_addr, _oopmap); + } } }; diff --git a/src/hotspot/share/cds/archiveUtils.cpp b/src/hotspot/share/cds/archiveUtils.cpp index 217b30e401d67..0b01494574c0b 100644 --- a/src/hotspot/share/cds/archiveUtils.cpp +++ b/src/hotspot/share/cds/archiveUtils.cpp @@ -270,9 +270,10 @@ void DumpRegion::append_intptr_t(intptr_t n, bool need_to_mark) { } void DumpRegion::print(size_t total_bytes) const { + char* base = used() > 0 ? ArchiveBuilder::current()->to_requested(_base) : nullptr; log_debug(cds)("%s space: %9zu [ %4.1f%% of total] out of %9zu bytes [%5.1f%% used] at " INTPTR_FORMAT, _name, used(), percent_of(used(), total_bytes), reserved(), percent_of(used(), reserved()), - p2i(ArchiveBuilder::current()->to_requested(_base))); + p2i(base)); } void DumpRegion::print_out_of_space_msg(const char* failing_region, size_t needed_bytes) { @@ -295,7 +296,10 @@ void DumpRegion::init(ReservedSpace* rs, VirtualSpace* vs) { } void DumpRegion::pack(DumpRegion* next) { - assert(!is_packed(), "sanity"); + if (!is_packed()) { + _end = (char*)align_up(_top, MetaspaceShared::core_region_alignment()); + _is_packed = true; + } _end = (char*)align_up(_top, MetaspaceShared::core_region_alignment()); _is_packed = true; if (next != nullptr) { diff --git a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp index 56d28ec910ce7..836ec93f6411f 100644 --- a/src/hotspot/share/cds/archiveUtils.hpp +++ b/src/hotspot/share/cds/archiveUtils.hpp @@ -180,6 +180,7 @@ class DumpRegion { bool is_allocatable() const { return !is_packed() && _base != nullptr; } + bool is_empty() const { return _base == _top; } void print(size_t total_bytes) const; void print_out_of_space_msg(const char* failing_region, size_t needed_bytes); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 64ad07b0cf816..701b25847741c 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -402,6 +402,10 @@ void CDSConfig::check_aot_flags() { CHECK_SINGLE_PATH(AOTCache); CHECK_SINGLE_PATH(AOTConfiguration); + if (FLAG_IS_DEFAULT(AOTCache) && AOTAdapterCaching) { + log_debug(aot,codecache,init)("AOTCache is not specified - AOTAdapterCaching is ignored"); + } + if (FLAG_IS_DEFAULT(AOTCache) && FLAG_IS_DEFAULT(AOTConfiguration) && FLAG_IS_DEFAULT(AOTMode)) { // AOTCache/AOTConfiguration/AOTMode not used. return; @@ -517,7 +521,7 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla } if (is_dumping_static_archive()) { - if (is_dumping_preimage_static_archive()) { + if (is_dumping_preimage_static_archive() || is_dumping_final_static_archive()) { // Don't tweak execution mode } else if (!mode_flag_cmd_line) { // By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive. @@ -536,9 +540,6 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla // run to another which resulting in non-determinstic CDS archives. // Disable UseStringDeduplication while dumping CDS archive. UseStringDeduplication = false; - - // Don't use SoftReferences so that objects used by java.lang.invoke tables can be archived. - Arguments::PropertyList_add(new SystemProperty("java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE", "false", false)); } // RecordDynamicDumpInfo is not compatible with ArchiveClassesAtExit @@ -847,3 +848,22 @@ bool CDSConfig::is_dumping_method_handles() { } #endif // INCLUDE_CDS_JAVA_HEAP + +// AOT code generation and its archiving is disabled by default. +// We enable it only in the final image dump after the metadata and heap are dumped. +// This affects only JITed code because it may have embedded oops and metadata pointers +// which AOT code encodes as offsets in final CDS archive regions. + +static bool _is_dumping_aot_code = false; + +bool CDSConfig::is_dumping_aot_code() { + return _is_dumping_aot_code; +} + +void CDSConfig::disable_dumping_aot_code() { + _is_dumping_aot_code = false; +} + +void CDSConfig::enable_dumping_aot_code() { + _is_dumping_aot_code = true; +} diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index e96291f653487..e82acebd175f6 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -181,6 +181,12 @@ class CDSConfig : public AllStatic { static void stop_dumping_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN; static void stop_using_full_module_graph(const char* reason = nullptr) NOT_CDS_JAVA_HEAP_RETURN; + // --- AOT code + + static bool is_dumping_aot_code() NOT_CDS_RETURN_(false); + static void disable_dumping_aot_code() NOT_CDS_RETURN; + static void enable_dumping_aot_code() NOT_CDS_RETURN; + // Some CDS functions assume that they are called only within a single-threaded context. I.e., // they are called from: // - The VM thread (e.g., inside VM_PopulateDumpSharedSpace) diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp index 2dae9b452212e..26853a3fb74ea 100644 --- a/src/hotspot/share/cds/cds_globals.hpp +++ b/src/hotspot/share/cds/cds_globals.hpp @@ -127,6 +127,22 @@ product(bool, AOTCacheParallelRelocation, true, DIAGNOSTIC, \ "Use parallel relocation code to speed up startup.") \ \ + /* AOT Code flags */ \ + \ + product(bool, AOTAdapterCaching, false, DIAGNOSTIC, \ + "Enable saving and restoring i2c2i adapters in AOT cache") \ + \ + product(uint, AOTCodeMaxSize, 10*M, DIAGNOSTIC, \ + "Buffer size in bytes for AOT code caching") \ + range(1*M, max_jint) \ + \ + product(bool, AbortVMOnAOTCodeFailure, false, DIAGNOSTIC, \ + "Abort VM on the first occurrence of AOT code load or store " \ + "failure. By default VM will continue execute without AOT code.") \ + \ + develop(bool, TestAOTAdapterLinkFailure, false, \ + "Test failure of adapter linking when loading from AOT cache.") \ + // end of CDS_FLAGS DECLARE_FLAGS(CDS_FLAGS) diff --git a/src/hotspot/share/cds/cppVtables.cpp b/src/hotspot/share/cds/cppVtables.cpp index b8243cedf6d2c..7d484c8d3270e 100644 --- a/src/hotspot/share/cds/cppVtables.cpp +++ b/src/hotspot/share/cds/cppVtables.cpp @@ -281,6 +281,8 @@ intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address ob case MetaspaceObj::AnnotationsType: case MetaspaceObj::MethodCountersType: case MetaspaceObj::RecordComponentType: + case MetaspaceObj::AdapterHandlerEntryType: + case MetaspaceObj::AdapterFingerPrintType: // These have no vtables. break; case MetaspaceObj::MethodDataType: diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 580699b60b583..fbef3c9532dbb 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -824,7 +824,7 @@ bool FileMapRegion::check_region_crc(char* base) const { static const char* region_name(int region_index) { static const char* names[] = { - "rw", "ro", "bm", "hp" + "rw", "ro", "bm", "hp", "ac" }; const int num_regions = sizeof(names)/sizeof(names[0]); assert(0 <= region_index && region_index < num_regions, "sanity"); @@ -910,6 +910,9 @@ void FileMapInfo::write_region(int region, char* base, size_t size, " bytes, addr " INTPTR_FORMAT " file offset 0x%08" PRIxPTR " crc 0x%08x", region_name(region), region, size, p2i(requested_base), _file_offset, crc); + } else { + log_info(cds)("Shared file region (%s) %d: %8zu" + " bytes", region_name(region), region, size); } r->init(region, mapping_offset, size, read_only, allow_exec, crc); @@ -1066,10 +1069,10 @@ void FileMapInfo::close() { */ static char* map_memory(int fd, const char* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only, - bool allow_exec, MemTag mem_tag = mtNone) { + bool allow_exec, MemTag mem_tag) { char* mem = os::map_memory(fd, file_name, file_offset, addr, bytes, - AlwaysPreTouch ? false : read_only, - allow_exec, mem_tag); + mem_tag, AlwaysPreTouch ? false : read_only, + allow_exec); if (mem != nullptr && AlwaysPreTouch) { os::pretouch_memory(mem, mem + bytes); } @@ -1094,7 +1097,7 @@ bool FileMapInfo::remap_shared_readonly_as_readwrite() { assert(WINDOWS_ONLY(false) NOT_WINDOWS(true), "Don't call on Windows"); // Replace old mapping with new one that is writable. char *base = os::map_memory(_fd, _full_path, r->file_offset(), - addr, size, false /* !read_only */, + addr, size, mtNone, false /* !read_only */, r->allow_exec()); close(); // These have to be errors because the shared region is now unmapped. @@ -1111,7 +1114,7 @@ bool FileMapInfo::remap_shared_readonly_as_readwrite() { } // Memory map a region in the address space. -static const char* shared_region_name[] = { "ReadWrite", "ReadOnly", "Bitmap", "Heap" }; +static const char* shared_region_name[] = { "ReadWrite", "ReadOnly", "Bitmap", "Heap", "Code" }; MapArchiveResult FileMapInfo::map_regions(int regions[], int num_regions, char* mapped_base_address, ReservedSpace rs) { DEBUG_ONLY(FileMapRegion* last_region = nullptr); @@ -1274,6 +1277,42 @@ char* FileMapInfo::map_bitmap_region() { return bitmap_base; } +bool FileMapInfo::map_aot_code_region(ReservedSpace rs) { + FileMapRegion* r = region_at(MetaspaceShared::ac); + assert(r->used() > 0 && r->used_aligned() == rs.size(), "must be"); + + char* requested_base = rs.base(); + assert(requested_base != nullptr, "should be inside code cache"); + + char* mapped_base; + if (MetaspaceShared::use_windows_memory_mapping()) { + if (!read_region(MetaspaceShared::ac, requested_base, r->used_aligned(), /* do_commit = */ true)) { + log_info(cds)("Failed to read aot code shared space into reserved space at " INTPTR_FORMAT, + p2i(requested_base)); + return false; + } + mapped_base = requested_base; + } else { + // We do not execute in-place in the AOT code region. + // AOT code is copied to the CodeCache for execution. + bool read_only = false, allow_exec = false; + mapped_base = map_memory(_fd, _full_path, r->file_offset(), + requested_base, r->used_aligned(), read_only, allow_exec, mtClassShared); + } + if (mapped_base == nullptr) { + log_info(cds)("failed to map aot code region"); + return false; + } else { + assert(mapped_base == requested_base, "must be"); + r->set_mapped_from_file(true); + r->set_mapped_base(mapped_base); + log_info(cds)("Mapped static region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)", + MetaspaceShared::ac, p2i(r->mapped_base()), p2i(r->mapped_end()), + shared_region_name[MetaspaceShared::ac]); + return true; + } +} + class SharedDataRelocationTask : public ArchiveWorkerTask { private: BitMapView* const _rw_bm; @@ -1620,7 +1659,7 @@ bool FileMapInfo::map_heap_region_impl() { } else { base = map_memory(_fd, _full_path, r->file_offset(), addr, _mapped_heap_memregion.byte_size(), r->read_only(), - r->allow_exec()); + r->allow_exec(), mtJavaHeap); if (base == nullptr || base != addr) { dealloc_heap_region(); log_info(cds)("UseSharedSpaces: Unable to map at required address in java heap. " diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index 8793e110948b4..d90be1ccff39b 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -368,6 +368,7 @@ class FileMapInfo : public CHeapObj { MemRegion get_heap_region_requested_range() NOT_CDS_JAVA_HEAP_RETURN_(MemRegion()); bool read_region(int i, char* base, size_t size, bool do_commit); char* map_bitmap_region(); + bool map_aot_code_region(ReservedSpace rs); void unmap_region(int i); void close(); bool is_open() { return _file_open; } diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index ce98b2b93b772..ef71d5895f6b8 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -25,6 +25,7 @@ #include "cds/aotArtifactFinder.hpp" #include "cds/aotClassInitializer.hpp" #include "cds/aotClassLocation.hpp" +#include "cds/aotReferenceObjSupport.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveHeapLoader.hpp" #include "cds/archiveHeapWriter.hpp" @@ -1363,34 +1364,37 @@ void HeapShared::clear_archived_roots_of(Klass* k) { } } -// Push all oops that are referenced by _referencing_obj onto the _stack. -class HeapShared::ReferentPusher: public BasicOopIterateClosure { +// Push all oop fields (or oop array elemenets in case of an objArray) in +// _referencing_obj onto the _stack. +class HeapShared::OopFieldPusher: public BasicOopIterateClosure { PendingOopStack* _stack; GrowableArray _found_oop_fields; int _level; bool _record_klasses_only; KlassSubGraphInfo* _subgraph_info; oop _referencing_obj; + bool _is_java_lang_ref; public: - ReferentPusher(PendingOopStack* stack, - int level, - bool record_klasses_only, - KlassSubGraphInfo* subgraph_info, - oop orig) : + OopFieldPusher(PendingOopStack* stack, + int level, + bool record_klasses_only, + KlassSubGraphInfo* subgraph_info, + oop orig) : _stack(stack), _found_oop_fields(), _level(level), _record_klasses_only(record_klasses_only), _subgraph_info(subgraph_info), _referencing_obj(orig) { + _is_java_lang_ref = AOTReferenceObjSupport::check_if_ref_obj(orig); } - void do_oop(narrowOop *p) { ReferentPusher::do_oop_work(p); } - void do_oop( oop *p) { ReferentPusher::do_oop_work(p); } + void do_oop(narrowOop *p) { OopFieldPusher::do_oop_work(p); } + void do_oop( oop *p) { OopFieldPusher::do_oop_work(p); } - ~ReferentPusher() { + ~OopFieldPusher() { while (_found_oop_fields.length() > 0) { // This produces the exact same traversal order as the previous version - // of ReferentPusher that recurses on the C stack -- a depth-first search, + // of OopFieldPusher that recurses on the C stack -- a depth-first search, // walking the oop fields in _referencing_obj by ascending field offsets. oop obj = _found_oop_fields.pop(); _stack->push(PendingOop(obj, _referencing_obj, _level + 1)); @@ -1399,14 +1403,18 @@ class HeapShared::ReferentPusher: public BasicOopIterateClosure { protected: template void do_oop_work(T *p) { - oop obj = RawAccess<>::oop_load(p); + int field_offset = pointer_delta_as_int((char*)p, cast_from_oop(_referencing_obj)); + oop obj = HeapAccess::oop_load_at(_referencing_obj, field_offset); if (!CompressedOops::is_null(obj)) { - size_t field_delta = pointer_delta(p, _referencing_obj, sizeof(char)); + if (_is_java_lang_ref && AOTReferenceObjSupport::skip_field(field_offset)) { + // Do not follow these fields. They will be cleared to null. + return; + } if (!_record_klasses_only && log_is_enabled(Debug, cds, heap)) { ResourceMark rm; - log_debug(cds, heap)("(%d) %s[%zu] ==> " PTR_FORMAT " size %zu %s", _level, - _referencing_obj->klass()->external_name(), field_delta, + log_debug(cds, heap)("(%d) %s[%d] ==> " PTR_FORMAT " size %zu %s", _level, + _referencing_obj->klass()->external_name(), field_offset, p2i(obj), obj->size() * HeapWordSize, obj->klass()->external_name()); if (log_is_enabled(Trace, cds, heap)) { LogTarget(Trace, cds, heap) log; @@ -1586,7 +1594,7 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap // Find all the oops that are referenced by orig_obj, push them onto the stack // so we can work on them next. ResourceMark rm; - ReferentPusher pusher(stack, level, record_klasses_only, subgraph_info, orig_obj); + OopFieldPusher pusher(stack, level, record_klasses_only, subgraph_info, orig_obj); orig_obj->oop_iterate(&pusher); } @@ -1613,7 +1621,7 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap // - No java.lang.Class instance (java mirror) can be included inside // an archived sub-graph. Mirror can only be the sub-graph entry object. // -// The Java heap object sub-graph archiving process (see ReferentPusher): +// The Java heap object sub-graph archiving process (see OopFieldPusher): // // 1) Java object sub-graph archiving starts from a given static field // within a Class instance (java mirror). If the static field is a diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 04c9ae9138123..f4e86aa5895a3 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -164,8 +164,8 @@ class HeapShared: AllStatic { static void count_allocation(size_t size); static void print_stats(); - static void debug_trace(); public: + static void debug_trace(); static unsigned oop_hash(oop const& p); static unsigned string_oop_hash(oop const& string) { return java_lang_String::hash_code(string); @@ -357,7 +357,7 @@ class HeapShared: AllStatic { int level() const { return _level; } }; - class ReferentPusher; + class OopFieldPusher; using PendingOopStack = GrowableArrayCHeap; static PendingOop _object_being_archived; diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index ef2a6dcb8e63c..5f7b496f8d265 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -28,6 +28,7 @@ #include "cds/aotClassLocation.hpp" #include "cds/aotConstantPoolResolver.hpp" #include "cds/aotLinkedClassBulkLoader.hpp" +#include "cds/aotReferenceObjSupport.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveHeapLoader.hpp" #include "cds/archiveHeapWriter.hpp" @@ -58,6 +59,7 @@ #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" +#include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" #include "gc/shared/gcVMOperations.hpp" #include "interpreter/bytecodeStream.hpp" @@ -490,6 +492,8 @@ void MetaspaceShared::serialize(SerializeClosure* soc) { soc->do_ptr((void**)&_archived_method_handle_intrinsics); LambdaFormInvokers::serialize(soc); + AdapterHandlerLibrary::serialize_shared_table_header(soc); + soc->do_tag(666); } @@ -608,6 +612,10 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*& // Write lambform lines into archive LambdaFormInvokers::dump_static_archive_invokers(); + if (AOTCodeCache::is_dumping_adapters()) { + AdapterHandlerLibrary::dump_aot_adapter_table(); + } + // Write the other data to the output array. DumpRegion* ro_region = ArchiveBuilder::current()->ro_region(); char* start = ro_region->top(); @@ -962,22 +970,14 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS #if INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_dumping_heap()) { ArchiveHeapWriter::init(); + if (CDSConfig::is_dumping_full_module_graph()) { ClassLoaderDataShared::ensure_module_entry_tables_exist(); HeapShared::reset_archived_object_states(CHECK); } - if (CDSConfig::is_dumping_method_handles()) { - // This assert means that the MethodType and MethodTypeForm tables won't be - // updated concurrently when we are saving their contents into a side table. - assert(CDSConfig::allow_only_single_java_thread(), "Required"); - - JavaValue result(T_VOID); - JavaCalls::call_static(&result, vmClasses::MethodType_klass(), - vmSymbols::createArchivedObjects(), - vmSymbols::void_method_signature(), - CHECK); - } + AOTReferenceObjSupport::initialize(CHECK); + AOTReferenceObjSupport::stabilize_cached_reference_objects(CHECK); if (CDSConfig::is_initing_classes_at_dump_time()) { // java.lang.Class::reflectionFactory cannot be archived yet. We set this field @@ -1005,6 +1005,17 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS VM_PopulateDumpSharedSpace op(builder); VMThread::execute(&op); + if (AOTCodeCache::is_on_for_dump() && CDSConfig::is_dumping_final_static_archive()) { + CDSConfig::enable_dumping_aot_code(); + { + builder.start_ac_region(); + // Write the contents to AOT code region and close AOTCodeCache before packing the region + AOTCodeCache::close(); + builder.end_ac_region(); + } + CDSConfig::disable_dumping_aot_code(); + } + if (!write_static_archive(&builder, op.map_info(), op.heap_info())) { THROW_MSG(vmSymbols::java_io_IOException(), "Encountered error while dumping"); } @@ -1345,7 +1356,7 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File if (prot_zone_size > 0) { assert(prot_zone_size >= os::vm_allocation_granularity(), "must be"); // not just page size! char* p = os::attempt_reserve_memory_at(mapped_base_address, prot_zone_size, - false, MemTag::mtClassShared); + mtClassShared); assert(p == mapped_base_address || p == nullptr, "must be"); if (p == nullptr) { log_debug(cds)("Failed to re-reserve protection zone"); @@ -1537,7 +1548,8 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma archive_space_rs = MemoryReserver::reserve((char*)base_address, archive_space_size, archive_space_alignment, - os::vm_page_size()); + os::vm_page_size(), + mtNone); if (archive_space_rs.is_reserved()) { assert(base_address == nullptr || (address)archive_space_rs.base() == base_address, "Sanity"); @@ -1605,11 +1617,13 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma archive_space_rs = MemoryReserver::reserve((char*)base_address, archive_space_size, archive_space_alignment, - os::vm_page_size()); + os::vm_page_size(), + mtNone); class_space_rs = MemoryReserver::reserve((char*)ccs_base, class_space_size, class_space_alignment, - os::vm_page_size()); + os::vm_page_size(), + mtNone); } if (!archive_space_rs.is_reserved() || !class_space_rs.is_reserved()) { release_reserved_spaces(total_space_rs, archive_space_rs, class_space_rs); @@ -1622,7 +1636,8 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma total_space_rs = MemoryReserver::reserve((char*) base_address, total_range_size, base_address_alignment, - os::vm_page_size()); + os::vm_page_size(), + mtNone); } else { // We did not manage to reserve at the preferred address, or were instructed to relocate. In that // case we reserve wherever possible, but the start address needs to be encodable as narrow Klass @@ -1772,6 +1787,7 @@ void MetaspaceShared::initialize_shared_spaces() { static_mapinfo->patch_heap_embedded_pointers(); ArchiveHeapLoader::finish_initialization(); Universe::load_archived_object_instances(); + AOTCodeCache::initialize(); // Close the mapinfo file static_mapinfo->close(); @@ -1823,6 +1839,11 @@ void MetaspaceShared::initialize_shared_spaces() { SystemDictionaryShared::print_shared_archive(tty, false/*dynamic*/); } + if (AOTCodeCache::is_on_for_use()) { + tty->print_cr("\n\nAOT Code"); + AOTCodeCache::print_on(tty); + } + // collect shared symbols and strings CountSharedSymbols cl; SymbolTable::shared_symbols_do(&cl); diff --git a/src/hotspot/share/cds/metaspaceShared.hpp b/src/hotspot/share/cds/metaspaceShared.hpp index e03994be1b993..875801cc0e668 100644 --- a/src/hotspot/share/cds/metaspaceShared.hpp +++ b/src/hotspot/share/cds/metaspaceShared.hpp @@ -67,8 +67,9 @@ class MetaspaceShared : AllStatic { ro = 1, // read-only shared space bm = 2, // relocation bitmaps (freed after file mapping is finished) hp = 3, // heap region + ac = 4, // aot code num_core_region = 2, // rw and ro - n_regions = 4 // total number of regions + n_regions = 5 // total number of regions }; static void preload_and_dump(TRAPS) NOT_CDS_RETURN; diff --git a/src/hotspot/share/ci/ciInstance.cpp b/src/hotspot/share/ci/ciInstance.cpp index ad456ba6726b8..9591298e3ab04 100644 --- a/src/hotspot/share/ci/ciInstance.cpp +++ b/src/hotspot/share/ci/ciInstance.cpp @@ -138,3 +138,9 @@ ciKlass* ciInstance::java_lang_Class_klass() { assert(java_lang_Class::as_Klass(get_oop()) != nullptr, "klass is null"); return CURRENT_ENV->get_metadata(java_lang_Class::as_Klass(get_oop()))->as_klass(); } + +char* ciInstance::java_lang_String_str(char* buf, size_t buflen) { + VM_ENTRY_MARK; + assert(get_oop()->is_a(vmClasses::String_klass()), "not a String"); + return java_lang_String::as_utf8_string(get_oop(), buf, buflen); +} diff --git a/src/hotspot/share/ci/ciInstance.hpp b/src/hotspot/share/ci/ciInstance.hpp index 3af07edcd9e89..1fb0998593088 100644 --- a/src/hotspot/share/ci/ciInstance.hpp +++ b/src/hotspot/share/ci/ciInstance.hpp @@ -67,6 +67,7 @@ class ciInstance : public ciObject { ciConstant field_value_by_offset(int field_offset); ciKlass* java_lang_Class_klass(); + char* java_lang_String_str(char* buf, size_t buflen); }; #endif // SHARE_CI_CIINSTANCE_HPP diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index 3b2670c3eb0bc..6b6f1b6f0a842 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -914,8 +914,14 @@ int ciMethod::scale_count(int count, float prof_factor) { method_life = counter_life; } if (counter_life > 0) { - count = (int)((double)count * prof_factor * method_life / counter_life + 0.5); - count = (count > 0) ? count : 1; + double count_d = (double)count * prof_factor * method_life / counter_life + 0.5; + if (count_d >= static_cast(INT_MAX)) { + // Clamp in case of overflowing int range. + count = INT_MAX; + } else { + count = int(count_d); + count = (count > 0) ? count : 1; + } } else { count = 1; } diff --git a/src/hotspot/share/ci/ciTypeFlow.cpp b/src/hotspot/share/ci/ciTypeFlow.cpp index 234b4611ea157..6df090a7ce534 100644 --- a/src/hotspot/share/ci/ciTypeFlow.cpp +++ b/src/hotspot/share/ci/ciTypeFlow.cpp @@ -2924,7 +2924,7 @@ void ciTypeFlow::flow_types() { // Continue flow analysis until fixed point reached - debug_only(int max_block = _next_pre_order;) + DEBUG_ONLY(int max_block = _next_pre_order;) while (!work_list_empty()) { Block* blk = work_list_next(); diff --git a/src/hotspot/share/ci/ciTypeFlow.hpp b/src/hotspot/share/ci/ciTypeFlow.hpp index 92db6253aa0ca..adfb85dc17f2b 100644 --- a/src/hotspot/share/ci/ciTypeFlow.hpp +++ b/src/hotspot/share/ci/ciTypeFlow.hpp @@ -253,7 +253,7 @@ class ciTypeFlow : public ArenaObj { set_type_at_tos(type); } void pop() { - debug_only(set_type_at_tos(bottom_type())); + DEBUG_ONLY(set_type_at_tos(bottom_type())); _stack_size--; } ciType* pop_value() { diff --git a/src/hotspot/share/ci/ciUtilities.inline.hpp b/src/hotspot/share/ci/ciUtilities.inline.hpp index 05e73d8ca6425..91f848368acb6 100644 --- a/src/hotspot/share/ci/ciUtilities.inline.hpp +++ b/src/hotspot/share/ci/ciUtilities.inline.hpp @@ -37,7 +37,7 @@ ThreadInVMfromNative __tiv(thread); \ HandleMarkCleaner __hm(thread); \ JavaThread* THREAD = thread; /* For exception macros. */ \ - debug_only(VMNativeEntryWrapper __vew;) + DEBUG_ONLY(VMNativeEntryWrapper __vew;) @@ -49,10 +49,10 @@ * [TODO] The NoHandleMark line does nothing but declare a function prototype \ * The NoHandkeMark constructor is NOT executed. If the ()'s are \ * removed, causes the NoHandleMark assert to trigger. \ - * debug_only(NoHandleMark __hm();) \ + * DEBUG_ONLY(NoHandleMark __hm();) \ */ \ JavaThread* THREAD = thread; /* For exception macros. */ \ - debug_only(VMNativeEntryWrapper __vew;) + DEBUG_ONLY(VMNativeEntryWrapper __vew;) #define EXCEPTION_CONTEXT \ diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 3e6246d4aeedc..757f0e72c8cce 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -176,7 +176,7 @@ void ClassFileParser::parse_constant_pool_entries(const ClassFileStream* const s const ClassFileStream cfs1 = *stream; const ClassFileStream* const cfs = &cfs1; - debug_only(const u1* const old_current = stream->current();) + DEBUG_ONLY(const u1* const old_current = stream->current();) // Used for batching symbol allocations. const char* names[SymbolTable::symbol_alloc_batch_size]; @@ -5243,7 +5243,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, // it's official set_klass(ik); - debug_only(ik->verify();) + DEBUG_ONLY(ik->verify();) } void ClassFileParser::update_class_name(Symbol* new_class_name) { diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index a7d6cc39614c7..d7660647fb280 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -1219,6 +1219,7 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, // must be valid since the class has been successfully parsed. const char* path = ClassLoader::uri_to_path(src); assert(path != nullptr, "sanity"); + bool found_invalid = false; AOTClassLocationConfig::dumptime_iterate([&] (AOTClassLocation* cl) { int i = cl->index(); // for index 0 and the stream->source() is the modules image or has the jrt: protocol. @@ -1242,10 +1243,15 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, classpath_index = i; } else { if (cl->from_boot_classpath()) { - // The class must be from boot loader append path which consists of - // -Xbootclasspath/a and jvmti appended entries. - assert(loader == nullptr, "sanity"); - classpath_index = i; + if (loader != nullptr) { + // Probably loaded by jdk/internal/loader/ClassLoaders$BootClassLoader. Don't archive + // such classes. + ik->set_shared_classpath_index(-1); + ik->set_shared_class_loader_type(ClassLoader::BOOT_LOADER); + found_invalid = true; + } else { + classpath_index = i; + } } } } else { @@ -1256,13 +1262,17 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, } } } - if (classpath_index >= 0) { + if (classpath_index >= 0 || found_invalid) { return false; // quit iterating } else { return true; // Keep iterating } }); + if (found_invalid) { + return; + } + // No path entry found for this class: most likely a shared class loaded by the // user defined classloader. if (classpath_index < 0 && !SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) { diff --git a/src/hotspot/share/classfile/compactHashtable.cpp b/src/hotspot/share/classfile/compactHashtable.cpp index 8d50e8136a3be..15ae5ba8013e0 100644 --- a/src/hotspot/share/classfile/compactHashtable.cpp +++ b/src/hotspot/share/classfile/compactHashtable.cpp @@ -226,7 +226,7 @@ HashtableTextDump::HashtableTextDump(const char* filename) : _fd(-1) { if (_fd < 0) { quit("Unable to open hashtable dump file", filename); } - _base = os::map_memory(_fd, filename, 0, nullptr, _size, true, false); + _base = os::map_memory(_fd, filename, 0, nullptr, _size, mtNone, true, false); if (_base == nullptr) { quit("Unable to map hashtable dump file", filename); } diff --git a/src/hotspot/share/classfile/compactHashtable.hpp b/src/hotspot/share/classfile/compactHashtable.hpp index 2985f0f9a1a43..47fbd06f629fb 100644 --- a/src/hotspot/share/classfile/compactHashtable.hpp +++ b/src/hotspot/share/classfile/compactHashtable.hpp @@ -282,7 +282,15 @@ class CompactHashtable : public SimpleCompactHashtable { } template - inline void iterate(ITER* iter) const { + inline void iterate(ITER* iter) const { iterate([&](V v) { iter->do_value(v); }); } + + template + inline void iterate(const Function& function) const { // lambda enabled API + iterate(const_cast(function)); + } + + template + inline void iterate(Function& function) const { // lambda enabled API for (u4 i = 0; i < _bucket_count; i++) { u4 bucket_info = _buckets[i]; u4 bucket_offset = BUCKET_OFFSET(bucket_info); @@ -290,11 +298,11 @@ class CompactHashtable : public SimpleCompactHashtable { u4* entry = _entries + bucket_offset; if (bucket_type == VALUE_ONLY_BUCKET_TYPE) { - iter->do_value(decode(entry[0])); + function(decode(entry[0])); } else { - u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]); + u4* entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]); while (entry < entry_max) { - iter->do_value(decode(entry[1])); + function(decode(entry[1])); entry += 2; } } diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index a2ad1dce5e4a8..c7cca9682fe84 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -22,6 +22,7 @@ * */ +#include "cds/aotReferenceObjSupport.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveHeapLoader.hpp" #include "cds/cdsConfig.hpp" @@ -4821,7 +4822,7 @@ bool java_lang_ClassLoader::isAncestor(oop loader, oop cl) { assert(is_instance(loader), "loader must be oop"); assert(cl == nullptr || is_instance(cl), "cl argument must be oop"); oop acl = loader; - debug_only(jint loop_count = 0); + DEBUG_ONLY(jint loop_count = 0); // This loop taken verbatim from ClassLoader.java: do { acl = parent(acl); @@ -5460,9 +5461,7 @@ bool JavaClasses::is_supported_for_archiving(oop obj) { } } - if (klass->is_subclass_of(vmClasses::Reference_klass())) { - // It's problematic to archive Reference objects. One of the reasons is that - // Reference::discovered may pull in unwanted objects (see JDK-8284336) + if (!AOTReferenceObjSupport::is_enabled() && klass->is_subclass_of(vmClasses::Reference_klass())) { return false; } diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index 3f2ff90ccab31..a506c4502a4e6 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -466,13 +466,9 @@ void Modules::define_module(Handle module, jboolean is_open, jstring version, if (EnableVectorSupport && EnableVectorReboxing && FLAG_IS_DEFAULT(EnableVectorAggressiveReboxing)) { FLAG_SET_DEFAULT(EnableVectorAggressiveReboxing, true); } - if (EnableVectorSupport && FLAG_IS_DEFAULT(UseVectorStubs)) { - FLAG_SET_DEFAULT(UseVectorStubs, true); - } log_info(compilation)("EnableVectorSupport=%s", (EnableVectorSupport ? "true" : "false")); log_info(compilation)("EnableVectorReboxing=%s", (EnableVectorReboxing ? "true" : "false")); log_info(compilation)("EnableVectorAggressiveReboxing=%s", (EnableVectorAggressiveReboxing ? "true" : "false")); - log_info(compilation)("UseVectorStubs=%s", (UseVectorStubs ? "true" : "false")); } #endif // COMPILER2_OR_JVMCI } @@ -580,13 +576,14 @@ class Modules::ArchivedProperty { }; Modules::ArchivedProperty Modules::_archived_props[] = { - // numbered + // non-numbered {"jdk.module.main", false}, - // non-numbered + // numbered {"jdk.module.addexports", true}, // --add-exports {"jdk.module.addmods", true}, // --add-modules {"jdk.module.enable.native.access", true}, // --enable-native-access + {"jdk.module.addopens", true}, // --add-opens }; constexpr size_t Modules::num_archived_props() { diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 68de40f1788c6..2959f35ef2c3e 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -1001,7 +1001,8 @@ class methodHandle; do_intrinsic(_VectorUnaryOp, jdk_internal_vm_vector_VectorSupport, vector_unary_op_name, vector_unary_op_sig, F_S) \ do_signature(vector_unary_op_sig, "(I" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;Ljava/lang/Class;" \ + "Ljava/lang/Class;" \ + "Ljava/lang/Class;" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ @@ -1022,6 +1023,29 @@ class methodHandle; "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;") \ do_name(vector_binary_op_name, "binaryOp") \ \ + do_intrinsic(_VectorUnaryLibOp, jdk_internal_vm_vector_VectorSupport, vector_unary_lib_op_name, vector_unary_lib_op_sig, F_S) \ + do_signature(vector_unary_lib_op_sig,"(J" \ + "Ljava/lang/Class;" \ + "Ljava/lang/Class;" \ + "I" \ + "Ljava/lang/String;" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ + "Ljdk/internal/vm/vector/VectorSupport$UnaryOperation;)" \ + "Ljdk/internal/vm/vector/VectorSupport$Vector;") \ + do_name(vector_unary_lib_op_name, "libraryUnaryOp") \ + \ + do_intrinsic(_VectorBinaryLibOp, jdk_internal_vm_vector_VectorSupport, vector_binary_lib_op_name, vector_binary_lib_op_sig, F_S) \ + do_signature(vector_binary_lib_op_sig,"(J" \ + "Ljava/lang/Class;" \ + "Ljava/lang/Class;" \ + "I" \ + "Ljava/lang/String;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \ + "Ljdk/internal/vm/vector/VectorSupport$BinaryOperation;)" \ + "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;") \ + do_name(vector_binary_lib_op_name, "libraryBinaryOp") \ + \ do_intrinsic(_VectorTernaryOp, jdk_internal_vm_vector_VectorSupport, vector_ternary_op_name, vector_ternary_op_sig, F_S) \ do_signature(vector_ternary_op_sig, "(I" \ "Ljava/lang/Class;" \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index e66066738ef38..f9f6bd0725432 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -719,7 +719,6 @@ class SerializeClosure; JFR_TEMPLATES(template) \ \ /* CDS */ \ - template(createArchivedObjects, "createArchivedObjects") \ template(dumpSharedArchive, "dumpSharedArchive") \ template(dumpSharedArchive_signature, "(ZLjava/lang/String;)Ljava/lang/String;") \ template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \ diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp new file mode 100644 index 0000000000000..e94c9ac866574 --- /dev/null +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -0,0 +1,1324 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "cds/aotCacheAccess.hpp" +#include "cds/cds_globals.hpp" +#include "cds/cdsConfig.hpp" +#include "cds/heapShared.hpp" +#include "cds/metaspaceShared.hpp" +#include "classfile/javaAssertions.hpp" +#include "code/aotCodeCache.hpp" +#include "code/codeCache.hpp" +#include "gc/shared/gcConfig.hpp" +#include "logging/logStream.hpp" +#include "memory/memoryReserver.hpp" +#include "runtime/flags/flagSetting.hpp" +#include "runtime/globals_extension.hpp" +#include "runtime/java.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/os.inline.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#ifdef COMPILER2 +#include "opto/runtime.hpp" +#endif +#if INCLUDE_G1GC +#include "gc/g1/g1BarrierSetRuntime.hpp" +#endif +#if INCLUDE_ZGC +#include "gc/z/zBarrierSetRuntime.hpp" +#endif + +#include +#include + +static void report_load_failure() { + if (AbortVMOnAOTCodeFailure) { + vm_exit_during_initialization("Unable to use AOT Code Cache.", nullptr); + } + log_info(aot, codecache, init)("Unable to use AOT Code Cache."); + AOTAdapterCaching = false; +} + +static void report_store_failure() { + if (AbortVMOnAOTCodeFailure) { + tty->print_cr("Unable to create AOT Code Cache."); + vm_abort(false); + } + log_info(aot, codecache, exit)("Unable to create AOT Code Cache."); + AOTAdapterCaching = false; +} + +bool AOTCodeCache::is_dumping_adapters() { + return AOTAdapterCaching && is_on_for_dump(); +} + +bool AOTCodeCache::is_using_adapters() { + return AOTAdapterCaching && is_on_for_use(); +} + +static uint _max_aot_code_size = 0; +uint AOTCodeCache::max_aot_code_size() { + return _max_aot_code_size; +} + +void AOTCodeCache::initialize() { + if (FLAG_IS_DEFAULT(AOTCache)) { + log_info(aot, codecache, init)("AOT Code Cache is not used: AOTCache is not specified."); + return; // AOTCache must be specified to dump and use AOT code + } + + 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); + is_dumping = true; + } else if (CDSConfig::is_using_archive() && CDSConfig::is_using_aot_linked_classes()) { + FLAG_SET_ERGO_IF_DEFAULT(AOTAdapterCaching, true); + is_using = true; + } else { + log_info(aot, codecache, init)("AOT Code Cache is not used: AOT Class Linking is not used."); + return; // nothing to do + } + if (!AOTAdapterCaching) { + return; // AOT code caching disabled on command line + } + _max_aot_code_size = AOTCodeMaxSize; + if (!FLAG_IS_DEFAULT(AOTCodeMaxSize)) { + if (!is_aligned(AOTCodeMaxSize, os::vm_allocation_granularity())) { + _max_aot_code_size = align_up(AOTCodeMaxSize, os::vm_allocation_granularity()); + log_debug(aot,codecache,init)("Max AOT Code Cache size is aligned up to %uK", (int)(max_aot_code_size()/K)); + } + } + size_t aot_code_size = is_using ? AOTCacheAccess::get_aot_code_region_size() : 0; + if (is_using && aot_code_size == 0) { + log_info(aot, codecache, init)("AOT Code Cache is empty"); + return; + } + if (!open_cache(is_dumping, is_using)) { + if (is_using) { + report_load_failure(); + } else { + report_store_failure(); + } + return; + } + if (is_dumping) { + FLAG_SET_DEFAULT(ForceUnreachable, true); + } + FLAG_SET_DEFAULT(DelayCompilerStubsGeneration, false); +} + +void AOTCodeCache::init2() { + if (!is_on()) { + return; + } + if (!verify_vm_config()) { + close(); + report_load_failure(); + } + // initialize the table of external routines so we can save + // generated code blobs that reference them + init_extrs_table(); +} + +AOTCodeCache* AOTCodeCache::_cache = nullptr; + +bool AOTCodeCache::open_cache(bool is_dumping, bool is_using) { + AOTCodeCache* cache = new AOTCodeCache(is_dumping, is_using); + if (cache->failed()) { + delete cache; + _cache = nullptr; + return false; + } + _cache = cache; + return true; +} + +void AOTCodeCache::close() { + if (is_on()) { + delete _cache; // Free memory + _cache = nullptr; + } +} + +#define DATA_ALIGNMENT HeapWordSize + +AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) : + _load_header(nullptr), + _load_buffer(nullptr), + _store_buffer(nullptr), + _C_store_buffer(nullptr), + _write_position(0), + _load_size(0), + _store_size(0), + _for_use(is_using), + _for_dump(is_dumping), + _closing(false), + _failed(false), + _lookup_failed(false), + _table(nullptr), + _load_entries(nullptr), + _search_entries(nullptr), + _store_entries(nullptr), + _C_strings_buf(nullptr), + _store_entries_cnt(0) +{ + // Read header at the begining of cache + if (_for_use) { + // Read cache + size_t load_size = AOTCacheAccess::get_aot_code_region_size(); + ReservedSpace rs = MemoryReserver::reserve(load_size, mtCode); + if (!rs.is_reserved()) { + log_warning(aot, codecache, init)("Failed to reserved %u bytes of memory for mapping AOT code region into AOT Code Cache", (uint)load_size); + set_failed(); + return; + } + if (!AOTCacheAccess::map_aot_code_region(rs)) { + log_warning(aot, codecache, init)("Failed to read/mmap cached code region into AOT Code Cache"); + set_failed(); + return; + } + + _load_size = (uint)load_size; + _load_buffer = (char*)rs.base(); + assert(is_aligned(_load_buffer, DATA_ALIGNMENT), "load_buffer is not aligned"); + log_debug(aot, codecache, init)("Mapped %u bytes at address " INTPTR_FORMAT " at AOT Code Cache", _load_size, p2i(_load_buffer)); + + _load_header = (Header*)addr(0); + if (!_load_header->verify_config(_load_size)) { + set_failed(); + return; + } + log_info (aot, codecache, init)("Loaded %u AOT code entries from AOT Code Cache", _load_header->entries_count()); + log_debug(aot, codecache, init)(" Adapters: total=%u", _load_header->adapters_count()); + log_debug(aot, codecache, init)(" All Blobs: total=%u", _load_header->blobs_count()); + log_debug(aot, codecache, init)(" AOT code cache size: %u bytes", _load_header->cache_size()); + + // Read strings + load_strings(); + } + if (_for_dump) { + _C_store_buffer = NEW_C_HEAP_ARRAY(char, max_aot_code_size() + DATA_ALIGNMENT, mtCode); + _store_buffer = align_up(_C_store_buffer, DATA_ALIGNMENT); + // Entries allocated at the end of buffer in reverse (as on stack). + _store_entries = (AOTCodeEntry*)align_up(_C_store_buffer + max_aot_code_size(), DATA_ALIGNMENT); + log_debug(aot, codecache, init)("Allocated store buffer at address " INTPTR_FORMAT " of size %u", p2i(_store_buffer), max_aot_code_size()); + } + _table = new AOTCodeAddressTable(); +} + +void AOTCodeCache::init_extrs_table() { + AOTCodeAddressTable* table = addr_table(); + if (table != nullptr) { + table->init_extrs(); + } +} + +void AOTCodeCache::init_shared_blobs_table() { + AOTCodeAddressTable* table = addr_table(); + if (table != nullptr) { + table->init_shared_blobs(); + } +} + +AOTCodeCache::~AOTCodeCache() { + if (_closing) { + return; // Already closed + } + // Stop any further access to cache. + _closing = true; + + MutexLocker ml(Compile_lock); + if (for_dump()) { // Finalize cache + finish_write(); + } + _load_buffer = nullptr; + if (_C_store_buffer != nullptr) { + FREE_C_HEAP_ARRAY(char, _C_store_buffer); + _C_store_buffer = nullptr; + _store_buffer = nullptr; + } + if (_table != nullptr) { + delete _table; + _table = nullptr; + } +} + +void AOTCodeCache::Config::record() { + _flags = 0; +#ifdef ASSERT + _flags |= debugVM; +#endif + if (UseCompressedOops) { + _flags |= compressedOops; + } + if (UseCompressedClassPointers) { + _flags |= compressedClassPointers; + } + if (UseTLAB) { + _flags |= useTLAB; + } + if (JavaAssertions::systemClassDefault()) { + _flags |= systemClassAssertions; + } + if (JavaAssertions::userClassDefault()) { + _flags |= userClassAssertions; + } + if (EnableContended) { + _flags |= enableContendedPadding; + } + if (RestrictContended) { + _flags |= restrictContendedPadding; + } + _compressedOopShift = CompressedOops::shift(); + _compressedKlassShift = CompressedKlassPointers::shift(); + _contendedPaddingWidth = ContendedPaddingWidth; + _objectAlignment = ObjectAlignmentInBytes; + _gc = (uint)Universe::heap()->kind(); +} + +bool AOTCodeCache::Config::verify() const { +#ifdef ASSERT + if ((_flags & debugVM) == 0) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created by product VM, it can't be used by debug VM"); + return false; + } +#else + if ((_flags & debugVM) != 0) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created by debug VM, it can't be used by product VM"); + return false; + } +#endif + + CollectedHeap::Name aot_gc = (CollectedHeap::Name)_gc; + if (aot_gc != Universe::heap()->kind()) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with different GC: %s vs current %s", GCConfig::hs_err_name(aot_gc), GCConfig::hs_err_name()); + return false; + } + + if (((_flags & compressedOops) != 0) != UseCompressedOops) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseCompressedOops = %s", UseCompressedOops ? "false" : "true"); + return false; + } + if (((_flags & compressedClassPointers) != 0) != UseCompressedClassPointers) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseCompressedClassPointers = %s", UseCompressedClassPointers ? "false" : "true"); + return false; + } + + if (((_flags & systemClassAssertions) != 0) != JavaAssertions::systemClassDefault()) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with JavaAssertions::systemClassDefault() = %s", JavaAssertions::systemClassDefault() ? "disabled" : "enabled"); + return false; + } + if (((_flags & userClassAssertions) != 0) != JavaAssertions::userClassDefault()) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with JavaAssertions::userClassDefault() = %s", JavaAssertions::userClassDefault() ? "disabled" : "enabled"); + return false; + } + + if (((_flags & enableContendedPadding) != 0) != EnableContended) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with EnableContended = %s", EnableContended ? "false" : "true"); + return false; + } + if (((_flags & restrictContendedPadding) != 0) != RestrictContended) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with RestrictContended = %s", RestrictContended ? "false" : "true"); + return false; + } + if (_compressedOopShift != (uint)CompressedOops::shift()) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with CompressedOops::shift() = %d vs current %d", _compressedOopShift, CompressedOops::shift()); + return false; + } + if (_compressedKlassShift != (uint)CompressedKlassPointers::shift()) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with CompressedKlassPointers::shift() = %d vs current %d", _compressedKlassShift, CompressedKlassPointers::shift()); + return false; + } + if (_contendedPaddingWidth != (uint)ContendedPaddingWidth) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with ContendedPaddingWidth = %d vs current %d", _contendedPaddingWidth, ContendedPaddingWidth); + return false; + } + if (_objectAlignment != (uint)ObjectAlignmentInBytes) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with ObjectAlignmentInBytes = %d vs current %d", _objectAlignment, ObjectAlignmentInBytes); + return false; + } + return true; +} + +bool AOTCodeCache::Header::verify_config(uint load_size) const { + if (_version != AOT_CODE_VERSION) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: different AOT Code version %d vs %d recorded in AOT Code header", AOT_CODE_VERSION, _version); + return false; + } + if (load_size < _cache_size) { + log_debug(aot, codecache, init)("AOT Code Cache disabled: AOT Code Cache size %d < %d recorded in AOT Code header", load_size, _cache_size); + return false; + } + return true; +} + +AOTCodeCache* AOTCodeCache::open_for_use() { + if (AOTCodeCache::is_on_for_use()) { + return AOTCodeCache::cache(); + } + return nullptr; +} + +AOTCodeCache* AOTCodeCache::open_for_dump() { + if (AOTCodeCache::is_on_for_dump()) { + AOTCodeCache* cache = AOTCodeCache::cache(); + cache->clear_lookup_failed(); // Reset bit + return cache; + } + return nullptr; +} + +void copy_bytes(const char* from, address to, uint size) { + assert(size > 0, "sanity"); + bool by_words = true; + if ((size > 2 * HeapWordSize) && (((intptr_t)from | (intptr_t)to) & (HeapWordSize - 1)) == 0) { + // Use wordwise copies if possible: + Copy::disjoint_words((HeapWord*)from, + (HeapWord*)to, + ((size_t)size + HeapWordSize-1) / HeapWordSize); + } else { + by_words = false; + Copy::conjoint_jbytes(from, to, (size_t)size); + } + log_trace(aot, codecache)("Copied %d bytes as %s from " INTPTR_FORMAT " to " INTPTR_FORMAT, size, (by_words ? "HeapWord" : "bytes"), p2i(from), p2i(to)); +} + +AOTCodeReader::AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry) { + _cache = cache; + _entry = entry; + _load_buffer = cache->cache_buffer(); + _read_position = 0; + _lookup_failed = false; +} + +void AOTCodeReader::set_read_position(uint pos) { + if (pos == _read_position) { + return; + } + assert(pos < _cache->load_size(), "offset:%d >= file size:%d", pos, _cache->load_size()); + _read_position = pos; +} + +bool AOTCodeCache::set_write_position(uint pos) { + if (pos == _write_position) { + return true; + } + if (_store_size < _write_position) { + _store_size = _write_position; // Adjust during write + } + assert(pos < _store_size, "offset:%d >= file size:%d", pos, _store_size); + _write_position = pos; + return true; +} + +static char align_buffer[256] = { 0 }; + +bool AOTCodeCache::align_write() { + // We are not executing code from cache - we copy it by bytes first. + // No need for big alignment (or at all). + uint padding = DATA_ALIGNMENT - (_write_position & (DATA_ALIGNMENT - 1)); + if (padding == DATA_ALIGNMENT) { + return true; + } + uint n = write_bytes((const void*)&align_buffer, padding); + if (n != padding) { + return false; + } + log_trace(aot, codecache)("Adjust write alignment in AOT Code Cache"); + return true; +} + +// Check to see if AOT code cache has required space to store "nbytes" of data +address AOTCodeCache::reserve_bytes(uint nbytes) { + assert(for_dump(), "Code Cache file is not created"); + uint new_position = _write_position + nbytes; + if (new_position >= (uint)((char*)_store_entries - _store_buffer)) { + log_warning(aot,codecache)("Failed to ensure %d bytes at offset %d in AOT Code Cache. Increase AOTCodeMaxSize.", + nbytes, _write_position); + set_failed(); + report_store_failure(); + return nullptr; + } + address buffer = (address)(_store_buffer + _write_position); + log_trace(aot, codecache)("Reserved %d bytes at offset %d in AOT Code Cache", nbytes, _write_position); + _write_position += nbytes; + if (_store_size < _write_position) { + _store_size = _write_position; + } + return buffer; +} + +uint AOTCodeCache::write_bytes(const void* buffer, uint nbytes) { + assert(for_dump(), "Code Cache file is not created"); + if (nbytes == 0) { + return 0; + } + uint new_position = _write_position + nbytes; + if (new_position >= (uint)((char*)_store_entries - _store_buffer)) { + log_warning(aot, codecache)("Failed to write %d bytes at offset %d to AOT Code Cache. Increase AOTCodeMaxSize.", + nbytes, _write_position); + set_failed(); + report_store_failure(); + return 0; + } + copy_bytes((const char* )buffer, (address)(_store_buffer + _write_position), nbytes); + log_trace(aot, codecache)("Wrote %d bytes at offset %d to AOT Code Cache", nbytes, _write_position); + _write_position += nbytes; + if (_store_size < _write_position) { + _store_size = _write_position; + } + return nbytes; +} + +void* AOTCodeEntry::operator new(size_t x, AOTCodeCache* cache) { + return (void*)(cache->add_entry()); +} + +static bool check_entry(AOTCodeEntry::Kind kind, uint id, AOTCodeEntry* entry) { + if (entry->kind() == kind) { + assert(entry->id() == id, "sanity"); + return true; // Found + } + return false; +} + +AOTCodeEntry* AOTCodeCache::find_entry(AOTCodeEntry::Kind kind, uint id) { + assert(_for_use, "sanity"); + uint count = _load_header->entries_count(); + if (_load_entries == nullptr) { + // Read it + _search_entries = (uint*)addr(_load_header->entries_offset()); // [id, index] + _load_entries = (AOTCodeEntry*)(_search_entries + 2 * count); + log_debug(aot, codecache, init)("Read %d entries table at offset %d from AOT Code Cache", count, _load_header->entries_offset()); + } + // Binary search + int l = 0; + int h = count - 1; + while (l <= h) { + int mid = (l + h) >> 1; + int ix = mid * 2; + uint is = _search_entries[ix]; + if (is == id) { + int index = _search_entries[ix + 1]; + AOTCodeEntry* entry = &(_load_entries[index]); + if (check_entry(kind, id, entry)) { + return entry; // Found + } + break; // Not found match + } else if (is < id) { + l = mid + 1; + } else { + h = mid - 1; + } + } + return nullptr; +} + +extern "C" { + static int uint_cmp(const void *i, const void *j) { + uint a = *(uint *)i; + uint b = *(uint *)j; + return a > b ? 1 : a < b ? -1 : 0; + } +} + +bool AOTCodeCache::finish_write() { + if (!align_write()) { + return false; + } + uint strings_offset = _write_position; + int strings_count = store_strings(); + if (strings_count < 0) { + return false; + } + if (!align_write()) { + return false; + } + uint strings_size = _write_position - strings_offset; + + uint entries_count = 0; // Number of entrant (useful) code entries + uint entries_offset = _write_position; + + uint store_count = _store_entries_cnt; + if (store_count > 0) { + uint header_size = (uint)align_up(sizeof(AOTCodeCache::Header), DATA_ALIGNMENT); + uint code_count = store_count; + uint search_count = code_count * 2; + uint search_size = search_count * sizeof(uint); + uint entries_size = (uint)align_up(code_count * sizeof(AOTCodeEntry), DATA_ALIGNMENT); // In bytes + // _write_position includes size of code and strings + uint code_alignment = code_count * DATA_ALIGNMENT; // We align_up code size when storing it. + uint total_size = header_size + _write_position + code_alignment + search_size + entries_size; + assert(total_size < max_aot_code_size(), "AOT Code size (" UINT32_FORMAT " bytes) is greater than AOTCodeMaxSize(" UINT32_FORMAT " bytes).", total_size, max_aot_code_size()); + + // Create ordered search table for entries [id, index]; + uint* search = NEW_C_HEAP_ARRAY(uint, search_count, mtCode); + // Allocate in AOT Cache buffer + char* buffer = (char *)AOTCacheAccess::allocate_aot_code_region(total_size + DATA_ALIGNMENT); + char* start = align_up(buffer, DATA_ALIGNMENT); + char* current = start + header_size; // Skip header + + AOTCodeEntry* entries_address = _store_entries; // Pointer to latest entry + uint adapters_count = 0; + uint blobs_count = 0; + uint max_size = 0; + // AOTCodeEntry entries were allocated in reverse in store buffer. + // Process them in reverse order to cache first code first. + for (int i = store_count - 1; i >= 0; i--) { + entries_address[i].set_next(nullptr); // clear pointers before storing data + uint size = align_up(entries_address[i].size(), DATA_ALIGNMENT); + if (size > max_size) { + max_size = size; + } + copy_bytes((_store_buffer + entries_address[i].offset()), (address)current, size); + entries_address[i].set_offset(current - start); // New offset + current += size; + uint n = write_bytes(&(entries_address[i]), sizeof(AOTCodeEntry)); + if (n != sizeof(AOTCodeEntry)) { + FREE_C_HEAP_ARRAY(uint, search); + return false; + } + search[entries_count*2 + 0] = entries_address[i].id(); + search[entries_count*2 + 1] = entries_count; + entries_count++; + AOTCodeEntry::Kind kind = entries_address[i].kind(); + if (kind == AOTCodeEntry::Adapter) { + adapters_count++; + } else if (kind == AOTCodeEntry::Blob) { + blobs_count++; + } + } + if (entries_count == 0) { + log_info(aot, codecache, exit)("AOT Code Cache was not created: no entires"); + FREE_C_HEAP_ARRAY(uint, search); + return true; // Nothing to write + } + assert(entries_count <= store_count, "%d > %d", entries_count, store_count); + // Write strings + if (strings_count > 0) { + copy_bytes((_store_buffer + strings_offset), (address)current, strings_size); + strings_offset = (current - start); // New offset + current += strings_size; + } + + uint new_entries_offset = (current - start); // New offset + // Sort and store search table + qsort(search, entries_count, 2*sizeof(uint), uint_cmp); + search_size = 2 * entries_count * sizeof(uint); + copy_bytes((const char*)search, (address)current, search_size); + FREE_C_HEAP_ARRAY(uint, search); + current += search_size; + + // Write entries + entries_size = entries_count * sizeof(AOTCodeEntry); // New size + copy_bytes((_store_buffer + entries_offset), (address)current, entries_size); + current += entries_size; + uint size = (current - start); + assert(size <= total_size, "%d > %d", size , total_size); + + log_debug(aot, codecache, exit)(" Adapters: total=%u", adapters_count); + log_debug(aot, codecache, exit)(" All Blobs: total=%u", blobs_count); + log_debug(aot, codecache, exit)(" AOT code cache size: %u bytes, max entry's size: %u bytes", size, max_size); + + // Finalize header + AOTCodeCache::Header* header = (AOTCodeCache::Header*)start; + header->init(size, (uint)strings_count, strings_offset, + entries_count, new_entries_offset, + adapters_count, blobs_count); + + log_info(aot, codecache, exit)("Wrote %d AOT code entries to AOT Code Cache", entries_count); + } + return true; +} + +//------------------Store/Load AOT code ---------------------- + +bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind, uint id, const char* name, int entry_offset_count, int* entry_offsets) { + AOTCodeCache* cache = open_for_dump(); + if (cache == nullptr) { + return false; + } + assert(AOTCodeEntry::is_valid_entry_kind(entry_kind), "invalid entry_kind %d", entry_kind); + + if ((entry_kind == AOTCodeEntry::Adapter) && !AOTAdapterCaching) { + return false; + } + log_debug(aot, codecache, stubs)("Writing blob '%s' to AOT Code Cache", name); + +#ifdef ASSERT + LogStreamHandle(Trace, aot, codecache, stubs) log; + if (log.is_enabled()) { + FlagSetting fs(PrintRelocations, true); + blob.print_on(&log); + } +#endif + // we need to take a lock to prevent race between compiler threads generating AOT code + // and the main thread generating adapter + MutexLocker ml(Compile_lock); + if (!cache->align_write()) { + return false; + } + uint entry_position = cache->_write_position; + + // Write name + uint name_offset = cache->_write_position - entry_position; + uint name_size = (uint)strlen(name) + 1; // Includes '/0' + uint n = cache->write_bytes(name, name_size); + if (n != name_size) { + return false; + } + + // Write CodeBlob + if (!cache->align_write()) { + return false; + } + uint blob_offset = cache->_write_position - entry_position; + address archive_buffer = cache->reserve_bytes(blob.size()); + if (archive_buffer == nullptr) { + return false; + } + CodeBlob::archive_blob(&blob, archive_buffer); + + uint reloc_data_size = blob.relocation_size(); + n = cache->write_bytes((address)blob.relocation_begin(), reloc_data_size); + if (n != reloc_data_size) { + return false; + } + + bool has_oop_maps = false; + if (blob.oop_maps() != nullptr) { + if (!cache->write_oop_map_set(blob)) { + return false; + } + has_oop_maps = true; + } + + if (!cache->write_relocations(blob)) { + return false; + } + + // Write entries offsets + n = cache->write_bytes(&entry_offset_count, sizeof(int)); + if (n != sizeof(int)) { + return false; + } + for (int i = 0; i < entry_offset_count; i++) { + uint32_t off = (uint32_t)entry_offsets[i]; + n = cache->write_bytes(&off, sizeof(uint32_t)); + if (n != sizeof(uint32_t)) { + return false; + } + } + uint entry_size = cache->_write_position - entry_position; + AOTCodeEntry* entry = new(cache) AOTCodeEntry(entry_kind, id, + entry_position, entry_size, name_offset, name_size, + blob_offset, has_oop_maps, blob.content_begin()); + log_debug(aot, codecache, stubs)("Wrote code blob '%s(id=%d)' to AOT Code Cache", name, id); + return true; +} + +CodeBlob* AOTCodeCache::load_code_blob(AOTCodeEntry::Kind entry_kind, uint id, const char* name, int entry_offset_count, int* entry_offsets) { + AOTCodeCache* cache = open_for_use(); + if (cache == nullptr) { + return nullptr; + } + assert(AOTCodeEntry::is_valid_entry_kind(entry_kind), "invalid entry_kind %d", entry_kind); + + if ((entry_kind == AOTCodeEntry::Adapter) && !AOTAdapterCaching) { + return nullptr; + } + log_debug(aot, codecache, stubs)("Reading blob '%s' from AOT Code Cache", name); + + AOTCodeEntry* entry = cache->find_entry(entry_kind, id); + if (entry == nullptr) { + return nullptr; + } + AOTCodeReader reader(cache, entry); + return reader.compile_code_blob(name, entry_offset_count, entry_offsets); +} + +CodeBlob* AOTCodeReader::compile_code_blob(const char* name, int entry_offset_count, int* entry_offsets) { + uint entry_position = _entry->offset(); + + // Read name + uint name_offset = entry_position + _entry->name_offset(); + uint name_size = _entry->name_size(); // Includes '/0' + const char* stored_name = addr(name_offset); + + if (strncmp(stored_name, name, (name_size - 1)) != 0) { + log_warning(aot, codecache, stubs)("Saved blob's name '%s' is different from the expected name '%s'", + stored_name, name); + ((AOTCodeCache*)_cache)->set_failed(); + report_load_failure(); + return nullptr; + } + + // Read archived code blob + uint offset = entry_position + _entry->blob_offset(); + CodeBlob* archived_blob = (CodeBlob*)addr(offset); + offset += archived_blob->size(); + + address reloc_data = (address)addr(offset); + offset += archived_blob->relocation_size(); + set_read_position(offset); + + ImmutableOopMapSet* oop_maps = nullptr; + if (_entry->has_oop_maps()) { + oop_maps = read_oop_map_set(); + } + + CodeBlob* code_blob = CodeBlob::create(archived_blob, stored_name, reloc_data, oop_maps); + if (code_blob == nullptr) { // no space left in CodeCache + return nullptr; + } + + fix_relocations(code_blob); + + // Read entries offsets + offset = read_position(); + int stored_count = *(int*)addr(offset); + assert(stored_count == entry_offset_count, "entry offset count mismatch, count in AOT code cache=%d, expected=%d", stored_count, entry_offset_count); + offset += sizeof(int); + set_read_position(offset); + for (int i = 0; i < stored_count; i++) { + uint32_t off = *(uint32_t*)addr(offset); + offset += sizeof(uint32_t); + const char* entry_name = (_entry->kind() == AOTCodeEntry::Adapter) ? AdapterHandlerEntry::entry_name(i) : ""; + log_trace(aot, codecache, stubs)("Reading adapter '%s:%s' (0x%x) offset: 0x%x from AOT Code Cache", + stored_name, entry_name, _entry->id(), off); + entry_offsets[i] = off; + } + + log_debug(aot, codecache, stubs)("Read blob '%s' from AOT Code Cache", name); +#ifdef ASSERT + LogStreamHandle(Trace, aot, codecache, stubs) log; + if (log.is_enabled()) { + FlagSetting fs(PrintRelocations, true); + code_blob->print_on(&log); + } +#endif + return code_blob; +} + +// ------------ process code and data -------------- + +bool AOTCodeCache::write_relocations(CodeBlob& code_blob) { + GrowableArray reloc_data; + RelocIterator iter(&code_blob); + LogStreamHandle(Trace, aot, codecache, reloc) log; + while (iter.next()) { + int idx = reloc_data.append(0); // default value + switch (iter.type()) { + case relocInfo::none: + break; + case relocInfo::runtime_call_type: { + // Record offset of runtime destination + CallRelocation* r = (CallRelocation*)iter.reloc(); + address dest = r->destination(); + if (dest == r->addr()) { // possible call via trampoline on Aarch64 + dest = (address)-1; // do nothing in this case when loading this relocation + } + reloc_data.at_put(idx, _table->id_for_address(dest, iter, &code_blob)); + break; + } + case relocInfo::runtime_call_w_cp_type: + fatal("runtime_call_w_cp_type unimplemented"); + break; + case relocInfo::external_word_type: { + // Record offset of runtime target + address target = ((external_word_Relocation*)iter.reloc())->target(); + reloc_data.at_put(idx, _table->id_for_address(target, iter, &code_blob)); + break; + } + case relocInfo::internal_word_type: + break; + case relocInfo::section_word_type: + break; + default: + fatal("relocation %d unimplemented", (int)iter.type()); + break; + } + if (log.is_enabled()) { + iter.print_current_on(&log); + } + } + + // Write additional relocation data: uint per relocation + // Write the count first + int count = reloc_data.length(); + write_bytes(&count, sizeof(int)); + for (GrowableArrayIterator iter = reloc_data.begin(); + iter != reloc_data.end(); ++iter) { + uint value = *iter; + int n = write_bytes(&value, sizeof(uint)); + if (n != sizeof(uint)) { + return false; + } + } + return true; +} + +void AOTCodeReader::fix_relocations(CodeBlob* code_blob) { + LogStreamHandle(Trace, aot, reloc) log; + uint offset = read_position(); + int count = *(int*)addr(offset); + offset += sizeof(int); + if (log.is_enabled()) { + log.print_cr("======== extra relocations count=%d", count); + } + uint* reloc_data = (uint*)addr(offset); + offset += (count * sizeof(uint)); + set_read_position(offset); + + RelocIterator iter(code_blob); + int j = 0; + while (iter.next()) { + switch (iter.type()) { + case relocInfo::none: + break; + case relocInfo::runtime_call_type: { + address dest = _cache->address_for_id(reloc_data[j]); + if (dest != (address)-1) { + ((CallRelocation*)iter.reloc())->set_destination(dest); + } + break; + } + case relocInfo::runtime_call_w_cp_type: + fatal("runtime_call_w_cp_type unimplemented"); + break; + case relocInfo::external_word_type: { + address target = _cache->address_for_id(reloc_data[j]); + // Add external address to global table + int index = ExternalsRecorder::find_index(target); + // Update index in relocation + Relocation::add_jint(iter.data(), index); + external_word_Relocation* reloc = (external_word_Relocation*)iter.reloc(); + assert(reloc->target() == target, "sanity"); + reloc->set_value(target); // Patch address in the code + break; + } + case relocInfo::internal_word_type: { + internal_word_Relocation* r = (internal_word_Relocation*)iter.reloc(); + r->fix_relocation_after_aot_load(aot_code_entry()->dumptime_content_start_addr(), code_blob->content_begin()); + break; + } + case relocInfo::section_word_type: { + section_word_Relocation* r = (section_word_Relocation*)iter.reloc(); + r->fix_relocation_after_aot_load(aot_code_entry()->dumptime_content_start_addr(), code_blob->content_begin()); + break; + } + default: + fatal("relocation %d unimplemented", (int)iter.type()); + break; + } + if (log.is_enabled()) { + iter.print_current_on(&log); + } + j++; + } + assert(j == count, "sanity"); +} + +bool AOTCodeCache::write_oop_map_set(CodeBlob& cb) { + ImmutableOopMapSet* oopmaps = cb.oop_maps(); + int oopmaps_size = oopmaps->nr_of_bytes(); + if (!write_bytes(&oopmaps_size, sizeof(int))) { + return false; + } + uint n = write_bytes(oopmaps, oopmaps->nr_of_bytes()); + if (n != (uint)oopmaps->nr_of_bytes()) { + return false; + } + return true; +} + +ImmutableOopMapSet* AOTCodeReader::read_oop_map_set() { + uint offset = read_position(); + int size = *(int *)addr(offset); + offset += sizeof(int); + ImmutableOopMapSet* oopmaps = (ImmutableOopMapSet *)addr(offset); + offset += size; + set_read_position(offset); + return oopmaps; +} + +//======================= AOTCodeAddressTable =============== + +// address table ids for generated routines, external addresses and C +// string addresses are partitioned into positive integer ranges +// defined by the following positive base and max values +// i.e. [_extrs_base, _extrs_base + _extrs_max -1], +// [_blobs_base, _blobs_base + _blobs_max -1], +// ... +// [_c_str_base, _c_str_base + _c_str_max -1], +#define _extrs_max 10 +#define _blobs_max 10 +#define _all_max 20 + +#define _extrs_base 0 +#define _blobs_base (_extrs_base + _extrs_max) +#define _blobs_end (_blobs_base + _blobs_max) + +#if (_blobs_end > _all_max) +#error AOTCodeAddress table ranges need adjusting +#endif + +#define SET_ADDRESS(type, addr) \ + { \ + type##_addr[type##_length++] = (address) (addr); \ + assert(type##_length <= type##_max, "increase size"); \ + } + +static bool initializing_extrs = false; + +void AOTCodeAddressTable::init_extrs() { + if (_extrs_complete || initializing_extrs) return; // Done already + initializing_extrs = true; + _extrs_addr = NEW_C_HEAP_ARRAY(address, _extrs_max, mtCode); + + _extrs_length = 0; + + // Recored addresses of VM runtime methods + SET_ADDRESS(_extrs, SharedRuntime::fixup_callers_callsite); + SET_ADDRESS(_extrs, SharedRuntime::handle_wrong_method); + SET_ADDRESS(_extrs, SharedRuntime::handle_wrong_method_abstract); + SET_ADDRESS(_extrs, SharedRuntime::handle_wrong_method_ic_miss); +#if INCLUDE_G1GC + SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_post_entry); + SET_ADDRESS(_extrs, G1BarrierSetRuntime::write_ref_field_pre_entry); +#endif +#if INCLUDE_ZGC + SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr()); +#if defined(AMD64) + SET_ADDRESS(_extrs, &ZPointerLoadShift); +#endif +#endif +#ifdef COMPILER2 + SET_ADDRESS(_extrs, OptoRuntime::handle_exception_C); +#endif +#ifndef ZERO +#if defined(AMD64) || defined(AARCH64) || defined(RISCV64) + SET_ADDRESS(_extrs, MacroAssembler::debug64); +#endif +#endif // ZERO + + _extrs_complete = true; + log_debug(aot, codecache, init)("External addresses recorded"); +} + +static bool initializing_shared_blobs = false; + +void AOTCodeAddressTable::init_shared_blobs() { + if (_complete || initializing_shared_blobs) return; // Done already + initializing_shared_blobs = true; + _blobs_addr = NEW_C_HEAP_ARRAY(address, _blobs_max, mtCode); + + _blobs_length = 0; // for shared blobs + + // Recored addresses of generated code blobs + SET_ADDRESS(_blobs, SharedRuntime::get_handle_wrong_method_stub()); + SET_ADDRESS(_blobs, SharedRuntime::get_ic_miss_stub()); + + _shared_blobs_complete = true; + log_debug(aot, codecache, init)("Early shared blobs recorded"); + _complete = true; +} + +#undef SET_ADDRESS + +AOTCodeAddressTable::~AOTCodeAddressTable() { + if (_extrs_addr != nullptr) { + FREE_C_HEAP_ARRAY(address, _extrs_addr); + } + if (_blobs_addr != nullptr) { + FREE_C_HEAP_ARRAY(address, _blobs_addr); + } +} + +#ifdef PRODUCT +#define MAX_STR_COUNT 200 +#else +#define MAX_STR_COUNT 500 +#endif +#define _c_str_max MAX_STR_COUNT +#define _c_str_base _all_max + +static const char* _C_strings_in[MAX_STR_COUNT] = {nullptr}; // Incoming strings +static const char* _C_strings[MAX_STR_COUNT] = {nullptr}; // Our duplicates +static int _C_strings_count = 0; +static int _C_strings_s[MAX_STR_COUNT] = {0}; +static int _C_strings_id[MAX_STR_COUNT] = {0}; +static int _C_strings_used = 0; + +void AOTCodeCache::load_strings() { + uint strings_count = _load_header->strings_count(); + if (strings_count == 0) { + return; + } + uint strings_offset = _load_header->strings_offset(); + uint* string_lengths = (uint*)addr(strings_offset); + strings_offset += (strings_count * sizeof(uint)); + uint strings_size = _load_header->entries_offset() - strings_offset; + // We have to keep cached strings longer than _cache buffer + // because they are refernced from compiled code which may + // still be executed on VM exit after _cache is freed. + char* p = NEW_C_HEAP_ARRAY(char, strings_size+1, mtCode); + memcpy(p, addr(strings_offset), strings_size); + _C_strings_buf = p; + assert(strings_count <= MAX_STR_COUNT, "sanity"); + for (uint i = 0; i < strings_count; i++) { + _C_strings[i] = p; + uint len = string_lengths[i]; + _C_strings_s[i] = i; + _C_strings_id[i] = i; + p += len; + } + assert((uint)(p - _C_strings_buf) <= strings_size, "(" INTPTR_FORMAT " - " INTPTR_FORMAT ") = %d > %d ", p2i(p), p2i(_C_strings_buf), (uint)(p - _C_strings_buf), strings_size); + _C_strings_count = strings_count; + _C_strings_used = strings_count; + log_debug(aot, codecache, init)(" Loaded %d C strings of total length %d at offset %d from AOT Code Cache", _C_strings_count, strings_size, strings_offset); +} + +int AOTCodeCache::store_strings() { + if (_C_strings_used > 0) { + uint offset = _write_position; + uint length = 0; + uint* lengths = (uint *)reserve_bytes(sizeof(uint) * _C_strings_used); + if (lengths == nullptr) { + return -1; + } + for (int i = 0; i < _C_strings_used; i++) { + const char* str = _C_strings[_C_strings_s[i]]; + uint len = (uint)strlen(str) + 1; + length += len; + assert(len < 1000, "big string: %s", str); + lengths[i] = len; + uint n = write_bytes(str, len); + if (n != len) { + return -1; + } + } + log_debug(aot, codecache, exit)(" Wrote %d C strings of total length %d at offset %d to AOT Code Cache", + _C_strings_used, length, offset); + } + return _C_strings_used; +} + +const char* AOTCodeCache::add_C_string(const char* str) { + if (is_on_for_dump() && str != nullptr) { + return _cache->_table->add_C_string(str); + } + return str; +} + +const char* AOTCodeAddressTable::add_C_string(const char* str) { + if (_extrs_complete) { + LogStreamHandle(Trace, aot, codecache, stringtable) log; // ctor outside lock + MutexLocker ml(AOTCodeCStrings_lock, Mutex::_no_safepoint_check_flag); + // Check previous strings address + for (int i = 0; i < _C_strings_count; i++) { + if (_C_strings_in[i] == str) { + return _C_strings[i]; // Found previous one - return our duplicate + } else if (strcmp(_C_strings[i], str) == 0) { + return _C_strings[i]; + } + } + // Add new one + if (_C_strings_count < MAX_STR_COUNT) { + // Passed in string can be freed and used space become inaccessible. + // Keep original address but duplicate string for future compare. + _C_strings_id[_C_strings_count] = -1; // Init + _C_strings_in[_C_strings_count] = str; + const char* dup = os::strdup(str); + _C_strings[_C_strings_count++] = dup; + if (log.is_enabled()) { + log.print_cr("add_C_string: [%d] " INTPTR_FORMAT " '%s'", _C_strings_count, p2i(dup), dup); + } + return dup; + } else { + fatal("Number of C strings >= MAX_STR_COUNT"); + } + } + return str; +} + +int AOTCodeAddressTable::id_for_C_string(address str) { + if (str == nullptr) { + return -1; + } + MutexLocker ml(AOTCodeCStrings_lock, Mutex::_no_safepoint_check_flag); + for (int i = 0; i < _C_strings_count; i++) { + if (_C_strings[i] == (const char*)str) { // found + int id = _C_strings_id[i]; + if (id >= 0) { + assert(id < _C_strings_used, "%d >= %d", id , _C_strings_used); + return id; // Found recorded + } + // Not found in recorded, add new + id = _C_strings_used++; + _C_strings_s[id] = i; + _C_strings_id[i] = id; + return id; + } + } + return -1; +} + +address AOTCodeAddressTable::address_for_C_string(int idx) { + assert(idx < _C_strings_count, "sanity"); + return (address)_C_strings[idx]; +} + +static int search_address(address addr, address* table, uint length) { + for (int i = 0; i < (int)length; i++) { + if (table[i] == addr) { + return i; + } + } + return -1; +} + +address AOTCodeAddressTable::address_for_id(int idx) { + if (!_extrs_complete) { + fatal("AOT Code Cache VM runtime addresses table is not complete"); + } + if (idx == -1) { + return (address)-1; + } + uint id = (uint)idx; + // special case for symbols based relative to os::init + if (id > (_c_str_base + _c_str_max)) { + return (address)os::init + idx; + } + if (idx < 0) { + fatal("Incorrect id %d for AOT Code Cache addresses table", id); + } + // no need to compare unsigned id against 0 + if (/* id >= _extrs_base && */ id < _extrs_length) { + return _extrs_addr[id - _extrs_base]; + } + if (id >= _blobs_base && id < _blobs_base + _blobs_length) { + return _blobs_addr[id - _blobs_base]; + } + if (id >= _c_str_base && id < (_c_str_base + (uint)_C_strings_count)) { + return address_for_C_string(id - _c_str_base); + } + fatal("Incorrect id %d for AOT Code Cache addresses table", id); + return nullptr; +} + +int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeBlob* code_blob) { + if (!_extrs_complete) { + fatal("AOT Code Cache VM runtime addresses table is not complete"); + } + int id = -1; + if (addr == (address)-1) { // Static call stub has jump to itself + return id; + } + // Seach for C string + id = id_for_C_string(addr); + if (id >= 0) { + return id + _c_str_base; + } + if (StubRoutines::contains(addr)) { + // Search in stubs + StubCodeDesc* desc = StubCodeDesc::desc_for(addr); + if (desc == nullptr) { + desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset); + } + const char* sub_name = (desc != nullptr) ? desc->name() : ""; + fatal("Address " INTPTR_FORMAT " for Stub:%s is missing in AOT Code Cache addresses table", p2i(addr), sub_name); + } else { + CodeBlob* cb = CodeCache::find_blob(addr); + if (cb != nullptr) { + // Search in code blobs + int id_base = _blobs_base; + id = search_address(addr, _blobs_addr, _blobs_length); + if (id < 0) { + fatal("Address " INTPTR_FORMAT " for Blob:%s is missing in AOT Code Cache addresses table", p2i(addr), cb->name()); + } else { + return id_base + id; + } + } else { + // Search in runtime functions + id = search_address(addr, _extrs_addr, _extrs_length); + if (id < 0) { + ResourceMark rm; + const int buflen = 1024; + char* func_name = NEW_RESOURCE_ARRAY(char, buflen); + int offset = 0; + if (os::dll_address_to_function_name(addr, func_name, buflen, &offset)) { + if (offset > 0) { + // Could be address of C string + uint dist = (uint)pointer_delta(addr, (address)os::init, 1); + log_debug(aot, codecache)("Address " INTPTR_FORMAT " (offset %d) for runtime target '%s' is missing in AOT Code Cache addresses table", + p2i(addr), dist, (const char*)addr); + assert(dist > (uint)(_all_max + MAX_STR_COUNT), "change encoding of distance"); + return dist; + } + reloc.print_current_on(tty); + code_blob->print_on(tty); + code_blob->print_code_on(tty); + fatal("Address " INTPTR_FORMAT " for runtime target '%s+%d' is missing in AOT Code Cache addresses table", p2i(addr), func_name, offset); + } else { + reloc.print_current_on(tty); + code_blob->print_on(tty); + code_blob->print_code_on(tty); + os::find(addr, tty); + fatal("Address " INTPTR_FORMAT " for /('%s') is missing in AOT Code Cache addresses table", p2i(addr), (const char*)addr); + } + } else { + return _extrs_base + id; + } + } + } + return id; +} + +void AOTCodeCache::print_on(outputStream* st) { + AOTCodeCache* cache = open_for_use(); + if (cache != nullptr) { + uint count = cache->_load_header->entries_count(); + uint* search_entries = (uint*)cache->addr(cache->_load_header->entries_offset()); // [id, index] + AOTCodeEntry* load_entries = (AOTCodeEntry*)(search_entries + 2 * count); + + for (uint i = 0; i < count; i++) { + // Use search_entries[] to order ouput + int index = search_entries[2*i + 1]; + AOTCodeEntry* entry = &(load_entries[index]); + + uint entry_position = entry->offset(); + uint name_offset = entry->name_offset() + entry_position; + const char* saved_name = cache->addr(name_offset); + + st->print_cr("%4u: entry_idx:%4u Kind:%u Id:%u size=%u '%s'", + i, index, entry->kind(), entry->id(), entry->size(), saved_name); + } + } else { + st->print_cr("failed to map code cache"); + } +} + diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp new file mode 100644 index 0000000000000..d5c938e3b8b12 --- /dev/null +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_CODE_AOTCODECACHE_HPP +#define SHARE_CODE_AOTCODECACHE_HPP + +/* + * AOT Code Cache collects code from Code Cache and corresponding metadata + * during application training run. + * In following "production" runs this code and data can be loaded into + * Code Cache skipping its generation. + */ + +class CodeBuffer; +class RelocIterator; +class AOTCodeCache; +class AdapterBlob; +class ExceptionBlob; +class ImmutableOopMapSet; + +enum class vmIntrinsicID : int; +enum CompLevel : signed char; + +// Descriptor of AOT Code Cache's entry +class AOTCodeEntry { +public: + enum Kind { + None = 0, + Adapter = 1, + Blob = 2 + }; + +private: + AOTCodeEntry* _next; + Kind _kind; + uint _id; // Adapter's id, vmIntrinsic::ID for stub or name's hash for nmethod + uint _offset; // Offset to entry + uint _size; // Entry size + uint _name_offset; // Code blob name + uint _name_size; + uint _blob_offset; // Start of code in cache + bool _has_oop_maps; + address _dumptime_content_start_addr; // CodeBlob::content_begin() at dump time; used for applying relocations + +public: + AOTCodeEntry(Kind kind, uint id, + uint offset, uint size, + uint name_offset, uint name_size, + uint blob_offset, bool has_oop_maps, + address dumptime_content_start_addr) { + _next = nullptr; + _kind = kind; + _id = id; + _offset = offset; + _size = size; + _name_offset = name_offset; + _name_size = name_size; + _blob_offset = blob_offset; + _has_oop_maps = has_oop_maps; + _dumptime_content_start_addr = dumptime_content_start_addr; + } + void* operator new(size_t x, AOTCodeCache* cache); + // Delete is a NOP + void operator delete( void *ptr ) {} + + AOTCodeEntry* next() const { return _next; } + void set_next(AOTCodeEntry* next) { _next = next; } + + Kind kind() const { return _kind; } + uint id() const { return _id; } + + uint offset() const { return _offset; } + void set_offset(uint off) { _offset = off; } + + uint size() const { return _size; } + uint name_offset() const { return _name_offset; } + uint name_size() const { return _name_size; } + uint blob_offset() const { return _blob_offset; } + bool has_oop_maps() const { return _has_oop_maps; } + address dumptime_content_start_addr() const { return _dumptime_content_start_addr; } + + static bool is_valid_entry_kind(Kind kind) { return kind == Adapter || kind == Blob; } +}; + +// Addresses of stubs, blobs and runtime finctions called from compiled code. +class AOTCodeAddressTable : public CHeapObj { +private: + address* _extrs_addr; + address* _blobs_addr; + uint _extrs_length; + uint _blobs_length; + + bool _extrs_complete; + bool _shared_blobs_complete; + bool _complete; + +public: + AOTCodeAddressTable() : + _extrs_addr(nullptr), + _blobs_addr(nullptr), + _extrs_length(0), + _blobs_length(0), + _extrs_complete(false), + _shared_blobs_complete(false), + _complete(false) + { } + ~AOTCodeAddressTable(); + void init_extrs(); + void init_shared_blobs(); + const char* add_C_string(const char* str); + int id_for_C_string(address str); + address address_for_C_string(int idx); + int id_for_address(address addr, RelocIterator iter, CodeBlob* code_blob); + address address_for_id(int id); +}; + +class AOTCodeCache : public CHeapObj { + +// Classes used to describe AOT code cache. +protected: + class Config { + uint _compressedOopShift; + uint _compressedKlassShift; + uint _contendedPaddingWidth; + uint _objectAlignment; + uint _gc; + enum Flags { + none = 0, + debugVM = 1, + compressedOops = 2, + compressedClassPointers = 4, + useTLAB = 8, + systemClassAssertions = 16, + userClassAssertions = 32, + enableContendedPadding = 64, + restrictContendedPadding = 128 + }; + uint _flags; + + public: + void record(); + bool verify() const; + }; + + class Header : public CHeapObj { + private: + enum { + AOT_CODE_VERSION = 1 + }; + uint _version; // AOT code version (should match when reading code cache) + uint _cache_size; // cache size in bytes + uint _strings_count; // number of recorded C strings + uint _strings_offset; // offset to recorded C strings + uint _entries_count; // number of recorded entries + uint _entries_offset; // offset of AOTCodeEntry array describing entries + uint _adapters_count; + uint _blobs_count; + Config _config; + +public: + void init(uint cache_size, + uint strings_count, uint strings_offset, + uint entries_count, uint entries_offset, + uint adapters_count, uint blobs_count) { + _version = AOT_CODE_VERSION; + _cache_size = cache_size; + _strings_count = strings_count; + _strings_offset = strings_offset; + _entries_count = entries_count; + _entries_offset = entries_offset; + _adapters_count = adapters_count; + _blobs_count = blobs_count; + + _config.record(); + } + + + uint cache_size() const { return _cache_size; } + uint strings_count() const { return _strings_count; } + uint strings_offset() const { return _strings_offset; } + uint entries_count() const { return _entries_count; } + uint entries_offset() const { return _entries_offset; } + uint adapters_count() const { return _adapters_count; } + uint blobs_count() const { return _blobs_count; } + + bool verify_config(uint load_size) const; + bool verify_vm_config() const { // Called after Universe initialized + return _config.verify(); + } + }; + +// Continue with AOTCodeCache class definition. +private: + Header* _load_header; + char* _load_buffer; // Aligned buffer for loading cached code + char* _store_buffer; // Aligned buffer for storing cached code + char* _C_store_buffer; // Original unaligned buffer + + uint _write_position; // Position in _store_buffer + uint _load_size; // Used when reading cache + uint _store_size; // Used when writing cache + bool _for_use; // AOT cache is open for using AOT code + bool _for_dump; // AOT cache is open for dumping AOT code + bool _closing; // Closing cache file + bool _failed; // Failed read/write to/from cache (cache is broken?) + bool _lookup_failed; // Failed to lookup for info (skip only this code load) + + AOTCodeAddressTable* _table; + + AOTCodeEntry* _load_entries; // Used when reading cache + uint* _search_entries; // sorted by ID table [id, index] + AOTCodeEntry* _store_entries; // Used when writing cache + const char* _C_strings_buf; // Loaded buffer for _C_strings[] table + uint _store_entries_cnt; + + static AOTCodeCache* open_for_use(); + static AOTCodeCache* open_for_dump(); + + bool set_write_position(uint pos); + bool align_write(); + address reserve_bytes(uint nbytes); + uint write_bytes(const void* buffer, uint nbytes); + const char* addr(uint offset) const { return _load_buffer + offset; } + static AOTCodeAddressTable* addr_table() { + return is_on() && (cache()->_table != nullptr) ? cache()->_table : nullptr; + } + + void set_lookup_failed() { _lookup_failed = true; } + void clear_lookup_failed() { _lookup_failed = false; } + bool lookup_failed() const { return _lookup_failed; } + +public: + AOTCodeCache(bool is_dumping, bool is_using); + ~AOTCodeCache(); + + const char* cache_buffer() const { return _load_buffer; } + bool failed() const { return _failed; } + void set_failed() { _failed = true; } + + static uint max_aot_code_size(); + + uint load_size() const { return _load_size; } + uint write_position() const { return _write_position; } + + void load_strings(); + int store_strings(); + + static void init_extrs_table() NOT_CDS_RETURN; + static void init_shared_blobs_table() NOT_CDS_RETURN; + + address address_for_id(int id) const { return _table->address_for_id(id); } + + bool for_use() const { return _for_use && !_failed; } + bool for_dump() const { return _for_dump && !_failed; } + + bool closing() const { return _closing; } + + AOTCodeEntry* add_entry() { + _store_entries_cnt++; + _store_entries -= 1; + return _store_entries; + } + + AOTCodeEntry* find_entry(AOTCodeEntry::Kind kind, uint id); + + bool finish_write(); + + bool write_relocations(CodeBlob& code_blob); + bool write_oop_map_set(CodeBlob& cb); + + static bool store_code_blob(CodeBlob& blob, + AOTCodeEntry::Kind entry_kind, + uint id, const char* name, + int entry_offset_count, + int* entry_offsets) NOT_CDS_RETURN_(false); + + static CodeBlob* load_code_blob(AOTCodeEntry::Kind kind, + uint id, const char* name, + int entry_offset_count, + int* entry_offsets) NOT_CDS_RETURN_(nullptr); + + static uint store_entries_cnt() { + if (is_on_for_dump()) { + return cache()->_store_entries_cnt; + } + return -1; + } + +// Static access + +private: + static AOTCodeCache* _cache; + + static bool open_cache(bool is_dumping, bool is_using); + static bool verify_vm_config() { + if (is_on_for_use()) { + return _cache->_load_header->verify_vm_config(); + } + return true; + } +public: + static AOTCodeCache* cache() { return _cache; } + static void initialize() NOT_CDS_RETURN; + static void init2() NOT_CDS_RETURN; + static void close() NOT_CDS_RETURN; + static bool is_on() CDS_ONLY({ return _cache != nullptr && !_cache->closing(); }) NOT_CDS_RETURN_(false); + static bool is_on_for_use() { return is_on() && _cache->for_use(); } + static bool is_on_for_dump() { return is_on() && _cache->for_dump(); } + + static bool is_dumping_adapters() NOT_CDS_RETURN_(false); + static bool is_using_adapters() NOT_CDS_RETURN_(false); + + static const char* add_C_string(const char* str) NOT_CDS_RETURN_(str); + + static void print_on(outputStream* st) NOT_CDS_RETURN; +}; + +// Concurent AOT code reader +class AOTCodeReader { +private: + const AOTCodeCache* _cache; + const AOTCodeEntry* _entry; + const char* _load_buffer; // Loaded cached code buffer + uint _read_position; // Position in _load_buffer + uint read_position() const { return _read_position; } + void set_read_position(uint pos); + const char* addr(uint offset) const { return _load_buffer + offset; } + + bool _lookup_failed; // Failed to lookup for info (skip only this code load) + void set_lookup_failed() { _lookup_failed = true; } + void clear_lookup_failed() { _lookup_failed = false; } + bool lookup_failed() const { return _lookup_failed; } + + AOTCodeEntry* aot_code_entry() { return (AOTCodeEntry*)_entry; } +public: + AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry); + + CodeBlob* compile_code_blob(const char* name, int entry_offset_count, int* entry_offsets); + + ImmutableOopMapSet* read_oop_map_set(); + + void fix_relocations(CodeBlob* code_blob); +}; +#endif // SHARE_CODE_AOTCODECACH_HPP diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 4ae4ccbc858fe..1512f498b37cb 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -78,9 +78,12 @@ const BufferBlob::Vptr BufferBlob::_vpntr; const RuntimeStub::Vptr RuntimeStub::_vpntr; const SingletonBlob::Vptr SingletonBlob::_vpntr; const DeoptimizationBlob::Vptr DeoptimizationBlob::_vpntr; +#ifdef COMPILER2 +const ExceptionBlob::Vptr ExceptionBlob::_vpntr; +#endif // COMPILER2 const UpcallStub::Vptr UpcallStub::_vpntr; -const CodeBlob::Vptr* CodeBlob::vptr() const { +const CodeBlob::Vptr* CodeBlob::vptr(CodeBlobKind kind) { constexpr const CodeBlob::Vptr* array[(size_t)CodeBlobKind::Number_Of_Kinds] = { nullptr/* None */, &nmethod::_vpntr, @@ -98,7 +101,11 @@ const CodeBlob::Vptr* CodeBlob::vptr() const { &UpcallStub::_vpntr }; - return array[(size_t)_kind]; + return array[(size_t)kind]; +} + +const CodeBlob::Vptr* CodeBlob::vptr() const { + return vptr(_kind); } unsigned int CodeBlob::align_code_offset(int offset) { @@ -181,6 +188,19 @@ CodeBlob::CodeBlob(const char* name, CodeBlobKind kind, int size, uint16_t heade assert(_mutable_data = blob_end(), "sanity"); } +void CodeBlob::restore_mutable_data(address reloc_data) { + // Relocation data is now stored as part of the mutable data area; allocate it before copy relocations + if (_mutable_data_size > 0) { + _mutable_data = (address)os::malloc(_mutable_data_size, mtCode); + if (_mutable_data == nullptr) { + vm_exit_out_of_memory(_mutable_data_size, OOM_MALLOC_ERROR, "codebuffer: no space for mutable data"); + } + } + if (_relocation_size > 0) { + memcpy((address)relocation_begin(), reloc_data, relocation_size()); + } +} + void CodeBlob::purge() { assert(_mutable_data != nullptr, "should never be null"); if (_mutable_data != blob_end()) { @@ -215,6 +235,68 @@ void CodeBlob::print_code_on(outputStream* st) { Disassembler::decode(this, st); } +void CodeBlob::prepare_for_archiving_impl() { + set_name(nullptr); + _oop_maps = nullptr; + _mutable_data = nullptr; +#ifndef PRODUCT + asm_remarks().clear(); + dbg_strings().clear(); +#endif /* PRODUCT */ +} + +void CodeBlob::prepare_for_archiving() { + vptr(_kind)->prepare_for_archiving(this); +} + +void CodeBlob::archive_blob(CodeBlob* blob, address archive_buffer) { + blob->copy_to(archive_buffer); + CodeBlob* archived_blob = (CodeBlob*)archive_buffer; + archived_blob->prepare_for_archiving(); +} + +void CodeBlob::post_restore_impl() { + // Track memory usage statistic after releasing CodeCache_lock + MemoryService::track_code_cache_memory_usage(); +} + +void CodeBlob::post_restore() { + vptr(_kind)->post_restore(this); +} + +CodeBlob* CodeBlob::restore(address code_cache_buffer, const char* name, address archived_reloc_data, ImmutableOopMapSet* archived_oop_maps) { + copy_to(code_cache_buffer); + CodeBlob* code_blob = (CodeBlob*)code_cache_buffer; + code_blob->set_name(name); + code_blob->restore_mutable_data(archived_reloc_data); + code_blob->set_oop_maps(archived_oop_maps); + return code_blob; +} + +CodeBlob* CodeBlob::create(CodeBlob* archived_blob, const char* name, address archived_reloc_data, ImmutableOopMapSet* archived_oop_maps) { + ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + + CodeCache::gc_on_allocation(); + + CodeBlob* blob = nullptr; + unsigned int size = archived_blob->size(); + { + MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + address code_cache_buffer = (address)CodeCache::allocate(size, CodeBlobType::NonNMethod); + if (code_cache_buffer != nullptr) { + blob = archived_blob->restore(code_cache_buffer, name, archived_reloc_data, archived_oop_maps); + assert(blob != nullptr, "sanity check"); + // Flush the code block + ICache::invalidate_range(blob->code_begin(), blob->code_size()); + CodeCache::commit(blob); // Count adapters + } + } + if (blob != nullptr) { + blob->post_restore(); + } + return blob; +} + //----------------------------------------------------------------------------------------- // Creates a RuntimeBlob from a CodeBuffer and copy code and relocation info. @@ -720,7 +802,7 @@ void CodeBlob::print_value_on(outputStream* st) const { } void CodeBlob::print_on_impl(outputStream* st) const { - st->print_cr("[CodeBlob (" INTPTR_FORMAT ")]", p2i(this)); + st->print_cr("[CodeBlob kind:%d (" INTPTR_FORMAT ")]", (int)_kind, p2i(this)); st->print_cr("Framesize: %d", _frame_size); } diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index 1daa35c16b5c5..37f83824987be 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -97,11 +97,16 @@ enum class CodeBlobKind : u1 { class UpcallStub; // for as_upcall_stub() class RuntimeStub; // for as_runtime_stub() class JavaFrameAnchor; // for UpcallStub::jfa_for_frame +class AdapterBlob; +class ExceptionBlob; class CodeBlob { friend class VMStructs; friend class JVMCIVMStructs; +private: + void restore_mutable_data(address reloc_data); + protected: // order fields from large to small to minimize padding between fields ImmutableOopMapSet* _oop_maps; // OopMap for this CodeBlob @@ -140,8 +145,15 @@ class CodeBlob { public: virtual void print_on(const CodeBlob* instance, outputStream* st) const = 0; virtual void print_value_on(const CodeBlob* instance, outputStream* st) const = 0; + virtual void prepare_for_archiving(CodeBlob* instance) const { + instance->prepare_for_archiving_impl(); + }; + virtual void post_restore(CodeBlob* instance) const { + instance->post_restore_impl(); + }; }; + static const Vptr* vptr(CodeBlobKind kind); const Vptr* vptr() const; CodeBlob(const char* name, CodeBlobKind kind, CodeBuffer* cb, int size, uint16_t header_size, @@ -151,8 +163,12 @@ class CodeBlob { // Simple CodeBlob used for simple BufferBlob. CodeBlob(const char* name, CodeBlobKind kind, int size, uint16_t header_size); + void operator delete(void* p) { } + void prepare_for_archiving_impl(); + void post_restore_impl(); + public: ~CodeBlob() { @@ -188,6 +204,7 @@ class CodeBlob { nmethod* as_nmethod_or_null() const { return is_nmethod() ? (nmethod*) this : nullptr; } nmethod* as_nmethod() const { assert(is_nmethod(), "must be nmethod"); return (nmethod*) this; } CodeBlob* as_codeblob() const { return (CodeBlob*) this; } + AdapterBlob* as_adapter_blob() const { assert(is_adapter_blob(), "must be adapter blob"); return (AdapterBlob*) this; } UpcallStub* as_upcall_stub() const { assert(is_upcall_stub(), "must be upcall stub"); return (UpcallStub*) this; } RuntimeStub* as_runtime_stub() const { assert(is_runtime_stub(), "must be runtime blob"); return (RuntimeStub*) this; } @@ -244,6 +261,7 @@ class CodeBlob { // OopMap for frame ImmutableOopMapSet* oop_maps() const { return _oop_maps; } void set_oop_maps(OopMapSet* p); + void set_oop_maps(ImmutableOopMapSet* p) { _oop_maps = p; } const ImmutableOopMap* oop_map_for_slot(int slot, address return_address) const; const ImmutableOopMap* oop_map_for_return_address(address return_address) const; @@ -278,6 +296,19 @@ class CodeBlob { void use_remarks(AsmRemarks &remarks) { _asm_remarks.share(remarks); } void use_strings(DbgStrings &strings) { _dbg_strings.share(strings); } #endif + + void copy_to(address buffer) { + memcpy(buffer, this, this->size()); + } + + // methods to archive a blob into AOT code cache + void prepare_for_archiving(); + static void archive_blob(CodeBlob* blob, address archive_buffer); + + // methods to restore a blob from AOT code cache into the CodeCache + void post_restore(); + CodeBlob* restore(address code_cache_buffer, const char* name, address archived_reloc_data, ImmutableOopMapSet* archived_oop_maps); + static CodeBlob* create(CodeBlob* archived_blob, const char* name, address archived_reloc_data, ImmutableOopMapSet* archived_oop_maps); }; //---------------------------------------------------------------------------------------------------- @@ -617,6 +648,18 @@ class ExceptionBlob: public SingletonBlob { OopMapSet* oop_maps, int frame_size ); + + void post_restore_impl() { + trace_new_stub(this, "ExceptionBlob"); + } + + class Vptr : public SingletonBlob::Vptr { + void post_restore(CodeBlob* instance) const override { + ((ExceptionBlob*)instance)->post_restore_impl(); + } + }; + + static const Vptr _vpntr; }; #endif // COMPILER2 diff --git a/src/hotspot/share/code/debugInfoRec.cpp b/src/hotspot/share/code/debugInfoRec.cpp index 02cd23407bf90..8449f5d692982 100644 --- a/src/hotspot/share/code/debugInfoRec.cpp +++ b/src/hotspot/share/code/debugInfoRec.cpp @@ -140,7 +140,7 @@ DebugInformationRecorder::DebugInformationRecorder(OopRecorder* oop_recorder) add_new_pc_offset(PcDesc::lower_offset_limit); // sentinel record - debug_only(_recording_state = rs_null); + DEBUG_ONLY(_recording_state = rs_null); } @@ -159,7 +159,7 @@ void DebugInformationRecorder::add_safepoint(int pc_offset, OopMap* map) { add_new_pc_offset(pc_offset); assert(_recording_state == rs_null, "nesting of recording calls"); - debug_only(_recording_state = rs_safepoint); + DEBUG_ONLY(_recording_state = rs_safepoint); } void DebugInformationRecorder::add_non_safepoint(int pc_offset) { @@ -169,7 +169,7 @@ void DebugInformationRecorder::add_non_safepoint(int pc_offset) { add_new_pc_offset(pc_offset); assert(_recording_state == rs_null, "nesting of recording calls"); - debug_only(_recording_state = rs_non_safepoint); + DEBUG_ONLY(_recording_state = rs_non_safepoint); } void DebugInformationRecorder::add_new_pc_offset(int pc_offset) { @@ -360,7 +360,7 @@ void DebugInformationRecorder::dump_object_pool(GrowableArray* obje void DebugInformationRecorder::end_scopes(int pc_offset, bool is_safepoint) { assert(_recording_state == (is_safepoint? rs_safepoint: rs_non_safepoint), "nesting of recording calls"); - debug_only(_recording_state = rs_null); + DEBUG_ONLY(_recording_state = rs_null); // Try to compress away an equivalent non-safepoint predecessor. // (This only works because we have previously recognized redundant @@ -413,13 +413,13 @@ DebugToken* DebugInformationRecorder::create_monitor_values(GrowableArrayposition(); } int DebugInformationRecorder::pcs_size() { - debug_only(mark_recorders_frozen()); // mark it "frozen" for asserts + DEBUG_ONLY(mark_recorders_frozen()); // mark it "frozen" for asserts if (last_pc()->pc_offset() != PcDesc::upper_offset_limit) add_new_pc_offset(PcDesc::upper_offset_limit); return _pcs_length * sizeof(PcDesc); diff --git a/src/hotspot/share/code/dependencyContext.cpp b/src/hotspot/share/code/dependencyContext.cpp index 2b3253030c5c9..a8ef707978d31 100644 --- a/src/hotspot/share/code/dependencyContext.cpp +++ b/src/hotspot/share/code/dependencyContext.cpp @@ -91,18 +91,40 @@ void DependencyContext::mark_dependent_nmethods(DeoptimizationScope* deopt_scope // void DependencyContext::add_dependent_nmethod(nmethod* nm) { assert_lock_strong(CodeCache_lock); - for (nmethodBucket* b = dependencies_not_unloading(); b != nullptr; b = b->next_not_unloading()) { - if (nm == b->get_nmethod()) { - return; - } + assert(nm->is_not_installed(), "Precondition: new nmethod"); + + // This method tries to add never before seen nmethod, holding the CodeCache_lock + // until all dependencies are added. The caller code can call multiple times + // with the same nmethod, but always under the same lock hold. + // + // This means the buckets list is guaranteed to be in either of two states, with + // regards to the newly added nmethod: + // 1. The nmethod is not in the list, and can be just added to the head of the list. + // 2. The nmethod is in the list, and it is already at the head of the list. + // + // This path is the only path that adds to the list. There can be concurrent removals + // from the list, but they do not break this invariant. This invariant allows us + // to skip list scans. The individual method checks are cheap, but walking the large + // list of dependencies gets expensive. + + nmethodBucket* head = Atomic::load(_dependency_context_addr); + if (head != nullptr && nm == head->get_nmethod()) { + return; + } + +#ifdef ASSERT + for (nmethodBucket* b = head; b != nullptr; b = b->next()) { + assert(nm != b->get_nmethod(), "Invariant: should not be in the list yet"); } +#endif + nmethodBucket* new_head = new nmethodBucket(nm, nullptr); for (;;) { - nmethodBucket* head = Atomic::load(_dependency_context_addr); new_head->set_next(head); if (Atomic::cmpxchg(_dependency_context_addr, head, new_head) == head) { break; } + head = Atomic::load(_dependency_context_addr); } if (UsePerfData) { _perf_total_buckets_allocated_count->inc(); diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 56ba76a806e4f..e3bd3131b4509 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1122,7 +1122,7 @@ nmethod* nmethod::new_native_nmethod(const methodHandle& method, if (nm != nullptr) { // verify nmethod - debug_only(nm->verify();) // might block + DEBUG_ONLY(nm->verify();) // might block nm->log_new_nmethod(); } @@ -1269,7 +1269,7 @@ void nmethod::post_init() { finalize_relocations(); Universe::heap()->register_nmethod(this); - debug_only(Universe::heap()->verify_nmethod(this)); + DEBUG_ONLY(Universe::heap()->verify_nmethod(this)); CodeCache::commit(this); } @@ -1296,7 +1296,7 @@ nmethod::nmethod( _native_basic_lock_sp_offset(basic_lock_sp_offset) { { - debug_only(NoSafepointVerifier nsv;) + DEBUG_ONLY(NoSafepointVerifier nsv;) assert_locked_or_safepoint(CodeCache_lock); init_defaults(code_buffer, offsets); @@ -1437,7 +1437,7 @@ nmethod::nmethod( { assert(debug_info->oop_recorder() == code_buffer->oop_recorder(), "shared OR"); { - debug_only(NoSafepointVerifier nsv;) + DEBUG_ONLY(NoSafepointVerifier nsv;) assert_locked_or_safepoint(CodeCache_lock); init_defaults(code_buffer, offsets); @@ -2802,7 +2802,7 @@ PcDesc* PcDescContainer::find_pc_desc_internal(address pc, bool approximate, add } // Take giant steps at first (4096, then 256, then 16, then 1) - const int LOG2_RADIX = 4 /*smaller steps in debug mode:*/ debug_only(-1); + const int LOG2_RADIX = 4 /*smaller steps in debug mode:*/ DEBUG_ONLY(-1); const int RADIX = (1 << LOG2_RADIX); for (int step = (1 << (LOG2_RADIX*3)); step > 1; step >>= LOG2_RADIX) { while ((mid = lower + step) < upper) { @@ -3243,7 +3243,7 @@ void nmethod::print_relocations() { ResourceMark m; // in case methods get printed via the debugger tty->print_cr("relocations:"); RelocIterator iter(this); - iter.print(); + iter.print_on(tty); } #endif diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 7b19cf75a766b..1c536a5d7e1a4 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -100,7 +100,7 @@ class PcDescCache { typedef PcDesc* PcDescPtr; volatile PcDescPtr _pc_descs[cache_size]; // last cache_size pc_descs found public: - PcDescCache() { debug_only(_pc_descs[0] = nullptr); } + PcDescCache() { DEBUG_ONLY(_pc_descs[0] = nullptr); } void init_to(PcDesc* initial_pc_desc); PcDesc* find_pc_desc(int pc_offset, bool approximate); void add_pc_desc(PcDesc* pc_desc); diff --git a/src/hotspot/share/code/oopRecorder.cpp b/src/hotspot/share/code/oopRecorder.cpp index af23bf12b4386..c37651892cc74 100644 --- a/src/hotspot/share/code/oopRecorder.cpp +++ b/src/hotspot/share/code/oopRecorder.cpp @@ -122,7 +122,7 @@ template int ValueRecorder::add_handle(T h, bool make_findable) { template int ValueRecorder::maybe_find_index(T h) { - debug_only(_find_index_calls++); + DEBUG_ONLY(_find_index_calls++); assert(!_complete, "cannot allocate more elements after size query"); maybe_initialize(); if (h == nullptr) return null_index; @@ -134,7 +134,7 @@ template int ValueRecorder::maybe_find_index(T h) { return -1; // We know this handle is completely new. } if (cindex >= first_index && _handles->at(cindex - first_index) == h) { - debug_only(_hit_indexes++); + DEBUG_ONLY(_hit_indexes++); return cindex; } if (!_indexes->cache_location_collision(cloc)) { @@ -151,7 +151,7 @@ template int ValueRecorder::maybe_find_index(T h) { if (cloc != nullptr) { _indexes->set_cache_location_index(cloc, findex); } - debug_only(_missed_indexes++); + DEBUG_ONLY(_missed_indexes++); return findex; } } diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index ad194c71bb276..81fa83dbe8d28 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -78,7 +78,7 @@ relocInfo* relocInfo::finish_prefix(short* prefix_limit) { assert(prefix_limit >= p, "must be a valid span of data"); int plen = checked_cast(prefix_limit - p); if (plen == 0) { - debug_only(_value = 0xFFFF); + DEBUG_ONLY(_value = 0xFFFF); return this; // no data: remove self completely } if (plen == 1 && fits_into_immediate(p[0])) { @@ -179,8 +179,31 @@ RelocIterator::RelocIterator(CodeSection* cs, address begin, address limit) { set_limits(begin, limit); } +RelocIterator::RelocIterator(CodeBlob* cb) { + initialize_misc(); + if (cb->is_nmethod()) { + _code = cb->as_nmethod(); + } else { + _code = nullptr; + } + _current = cb->relocation_begin() - 1; + _end = cb->relocation_end(); + _addr = cb->content_begin(); + + _section_start[CodeBuffer::SECT_CONSTS] = cb->content_begin(); + _section_start[CodeBuffer::SECT_INSTS] = cb->code_begin(); + + _section_end[CodeBuffer::SECT_CONSTS] = cb->code_begin(); + _section_start[CodeBuffer::SECT_INSTS] = cb->code_end(); + assert(!has_current(), "just checking"); + set_limits(nullptr, nullptr); +} + bool RelocIterator::addr_in_const() const { const int n = CodeBuffer::SECT_CONSTS; + if (_section_start[n] == nullptr) { + return false; + } return section_start(n) <= addr() && addr() < section_end(n); } @@ -342,7 +365,7 @@ address Relocation::old_addr_for(address newa, address Relocation::new_addr_for(address olda, const CodeBuffer* src, CodeBuffer* dest) { - debug_only(const CodeBuffer* src0 = src); + DEBUG_ONLY(const CodeBuffer* src0 = src); int sect = CodeBuffer::SECT_NONE; // Look for olda in the source buffer, and all previous incarnations // if the source buffer has been expanded. @@ -471,7 +494,9 @@ void trampoline_stub_Relocation::unpack_data() { void external_word_Relocation::pack_data_to(CodeSection* dest) { short* p = (short*) dest->locs_end(); int index = ExternalsRecorder::find_index(_target); - p = pack_1_int_to(p, index); + // Use 4 bytes to store index to be able patch it when + // updating relocations in AOTCodeReader::read_relocations(). + p = add_jint(p, index); dest->set_locs_end((relocInfo*) p); } @@ -766,6 +791,14 @@ void internal_word_Relocation::fix_relocation_after_move(const CodeBuffer* src, set_value(target); } +void internal_word_Relocation::fix_relocation_after_aot_load(address orig_base_addr, address current_base_addr) { + address target = _target; + if (target == nullptr) { + target = this->target(); + target = current_base_addr + (target - orig_base_addr); + } + set_value(target); +} address internal_word_Relocation::target() { address target = _target; @@ -779,12 +812,7 @@ address internal_word_Relocation::target() { return target; } -//--------------------------------------------------------------------------------- -// Non-product code - -#ifndef PRODUCT - -static const char* reloc_type_string(relocInfo::relocType t) { +const char* relocInfo::type_name(relocInfo::relocType t) { switch (t) { #define EACH_CASE(name) \ case relocInfo::name##_type: \ @@ -802,26 +830,25 @@ static const char* reloc_type_string(relocInfo::relocType t) { } } - -void RelocIterator::print_current() { +void RelocIterator::print_current_on(outputStream* st) { if (!has_current()) { - tty->print_cr("(no relocs)"); + st->print_cr("(no relocs)"); return; } - tty->print("relocInfo@" INTPTR_FORMAT " [type=%d(%s) addr=" INTPTR_FORMAT " offset=%d", - p2i(_current), type(), reloc_type_string((relocInfo::relocType) type()), p2i(_addr), _current->addr_offset()); + st->print("relocInfo@" INTPTR_FORMAT " [type=%d(%s) addr=" INTPTR_FORMAT " offset=%d", + p2i(_current), type(), relocInfo::type_name((relocInfo::relocType) type()), p2i(_addr), _current->addr_offset()); if (current()->format() != 0) - tty->print(" format=%d", current()->format()); + st->print(" format=%d", current()->format()); if (datalen() == 1) { - tty->print(" data=%d", data()[0]); + st->print(" data=%d", data()[0]); } else if (datalen() > 0) { - tty->print(" data={"); + st->print(" data={"); for (int i = 0; i < datalen(); i++) { - tty->print("%04x", data()[i] & 0xFFFF); + st->print("%04x", data()[i] & 0xFFFF); } - tty->print("}"); + st->print("}"); } - tty->print("]"); + st->print("]"); switch (type()) { case relocInfo::oop_type: { @@ -834,14 +861,14 @@ void RelocIterator::print_current() { raw_oop = *oop_addr; oop_value = r->oop_value(); } - tty->print(" | [oop_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT "]", + st->print(" | [oop_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT "]", p2i(oop_addr), p2i(raw_oop)); // Do not print the oop by default--we want this routine to // work even during GC or other inconvenient times. if (WizardMode && oop_value != nullptr) { - tty->print("oop_value=" INTPTR_FORMAT ": ", p2i(oop_value)); + st->print("oop_value=" INTPTR_FORMAT ": ", p2i(oop_value)); if (oopDesc::is_oop(oop_value)) { - oop_value->print_value_on(tty); + oop_value->print_value_on(st); } } break; @@ -857,11 +884,11 @@ void RelocIterator::print_current() { raw_metadata = *metadata_addr; metadata_value = r->metadata_value(); } - tty->print(" | [metadata_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT "]", + st->print(" | [metadata_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT "]", p2i(metadata_addr), p2i(raw_metadata)); if (metadata_value != nullptr) { - tty->print("metadata_value=" INTPTR_FORMAT ": ", p2i(metadata_value)); - metadata_value->print_value_on(tty); + st->print("metadata_value=" INTPTR_FORMAT ": ", p2i(metadata_value)); + metadata_value->print_value_on(st); } break; } @@ -870,17 +897,17 @@ void RelocIterator::print_current() { case relocInfo::section_word_type: { DataRelocation* r = (DataRelocation*) reloc(); - tty->print(" | [target=" INTPTR_FORMAT "]", p2i(r->value())); //value==target + st->print(" | [target=" INTPTR_FORMAT "]", p2i(r->value())); //value==target break; } case relocInfo::static_call_type: { static_call_Relocation* r = (static_call_Relocation*) reloc(); - tty->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]", + st->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]", p2i(r->destination()), p2i(r->method_value())); CodeBlob* cb = CodeCache::find_blob(r->destination()); if (cb != nullptr) { - tty->print(" Blob::%s", cb->name()); + st->print(" Blob::%s", cb->name()); } break; } @@ -889,28 +916,28 @@ void RelocIterator::print_current() { { CallRelocation* r = (CallRelocation*) reloc(); address dest = r->destination(); - tty->print(" | [destination=" INTPTR_FORMAT "]", p2i(dest)); + st->print(" | [destination=" INTPTR_FORMAT "]", p2i(dest)); if (StubRoutines::contains(dest)) { StubCodeDesc* desc = StubCodeDesc::desc_for(dest); if (desc == nullptr) { desc = StubCodeDesc::desc_for(dest + frame::pc_return_offset); } if (desc != nullptr) { - tty->print(" Stub::%s", desc->name()); + st->print(" Stub::%s", desc->name()); } } else { CodeBlob* cb = CodeCache::find_blob(dest); if (cb != nullptr) { - tty->print(" %s", cb->name()); + st->print(" %s", cb->name()); } else { ResourceMark rm; const int buflen = 1024; char* buf = NEW_RESOURCE_ARRAY(char, buflen); int offset; if (os::dll_address_to_function_name(dest, buf, buflen, &offset)) { - tty->print(" %s", buf); + st->print(" %s", buf); if (offset != 0) { - tty->print("+%d", offset); + st->print("+%d", offset); } } } @@ -920,45 +947,45 @@ void RelocIterator::print_current() { case relocInfo::virtual_call_type: { virtual_call_Relocation* r = (virtual_call_Relocation*) reloc(); - tty->print(" | [destination=" INTPTR_FORMAT " cached_value=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]", + st->print(" | [destination=" INTPTR_FORMAT " cached_value=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]", p2i(r->destination()), p2i(r->cached_value()), p2i(r->method_value())); CodeBlob* cb = CodeCache::find_blob(r->destination()); if (cb != nullptr) { - tty->print(" Blob::%s", cb->name()); + st->print(" Blob::%s", cb->name()); } break; } case relocInfo::static_stub_type: { static_stub_Relocation* r = (static_stub_Relocation*) reloc(); - tty->print(" | [static_call=" INTPTR_FORMAT "]", p2i(r->static_call())); + st->print(" | [static_call=" INTPTR_FORMAT "]", p2i(r->static_call())); break; } case relocInfo::trampoline_stub_type: { trampoline_stub_Relocation* r = (trampoline_stub_Relocation*) reloc(); - tty->print(" | [trampoline owner=" INTPTR_FORMAT "]", p2i(r->owner())); + st->print(" | [trampoline owner=" INTPTR_FORMAT "]", p2i(r->owner())); break; } case relocInfo::opt_virtual_call_type: { opt_virtual_call_Relocation* r = (opt_virtual_call_Relocation*) reloc(); - tty->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]", + st->print(" | [destination=" INTPTR_FORMAT " metadata=" INTPTR_FORMAT "]", p2i(r->destination()), p2i(r->method_value())); CodeBlob* cb = CodeCache::find_blob(r->destination()); if (cb != nullptr) { - tty->print(" Blob::%s", cb->name()); + st->print(" Blob::%s", cb->name()); } break; } default: break; } - tty->cr(); + st->cr(); } -void RelocIterator::print() { +void RelocIterator::print_on(outputStream* st) { RelocIterator save_this = (*this); relocInfo* scan = _current; if (!has_current()) scan += 1; // nothing to scan here! @@ -969,32 +996,37 @@ void RelocIterator::print() { got_next = (skip_next || next()); skip_next = false; - tty->print(" @" INTPTR_FORMAT ": ", p2i(scan)); + st->print(" @" INTPTR_FORMAT ": ", p2i(scan)); relocInfo* newscan = _current+1; if (!has_current()) newscan -= 1; // nothing to scan here! while (scan < newscan) { - tty->print("%04x", *(short*)scan & 0xFFFF); + st->print("%04x", *(short*)scan & 0xFFFF); scan++; } - tty->cr(); + st->cr(); if (!got_next) break; - print_current(); + print_current_on(st); } (*this) = save_this; } +//--------------------------------------------------------------------------------- +// Non-product code + +#ifndef PRODUCT + // For the debugger: extern "C" void print_blob_locs(nmethod* nm) { nm->print(); RelocIterator iter(nm); - iter.print(); + iter.print_on(tty); } extern "C" void print_buf_locs(CodeBuffer* cb) { FlagSetting fs(PrintRelocations, true); - cb->print(); + cb->print_on(tty); } #endif // !PRODUCT diff --git a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp index 25cca49e50bb8..714a964b28d28 100644 --- a/src/hotspot/share/code/relocInfo.hpp +++ b/src/hotspot/share/code/relocInfo.hpp @@ -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 @@ -451,6 +451,8 @@ class relocInfo { length_limit = 1 + 1 + (3*BytesPerWord/BytesPerShort) + 1, have_format = format_width > 0 }; + + static const char* type_name(relocInfo::relocType t); }; #define FORWARD_DECLARE_EACH_CLASS(name) \ @@ -574,7 +576,7 @@ class RelocIterator : public StackObj { void set_has_current(bool b) { _datalen = !b ? -1 : 0; - debug_only(_data = nullptr); + DEBUG_ONLY(_data = nullptr); } void set_current(relocInfo& ri) { _current = &ri; @@ -600,6 +602,7 @@ class RelocIterator : public StackObj { // constructor RelocIterator(nmethod* nm, address begin = nullptr, address limit = nullptr); RelocIterator(CodeSection* cb, address begin = nullptr, address limit = nullptr); + RelocIterator(CodeBlob* cb); // get next reloc info, return !eos bool next() { @@ -638,11 +641,11 @@ class RelocIterator : public StackObj { bool addr_in_const() const; address section_start(int n) const { - assert(_section_start[n], "must be initialized"); + assert(_section_start[n], "section %d must be initialized", n); return _section_start[n]; } address section_end(int n) const { - assert(_section_end[n], "must be initialized"); + assert(_section_end[n], "section %d must be initialized", n); return _section_end[n]; } @@ -658,11 +661,9 @@ class RelocIterator : public StackObj { // generic relocation accessor; switches on type to call the above Relocation* reloc(); -#ifndef PRODUCT public: - void print(); - void print_current(); -#endif + void print_on(outputStream* st); + void print_current_on(outputStream* st); }; @@ -672,6 +673,7 @@ class RelocIterator : public StackObj { class Relocation { friend class RelocIterator; + friend class AOTCodeReader; private: // When a relocation has been created by a RelocIterator, @@ -1377,6 +1379,8 @@ class internal_word_Relocation : public DataRelocation { void unpack_data() override; void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) override; + void fix_relocation_after_aot_load(address orig_base_addr, address current_base_addr); + address target(); // if _target==nullptr, fetch addr from code stream int section() { return _section; } address value() override { return target(); } diff --git a/src/hotspot/share/code/stubs.cpp b/src/hotspot/share/code/stubs.cpp index 074241ff61133..6ae71f9370936 100644 --- a/src/hotspot/share/code/stubs.cpp +++ b/src/hotspot/share/code/stubs.cpp @@ -176,14 +176,14 @@ void StubQueue::commit(int committed_code_size) { _queue_end += committed_size; _number_of_stubs++; if (_mutex != nullptr) _mutex->unlock(); - debug_only(stub_verify(s);) + DEBUG_ONLY(stub_verify(s);) } void StubQueue::remove_first() { if (number_of_stubs() == 0) return; Stub* s = first(); - debug_only(stub_verify(s);) + DEBUG_ONLY(stub_verify(s);) stub_finalize(s); _queue_begin += stub_size(s); assert(_queue_begin <= _buffer_limit, "sanity check"); @@ -210,7 +210,7 @@ void StubQueue::remove_first(int n) { void StubQueue::remove_all(){ - debug_only(verify();) + DEBUG_ONLY(verify();) remove_first(number_of_stubs()); assert(number_of_stubs() == 0, "sanity check"); } diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp index 0c2822c94d21b..3138eb3a8c0fe 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp @@ -374,7 +374,7 @@ void ArenaStatCounter::on_arena_chunk_deallocation(size_t size, uint64_t stamp) void ArenaStatCounter::print_peak_state_on(outputStream* st) const { st->print("Total Usage: %zu ", _peak); if (_peak > 0) { -#ifdef COMPILER2 +#ifdef COMPILER1 // C1: print allocations broken down by arena types if (_comp_type == CompilerType::compiler_c1) { st->print("["); diff --git a/src/hotspot/share/compiler/oopMap.cpp b/src/hotspot/share/compiler/oopMap.cpp index ea2c770d66f74..aeb6ac3828efb 100644 --- a/src/hotspot/share/compiler/oopMap.cpp +++ b/src/hotspot/share/compiler/oopMap.cpp @@ -326,7 +326,7 @@ void OopMap::set_xxx(VMReg reg, OopMapValue::oop_types x, VMReg optional) { assert(reg->value() < _locs_length, "too big reg value for stack size"); assert( _locs_used[reg->value()] == OopMapValue::unused_value, "cannot insert twice" ); - debug_only( _locs_used[reg->value()] = x; ) + DEBUG_ONLY( _locs_used[reg->value()] = x; ) OopMapValue o(reg, x, optional); o.write_on(write_stream()); @@ -511,7 +511,7 @@ void ImmutableOopMap::update_register_map(const frame *fr, RegisterMap *reg_map) // Any reg might be saved by a safepoint handler (see generate_handler_blob). assert( reg_map->_update_for_id == nullptr || fr->is_older(reg_map->_update_for_id), "already updated this map; do not 'update' it twice!" ); - debug_only(reg_map->_update_for_id = fr->id()); + DEBUG_ONLY(reg_map->_update_for_id = fr->id()); // Check if caller must update oop argument assert((reg_map->include_argument_oops() || @@ -729,7 +729,6 @@ bool ImmutableOopMap::has_any(OopMapValue::oop_types type) const { return false; } -#ifdef ASSERT int ImmutableOopMap::nr_of_bytes() const { OopMapStream oms(this); @@ -738,7 +737,6 @@ int ImmutableOopMap::nr_of_bytes() const { } return sizeof(ImmutableOopMap) + oms.stream_position(); } -#endif ImmutableOopMapBuilder::ImmutableOopMapBuilder(const OopMapSet* set) : _set(set), _empty(nullptr), _last(nullptr), _empty_offset(-1), _last_offset(-1), _offset(0), _required(-1), _new_set(nullptr) { _mapping = NEW_RESOURCE_ARRAY(Mapping, _set->size()); diff --git a/src/hotspot/share/compiler/oopMap.hpp b/src/hotspot/share/compiler/oopMap.hpp index 634fb8b0bfae7..4b962444738a5 100644 --- a/src/hotspot/share/compiler/oopMap.hpp +++ b/src/hotspot/share/compiler/oopMap.hpp @@ -162,7 +162,7 @@ class OopMap: public ResourceObj { bool _has_derived_oops; CompressedWriteStream* _write_stream; - debug_only( OopMapValue::oop_types* _locs_used; int _locs_length;) + DEBUG_ONLY( OopMapValue::oop_types* _locs_used; int _locs_length;) // Accessors int omv_count() const { return _omv_count; } @@ -292,9 +292,7 @@ class ImmutableOopMap { bool has_derived_oops() const { return _has_derived_oops; } bool has_any(OopMapValue::oop_types type) const; -#ifdef ASSERT - int nr_of_bytes() const; // this is an expensive operation, only used in debug builds -#endif + int nr_of_bytes() const; // this is an expensive operation, only used in debug builds or in aot code generation void oops_do(const frame* fr, const RegisterMap* reg_map, OopClosure* f, DerivedOopClosure* df) const; void oops_do(const frame* fr, const RegisterMap* reg_map, OopClosure* f, DerivedPointerIterationMode derived_mode) const; @@ -378,9 +376,7 @@ class OopMapStream : public StackObj { bool is_done() { if(!_valid_omv) { find_next(); } return !_valid_omv; } void next() { find_next(); } OopMapValue current() { return _omv; } -#ifdef ASSERT int stream_position() const { return _stream.position(); } -#endif }; class ImmutableOopMapBuilder { diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp index d5ee7835fea0d..c3d3af265289e 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp @@ -37,6 +37,7 @@ #include "memory/universe.hpp" #include "runtime/atomic.hpp" #include "runtime/globals.hpp" +#include "utilities/ostream.hpp" jint EpsilonHeap::initialize() { size_t align = HeapAlignment; @@ -297,26 +298,18 @@ void EpsilonHeap::object_iterate(ObjectClosure *cl) { _space->object_iterate(cl); } -void EpsilonHeap::print_on(outputStream *st) const { +void EpsilonHeap::print_heap_on(outputStream *st) const { st->print_cr("Epsilon Heap"); + StreamAutoIndentor indentor(st, 1); + _virtual_space.print_on(st); if (_space != nullptr) { st->print_cr("Allocation space:"); - _space->print_on(st); - } - - MetaspaceUtils::print_on(st); -} - -void EpsilonHeap::print_on_error(outputStream *st) const { - print_on(st); - st->cr(); - BarrierSet* bs = BarrierSet::barrier_set(); - if (bs != nullptr) { - bs->print_on(st); + StreamAutoIndentor indentor(st, 1); + _space->print_on(st, ""); } } diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp index 81bcd61916fe6..129e81c1c348e 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp @@ -131,8 +131,8 @@ class EpsilonHeap : public CollectedHeap { bool can_load_archived_objects() const override { return true; } HeapWord* allocate_loaded_archive_space(size_t size) override; - void print_on(outputStream* st) const override; - void print_on_error(outputStream* st) const override; + void print_heap_on(outputStream* st) const override; + void print_gc_on(outputStream* st) const override {} void print_tracing_info() const override; bool print_location(outputStream* st, void* addr) const override; diff --git a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp index 540ede9dd83ac..a2ae6f8a14a9d 100644 --- a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp @@ -22,8 +22,8 @@ * */ -#include "gc/epsilon/epsilonMonitoringSupport.hpp" #include "gc/epsilon/epsilonHeap.hpp" +#include "gc/epsilon/epsilonMonitoringSupport.hpp" #include "gc/shared/generationCounters.hpp" #include "memory/allocation.hpp" #include "memory/metaspaceCounters.hpp" diff --git a/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp b/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp index a0eb218dd6caa..c4791311e9b21 100644 --- a/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp +++ b/src/hotspot/share/gc/g1/c1/g1BarrierSetC1.cpp @@ -22,8 +22,8 @@ * */ -#include "c1/c1_LIRGenerator.hpp" #include "c1/c1_CodeStubs.hpp" +#include "c1/c1_LIRGenerator.hpp" #include "gc/g1/c1/g1BarrierSetC1.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BarrierSetAssembler.hpp" diff --git a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp index 57e481668f8d8..bca2255479b58 100644 --- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp +++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp @@ -29,8 +29,8 @@ #include "gc/g1/g1BarrierSetAssembler.hpp" #include "gc/g1/g1BarrierSetRuntime.hpp" #include "gc/g1/g1CardTable.hpp" -#include "gc/g1/g1ThreadLocalData.hpp" #include "gc/g1/g1HeapRegion.hpp" +#include "gc/g1/g1ThreadLocalData.hpp" #include "opto/arraycopynode.hpp" #include "opto/block.hpp" #include "opto/compile.hpp" diff --git a/src/hotspot/share/gc/g1/g1AllocRegion.cpp b/src/hotspot/share/gc/g1/g1AllocRegion.cpp index 29907cbc05157..7e748cf7e9f0b 100644 --- a/src/hotspot/share/gc/g1/g1AllocRegion.cpp +++ b/src/hotspot/share/gc/g1/g1AllocRegion.cpp @@ -23,8 +23,8 @@ */ #include "gc/g1/g1AllocRegion.inline.hpp" -#include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1EvacStats.inline.hpp" #include "gc/shared/tlab_globals.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" diff --git a/src/hotspot/share/gc/g1/g1Allocator.cpp b/src/hotspot/share/gc/g1/g1Allocator.cpp index 49a4958f7186b..c44234fa11cb8 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.cpp +++ b/src/hotspot/share/gc/g1/g1Allocator.cpp @@ -24,9 +24,9 @@ #include "gc/g1/g1Allocator.inline.hpp" #include "gc/g1/g1AllocRegion.inline.hpp" +#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" #include "gc/g1/g1EvacStats.inline.hpp" -#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionPrinter.hpp" #include "gc/g1/g1HeapRegionSet.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1AnalyticsSequences.inline.hpp b/src/hotspot/share/gc/g1/g1AnalyticsSequences.inline.hpp index cd59cac80b423..9fae49320bc94 100644 --- a/src/hotspot/share/gc/g1/g1AnalyticsSequences.inline.hpp +++ b/src/hotspot/share/gc/g1/g1AnalyticsSequences.inline.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1ANALYTICSSEQUENCES_INLINE_HPP #include "gc/g1/g1AnalyticsSequences.hpp" + #include "gc/g1/g1Predictions.hpp" bool G1PhaseDependentSeq::enough_samples_to_use_mixed_seq() const { diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp index b1cf9fd304620..bd156a69fe6fc 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.cpp +++ b/src/hotspot/share/gc/g1/g1Arguments.cpp @@ -71,6 +71,9 @@ void G1Arguments::initialize_alignments() { } size_t G1Arguments::conservative_max_heap_alignment() { + if (FLAG_IS_DEFAULT(G1HeapRegionSize)) { + return G1HeapRegion::max_ergonomics_size(); + } return G1HeapRegion::max_region_size(); } diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp index 3212aec278010..8c2605b974677 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -27,9 +27,9 @@ #include "gc/g1/g1DirtyCardQueue.hpp" #include "gc/g1/g1SATBMarkQueueSet.hpp" +#include "gc/shared/bufferNode.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" -#include "gc/shared/bufferNode.hpp" class G1CardTable; diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp index 7e6b0e56d9346..e35ae08dfada2 100644 --- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp +++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp @@ -26,11 +26,12 @@ #define SHARE_GC_G1_G1BLOCKOFFSETTABLE_INLINE_HPP #include "gc/g1/g1BlockOffsetTable.hpp" + #include "gc/g1/g1HeapRegion.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/memset_with_concurrent_readers.hpp" -#include "runtime/atomic.hpp" #include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" inline HeapWord* G1BlockOffsetTable::block_start_reaching_into_card(const void* addr) const { assert(_reserved.contains(addr), "invalid address"); diff --git a/src/hotspot/share/gc/g1/g1CardSet.inline.hpp b/src/hotspot/share/gc/g1/g1CardSet.inline.hpp index 7d753f00bfba6..4909e922c6bdc 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardSet.inline.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1CARDSET_INLINE_HPP #include "gc/g1/g1CardSet.hpp" + #include "gc/g1/g1CardSetContainers.inline.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "logging/log.hpp" diff --git a/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp b/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp index 61707dcf012d5..0efc44dea12fe 100644 --- a/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1CARDSETCONTAINERS_INLINE_HPP #include "gc/g1/g1CardSetContainers.hpp" + #include "gc/g1/g1GCPhaseTimes.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/checkedCast.hpp" diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp b/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp index 8c0fab3fd1160..7bb618e79b463 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.inline.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1CARDSETMEMORY_INLINE_HPP #include "gc/g1/g1CardSetMemory.hpp" + #include "gc/g1/g1CardSetContainers.inline.hpp" #include "gc/g1/g1MonotonicArena.inline.hpp" #include "utilities/globalCounter.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 31f0cc12aa5ee..ad52388a64fa6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -35,16 +35,16 @@ #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.hpp" #include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineThread.hpp" -#include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1DirtyCardQueue.hpp" #include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1FullCollector.hpp" #include "gc/g1/g1GCCounters.hpp" #include "gc/g1/g1GCParPhaseTimesTracker.hpp" -#include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1GCPauseType.hpp" +#include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionPrinter.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" @@ -91,8 +91,8 @@ #include "gc/shared/taskqueue.inline.hpp" #include "gc/shared/taskTerminator.hpp" #include "gc/shared/tlab_globals.hpp" -#include "gc/shared/workerPolicy.hpp" #include "gc/shared/weakProcessor.inline.hpp" +#include "gc/shared/workerPolicy.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" #include "memory/heapInspection.hpp" @@ -1230,7 +1230,8 @@ G1RegionToSpaceMapper* G1CollectedHeap::create_aux_memory_mapper(const char* des // Allocate a new reserved space, preferring to use large pages. ReservedSpace rs = MemoryReserver::reserve(size, alignment, - preferred_page_size); + preferred_page_size, + mtGC); size_t page_size = rs.page_size(); G1RegionToSpaceMapper* result = @@ -2112,16 +2113,18 @@ void G1CollectedHeap::print_heap_regions() const { } } -void G1CollectedHeap::print_on(outputStream* st) const { +void G1CollectedHeap::print_heap_on(outputStream* st) const { size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked(); - st->print(" %-20s", "garbage-first heap"); + st->print("%-20s", "garbage-first heap"); st->print(" total reserved %zuK, committed %zuK, used %zuK", _hrm.reserved().byte_size()/K, capacity()/K, heap_used/K); st->print(" [" PTR_FORMAT ", " PTR_FORMAT ")", p2i(_hrm.reserved().start()), p2i(_hrm.reserved().end())); st->cr(); - st->print(" region size %zuK, ", G1HeapRegion::GrainBytes / K); + + StreamAutoIndentor indentor(st, 1); + st->print("region size %zuK, ", G1HeapRegion::GrainBytes / K); uint young_regions = young_regions_count(); st->print("%u young (%zuK), ", young_regions, (size_t) young_regions * G1HeapRegion::GrainBytes / K); @@ -2131,7 +2134,7 @@ void G1CollectedHeap::print_on(outputStream* st) const { st->cr(); if (_numa->is_enabled()) { uint num_nodes = _numa->num_active_nodes(); - st->print(" remaining free region(s) on each NUMA node: "); + st->print("remaining free region(s) on each NUMA node: "); const uint* node_ids = _numa->node_ids(); for (uint node_index = 0; node_index < num_nodes; node_index++) { uint num_free_regions = _hrm.num_free_regions(node_index); @@ -2139,7 +2142,6 @@ void G1CollectedHeap::print_on(outputStream* st) const { } st->cr(); } - MetaspaceUtils::print_on(st); } void G1CollectedHeap::print_regions_on(outputStream* st) const { @@ -2153,15 +2155,16 @@ void G1CollectedHeap::print_regions_on(outputStream* st) const { } void G1CollectedHeap::print_extended_on(outputStream* st) const { - print_on(st); + print_heap_on(st); // Print the per-region information. st->cr(); print_regions_on(st); } -void G1CollectedHeap::print_on_error(outputStream* st) const { - print_extended_on(st); +void G1CollectedHeap::print_gc_on(outputStream* st) const { + // Print the per-region information. + print_regions_on(st); st->cr(); BarrierSet* bs = BarrierSet::barrier_set(); @@ -2171,7 +2174,7 @@ void G1CollectedHeap::print_on_error(outputStream* st) const { if (_cm != nullptr) { st->cr(); - _cm->print_on_error(st); + _cm->print_on(st); } } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 8d449ceedc6d1..92e08e6fc610b 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -27,8 +27,8 @@ #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BiasedArray.hpp" -#include "gc/g1/g1CardTable.hpp" #include "gc/g1/g1CardSet.hpp" +#include "gc/g1/g1CardTable.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.hpp" @@ -1312,9 +1312,9 @@ class G1CollectedHeap : public CollectedHeap { void print_regions_on(outputStream* st) const; public: - void print_on(outputStream* st) const override; + void print_heap_on(outputStream* st) const override; void print_extended_on(outputStream* st) const; - void print_on_error(outputStream* st) const override; + void print_gc_on(outputStream* st) const override; void gc_threads_do(ThreadClosure* tc) const override; diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp index f295eff3edd2b..717f6860eb697 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.inline.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1COLLECTIONSET_INLINE_HPP #include "gc/g1/g1CollectionSet.hpp" + #include "gc/g1/g1HeapRegionRemSet.hpp" template diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 0b8e66f904343..07b5afcf98757 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -52,8 +52,8 @@ #include "gc/shared/referencePolicy.hpp" #include "gc/shared/strongRootsScope.hpp" #include "gc/shared/suspendibleThreadSet.hpp" -#include "gc/shared/taskTerminator.hpp" #include "gc/shared/taskqueue.inline.hpp" +#include "gc/shared/taskTerminator.hpp" #include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/workerPolicy.hpp" #include "jvm.h" @@ -2126,9 +2126,9 @@ void G1ConcurrentMark::threads_do(ThreadClosure* tc) const { _concurrent_workers->threads_do(tc); } -void G1ConcurrentMark::print_on_error(outputStream* st) const { +void G1ConcurrentMark::print_on(outputStream* st) const { st->print_cr("Marking Bits: (CMBitMap*) " PTR_FORMAT, p2i(mark_bitmap())); - _mark_bitmap.print_on_error(st, " Bits: "); + _mark_bitmap.print_on(st, " Bits: "); } static ReferenceProcessor* get_cm_oop_closure_ref_processor(G1CollectedHeap* g1h) { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index d54f9bc96f1d9..a4c2e94b2b1dc 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,8 +31,8 @@ #include "gc/g1/g1HeapVerifier.hpp" #include "gc/g1/g1RegionMarkStatsCache.hpp" #include "gc/shared/gcCause.hpp" -#include "gc/shared/taskTerminator.hpp" #include "gc/shared/taskqueue.hpp" +#include "gc/shared/taskTerminator.hpp" #include "gc/shared/verifyOption.hpp" #include "gc/shared/workerThread.hpp" #include "gc/shared/workerUtils.hpp" @@ -707,7 +707,7 @@ class G1ConcurrentMark : public CHeapObj { void threads_do(ThreadClosure* tc) const; - void print_on_error(outputStream* st) const; + void print_on(outputStream* st) const; // Mark the given object on the marking bitmap if it is below TAMS. inline bool mark_in_bitmap(uint worker_id, oop const obj); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp index d51c5c740824c..bc2fd565aab8c 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp @@ -27,9 +27,9 @@ #include "gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp" +#include "gc/shared/gc_globals.hpp" #include "oops/oop.inline.hpp" #include "oops/oopsHierarchy.hpp" -#include "gc/shared/gc_globals.hpp" inline bool G1CMObjArrayProcessor::should_be_sliced(oop obj) { return obj->is_objArray() && ((objArrayOop)obj)->size() >= 2 * ObjArrayMarkingStride; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp index 993df8b47ce3d..0633e18411dfb 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp @@ -23,10 +23,9 @@ */ -#include "gc/g1/g1ConcurrentRebuildAndScrub.hpp" - #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" +#include "gc/g1/g1ConcurrentRebuildAndScrub.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionManager.inline.hpp" #include "gc/shared/gc_globals.hpp" diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index ce4738d784edc..dc88f09f9809b 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -38,6 +38,7 @@ #include "runtime/mutexLocker.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" + #include G1ConcurrentRefineThread* G1ConcurrentRefineThreadControl::create_refinement_thread(uint worker_id, bool initializing) { diff --git a/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp b/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp index ce2f88c9b48ec..6beb536df87c3 100644 --- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp +++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp @@ -25,9 +25,9 @@ #ifndef SHARE_GC_G1_G1DIRTYCARDQUEUE_HPP #define SHARE_GC_G1_G1DIRTYCARDQUEUE_HPP -#include "gc/g1/g1FreeIdSet.hpp" #include "gc/g1/g1CardTable.hpp" #include "gc/g1/g1ConcurrentRefineStats.hpp" +#include "gc/g1/g1FreeIdSet.hpp" #include "gc/shared/bufferNode.hpp" #include "gc/shared/bufferNodeList.hpp" #include "gc/shared/ptrQueue.hpp" diff --git a/src/hotspot/share/gc/g1/g1EdenRegions.hpp b/src/hotspot/share/gc/g1/g1EdenRegions.hpp index 81d02381c9b67..a6e005ff2dc10 100644 --- a/src/hotspot/share/gc/g1/g1EdenRegions.hpp +++ b/src/hotspot/share/gc/g1/g1EdenRegions.hpp @@ -25,8 +25,8 @@ #ifndef SHARE_GC_G1_G1EDENREGIONS_HPP #define SHARE_GC_G1_G1EDENREGIONS_HPP -#include "gc/g1/g1RegionsOnNodes.hpp" #include "gc/g1/g1HeapRegion.hpp" +#include "gc/g1/g1RegionsOnNodes.hpp" #include "runtime/globals.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp index 0d4f5091d9eae..6d5084a144720 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp @@ -25,8 +25,9 @@ #ifndef SHARE_GC_G1_G1EVACFAILUREREGIONS_INLINE_HPP #define SHARE_GC_G1_G1EVACFAILUREREGIONS_INLINE_HPP -#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1EvacFailureRegions.hpp" + +#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "runtime/atomic.hpp" diff --git a/src/hotspot/share/gc/g1/g1EvacStats.cpp b/src/hotspot/share/gc/g1/g1EvacStats.cpp index b0e5f7b6864f2..049175a4eccd6 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.cpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.cpp @@ -23,8 +23,8 @@ */ #include "gc/g1/g1EvacStats.hpp" -#include "gc/shared/gcId.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcId.hpp" #include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "runtime/globals.hpp" diff --git a/src/hotspot/share/gc/g1/g1EvacStats.hpp b/src/hotspot/share/gc/g1/g1EvacStats.hpp index 8b7360f55e3f8..4227cbab93692 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.hpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.hpp @@ -25,8 +25,8 @@ #ifndef SHARE_GC_G1_G1EVACSTATS_HPP #define SHARE_GC_G1_G1EVACSTATS_HPP -#include "gc/shared/plab.hpp" #include "gc/shared/gcUtil.hpp" +#include "gc/shared/plab.hpp" // Records various memory allocation statistics gathered during evacuation. All sizes // are in HeapWords. diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index e9d6d23149fda..a1e1d152fd8d1 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -35,9 +35,9 @@ #include "gc/g1/g1OopClosures.hpp" #include "gc/g1/g1Policy.hpp" #include "gc/g1/g1RegionMarkStatsCache.inline.hpp" +#include "gc/shared/classUnloadingContext.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/preservedMarks.inline.hpp" -#include "gc/shared/classUnloadingContext.hpp" #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/verifyOption.hpp" #include "gc/shared/weakProcessor.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp index ebe77b2551dad..f7b0cb23b65cc 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp @@ -31,11 +31,11 @@ #include "gc/shared/oopStorageSet.hpp" #include "gc/shared/tlab_globals.hpp" #include "gc/shared/workerDataArray.inline.hpp" -#include "memory/resourceArea.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" -#include "runtime/timer.hpp" +#include "memory/resourceArea.hpp" #include "runtime/os.hpp" +#include "runtime/timer.hpp" #include "utilities/enumIterator.hpp" #include "utilities/macros.hpp" @@ -193,6 +193,7 @@ void G1GCPhaseTimes::reset() { _cur_region_register_time = 0.0; _cur_verify_before_time_ms = 0.0; _cur_verify_after_time_ms = 0.0; + _cur_prepare_concurrent_task_time_ms = 0.0; for (int i = 0; i < GCParPhasesSentinel; i++) { if (_gc_par_phases[i] != nullptr) { @@ -407,10 +408,7 @@ void G1GCPhaseTimes::trace_count(const char* name, size_t value) const { } double G1GCPhaseTimes::print_pre_evacuate_collection_set() const { - const double pre_concurrent_start_ms = average_time_ms(ResetMarkingState) + - average_time_ms(NoteStartOfMark); - - const double sum_ms = pre_concurrent_start_ms + + const double sum_ms = _cur_prepare_concurrent_task_time_ms + _cur_pre_evacuate_prepare_time_ms + _recorded_young_cset_choice_time_ms + _recorded_non_young_cset_choice_time_ms + @@ -419,9 +417,12 @@ double G1GCPhaseTimes::print_pre_evacuate_collection_set() const { info_time("Pre Evacuate Collection Set", sum_ms); - if (pre_concurrent_start_ms > 0.0) { - debug_phase(_gc_par_phases[ResetMarkingState]); - debug_phase(_gc_par_phases[NoteStartOfMark]); + // Concurrent tasks of ResetMarkingState and NoteStartOfMark are triggered during + // young collection. However, their execution time are not included in _gc_pause_time_ms. + if (_cur_prepare_concurrent_task_time_ms > 0.0) { + debug_time("Prepare Concurrent Start", _cur_prepare_concurrent_task_time_ms); + debug_phase(_gc_par_phases[ResetMarkingState], 1); + debug_phase(_gc_par_phases[NoteStartOfMark], 1); } debug_time("Pre Evacuate Prepare", _cur_pre_evacuate_prepare_time_ms); @@ -545,6 +546,11 @@ void G1GCPhaseTimes::print_other(double accounted_ms) const { info_time("Other", _gc_pause_time_ms - accounted_ms); } +// Root-region-scan-wait, verify-before and verify-after are part of young GC, +// but these are not measured by G1Policy. i.e. these are not included in +// G1Policy::record_young_collection_start() and record_young_collection_end(). +// In addition, these are not included in G1GCPhaseTimes::_gc_pause_time_ms. +// See G1YoungCollector::collect(). void G1GCPhaseTimes::print(bool evacuation_failed) { if (_root_region_scan_wait_time_ms > 0.0) { debug_time("Root Region Scan Waiting", _root_region_scan_wait_time_ms); @@ -559,15 +565,13 @@ void G1GCPhaseTimes::print(bool evacuation_failed) { double accounted_ms = 0.0; - accounted_ms += _root_region_scan_wait_time_ms; - accounted_ms += _cur_verify_before_time_ms; - accounted_ms += print_pre_evacuate_collection_set(); accounted_ms += print_evacuate_initial_collection_set(); accounted_ms += print_evacuate_optional_collection_set(); accounted_ms += print_post_evacuate_collection_set(evacuation_failed); - accounted_ms += _cur_verify_after_time_ms; + assert(_gc_pause_time_ms >= accounted_ms, "GC pause time(%.3lfms) cannot be " + "smaller than the sum of each phase(%.3lfms).", _gc_pause_time_ms, accounted_ms); print_other(accounted_ms); diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp index f3bc0efafb929..ea868cd939e8c 100644 --- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp +++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -177,12 +177,12 @@ class G1GCPhaseTimes : public CHeapObj { double _cur_merge_heap_roots_time_ms; double _cur_optional_merge_heap_roots_time_ms; + // Included in above merge and optional-merge time. + double _cur_distribute_log_buffers_time_ms; double _cur_prepare_merge_heap_roots_time_ms; double _cur_optional_prepare_merge_heap_roots_time_ms; - double _cur_distribute_log_buffers_time_ms; - double _cur_pre_evacuate_prepare_time_ms; double _cur_post_evacuate_cleanup_1_time_ms; @@ -192,6 +192,7 @@ class G1GCPhaseTimes : public CHeapObj { double _cur_ref_proc_time_ms; double _cur_collection_start_sec; + // Not included in _gc_pause_time_ms double _root_region_scan_wait_time_ms; double _external_accounted_time_ms; @@ -211,9 +212,13 @@ class G1GCPhaseTimes : public CHeapObj { double _cur_region_register_time; + // Not included in _gc_pause_time_ms double _cur_verify_before_time_ms; double _cur_verify_after_time_ms; + // Time spent to trigger concurrent tasks of ResetMarkingState and NoteStartOfMark. + double _cur_prepare_concurrent_task_time_ms; + ReferenceProcessorPhaseTimes _ref_phase_times; WeakProcessorTimes _weak_phase_times; @@ -318,6 +323,10 @@ class G1GCPhaseTimes : public CHeapObj { _cur_ref_proc_time_ms = ms; } + void record_prepare_concurrent_task_time_ms(double ms) { + _cur_prepare_concurrent_task_time_ms = ms; + } + void record_root_region_scan_wait_time(double time_ms) { _root_region_scan_wait_time_ms = time_ms; } @@ -389,8 +398,11 @@ class G1GCPhaseTimes : public CHeapObj { double cur_collection_par_time_ms() { return _cur_collection_initial_evac_time_ms + _cur_optional_evac_time_ms + + _cur_prepare_merge_heap_roots_time_ms + _cur_merge_heap_roots_time_ms + - _cur_optional_merge_heap_roots_time_ms; + _cur_optional_prepare_merge_heap_roots_time_ms + + _cur_optional_merge_heap_roots_time_ms + + _cur_collection_nmethod_list_cleanup_time_ms; } double cur_expand_heap_time_ms() { diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index 288ddae48fbb3..03610ab520bf5 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -57,6 +57,10 @@ size_t G1HeapRegion::max_region_size() { return G1HeapRegionBounds::max_size(); } +size_t G1HeapRegion::max_ergonomics_size() { + return G1HeapRegionBounds::max_ergonomics_size(); +} + size_t G1HeapRegion::min_region_size_in_words() { return G1HeapRegionBounds::min_size() >> LogHeapWordSize; } diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.hpp index 62804b47d9300..1962d3173b776 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.hpp @@ -316,6 +316,7 @@ class G1HeapRegion : public CHeapObj { } static size_t max_region_size(); + static size_t max_ergonomics_size(); static size_t min_region_size_in_words(); // It sets up the heap region size (GrainBytes / GrainWords), as well as diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp index 016b2046265d8..be469b655f71a 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp @@ -24,8 +24,8 @@ #include "gc/g1/g1Arguments.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1CommittedRegionMap.inline.hpp" +#include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1HeapRegionManager.inline.hpp" #include "gc/g1/g1HeapRegionPrinter.hpp" @@ -478,7 +478,7 @@ uint G1HeapRegionManager::find_contiguous_in_free_list(uint num_regions) { uint G1HeapRegionManager::find_contiguous_allow_expand(uint num_regions) { // Check if we can actually satisfy the allocation. - if (num_regions > available()) { + if (num_regions > (num_free_regions() + available())) { return G1_NO_HRM_INDEX; } // Find any candidate. diff --git a/src/hotspot/share/gc/g1/g1InitLogger.cpp b/src/hotspot/share/gc/g1/g1InitLogger.cpp index 1e09eed904315..1838b55e4d3ed 100644 --- a/src/hotspot/share/gc/g1/g1InitLogger.cpp +++ b/src/hotspot/share/gc/g1/g1InitLogger.cpp @@ -23,8 +23,8 @@ */ #include "gc/g1/g1InitLogger.hpp" -#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcLogPrecious.hpp" #include "runtime/globals.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp index 03bd1840206f7..6d1ffdb7c005a 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp @@ -23,9 +23,9 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1MemoryPool.hpp" #include "gc/g1/g1MonitoringSupport.hpp" #include "gc/g1/g1Policy.hpp" -#include "gc/g1/g1MemoryPool.hpp" #include "gc/shared/hSpaceCounters.hpp" #include "memory/metaspaceCounters.hpp" #include "runtime/mutexLocker.hpp" diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp index f22581ad9b152..62c502d497cad 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp @@ -27,9 +27,9 @@ #include "gc/shared/collectorCounters.hpp" #include "gc/shared/generationCounters.hpp" +#include "runtime/mutex.hpp" #include "services/memoryManager.hpp" #include "services/memoryService.hpp" -#include "runtime/mutex.hpp" class CollectorCounters; class G1CollectedHeap; diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp b/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp index f01248424a026..4ded4bcccae4d 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp @@ -27,6 +27,7 @@ #define SHARE_GC_G1_G1MONOTONICARENA_INLINE_HPP #include "gc/g1/g1MonotonicArena.hpp" + #include "runtime/atomic.hpp" #include "utilities/globalCounter.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1MonotonicArenaFreeMemoryTask.cpp b/src/hotspot/share/gc/g1/g1MonotonicArenaFreeMemoryTask.cpp index cc857a5396907..40c199dee49b7 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArenaFreeMemoryTask.cpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArenaFreeMemoryTask.cpp @@ -28,8 +28,8 @@ #include "gc/g1/g1HeapRegionRemSet.hpp" #include "gc/g1/g1MonotonicArenaFreeMemoryTask.hpp" #include "gc/g1/g1MonotonicArenaFreePool.hpp" -#include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/suspendibleThreadSet.hpp" #include "runtime/os.hpp" diff --git a/src/hotspot/share/gc/g1/g1NMethodClosure.cpp b/src/hotspot/share/gc/g1/g1NMethodClosure.cpp index 8dc817f145224..d74aa5eae1d01 100644 --- a/src/hotspot/share/gc/g1/g1NMethodClosure.cpp +++ b/src/hotspot/share/gc/g1/g1NMethodClosure.cpp @@ -23,11 +23,11 @@ */ #include "code/nmethod.hpp" -#include "gc/g1/g1NMethodClosure.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" +#include "gc/g1/g1NMethodClosure.hpp" #include "gc/shared/barrierSetNMethod.hpp" #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp index 5ab1f58bce11a..8d84f144f0245 100644 --- a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp +++ b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp @@ -38,8 +38,8 @@ #include "memory/iterator.inline.hpp" #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" -#include "oops/oopsHierarchy.hpp" #include "oops/oop.inline.hpp" +#include "oops/oopsHierarchy.hpp" #include "runtime/prefetch.inline.hpp" #include "utilities/align.hpp" diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp index 94ca0878df8e5..4d569622238e5 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp @@ -26,8 +26,8 @@ #define SHARE_GC_G1_G1PARSCANTHREADSTATE_HPP #include "gc/g1/g1CollectedHeap.hpp" -#include "gc/g1/g1RedirtyCardsQueue.hpp" #include "gc/g1/g1OopClosures.hpp" +#include "gc/g1/g1RedirtyCardsQueue.hpp" #include "gc/g1/g1YoungGCAllocationFailureInjector.hpp" #include "gc/shared/ageTable.hpp" #include "gc/shared/copyFailedInfo.hpp" diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index deb67656a417c..6488bf44e41e7 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -28,20 +28,21 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" +#include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineStats.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" +#include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" #include "gc/g1/g1IHOPControl.hpp" -#include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1Policy.hpp" #include "gc/g1/g1SurvivorRegions.hpp" #include "gc/g1/g1YoungGenSizer.hpp" #include "gc/shared/concurrentGCBreakpoints.hpp" #include "gc/shared/gcPolicyCounters.hpp" +#include "gc/shared/gcTraceTime.inline.hpp" #include "logging/log.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" @@ -49,8 +50,6 @@ #include "utilities/growableArray.hpp" #include "utilities/pair.hpp" -#include "gc/shared/gcTraceTime.inline.hpp" - G1Policy::G1Policy(STWGCTimer* gc_timer) : _predictor((100 - G1ConfidencePercent) / 100.0), _analytics(new G1Analytics(&_predictor)), diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index fb24b5ab72bbc..37d66ee6535bf 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -31,8 +31,8 @@ #include "gc/g1/g1HeapRegionAttr.hpp" #include "gc/g1/g1MMUTracker.hpp" #include "gc/g1/g1OldGenAllocationTracker.hpp" -#include "gc/g1/g1RemSetTrackingPolicy.hpp" #include "gc/g1/g1Predictions.hpp" +#include "gc/g1/g1RemSetTrackingPolicy.hpp" #include "gc/g1/g1YoungGenSizer.hpp" #include "gc/shared/gcCause.hpp" #include "runtime/atomic.hpp" diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index a25fce2d006c9..6ef99d4908573 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -40,12 +40,12 @@ #include "gc/g1/g1HeapRegionRemSet.inline.hpp" #include "gc/g1/g1OopClosures.inline.hpp" #include "gc/g1/g1Policy.hpp" -#include "gc/g1/g1RootClosures.hpp" #include "gc/g1/g1RemSet.hpp" +#include "gc/g1/g1RootClosures.hpp" #include "gc/shared/bufferNode.hpp" #include "gc/shared/bufferNodeList.hpp" -#include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcTraceTime.inline.hpp" #include "jfr/jfrEvents.hpp" #include "memory/iterator.hpp" #include "memory/resourceArea.hpp" @@ -1445,6 +1445,7 @@ void G1RemSet::print_merge_heap_roots_stats() { void G1RemSet::merge_heap_roots(bool initial_evacuation) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); + G1GCPhaseTimes* pt = g1h->phase_times(); { Ticks start = Ticks::now(); @@ -1453,9 +1454,9 @@ void G1RemSet::merge_heap_roots(bool initial_evacuation) { Tickspan total = Ticks::now() - start; if (initial_evacuation) { - g1h->phase_times()->record_prepare_merge_heap_roots_time(total.seconds() * 1000.0); + pt->record_prepare_merge_heap_roots_time(total.seconds() * 1000.0); } else { - g1h->phase_times()->record_or_add_optional_prepare_merge_heap_roots_time(total.seconds() * 1000.0); + pt->record_or_add_optional_prepare_merge_heap_roots_time(total.seconds() * 1000.0); } } @@ -1465,6 +1466,8 @@ void G1RemSet::merge_heap_roots(bool initial_evacuation) { uint const num_workers = initial_evacuation ? workers->active_workers() : MIN2(workers->active_workers(), (uint)increment_length); + Ticks start = Ticks::now(); + { G1MergeHeapRootsTask cl(_scan_state, num_workers, initial_evacuation); log_debug(gc, ergo)("Running %s using %u workers for %zu regions", @@ -1483,6 +1486,12 @@ void G1RemSet::merge_heap_roots(bool initial_evacuation) { } print_merge_heap_roots_stats(); + + if (initial_evacuation) { + pt->record_merge_heap_roots_time((Ticks::now() - start).seconds() * 1000.0); + } else { + pt->record_or_add_optional_merge_heap_roots_time((Ticks::now() - start).seconds() * 1000.0); + } } void G1RemSet::complete_evac_phase(bool has_more_than_one_evacuation_phase) { diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.cpp b/src/hotspot/share/gc/g1/g1ServiceThread.cpp index 5f664cfba3a21..22675ec2a64e1 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.cpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.cpp @@ -26,8 +26,8 @@ #include "logging/log.hpp" #include "runtime/cpuTimeCounters.hpp" #include "runtime/mutexLocker.hpp" -#include "runtime/timer.hpp" #include "runtime/os.hpp" +#include "runtime/timer.hpp" G1SentinelTask::G1SentinelTask() : G1ServiceTask("Sentinel Task") { set_time(max_jlong); diff --git a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp b/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp index dd3323f4079e2..edd4f35d5ed55 100644 --- a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp +++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp @@ -24,8 +24,8 @@ #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1SurvivorRegions.hpp" -#include "utilities/growableArray.hpp" #include "utilities/debug.hpp" +#include "utilities/growableArray.hpp" G1SurvivorRegions::G1SurvivorRegions() : _regions(new (mtGC) GrowableArray(8, mtGC)), diff --git a/src/hotspot/share/gc/g1/g1Trace.cpp b/src/hotspot/share/gc/g1/g1Trace.cpp index 3cc8a8a28e176..6c9a87e4d98c9 100644 --- a/src/hotspot/share/gc/g1/g1Trace.cpp +++ b/src/hotspot/share/gc/g1/g1Trace.cpp @@ -23,9 +23,9 @@ */ #include "gc/g1/g1EvacInfo.hpp" +#include "gc/g1/g1GCPauseType.hpp" #include "gc/g1/g1HeapRegionTraceType.hpp" #include "gc/g1/g1Trace.hpp" -#include "gc/g1/g1GCPauseType.hpp" #include "gc/shared/gcHeapSummary.hpp" #include "jfr/jfrEvents.hpp" #if INCLUDE_JFR diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 9552bd6d41812..260817f7a578f 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -25,8 +25,8 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1Policy.hpp" -#include "gc/g1/g1VMOperations.hpp" #include "gc/g1/g1Trace.hpp" +#include "gc/g1/g1VMOperations.hpp" #include "gc/shared/concurrentGCBreakpoints.hpp" #include "gc/shared/gcCause.hpp" #include "gc/shared/gcId.hpp" diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 44803564de1bc..450fc957bdc21 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -32,9 +32,9 @@ #include "gc/g1/g1CollectionSetCandidates.inline.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.hpp" -#include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" +#include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapRegionPrinter.hpp" #include "gc/g1/g1MonitoringSupport.hpp" #include "gc/g1/g1ParScanThreadState.inline.hpp" @@ -49,9 +49,9 @@ #include "gc/g1/g1YoungGCPostEvacuateTasks.hpp" #include "gc/g1/g1YoungGCPreEvacuateTasks.hpp" #include "gc/shared/concurrentGCBreakpoints.hpp" -#include "gc/shared/gcTraceTime.inline.hpp" -#include "gc/shared/gcTimer.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcTimer.hpp" +#include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/workerPolicy.hpp" @@ -486,7 +486,9 @@ void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info) calculate_collection_set(evacuation_info, policy()->max_pause_time_ms()); if (collector_state()->in_concurrent_start_gc()) { + Ticks start = Ticks::now(); concurrent_mark()->pre_concurrent_start(_gc_cause); + phase_times()->record_prepare_concurrent_task_time_ms((Ticks::now() - start).seconds() * 1000.0); } // Please see comment in g1CollectedHeap.hpp and @@ -743,11 +745,7 @@ void G1YoungCollector::evacuate_initial_collection_set(G1ParScanThreadStateSet* bool has_optional_evacuation_work) { G1GCPhaseTimes* p = phase_times(); - { - Ticks start = Ticks::now(); - rem_set()->merge_heap_roots(true /* initial_evacuation */); - p->record_merge_heap_roots_time((Ticks::now() - start).seconds() * 1000.0); - } + rem_set()->merge_heap_roots(true /* initial_evacuation */); Tickspan task_time; const uint num_workers = workers()->active_workers(); @@ -812,6 +810,7 @@ void G1YoungCollector::evacuate_next_optional_regions(G1ParScanThreadStateSet* p Tickspan total_processing = Ticks::now() - start_processing; G1GCPhaseTimes* p = phase_times(); + p->record_or_add_optional_evac_time(task_time.seconds() * 1000.0); p->record_or_add_nmethod_list_cleanup_time((total_processing - task_time).seconds() * 1000.0); } @@ -830,17 +829,9 @@ void G1YoungCollector::evacuate_optional_collection_set(G1ParScanThreadStateSet* break; } - { - Ticks start = Ticks::now(); - rem_set()->merge_heap_roots(false /* initial_evacuation */); - phase_times()->record_or_add_optional_merge_heap_roots_time((Ticks::now() - start).seconds() * 1000.0); - } + rem_set()->merge_heap_roots(false /* initial_evacuation */); - { - Ticks start = Ticks::now(); - evacuate_next_optional_regions(per_thread_states); - phase_times()->record_or_add_optional_evac_time((Ticks::now() - start).seconds() * 1000.0); - } + evacuate_next_optional_regions(per_thread_states); rem_set()->complete_evac_phase(true /* has_more_than_one_evacuation_phase */); } diff --git a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp index 63e7797981210..839b70d93afcd 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp @@ -25,9 +25,9 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1ConcurrentRefineStats.hpp" #include "gc/g1/g1DirtyCardQueue.hpp" -#include "gc/g1/g1YoungGCPreEvacuateTasks.hpp" #include "gc/g1/g1RegionPinCache.inline.hpp" #include "gc/g1/g1ThreadLocalData.hpp" +#include "gc/g1/g1YoungGCPreEvacuateTasks.hpp" #include "gc/shared/barrierSet.inline.hpp" #include "gc/shared/threadLocalAllocBuffer.inline.hpp" #include "memory/allocation.inline.hpp" diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index 874d3974ead53..abc0d7baa64ee 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -608,17 +608,20 @@ void MutableNUMASpace::print_short_on(outputStream* st) const { st->print(")"); } -void MutableNUMASpace::print_on(outputStream* st) const { - MutableSpace::print_on(st); +void MutableNUMASpace::print_on(outputStream* st, const char* prefix) const { + MutableSpace::print_on(st, prefix); + + StreamAutoIndentor indentor(st, 1); for (int i = 0; i < lgrp_spaces()->length(); i++) { LGRPSpace *ls = lgrp_spaces()->at(i); - st->print(" lgrp %u", ls->lgrp_id()); - ls->space()->print_on(st); + FormatBuffer<128> lgrp_message("lgrp %u ", ls->lgrp_id()); + ls->space()->print_on(st, lgrp_message); if (NUMAStats) { + StreamAutoIndentor indentor_numa(st, 1); for (int i = 0; i < lgrp_spaces()->length(); i++) { lgrp_spaces()->at(i)->accumulate_statistics(page_size()); } - st->print(" local/remote/unbiased/uncommitted: %zuK/" + st->print("local/remote/unbiased/uncommitted: %zuK/" "%zuK/%zuK/%zuK\n", ls->space_stats()->_local_space / K, ls->space_stats()->_remote_space / K, diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp index 10ca66e354992..abb4c77952af3 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -186,7 +186,7 @@ class MutableNUMASpace : public MutableSpace { virtual HeapWord* cas_allocate(size_t word_size); // Debugging - virtual void print_on(outputStream* st) const; + virtual void print_on(outputStream* st, const char* prefix) const; virtual void print_short_on(outputStream* st) const; virtual void verify(); diff --git a/src/hotspot/share/gc/parallel/mutableSpace.cpp b/src/hotspot/share/gc/parallel/mutableSpace.cpp index 19ec0fcf83991..498fb12511c36 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.cpp @@ -232,12 +232,13 @@ void MutableSpace::object_iterate(ObjectClosure* cl) { void MutableSpace::print_short() const { print_short_on(tty); } void MutableSpace::print_short_on( outputStream* st) const { - st->print(" space %zuK, %d%% used", capacity_in_bytes() / K, + st->print("space %zuK, %d%% used", capacity_in_bytes() / K, (int) ((double) used_in_bytes() * 100 / capacity_in_bytes())); } -void MutableSpace::print() const { print_on(tty); } -void MutableSpace::print_on(outputStream* st) const { +void MutableSpace::print() const { print_on(tty, ""); } +void MutableSpace::print_on(outputStream* st, const char* prefix) const { + st->print("%s", prefix); MutableSpace::print_short_on(st); st->print_cr(" [" PTR_FORMAT "," PTR_FORMAT "," PTR_FORMAT ")", p2i(bottom()), p2i(top()), p2i(end())); diff --git a/src/hotspot/share/gc/parallel/mutableSpace.hpp b/src/hotspot/share/gc/parallel/mutableSpace.hpp index 4583be0e1ea03..d09a2b2df89b7 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -138,7 +138,7 @@ class MutableSpace: public CHeapObj { // Debugging virtual void print() const; - virtual void print_on(outputStream* st) const; + virtual void print_on(outputStream* st, const char* prefix) const; virtual void print_short() const; virtual void print_short_on(outputStream* st) const; virtual void verify(); diff --git a/src/hotspot/share/gc/parallel/objectStartArray.cpp b/src/hotspot/share/gc/parallel/objectStartArray.cpp index e8d94b28d1879..d2c91b302b5bb 100644 --- a/src/hotspot/share/gc/parallel/objectStartArray.cpp +++ b/src/hotspot/share/gc/parallel/objectStartArray.cpp @@ -121,7 +121,7 @@ void ObjectStartArray::update_for_block_work(HeapWord* blk_start, assert(start_entry_for_region > end_entry, "Sanity check"); } - debug_only(verify_for_block(blk_start, blk_end);) + DEBUG_ONLY(verify_for_block(blk_start, blk_end);) } void ObjectStartArray::verify_for_block(HeapWord* blk_start, HeapWord* blk_end) const { diff --git a/src/hotspot/share/gc/parallel/parMarkBitMap.cpp b/src/hotspot/share/gc/parallel/parMarkBitMap.cpp index f33d7f93f1db6..d2d168cc2c0db 100644 --- a/src/hotspot/share/gc/parallel/parMarkBitMap.cpp +++ b/src/hotspot/share/gc/parallel/parMarkBitMap.cpp @@ -48,7 +48,8 @@ ParMarkBitMap::initialize(MemRegion covered_region) ReservedSpace rs = MemoryReserver::reserve(_reserved_byte_size, rs_align, - page_sz); + page_sz, + mtGC); if (!rs.is_reserved()) { // Failed to reserve memory for the bitmap, diff --git a/src/hotspot/share/gc/parallel/parMarkBitMap.hpp b/src/hotspot/share/gc/parallel/parMarkBitMap.hpp index 4c3cc39edec8b..aa788ff7be0da 100644 --- a/src/hotspot/share/gc/parallel/parMarkBitMap.hpp +++ b/src/hotspot/share/gc/parallel/parMarkBitMap.hpp @@ -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 @@ -58,9 +58,11 @@ class ParMarkBitMap: public CHeapObj { // Clear a range of bits corresponding to heap address range [beg, end). inline void clear_range(HeapWord* beg, HeapWord* end); - void print_on_error(outputStream* st) const { + void print_on(outputStream* st) const { st->print_cr("Marking Bits: (ParMarkBitMap*) " PTR_FORMAT, p2i(this)); - _beg_bits.print_on_error(st, " Begin Bits: "); + + StreamAutoIndentor indentor(st, 1); + _beg_bits.print_range_on(st, "Begin Bits: "); } #ifdef ASSERT diff --git a/src/hotspot/share/gc/parallel/parallelArguments.cpp b/src/hotspot/share/gc/parallel/parallelArguments.cpp index 6309f52c82ed2..2cddbafd87181 100644 --- a/src/hotspot/share/gc/parallel/parallelArguments.cpp +++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp @@ -66,6 +66,18 @@ void ParallelArguments::initialize() { } } + if (InitialSurvivorRatio < MinSurvivorRatio) { + if (FLAG_IS_CMDLINE(InitialSurvivorRatio)) { + if (FLAG_IS_CMDLINE(MinSurvivorRatio)) { + jio_fprintf(defaultStream::error_stream(), + "Inconsistent MinSurvivorRatio vs InitialSurvivorRatio: %d vs %d\n", MinSurvivorRatio, InitialSurvivorRatio); + } + FLAG_SET_DEFAULT(MinSurvivorRatio, InitialSurvivorRatio); + } else { + FLAG_SET_DEFAULT(InitialSurvivorRatio, MinSurvivorRatio); + } + } + // If InitialSurvivorRatio or MinSurvivorRatio were not specified, but the // SurvivorRatio has been set, reset their default values to SurvivorRatio + // 2. By doing this we make SurvivorRatio also work for Parallel Scavenger. @@ -101,17 +113,6 @@ void ParallelArguments::initialize_alignments() { void ParallelArguments::initialize_heap_flags_and_sizes_one_pass() { // Do basic sizing work GenArguments::initialize_heap_flags_and_sizes(); - - // The survivor ratio's are calculated "raw", unlike the - // default gc, which adds 2 to the ratio value. We need to - // make sure the values are valid before using them. - if (MinSurvivorRatio < 3) { - FLAG_SET_ERGO(MinSurvivorRatio, 3); - } - - if (InitialSurvivorRatio < 3) { - FLAG_SET_ERGO(InitialSurvivorRatio, 3); - } } void ParallelArguments::initialize_heap_flags_and_sizes() { diff --git a/src/hotspot/share/gc/parallel/parallelInitLogger.cpp b/src/hotspot/share/gc/parallel/parallelInitLogger.cpp index b28f0385c516f..1d9c9f840ba17 100644 --- a/src/hotspot/share/gc/parallel/parallelInitLogger.cpp +++ b/src/hotspot/share/gc/parallel/parallelInitLogger.cpp @@ -23,8 +23,8 @@ */ #include "gc/parallel/parallelInitLogger.hpp" -#include "gc/shared/genArguments.hpp" #include "gc/shared/gcLogPrecious.hpp" +#include "gc/shared/genArguments.hpp" void ParallelInitLogger::print_heap() { log_info_p(gc, init)("Alignments:" diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 244094f1acc78..0e63d760f48ca 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -662,27 +662,23 @@ bool ParallelScavengeHeap::print_location(outputStream* st, void* addr) const { return BlockLocationPrinter::print_location(st, addr); } -void ParallelScavengeHeap::print_on(outputStream* st) const { +void ParallelScavengeHeap::print_heap_on(outputStream* st) const { if (young_gen() != nullptr) { young_gen()->print_on(st); } if (old_gen() != nullptr) { old_gen()->print_on(st); } - MetaspaceUtils::print_on(st); } -void ParallelScavengeHeap::print_on_error(outputStream* st) const { - print_on(st); - st->cr(); - +void ParallelScavengeHeap::print_gc_on(outputStream* st) const { BarrierSet* bs = BarrierSet::barrier_set(); if (bs != nullptr) { bs->print_on(st); } - st->cr(); - PSParallelCompact::print_on_error(st); + + PSParallelCompact::print_on(st); } void ParallelScavengeHeap::gc_threads_do(ThreadClosure* tc) const { diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index ef9a4b4d5a1f9..887b78758cf6c 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -214,8 +214,8 @@ class ParallelScavengeHeap : public CollectedHeap { void prepare_for_verify() override; PSHeapSummary create_ps_heap_summary(); - void print_on(outputStream* st) const override; - void print_on_error(outputStream* st) const override; + void print_heap_on(outputStream* st) const override; + void print_gc_on(outputStream* st) const override; void gc_threads_do(ThreadClosure* tc) const override; void print_tracing_info() const override; diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp index 3bb0c0bf1192a..b4f860e053f8e 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp @@ -27,8 +27,8 @@ #include "gc/parallel/psGCAdaptivePolicyCounters.hpp" #include "gc/parallel/psScavenge.hpp" #include "gc/shared/gcCause.hpp" -#include "gc/shared/gcUtil.hpp" #include "gc/shared/gcPolicyCounters.hpp" +#include "gc/shared/gcUtil.hpp" #include "logging/log.hpp" #include "runtime/timer.hpp" #include "utilities/align.hpp" diff --git a/src/hotspot/share/gc/parallel/psCardTable.cpp b/src/hotspot/share/gc/parallel/psCardTable.cpp index f8841b9a164f5..22a38d816f639 100644 --- a/src/hotspot/share/gc/parallel/psCardTable.cpp +++ b/src/hotspot/share/gc/parallel/psCardTable.cpp @@ -32,8 +32,8 @@ #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/prefetch.inline.hpp" -#include "utilities/spinYield.hpp" #include "utilities/align.hpp" +#include "utilities/spinYield.hpp" // Checks an individual oop for missing precise marks. Mark // may be either dirty or newgen. diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.cpp b/src/hotspot/share/gc/parallel/psCompactionManager.cpp index f00e18c3297b9..ba9362778212f 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.cpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.cpp @@ -23,8 +23,8 @@ */ #include "gc/parallel/objectStartArray.hpp" -#include "gc/parallel/parMarkBitMap.inline.hpp" #include "gc/parallel/parallelScavengeHeap.hpp" +#include "gc/parallel/parMarkBitMap.inline.hpp" #include "gc/parallel/psCompactionManager.inline.hpp" #include "gc/parallel/psOldGen.hpp" #include "gc/parallel/psParallelCompact.inline.hpp" diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.hpp index 739d2cb1cc7e2..b013238a9f8cd 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.hpp @@ -28,8 +28,8 @@ #include "classfile/classLoaderData.hpp" #include "gc/parallel/psParallelCompact.hpp" #include "gc/shared/partialArraySplitter.hpp" -#include "gc/shared/partialArrayTaskStats.hpp" #include "gc/shared/partialArrayState.hpp" +#include "gc/shared/partialArrayTaskStats.hpp" #include "gc/shared/preservedMarks.hpp" #include "gc/shared/stringdedup/stringDedup.hpp" #include "gc/shared/taskqueue.hpp" diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp index fc3f7df926fe0..3bf125bc295da 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.cpp +++ b/src/hotspot/share/gc/parallel/psOldGen.cpp @@ -358,15 +358,12 @@ void PSOldGen::post_resize() { void PSOldGen::print() const { print_on(tty);} void PSOldGen::print_on(outputStream* st) const { - st->print(" %-15s", name()); - st->print(" total %zuK, used %zuK", - capacity_in_bytes()/K, used_in_bytes()/K); - st->print_cr(" [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", - p2i(virtual_space()->low_boundary()), - p2i(virtual_space()->high()), - p2i(virtual_space()->high_boundary())); - - st->print(" object"); object_space()->print_on(st); + st->print("%-15s", name()); + st->print(" total %zuK, used %zuK ", capacity_in_bytes() / K, used_in_bytes() / K); + virtual_space()->print_space_boundaries_on(st); + + StreamAutoIndentor indentor(st, 1); + object_space()->print_on(st, "object "); } void PSOldGen::update_counters() { diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index 99b975c282a91..ed8df5d42f609 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -210,8 +210,8 @@ void SplitInfo::verify_clear() #endif // #ifdef ASSERT -void PSParallelCompact::print_on_error(outputStream* st) { - _mark_bitmap.print_on_error(st); +void PSParallelCompact::print_on(outputStream* st) { + _mark_bitmap.print_on(st); } ParallelCompactData::ParallelCompactData() : @@ -246,7 +246,8 @@ ParallelCompactData::create_vspace(size_t count, size_t element_size) ReservedSpace rs = MemoryReserver::reserve(_reserved_byte_size, rs_align, - page_sz); + page_sz, + mtGC); if (!rs.is_reserved()) { // Failed to reserve memory. @@ -1629,7 +1630,7 @@ void PSParallelCompact::forward_to_new_addr() { } task(nworkers); ParallelScavengeHeap::heap()->workers().run_task(&task); - debug_only(verify_forward();) + DEBUG_ONLY(verify_forward();) } #ifdef ASSERT diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.hpp b/src/hotspot/share/gc/parallel/psParallelCompact.hpp index 878a09e283b18..290dd809ddde9 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.hpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.hpp @@ -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 @@ -846,7 +846,7 @@ class PSParallelCompact : AllStatic { // Return the SpaceId for the given address. static SpaceId space_id(HeapWord* addr); - static void print_on_error(outputStream* st); + static void print_on(outputStream* st); #ifdef ASSERT // Sanity check the new location of a word in the heap. diff --git a/src/hotspot/share/gc/parallel/psPromotionLAB.cpp b/src/hotspot/share/gc/parallel/psPromotionLAB.cpp index e3ed819ceb1ef..a6612d5cd2d71 100644 --- a/src/hotspot/share/gc/parallel/psPromotionLAB.cpp +++ b/src/hotspot/share/gc/parallel/psPromotionLAB.cpp @@ -45,7 +45,7 @@ void PSPromotionLAB::initialize(MemRegion lab) { // We can be initialized to a zero size! if (free() > 0) { if (ZapUnusedHeapArea) { - debug_only(Copy::fill_to_words(top(), free()/HeapWordSize, badHeapWord)); + DEBUG_ONLY(Copy::fill_to_words(top(), free()/HeapWordSize, badHeapWord)); } // NOTE! We need to allow space for a filler object. diff --git a/src/hotspot/share/gc/parallel/psPromotionLAB.hpp b/src/hotspot/share/gc/parallel/psPromotionLAB.hpp index e8e42d3754b36..60bd4ec250a9b 100644 --- a/src/hotspot/share/gc/parallel/psPromotionLAB.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionLAB.hpp @@ -55,7 +55,7 @@ class PSPromotionLAB : public CHeapObj { void set_end(HeapWord* value) { _end = value; } // The shared initialize code invokes this. - debug_only(virtual bool lab_is_valid(MemRegion lab) { return false; }); + DEBUG_ONLY(virtual bool lab_is_valid(MemRegion lab) { return false; }); PSPromotionLAB() : _top(nullptr), _bottom(nullptr), _end(nullptr), _state(zero_size) { } @@ -95,7 +95,7 @@ class PSYoungPromotionLAB : public PSPromotionLAB { // Not MT safe inline HeapWord* allocate(size_t size); - debug_only(virtual bool lab_is_valid(MemRegion lab);) + DEBUG_ONLY(virtual bool lab_is_valid(MemRegion lab);) }; class PSOldPromotionLAB : public PSPromotionLAB { @@ -127,7 +127,7 @@ class PSOldPromotionLAB : public PSPromotionLAB { return nullptr; } - debug_only(virtual bool lab_is_valid(MemRegion lab)); + DEBUG_ONLY(virtual bool lab_is_valid(MemRegion lab)); }; #endif // SHARE_GC_PARALLEL_PSPROMOTIONLAB_HPP diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index be31da5b05d3c..661bc3da3e111 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -44,8 +44,8 @@ #include "gc/shared/gcVMOperations.hpp" #include "gc/shared/isGCActiveMark.hpp" #include "gc/shared/oopStorage.inline.hpp" -#include "gc/shared/oopStorageSetParState.inline.hpp" #include "gc/shared/oopStorageParState.inline.hpp" +#include "gc/shared/oopStorageSetParState.inline.hpp" #include "gc/shared/referencePolicy.hpp" #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/referenceProcessorPhaseTimes.hpp" @@ -57,18 +57,18 @@ #include "gc/shared/workerPolicy.hpp" #include "gc/shared/workerThread.hpp" #include "gc/shared/workerUtils.hpp" +#include "logging/log.hpp" #include "memory/iterator.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" -#include "logging/log.hpp" #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/threadCritical.hpp" #include "runtime/threads.hpp" -#include "runtime/vmThread.hpp" #include "runtime/vmOperations.hpp" +#include "runtime/vmThread.hpp" #include "services/memoryService.hpp" #include "utilities/stack.inline.hpp" diff --git a/src/hotspot/share/gc/parallel/psScavenge.hpp b/src/hotspot/share/gc/parallel/psScavenge.hpp index 55abdfd3cf38e..8da555a8bb408 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.hpp +++ b/src/hotspot/share/gc/parallel/psScavenge.hpp @@ -28,8 +28,8 @@ #include "gc/parallel/psCardTable.hpp" #include "gc/parallel/psVirtualspace.hpp" #include "gc/shared/collectorCounters.hpp" -#include "gc/shared/referenceProcessor.hpp" #include "gc/shared/gcTrace.hpp" +#include "gc/shared/referenceProcessor.hpp" #include "memory/allStatic.hpp" #include "oops/oop.hpp" #include "utilities/stack.hpp" diff --git a/src/hotspot/share/gc/parallel/psVMOperations.cpp b/src/hotspot/share/gc/parallel/psVMOperations.cpp index 4873853a3ddfd..dc96d9506e278 100644 --- a/src/hotspot/share/gc/parallel/psVMOperations.cpp +++ b/src/hotspot/share/gc/parallel/psVMOperations.cpp @@ -22,8 +22,8 @@ * */ -#include "gc/parallel/psParallelCompact.inline.hpp" #include "gc/parallel/parallelScavengeHeap.inline.hpp" +#include "gc/parallel/psParallelCompact.inline.hpp" #include "gc/parallel/psScavenge.hpp" #include "gc/parallel/psVMOperations.hpp" #include "gc/shared/gcLocker.hpp" diff --git a/src/hotspot/share/gc/parallel/psVirtualspace.cpp b/src/hotspot/share/gc/parallel/psVirtualspace.cpp index ce4cd4ad977a6..b15264f5fcad2 100644 --- a/src/hotspot/share/gc/parallel/psVirtualspace.cpp +++ b/src/hotspot/share/gc/parallel/psVirtualspace.cpp @@ -124,6 +124,6 @@ void PSVirtualSpace::verify() const { #endif // #ifndef PRODUCT void PSVirtualSpace::print_space_boundaries_on(outputStream* st) const { - st->print_cr(" [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", + st->print_cr("[" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", p2i(low_boundary()), p2i(high()), p2i(high_boundary())); } diff --git a/src/hotspot/share/gc/parallel/psYoungGen.cpp b/src/hotspot/share/gc/parallel/psYoungGen.cpp index 309b293e5accf..cf21e45da9e72 100644 --- a/src/hotspot/share/gc/parallel/psYoungGen.cpp +++ b/src/hotspot/share/gc/parallel/psYoungGen.cpp @@ -700,13 +700,14 @@ void PSYoungGen::object_iterate(ObjectClosure* blk) { void PSYoungGen::print() const { print_on(tty); } void PSYoungGen::print_on(outputStream* st) const { - st->print(" %-15s", "PSYoungGen"); - st->print(" total %zuK, used %zuK", - capacity_in_bytes()/K, used_in_bytes()/K); + st->print("%-15s", name()); + st->print(" total %zuK, used %zuK ", capacity_in_bytes() / K, used_in_bytes() / K); virtual_space()->print_space_boundaries_on(st); - st->print(" eden"); eden_space()->print_on(st); - st->print(" from"); from_space()->print_on(st); - st->print(" to "); to_space()->print_on(st); + + StreamAutoIndentor indentor(st, 1); + eden_space()->print_on(st, "eden "); + from_space()->print_on(st, "from "); + to_space()->print_on(st, "to "); } size_t PSYoungGen::available_to_min_gen() { diff --git a/src/hotspot/share/gc/serial/defNewGeneration.cpp b/src/hotspot/share/gc/serial/defNewGeneration.cpp index 8ad42bc702dcb..cebfa743eac70 100644 --- a/src/hotspot/share/gc/serial/defNewGeneration.cpp +++ b/src/hotspot/share/gc/serial/defNewGeneration.cpp @@ -825,21 +825,15 @@ void DefNewGeneration::verify() { } void DefNewGeneration::print_on(outputStream* st) const { - st->print(" %-10s", name()); - - st->print(" total %zuK, used %zuK", - capacity()/K, used()/K); - st->print_cr(" [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", - p2i(_virtual_space.low_boundary()), - p2i(_virtual_space.high()), - p2i(_virtual_space.high_boundary())); - - st->print(" eden"); - eden()->print_on(st); - st->print(" from"); - from()->print_on(st); - st->print(" to "); - to()->print_on(st); + st->print("%-10s", name()); + + st->print(" total %zuK, used %zuK ", capacity() / K, used() / K); + _virtual_space.print_space_boundaries_on(st); + + StreamAutoIndentor indentor(st, 1); + eden()->print_on(st, "eden "); + from()->print_on(st, "from "); + to()->print_on(st, "to "); } HeapWord* DefNewGeneration::allocate(size_t word_size) { diff --git a/src/hotspot/share/gc/serial/serialArguments.cpp b/src/hotspot/share/gc/serial/serialArguments.cpp index 9b55e0cd9f69b..aed1c2353b4d8 100644 --- a/src/hotspot/share/gc/serial/serialArguments.cpp +++ b/src/hotspot/share/gc/serial/serialArguments.cpp @@ -22,10 +22,10 @@ * */ -#include "gc/shared/fullGCForwarding.hpp" -#include "gc/shared/gcArguments.hpp" #include "gc/serial/serialArguments.hpp" #include "gc/serial/serialHeap.hpp" +#include "gc/shared/fullGCForwarding.hpp" +#include "gc/shared/gcArguments.hpp" void SerialArguments::initialize() { GCArguments::initialize(); diff --git a/src/hotspot/share/gc/serial/serialBlockOffsetTable.cpp b/src/hotspot/share/gc/serial/serialBlockOffsetTable.cpp index 5baad7f995adf..afaf1aba53821 100644 --- a/src/hotspot/share/gc/serial/serialBlockOffsetTable.cpp +++ b/src/hotspot/share/gc/serial/serialBlockOffsetTable.cpp @@ -155,7 +155,7 @@ void SerialBlockOffsetTable::update_for_block_work(HeapWord* blk_start, assert(start_card_for_region > end_card, "Sanity check"); } - debug_only(verify_for_block(blk_start, blk_end);) + DEBUG_ONLY(verify_for_block(blk_start, blk_end);) } HeapWord* SerialBlockOffsetTable::block_start_reaching_into_card(const void* addr) const { diff --git a/src/hotspot/share/gc/serial/serialFullGC.cpp b/src/hotspot/share/gc/serial/serialFullGC.cpp index 9d0dc1932fcbc..1546870454754 100644 --- a/src/hotspot/share/gc/serial/serialFullGC.cpp +++ b/src/hotspot/share/gc/serial/serialFullGC.cpp @@ -22,8 +22,8 @@ * */ -#include "classfile/classLoaderDataGraph.hpp" #include "classfile/classLoaderData.inline.hpp" +#include "classfile/classLoaderDataGraph.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" @@ -43,11 +43,11 @@ #include "gc/shared/collectedHeap.inline.hpp" #include "gc/shared/continuationGCSupport.inline.hpp" #include "gc/shared/fullGCForwarding.inline.hpp" +#include "gc/shared/gc_globals.hpp" #include "gc/shared/gcHeapSummary.hpp" #include "gc/shared/gcTimer.hpp" #include "gc/shared/gcTrace.hpp" #include "gc/shared/gcTraceTime.inline.hpp" -#include "gc/shared/gc_globals.hpp" #include "gc/shared/modRefBarrierSet.hpp" #include "gc/shared/preservedMarks.inline.hpp" #include "gc/shared/referencePolicy.hpp" diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index ea2b656d4bd01..8d922692e5950 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -800,20 +800,15 @@ void SerialHeap::verify(VerifyOption option /* ignored */) { rem_set()->verify(); } -void SerialHeap::print_on(outputStream* st) const { +void SerialHeap::print_heap_on(outputStream* st) const { assert(_young_gen != nullptr, "precondition"); assert(_old_gen != nullptr, "precondition"); _young_gen->print_on(st); _old_gen->print_on(st); - - MetaspaceUtils::print_on(st); } -void SerialHeap::print_on_error(outputStream* st) const { - print_on(st); - st->cr(); - +void SerialHeap::print_gc_on(outputStream* st) const { BarrierSet* bs = BarrierSet::barrier_set(); if (bs != nullptr) { bs->print_on(st); diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index 2ec50814daf55..30b7b9a00858d 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -206,8 +206,8 @@ class SerialHeap : public CollectedHeap { void prepare_for_verify() override; void verify(VerifyOption option) override; - void print_on(outputStream* st) const override; - void print_on_error(outputStream* st) const override; + void print_heap_on(outputStream* st) const override; + void print_gc_on(outputStream* st) const override; void gc_threads_do(ThreadClosure* tc) const override; void print_tracing_info() const override; diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.cpp b/src/hotspot/share/gc/serial/tenuredGeneration.cpp index 22c4808a0bd17..975b0b5f98a99 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.cpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.cpp @@ -439,15 +439,12 @@ void TenuredGeneration::verify() { } void TenuredGeneration::print_on(outputStream* st) const { - st->print(" %-10s", name()); + st->print("%-10s", name()); - st->print(" total %zuK, used %zuK", + st->print(" total %zuK, used %zuK ", capacity()/K, used()/K); - st->print_cr(" [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", - p2i(_virtual_space.low_boundary()), - p2i(_virtual_space.high()), - p2i(_virtual_space.high_boundary())); + _virtual_space.print_space_boundaries_on(st); - st->print(" the"); - _the_space->print_on(st); + StreamAutoIndentor indentor(st, 1); + _the_space->print_on(st, "the "); } diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp b/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp index c6a6a0ae71634..d88b256629953 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_SERIAL_TENUREDGENERATION_INLINE_HPP #include "gc/serial/tenuredGeneration.hpp" + #include "gc/shared/space.hpp" inline size_t TenuredGeneration::capacity() const { diff --git a/src/hotspot/share/gc/shared/ageTable.cpp b/src/hotspot/share/gc/shared/ageTable.cpp index f5b62466556ed..2ed6ea967db4c 100644 --- a/src/hotspot/share/gc/shared/ageTable.cpp +++ b/src/hotspot/share/gc/shared/ageTable.cpp @@ -28,11 +28,11 @@ #include "gc/shared/gc_globals.hpp" #include "jvm.h" #include "logging/log.hpp" +#include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" #include "runtime/perfData.hpp" #include "utilities/copy.hpp" -#include "logging/logStream.hpp" /* Copyright (c) 1992, 2025, Oracle and/or its affiliates, and Stanford University. See the LICENSE file for license information. */ diff --git a/src/hotspot/share/gc/shared/barrierSetConfig.inline.hpp b/src/hotspot/share/gc/shared/barrierSetConfig.inline.hpp index 38c568889d1bf..f637c227ee1d9 100644 --- a/src/hotspot/share/gc/shared/barrierSetConfig.inline.hpp +++ b/src/hotspot/share/gc/shared/barrierSetConfig.inline.hpp @@ -27,8 +27,8 @@ #include "gc/shared/barrierSetConfig.hpp" -#include "gc/shared/modRefBarrierSet.inline.hpp" #include "gc/shared/cardTableBarrierSet.inline.hpp" +#include "gc/shared/modRefBarrierSet.inline.hpp" #if INCLUDE_EPSILONGC #include "gc/epsilon/epsilonBarrierSet.hpp" diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp index c5d8a7e54015f..41eb0a24b625a 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp @@ -35,8 +35,8 @@ #include "oops/method.inline.hpp" #include "runtime/frame.inline.hpp" #include "runtime/javaThread.hpp" -#include "runtime/threadWXSetters.inline.hpp" #include "runtime/threads.hpp" +#include "runtime/threadWXSetters.inline.hpp" #include "utilities/debug.hpp" #if INCLUDE_JVMCI #include "jvmci/jvmciRuntime.hpp" diff --git a/src/hotspot/share/gc/shared/bufferNode.cpp b/src/hotspot/share/gc/shared/bufferNode.cpp index e27b1279c7904..b064f9c7efedb 100644 --- a/src/hotspot/share/gc/shared/bufferNode.cpp +++ b/src/hotspot/share/gc/shared/bufferNode.cpp @@ -23,8 +23,8 @@ */ #include "gc/shared/bufferNode.hpp" -#include "utilities/debug.hpp" #include "memory/allocation.inline.hpp" +#include "utilities/debug.hpp" #include diff --git a/src/hotspot/share/gc/shared/c1/barrierSetC1.hpp b/src/hotspot/share/gc/shared/c1/barrierSetC1.hpp index 3d0621cdc7a67..e618a81b26be9 100644 --- a/src/hotspot/share/gc/shared/c1/barrierSetC1.hpp +++ b/src/hotspot/share/gc/shared/c1/barrierSetC1.hpp @@ -26,9 +26,9 @@ #define SHARE_GC_SHARED_C1_BARRIERSETC1_HPP #include "c1/c1_Decorators.hpp" -#include "c1/c1_LIRGenerator.hpp" #include "c1/c1_Instruction.hpp" #include "c1/c1_LIR.hpp" +#include "c1/c1_LIRGenerator.hpp" #include "memory/allocation.hpp" class LIRGenerator; diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp index 92154e7a46e96..f0ed9687f112d 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -24,8 +24,8 @@ #include "code/vmreg.inline.hpp" #include "gc/shared/barrierSet.hpp" -#include "gc/shared/tlab_globals.hpp" #include "gc/shared/c2/barrierSetC2.hpp" +#include "gc/shared/tlab_globals.hpp" #include "opto/arraycopynode.hpp" #include "opto/block.hpp" #include "opto/convertnode.hpp" diff --git a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp index 7df210d9fb622..536cd6da1ef8c 100644 --- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp @@ -23,9 +23,9 @@ */ #include "ci/ciUtilities.hpp" +#include "gc/shared/c2/cardTableBarrierSetC2.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" -#include "gc/shared/c2/cardTableBarrierSetC2.hpp" #include "gc/shared/gc_globals.hpp" #include "opto/arraycopynode.hpp" #include "opto/graphKit.hpp" diff --git a/src/hotspot/share/gc/shared/c2/modRefBarrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/modRefBarrierSetC2.cpp index cf5d3fc7e1668..ddc9caedd7165 100644 --- a/src/hotspot/share/gc/shared/c2/modRefBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/modRefBarrierSetC2.cpp @@ -22,10 +22,10 @@ * */ +#include "gc/shared/c2/modRefBarrierSetC2.hpp" #include "opto/arraycopynode.hpp" #include "opto/graphKit.hpp" #include "opto/idealKit.hpp" -#include "gc/shared/c2/modRefBarrierSetC2.hpp" Node* ModRefBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) const { DecoratorSet decorators = access.decorators(); diff --git a/src/hotspot/share/gc/shared/cardTable.cpp b/src/hotspot/share/gc/shared/cardTable.cpp index 3236d73933c49..76fe73abaf698 100644 --- a/src/hotspot/share/gc/shared/cardTable.cpp +++ b/src/hotspot/share/gc/shared/cardTable.cpp @@ -24,8 +24,8 @@ #include "gc/shared/cardTable.hpp" #include "gc/shared/collectedHeap.hpp" -#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/space.hpp" #include "logging/log.hpp" #include "memory/memoryReserver.hpp" @@ -80,7 +80,7 @@ void CardTable::initialize(void* region0_start, void* region1_start) { HeapWord* high_bound = _whole_heap.end(); const size_t rs_align = MAX2(_page_size, os::vm_allocation_granularity()); - ReservedSpace rs = MemoryReserver::reserve(_byte_map_size, rs_align, _page_size); + ReservedSpace rs = MemoryReserver::reserve(_byte_map_size, rs_align, _page_size, mtGC); if (!rs.is_reserved()) { vm_exit_during_initialization("Could not reserve enough space for the " diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index 9c55a894ee702..f0b90edeb7939 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -29,15 +29,15 @@ #include "gc/shared/barrierSet.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/collectedHeap.inline.hpp" -#include "gc/shared/gcLocker.hpp" +#include "gc/shared/gc_globals.hpp" #include "gc/shared/gcHeapSummary.hpp" -#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/shared/gcLocker.hpp" #include "gc/shared/gcTrace.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/gcVMOperations.hpp" #include "gc/shared/gcWhen.hpp" -#include "gc/shared/gc_globals.hpp" #include "gc/shared/memAllocator.hpp" +#include "gc/shared/stringdedup/stringDedup.hpp" #include "gc/shared/tlab_globals.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" @@ -45,7 +45,6 @@ #include "memory/metaspace.hpp" #include "memory/metaspaceUtils.hpp" #include "memory/reservedSpace.hpp" -#include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/instanceMirrorKlass.hpp" #include "oops/oop.inline.hpp" @@ -59,6 +58,7 @@ #include "utilities/align.hpp" #include "utilities/copy.hpp" #include "utilities/events.hpp" +#include "utilities/ostream.hpp" class ClassLoaderData; @@ -111,7 +111,12 @@ void GCHeapLog::log_heap(CollectedHeap* heap, bool before) { heap->total_collections(), heap->total_full_collections()); - heap->print_on(&st); + { + StreamAutoIndentor indentor(&st, 1); + heap->print_heap_on(&st); + MetaspaceUtils::print_on(&st); + } + st.print_cr("}"); } @@ -163,8 +168,10 @@ void CollectedHeap::print_heap_before_gc() { if (lt.is_enabled()) { LogStream ls(lt); ls.print_cr("Heap before GC invocations=%u (full %u):", total_collections(), total_full_collections()); - ResourceMark rm; - print_on(&ls); + + StreamAutoIndentor indentor(&ls, 1); + print_heap_on(&ls); + MetaspaceUtils::print_on(&ls); } if (_gc_heap_log != nullptr) { @@ -177,8 +184,10 @@ void CollectedHeap::print_heap_after_gc() { if (lt.is_enabled()) { LogStream ls(lt); ls.print_cr("Heap after GC invocations=%u (full %u):", total_collections(), total_full_collections()); - ResourceMark rm; - print_on(&ls); + + StreamAutoIndentor indentor(&ls, 1); + print_heap_on(&ls); + MetaspaceUtils::print_on(&ls); } if (_gc_heap_log != nullptr) { @@ -186,7 +195,10 @@ void CollectedHeap::print_heap_after_gc() { } } -void CollectedHeap::print() const { print_on(tty); } +void CollectedHeap::print() const { + print_heap_on(tty); + print_gc_on(tty); +} void CollectedHeap::trace_heap(GCWhen::Type when, const GCTracer* gc_tracer) { const GCHeapSummary& heap_summary = create_heap_summary(); diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index 7b3fbd1a1c483..8bf47d6b0bb96 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -179,7 +179,7 @@ class CollectedHeap : public CHeapObj { virtual void trace_heap(GCWhen::Type when, const GCTracer* tracer); // Verification functions - debug_only(static void check_for_valid_allocation_state();) + DEBUG_ONLY(static void check_for_valid_allocation_state();) public: enum Name { @@ -435,12 +435,14 @@ class CollectedHeap : public CHeapObj { // explicitly checks if the given memory location contains a null value. virtual bool contains_null(const oop* p) const; - // Print heap information on the given outputStream. - virtual void print_on(outputStream* st) const = 0; - // The default behavior is to call print_on() on tty. - virtual void print() const; + // Print heap information. + virtual void print_heap_on(outputStream* st) const = 0; + + // Print additional information about the GC that is not included in print_heap_on(). + virtual void print_gc_on(outputStream* st) const = 0; - virtual void print_on_error(outputStream* st) const = 0; + // The default behavior is to call print_heap_on() and print_gc_on() on tty. + virtual void print() const; // Used to print information about locations in the hs_err file. virtual bool print_location(outputStream* st, void* addr) const = 0; diff --git a/src/hotspot/share/gc/shared/gcHeapSummary.hpp b/src/hotspot/share/gc/shared/gcHeapSummary.hpp index fb048d06a41b0..ae08f60d3a647 100644 --- a/src/hotspot/share/gc/shared/gcHeapSummary.hpp +++ b/src/hotspot/share/gc/shared/gcHeapSummary.hpp @@ -26,8 +26,8 @@ #define SHARE_GC_SHARED_GCHEAPSUMMARY_HPP #include "memory/allocation.hpp" -#include "memory/metaspaceStats.hpp" #include "memory/metaspaceChunkFreeListSummary.hpp" +#include "memory/metaspaceStats.hpp" class VirtualSpaceSummary : public StackObj { HeapWord* _start; diff --git a/src/hotspot/share/gc/shared/gcInitLogger.cpp b/src/hotspot/share/gc/shared/gcInitLogger.cpp index f2d9fffdc110a..91bebf726c12d 100644 --- a/src/hotspot/share/gc/shared/gcInitLogger.cpp +++ b/src/hotspot/share/gc/shared/gcInitLogger.cpp @@ -22,9 +22,9 @@ * */ +#include "gc/shared/gc_globals.hpp" #include "gc/shared/gcInitLogger.hpp" #include "gc/shared/gcLogPrecious.hpp" -#include "gc/shared/gc_globals.hpp" #include "logging/log.hpp" #include "oops/compressedOops.hpp" #include "runtime/globals.hpp" diff --git a/src/hotspot/share/gc/shared/gcLocker.cpp b/src/hotspot/share/gc/shared/gcLocker.cpp index caf752f70c8d9..fbc953512bb28 100644 --- a/src/hotspot/share/gc/shared/gcLocker.cpp +++ b/src/hotspot/share/gc/shared/gcLocker.cpp @@ -25,15 +25,15 @@ #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gcLocker.hpp" #include "gc/shared/gcTrace.hpp" +#include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" -#include "logging/log.hpp" #include "runtime/atomic.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/safepoint.hpp" -#include "utilities/spinYield.hpp" #include "runtime/threadSMR.hpp" +#include "utilities/spinYield.hpp" #include "utilities/ticks.hpp" // GCLockerTimingDebugLogger tracks specific timing information for GC lock waits. diff --git a/src/hotspot/share/gc/shared/gcLogPrecious.hpp b/src/hotspot/share/gc/shared/gcLogPrecious.hpp index ec8b1c670f321..8aa70252b3d84 100644 --- a/src/hotspot/share/gc/shared/gcLogPrecious.hpp +++ b/src/hotspot/share/gc/shared/gcLogPrecious.hpp @@ -24,10 +24,10 @@ #ifndef SHARE_GC_SHARED_GCLOGPRECIOUS_HPP #define SHARE_GC_SHARED_GCLOGPRECIOUS_HPP -#include "utilities/globalDefinitions.hpp" #include "logging/logHandle.hpp" #include "memory/allStatic.hpp" #include "utilities/debug.hpp" +#include "utilities/globalDefinitions.hpp" class Mutex; class stringStream; diff --git a/src/hotspot/share/gc/shared/gcOverheadChecker.hpp b/src/hotspot/share/gc/shared/gcOverheadChecker.hpp index d4a9b5cd70d72..e29ae2ab911c6 100644 --- a/src/hotspot/share/gc/shared/gcOverheadChecker.hpp +++ b/src/hotspot/share/gc/shared/gcOverheadChecker.hpp @@ -26,9 +26,9 @@ #ifndef SHARE_GC_SHARED_GCOVERHEADCHECKER_HPP #define SHARE_GC_SHARED_GCOVERHEADCHECKER_HPP -#include "memory/allocation.hpp" -#include "gc/shared/gcCause.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcCause.hpp" +#include "memory/allocation.hpp" #include "runtime/globals.hpp" class SoftRefPolicy; diff --git a/src/hotspot/share/gc/shared/gcPolicyCounters.cpp b/src/hotspot/share/gc/shared/gcPolicyCounters.cpp index d24ad745aa14d..343ad6ca41ed5 100644 --- a/src/hotspot/share/gc/shared/gcPolicyCounters.cpp +++ b/src/hotspot/share/gc/shared/gcPolicyCounters.cpp @@ -22,8 +22,8 @@ * */ -#include "gc/shared/gcPolicyCounters.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcPolicyCounters.hpp" #include "memory/resourceArea.hpp" GCPolicyCounters::GCPolicyCounters(const char* name, int collectors, diff --git a/src/hotspot/share/gc/shared/gcTimer.cpp b/src/hotspot/share/gc/shared/gcTimer.cpp index 8585975015184..a5bc7bfb02fe5 100644 --- a/src/hotspot/share/gc/shared/gcTimer.cpp +++ b/src/hotspot/share/gc/shared/gcTimer.cpp @@ -22,8 +22,8 @@ * */ -#include "gc/shared/gcTimer.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcTimer.hpp" #include "utilities/growableArray.hpp" // the "time" parameter for most functions diff --git a/src/hotspot/share/gc/shared/gcTraceTime.cpp b/src/hotspot/share/gc/shared/gcTraceTime.cpp index def8a3f3f5c0c..5839027b585a0 100644 --- a/src/hotspot/share/gc/shared/gcTraceTime.cpp +++ b/src/hotspot/share/gc/shared/gcTraceTime.cpp @@ -23,8 +23,8 @@ */ #include "gc/shared/collectedHeap.hpp" -#include "gc/shared/gcTraceTime.inline.hpp" #include "gc/shared/gcTrace.hpp" +#include "gc/shared/gcTraceTime.inline.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/universe.hpp" diff --git a/src/hotspot/share/gc/shared/gcVMOperations.cpp b/src/hotspot/share/gc/shared/gcVMOperations.cpp index 8e5768ce6a98c..1632dca61d516 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.cpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.cpp @@ -25,10 +25,10 @@ #include "classfile/classLoaderData.hpp" #include "classfile/javaClasses.hpp" #include "gc/shared/allocTracer.hpp" +#include "gc/shared/gc_globals.hpp" #include "gc/shared/gcId.hpp" #include "gc/shared/gcLocker.hpp" #include "gc/shared/gcVMOperations.hpp" -#include "gc/shared/gc_globals.hpp" #include "gc/shared/softRefPolicy.hpp" #include "interpreter/oopMapCache.hpp" #include "logging/log.hpp" diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index ba29daf2fe144..56b8bc4e4ffd8 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -412,7 +412,7 @@ \ product(uintx, InitialSurvivorRatio, 8, \ "Initial ratio of young generation/survivor space size") \ - range(0, max_uintx) \ + range(3, max_uintx) \ \ product(bool, UseGCOverheadLimit, true, \ "Use policy to limit of proportion of time spent in GC " \ diff --git a/src/hotspot/share/gc/shared/hSpaceCounters.cpp b/src/hotspot/share/gc/shared/hSpaceCounters.cpp index de5dd2912a505..818d7422fba45 100644 --- a/src/hotspot/share/gc/shared/hSpaceCounters.cpp +++ b/src/hotspot/share/gc/shared/hSpaceCounters.cpp @@ -82,7 +82,7 @@ void HSpaceCounters::update_all(size_t capacity, size_t used) { update_used(used); } -debug_only( +DEBUG_ONLY( // for security reasons, we do not allow arbitrary reads from // the counters as they may live in shared memory. jlong HSpaceCounters::used() { diff --git a/src/hotspot/share/gc/shared/hSpaceCounters.hpp b/src/hotspot/share/gc/shared/hSpaceCounters.hpp index 63aabf1479bfb..01310e456f62c 100644 --- a/src/hotspot/share/gc/shared/hSpaceCounters.hpp +++ b/src/hotspot/share/gc/shared/hSpaceCounters.hpp @@ -56,7 +56,7 @@ class HSpaceCounters: public CHeapObj { void update_all(size_t capacity, size_t used); - debug_only( + DEBUG_ONLY( // for security reasons, we do not allow arbitrary reads from // the counters as they may live in shared memory. jlong used(); diff --git a/src/hotspot/share/gc/shared/locationPrinter.cpp b/src/hotspot/share/gc/shared/locationPrinter.cpp index b5efb540d459a..85fdcd37524fc 100644 --- a/src/hotspot/share/gc/shared/locationPrinter.cpp +++ b/src/hotspot/share/gc/shared/locationPrinter.cpp @@ -25,9 +25,9 @@ #include "gc/shared/collectedHeap.hpp" #include "gc/shared/locationPrinter.hpp" #include "memory/universe.hpp" -#include "runtime/os.hpp" #include "oops/klass.hpp" #include "oops/oop.inline.hpp" +#include "runtime/os.hpp" bool LocationPrinter::is_valid_obj(void* obj) { if (!is_object_aligned(obj)) { diff --git a/src/hotspot/share/gc/shared/markBitMap.cpp b/src/hotspot/share/gc/shared/markBitMap.cpp index b2b1e80246226..193cc0af4f79f 100644 --- a/src/hotspot/share/gc/shared/markBitMap.cpp +++ b/src/hotspot/share/gc/shared/markBitMap.cpp @@ -25,8 +25,8 @@ #include "gc/shared/markBitMap.inline.hpp" #include "memory/universe.hpp" -void MarkBitMap::print_on_error(outputStream* st, const char* prefix) const { - _bm.print_on_error(st, prefix); +void MarkBitMap::print_on(outputStream* st, const char* prefix) const { + _bm.print_range_on(st, prefix); } size_t MarkBitMap::compute_size(size_t heap_size) { diff --git a/src/hotspot/share/gc/shared/markBitMap.hpp b/src/hotspot/share/gc/shared/markBitMap.hpp index adfd930781359..a55a8220910ae 100644 --- a/src/hotspot/share/gc/shared/markBitMap.hpp +++ b/src/hotspot/share/gc/shared/markBitMap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -84,7 +84,7 @@ class MarkBitMap { inline HeapWord* get_next_marked_addr(const HeapWord* addr, HeapWord* limit) const; - void print_on_error(outputStream* st, const char* prefix) const; + void print_on(outputStream* st, const char* prefix) const; // Write marks. inline void mark(HeapWord* addr); diff --git a/src/hotspot/share/gc/shared/memAllocator.cpp b/src/hotspot/share/gc/shared/memAllocator.cpp index 64ca463571890..a8ba2074cd13e 100644 --- a/src/hotspot/share/gc/shared/memAllocator.cpp +++ b/src/hotspot/share/gc/shared/memAllocator.cpp @@ -35,8 +35,8 @@ #include "prims/jvmtiExport.hpp" #include "runtime/continuationJavaClasses.inline.hpp" #include "runtime/handles.inline.hpp" -#include "runtime/sharedRuntime.hpp" #include "runtime/javaThread.hpp" +#include "runtime/sharedRuntime.hpp" #include "services/lowMemoryDetector.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" @@ -126,6 +126,10 @@ bool MemAllocator::Allocation::check_out_of_memory() { // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support report_java_out_of_memory(message); if (JvmtiExport::should_post_resource_exhausted()) { +#ifdef CHECK_UNHANDLED_OOPS + // obj is null, no need to handle, but CheckUnhandledOops is not aware about null + THREAD->allow_unhandled_oop(_obj_ptr); +#endif // CHECK_UNHANDLED_OOPS JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, message); @@ -145,7 +149,7 @@ void MemAllocator::Allocation::verify_before() { // not take out a lock if from tlab, so clear here. JavaThread* THREAD = _thread; // For exception macros. assert(!HAS_PENDING_EXCEPTION, "Should not allocate with exception pending"); - debug_only(check_for_valid_allocation_state()); + DEBUG_ONLY(check_for_valid_allocation_state()); assert(!Universe::heap()->is_stw_gc_active(), "Allocation during GC pause not allowed"); } @@ -327,7 +331,7 @@ HeapWord* MemAllocator::mem_allocate(Allocation& allocation) const { } // Allocation of an oop can always invoke a safepoint. - debug_only(allocation._thread->check_for_valid_safepoint_state()); + DEBUG_ONLY(allocation._thread->check_for_valid_safepoint_state()); if (UseTLAB) { // Try refilling the TLAB and allocating the object in it. diff --git a/src/hotspot/share/gc/shared/parallelCleaning.cpp b/src/hotspot/share/gc/shared/parallelCleaning.cpp index 1b1eaf4d79ae2..8cb09939f2260 100644 --- a/src/hotspot/share/gc/shared/parallelCleaning.cpp +++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp @@ -22,13 +22,12 @@ * */ -#include "classfile/symbolTable.hpp" #include "classfile/stringTable.hpp" +#include "classfile/symbolTable.hpp" #include "code/codeCache.hpp" #include "gc/shared/parallelCleaning.hpp" #include "logging/log.hpp" #include "memory/resourceArea.hpp" -#include "logging/log.hpp" #include "runtime/atomic.hpp" CodeCacheUnloadingTask::CodeCacheUnloadingTask(uint num_workers, bool unloading_occurred) : diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index f79e012a36d4f..8db39281a05ec 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -32,6 +32,7 @@ #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" + #include PartialArrayState::PartialArrayState(oop src, oop dst, diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp index 1d43578b5fea5..9d127697e5875 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp @@ -25,8 +25,9 @@ #ifndef SHARE_GC_SHARED_PARTIALARRAYTASKSTEPPER_INLINE_HPP #define SHARE_GC_SHARED_PARTIALARRAYTASKSTEPPER_INLINE_HPP -#include "gc/shared/partialArrayState.hpp" #include "gc/shared/partialArrayTaskStepper.hpp" + +#include "gc/shared/partialArrayState.hpp" #include "runtime/atomic.hpp" #include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp index 83eaccd9813de..fd2bd8f3edc67 100644 --- a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp @@ -23,8 +23,8 @@ */ #include "gc/shared/gcTimer.hpp" -#include "gc/shared/referenceProcessorPhaseTimes.hpp" #include "gc/shared/referenceProcessor.inline.hpp" +#include "gc/shared/referenceProcessorPhaseTimes.hpp" #include "gc/shared/workerDataArray.inline.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" diff --git a/src/hotspot/share/gc/shared/satbMarkQueue.cpp b/src/hotspot/share/gc/shared/satbMarkQueue.cpp index 6e33d27897ccc..915eaa116fba4 100644 --- a/src/hotspot/share/gc/shared/satbMarkQueue.cpp +++ b/src/hotspot/share/gc/shared/satbMarkQueue.cpp @@ -22,8 +22,8 @@ * */ -#include "gc/shared/satbMarkQueue.hpp" #include "gc/shared/collectedHeap.hpp" +#include "gc/shared/satbMarkQueue.hpp" #include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "oops/oop.inline.hpp" diff --git a/src/hotspot/share/gc/shared/scavengableNMethods.cpp b/src/hotspot/share/gc/shared/scavengableNMethods.cpp index 0dff5526911af..887ac5f43a28a 100644 --- a/src/hotspot/share/gc/shared/scavengableNMethods.cpp +++ b/src/hotspot/share/gc/shared/scavengableNMethods.cpp @@ -131,13 +131,13 @@ bool ScavengableNMethods::has_scavengable_oops(nmethod* nm) { void ScavengableNMethods::nmethods_do_and_prune(NMethodToOopClosure* cl) { assert_locked_or_safepoint(CodeCache_lock); - debug_only(mark_on_list_nmethods()); + DEBUG_ONLY(mark_on_list_nmethods()); nmethod* prev = nullptr; nmethod* cur = _head; while (cur != nullptr) { ScavengableNMethodsData data = gc_data(cur); - debug_only(data.clear_marked()); + DEBUG_ONLY(data.clear_marked()); assert(data.on_list(), "else shouldn't be on this list"); if (cl != nullptr) { @@ -156,7 +156,7 @@ void ScavengableNMethods::nmethods_do_and_prune(NMethodToOopClosure* cl) { } // Check for stray marks. - debug_only(verify_nmethods()); + DEBUG_ONLY(verify_nmethods()); } void ScavengableNMethods::prune_nmethods_not_into_young() { @@ -166,13 +166,13 @@ void ScavengableNMethods::prune_nmethods_not_into_young() { void ScavengableNMethods::prune_unlinked_nmethods() { assert_locked_or_safepoint(CodeCache_lock); - debug_only(mark_on_list_nmethods()); + DEBUG_ONLY(mark_on_list_nmethods()); nmethod* prev = nullptr; nmethod* cur = _head; while (cur != nullptr) { ScavengableNMethodsData data = gc_data(cur); - debug_only(data.clear_marked()); + DEBUG_ONLY(data.clear_marked()); assert(data.on_list(), "else shouldn't be on this list"); nmethod* const next = data.next(); @@ -187,7 +187,7 @@ void ScavengableNMethods::prune_unlinked_nmethods() { } // Check for stray marks. - debug_only(verify_nmethods()); + DEBUG_ONLY(verify_nmethods()); } // Walk the list of methods which might contain oops to the java heap. diff --git a/src/hotspot/share/gc/shared/space.cpp b/src/hotspot/share/gc/shared/space.cpp index 6a9b2b90f44db..9256a70adbc98 100644 --- a/src/hotspot/share/gc/shared/space.cpp +++ b/src/hotspot/share/gc/shared/space.cpp @@ -76,10 +76,11 @@ void ContiguousSpace::mangle_unused_area(MemRegion mr) { #endif // NOT_PRODUCT -void ContiguousSpace::print() const { print_on(tty); } +void ContiguousSpace::print() const { print_on(tty, ""); } -void ContiguousSpace::print_on(outputStream* st) const { - st->print_cr(" space %zuK, %3d%% used [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", +void ContiguousSpace::print_on(outputStream* st, const char* prefix) const { + st->print_cr("%sspace %zuK, %3d%% used [" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", + prefix, capacity() / K, (int) ((double) used() * 100 / capacity()), p2i(bottom()), p2i(top()), p2i(end())); } diff --git a/src/hotspot/share/gc/shared/space.hpp b/src/hotspot/share/gc/shared/space.hpp index 5d361ce8e5063..75dd3f998d63e 100644 --- a/src/hotspot/share/gc/shared/space.hpp +++ b/src/hotspot/share/gc/shared/space.hpp @@ -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 @@ -94,7 +94,7 @@ class ContiguousSpace: public CHeapObj { size_t free() const { return byte_size(top(), end()); } void print() const; - void print_on(outputStream* st) const; + void print_on(outputStream* st, const char* prefix) const; // Initialization. // "initialize" should be called once on a space, before it is used for diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp index 9ad8decec7e08..b3f96da1cce47 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp @@ -25,9 +25,9 @@ #include "classfile/javaClasses.inline.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" +#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/oopStorage.hpp" #include "gc/shared/oopStorageSet.hpp" -#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/stringdedup/stringDedup.hpp" #include "gc/shared/stringdedup/stringDedupConfig.hpp" #include "gc/shared/stringdedup/stringDedupProcessor.hpp" diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp index 5c6628cba5cdd..83c6fea8c5b8e 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.cpp @@ -33,10 +33,10 @@ #include "gc/shared/stringdedup/stringDedupConfig.hpp" #include "gc/shared/stringdedup/stringDedupStat.hpp" #include "gc/shared/stringdedup/stringDedupTable.hpp" -#include "memory/allocation.hpp" -#include "memory/resourceArea.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" +#include "memory/allocation.hpp" +#include "memory/resourceArea.hpp" #include "oops/access.hpp" #include "oops/oopsHierarchy.hpp" #include "oops/typeArrayOop.inline.hpp" diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp index 19df8184a05e8..a163319e84c09 100644 --- a/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp +++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupTable.hpp @@ -25,9 +25,9 @@ #ifndef SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP #define SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUPTABLE_HPP -#include "memory/allStatic.hpp" #include "gc/shared/stringdedup/stringDedup.hpp" #include "gc/shared/stringdedup/stringDedupStat.hpp" +#include "memory/allStatic.hpp" #include "oops/typeArrayOop.hpp" #include "oops/weakHandle.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/gc/shared/taskTerminator.cpp b/src/hotspot/share/gc/shared/taskTerminator.cpp index 1353c3f8c8a06..cc84665087b43 100644 --- a/src/hotspot/share/gc/shared/taskTerminator.cpp +++ b/src/hotspot/share/gc/shared/taskTerminator.cpp @@ -24,8 +24,8 @@ */ #include "gc/shared/gc_globals.hpp" -#include "gc/shared/taskTerminator.hpp" #include "gc/shared/taskqueue.hpp" +#include "gc/shared/taskTerminator.hpp" #include "logging/log.hpp" #include "runtime/globals.hpp" #include "runtime/javaThread.hpp" diff --git a/src/hotspot/share/gc/shared/taskqueue.cpp b/src/hotspot/share/gc/shared/taskqueue.cpp index a244ba454150b..162eadc3cf0d2 100644 --- a/src/hotspot/share/gc/shared/taskqueue.cpp +++ b/src/hotspot/share/gc/shared/taskqueue.cpp @@ -23,8 +23,8 @@ */ #include "gc/shared/taskqueue.hpp" -#include "oops/oop.inline.hpp" #include "logging/log.hpp" +#include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.hpp" diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp index 32a830a2bb138..22d19b77806c1 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp @@ -29,8 +29,8 @@ #include "gc/shared/collectedHeap.hpp" #include "gc/shared/tlab_globals.hpp" -#include "memory/universe.hpp" #include "logging/log.hpp" +#include "memory/universe.hpp" #include "runtime/javaThread.hpp" #include "runtime/osThread.hpp" #include "utilities/copy.hpp" diff --git a/src/hotspot/share/gc/shared/weakProcessor.cpp b/src/hotspot/share/gc/shared/weakProcessor.cpp index d7de8b5d8a88a..bc7e9ad637554 100644 --- a/src/hotspot/share/gc/shared/weakProcessor.cpp +++ b/src/hotspot/share/gc/shared/weakProcessor.cpp @@ -27,8 +27,8 @@ #include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageParState.inline.hpp" #include "gc/shared/oopStorageSet.hpp" -#include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/oopStorageSetParState.inline.hpp" +#include "gc/shared/weakProcessor.inline.hpp" #include "gc/shared/weakProcessorTimes.hpp" #include "memory/allocation.inline.hpp" #include "memory/iterator.hpp" diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index 9a12e0ed1a112..9f58016a6f14c 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -26,6 +26,7 @@ #include "c1/c1_IR.hpp" #include "gc/shared/satbMarkQueue.hpp" +#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" @@ -33,7 +34,6 @@ #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp" #ifdef ASSERT #define __ gen->lir(__FILE__, __LINE__)-> diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 8df4413cc2f49..f12b3dc5fa84a 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -25,15 +25,15 @@ #include "classfile/javaClasses.hpp" #include "gc/shared/barrierSet.hpp" +#include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp" +#include "gc/shenandoah/c2/shenandoahSupport.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahForwarding.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp" -#include "gc/shenandoah/c2/shenandoahSupport.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "opto/arraycopynode.hpp" #include "opto/escape.hpp" #include "opto/graphKit.hpp" @@ -896,7 +896,7 @@ void ShenandoahBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCo uint gc_state_idx = Compile::AliasIdxRaw; const TypePtr* gc_state_adr_type = nullptr; // debug-mode-only argument - debug_only(gc_state_adr_type = phase->C->get_adr_type(gc_state_idx)); + DEBUG_ONLY(gc_state_adr_type = phase->C->get_adr_type(gc_state_idx)); Node* gc_state = phase->transform_later(new LoadBNode(ctrl, mem, gc_state_addr, gc_state_adr_type, TypeInt::BYTE, MemNode::unordered)); Node* stable_and = phase->transform_later(new AndINode(gc_state, phase->igvn().intcon(ShenandoahHeap::HAS_FORWARDED))); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 4652dbce0e36d..56d88d44d2797 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -25,8 +25,8 @@ #include "classfile/javaClasses.hpp" -#include "gc/shenandoah/c2/shenandoahSupport.hpp" #include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp" +#include "gc/shenandoah/c2/shenandoahSupport.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahForwarding.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 5280e9b2ac47b..eb87a9dc4c179 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -26,9 +26,9 @@ #include "gc/shared/gcCause.hpp" +#include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" -#include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index a47024f3a4b87..68e540960c794 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -26,10 +26,10 @@ #ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHADAPTIVEHEURISTICS_HPP #define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHADAPTIVEHEURISTICS_HPP -#include "memory/allocation.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" +#include "memory/allocation.hpp" #include "utilities/numberSeq.hpp" class ShenandoahAllocationRate : public CHeapObj { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp index 9d211314474a7..403405b984da5 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp @@ -24,8 +24,8 @@ */ -#include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp" +#include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index afc9abc496eb3..08fd45993462b 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -26,13 +26,12 @@ #include "gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahEvacInfo.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" -#include "gc/shenandoah/shenandoahEvacInfo.hpp" #include "gc/shenandoah/shenandoahTrace.hpp" - #include "logging/log.hpp" ShenandoahGenerationalHeuristics::ShenandoahGenerationalHeuristics(ShenandoahGeneration* generation) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp index 919f6c88afa9e..b8d85de04871d 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp @@ -25,10 +25,9 @@ #include "gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" -#include "gc/shenandoah/shenandoahGlobalGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahGlobalGeneration.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" - #include "utilities/quickSort.hpp" ShenandoahGlobalHeuristics::ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 4c7ed07b2a053..b151a75e6e7e5 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -25,10 +25,10 @@ */ #include "gc/shared/gcCause.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" #include "runtime/globals_extension.hpp" diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index cf1a76ff4ff68..2d0bbfd5e4a3c 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -639,12 +639,9 @@ bool ShenandoahOldHeuristics::should_resume_old_cycle() { bool ShenandoahOldHeuristics::should_start_gc() { const ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (_old_generation->is_doing_mixed_evacuations()) { - // Do not try to start an old cycle if we are waiting for old regions to be evacuated (we need - // a young cycle for this). Note that the young heuristic has a feature to expedite old evacuations. - // Future refinement: under certain circumstances, we might be more sophisticated about this choice. - // For example, we could choose to abandon the previous old collection before it has completed evacuations. - log_debug(gc)("Not starting an old cycle because we are waiting for mixed evacuations"); + if (!_old_generation->is_idle()) { + // Do not try to start an old cycle if old-gen is marking, doing mixed evacuations, or coalescing and filling. + log_debug(gc)("Not starting an old cycle because old gen is busy"); return false; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp index 9f8c6225d23d8..3aca436104b19 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -30,7 +30,6 @@ #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" - #include "utilities/quickSort.hpp" ShenandoahYoungHeuristics::ShenandoahYoungHeuristics(ShenandoahYoungGeneration* generation) @@ -121,6 +120,7 @@ bool ShenandoahYoungHeuristics::should_start_gc() { if (old_time_elapsed < ShenandoahMinimumOldTimeMs) { // Do not decline_trigger() when waiting for minimum quantum of Old-gen marking. It is not at our discretion // to trigger at this time. + log_debug(gc)("Young heuristics declines to trigger because old_time_elapsed < ShenandoahMinimumOldTimeMs"); return false; } } diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp index 6c8858341555d..fa784d5bb9051 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahGenerationalMode.cpp @@ -22,8 +22,8 @@ * */ -#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" #include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp index 893321db1dc2f..296a1979b01b3 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp @@ -23,8 +23,8 @@ */ #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" #include "gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp" +#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" #include "gc/shenandoah/mode/shenandoahPassiveMode.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "logging/log.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 446e19144074e..5d19a6a34e31e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -24,8 +24,8 @@ */ #include "gc/shared/barrierSetNMethod.hpp" -#include "gc/shenandoah/shenandoahBarrierSetClone.inline.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" +#include "gc/shenandoah/shenandoahBarrierSetClone.inline.hpp" #include "gc/shenandoah/shenandoahBarrierSetNMethod.hpp" #include "gc/shenandoah/shenandoahBarrierSetStackChunk.hpp" #include "gc/shenandoah/shenandoahCardTable.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index 20937b9d08e49..b176446452a19 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -30,6 +30,7 @@ #include "gc/shared/accessBarrierSupport.inline.hpp" #include "gc/shared/cardTable.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahCollectionSet.inline.hpp" @@ -40,7 +41,6 @@ #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "memory/iterator.inline.hpp" #include "oops/oop.inline.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCardTable.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCardTable.cpp index 00f119384895b..e9e52475fb92b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCardTable.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCardTable.cpp @@ -45,7 +45,7 @@ void ShenandoahCardTable::initialize() { // ReservedSpace constructor would assert rs_align >= os::vm_page_size(). const size_t rs_align = MAX2(_page_size, granularity); - ReservedSpace write_space = MemoryReserver::reserve(_byte_map_size, rs_align, _page_size); + ReservedSpace write_space = MemoryReserver::reserve(_byte_map_size, rs_align, _page_size, mtGC); initialize(write_space); // The assembler store_check code will do an unsigned shift of the oop, @@ -60,7 +60,7 @@ void ShenandoahCardTable::initialize() { _write_byte_map = _byte_map; _write_byte_map_base = _byte_map_base; - ReservedSpace read_space = MemoryReserver::reserve(_byte_map_size, rs_align, _page_size); + ReservedSpace read_space = MemoryReserver::reserve(_byte_map_size, rs_align, _page_size, mtGC); initialize(read_space); _read_byte_map = (CardValue*) read_space.base(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index c08c9501201b6..725e4e6e3e9f9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -31,10 +31,10 @@ #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahEvacOOMHandler.inline.hpp" -#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" +#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" +#include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "memory/iterator.inline.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp index 34db132ab83f9..d29c446f2102e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.hpp @@ -26,9 +26,9 @@ #define SHARE_GC_SHENANDOAH_SHENANDOAHCODEROOTS_HPP #include "code/codeCache.hpp" -#include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" +#include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "memory/allStatic.hpp" #include "memory/iterator.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp index 1d35316346302..25b900f8d7772 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp @@ -190,11 +190,11 @@ void ShenandoahCollectionSet::print_on(outputStream* out) const { byte_size_in_proper_unit(live()), proper_unit_for_byte_size(live()), byte_size_in_proper_unit(used()), proper_unit_for_byte_size(used())); - debug_only(size_t regions = 0;) + DEBUG_ONLY(size_t regions = 0;) for (size_t index = 0; index < _heap->num_regions(); index ++) { if (is_in(index)) { _heap->get_region(index)->print_on(out); - debug_only(regions ++;) + DEBUG_ONLY(regions ++;) } } assert(regions == count(), "Must match"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 7b9aafad149a0..5b5fe2c1af605 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -35,11 +35,10 @@ #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" -#include "gc/shenandoah/shenandoahOldGeneration.hpp" -#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" @@ -47,8 +46,9 @@ #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" #include "gc/shenandoah/shenandoahVMOperations.hpp" -#include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "gc/shenandoah/shenandoahWorkerPolicy.hpp" +#include "gc/shenandoah/shenandoahWorkGroup.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "memory/allocation.hpp" #include "prims/jvmtiTagMap.hpp" #include "runtime/vmThread.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index 4a0f43226d7dd..facba2236be81 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -33,13 +33,13 @@ #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMark.inline.hpp" +#include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" -#include "gc/shenandoah/shenandoahPhaseTimings.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahStringDedup.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" -#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "memory/iterator.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/continuation.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 0b20b4ddf9f61..d8eb8e0a4e1ad 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -23,6 +23,8 @@ * */ +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentGC.hpp" #include "gc/shenandoah/shenandoahControlThread.hpp" @@ -34,11 +36,9 @@ #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" #include "gc/shenandoah/shenandoahPacer.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "logging/log.hpp" -#include "memory/metaspaceUtils.hpp" #include "memory/metaspaceStats.hpp" +#include "memory/metaspaceUtils.hpp" ShenandoahControlThread::ShenandoahControlThread() : ShenandoahController(), @@ -299,7 +299,10 @@ void ShenandoahControlThread::service_concurrent_normal_cycle(GCCause::Cause cau // Full GC --------------------------/ // ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle)) return; + if (check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle)) { + log_info(gc)("Cancelled"); + return; + } ShenandoahGCSession session(cause, heap->global_generation()); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp index be99ef1d865fb..9d95b5df7ed30 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp @@ -25,10 +25,10 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLTHREAD_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLTHREAD_HPP -#include "gc/shared/gcCause.hpp" #include "gc/shared/concurrentGCThread.hpp" -#include "gc/shenandoah/shenandoahGC.hpp" +#include "gc/shared/gcCause.hpp" #include "gc/shenandoah/shenandoahController.hpp" +#include "gc/shenandoah/shenandoahGC.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.cpp b/src/hotspot/share/gc/shenandoah/shenandoahController.cpp index c430981bfe65f..52182b092c988 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahController.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.cpp @@ -24,9 +24,8 @@ */ #include "gc/shared/gc_globals.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahController.hpp" - -#include "shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp index 83cde94f50957..d24f52cb3f177 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp @@ -25,8 +25,8 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLLER_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLLER_HPP -#include "gc/shared/gcCause.hpp" #include "gc/shared/concurrentGCThread.hpp" +#include "gc/shared/gcCause.hpp" #include "gc/shenandoah/shenandoahAllocRequest.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 8cc4e2de8ea02..7aaf7bd9f482d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -39,9 +39,9 @@ #include "gc/shenandoah/shenandoahSTWMark.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" -#include "gc/shenandoah/shenandoahYoungGeneration.hpp" -#include "gc/shenandoah/shenandoahWorkerPolicy.hpp" #include "gc/shenandoah/shenandoahVMOperations.hpp" +#include "gc/shenandoah/shenandoahWorkerPolicy.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "runtime/vmThread.hpp" #include "utilities/events.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp index 3ddc8bf636e91..d393c17f64a01 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahEvacTracker.cpp @@ -24,11 +24,11 @@ */ #include "gc/shenandoah/shenandoahAgeCensus.hpp" -#include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahEvacTracker.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "runtime/threadSMR.inline.hpp" #include "runtime/thread.hpp" +#include "runtime/threadSMR.inline.hpp" ShenandoahEvacuationStats::ShenandoahEvacuationStats() : _evacuations_completed(0), _bytes_completed(0), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index e87c771dec49c..9787611d4fb93 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -31,9 +31,9 @@ #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" -#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" #include "gc/shenandoah/shenandoahSimpleBitMap.inline.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "runtime/orderAccess.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 80954af6d1a80..902e6547afd8f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -26,8 +26,8 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHFREESET_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHFREESET_HPP -#include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" // Each ShenandoahHeapRegion is associated with a ShenandoahFreeSetPartitionId. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index 9436f09cbe050..f017b3de96287 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -34,23 +34,22 @@ #include "gc/shared/workerThread.hpp" #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" -#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" -#include "gc/shenandoah/shenandoahConcurrentGC.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahConcurrentGC.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahFullGC.hpp" #include "gc/shenandoah/shenandoahGenerationalFullGC.hpp" #include "gc/shenandoah/shenandoahGlobalGeneration.hpp" -#include "gc/shenandoah/shenandoahPhaseTimings.hpp" -#include "gc/shenandoah/shenandoahMark.inline.hpp" -#include "gc/shenandoah/shenandoahMonitoringSupport.hpp" -#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" -#include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" +#include "gc/shenandoah/shenandoahHeapRegionSet.hpp" +#include "gc/shenandoah/shenandoahMark.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahMetrics.hpp" +#include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahSTWMark.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp index f10d5eef969da..f08bdce0a2025 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGC.hpp @@ -25,8 +25,8 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHGC_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHGC_HPP -#include "memory/allocation.hpp" #include "gc/shared/gcCause.hpp" +#include "memory/allocation.hpp" /* * Base class of three Shenandoah GC modes diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index ad91119ddad98..9a511de939ccb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -23,8 +23,9 @@ * */ -#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectionSetPreselector.hpp" +#include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" @@ -37,8 +38,6 @@ #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" - #include "utilities/quickSort.hpp" template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index 31c86985c6f58..242acbdea8cea 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -25,12 +25,12 @@ #ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP #define SHARE_VM_GC_SHENANDOAH_SHENANDOAHGENERATION_HPP -#include "memory/allocation.hpp" #include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp" #include "gc/shenandoah/shenandoahAffiliation.hpp" #include "gc/shenandoah/shenandoahGenerationType.hpp" #include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahMarkingContext.hpp" +#include "memory/allocation.hpp" class ShenandoahCollectionSet; class ShenandoahHeap; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp index c6827878cd1f2..17f3d2f199f1a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationSizer.cpp @@ -23,13 +23,13 @@ * */ +#include "gc/shared/gc_globals.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationSizer.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" -#include "gc/shared/gc_globals.hpp" #include "logging/log.hpp" #include "runtime/globals_extension.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 4d291ba50f792..6b33d5207d040 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -27,22 +27,22 @@ #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentGC.hpp" -#include "gc/shenandoah/shenandoahGenerationalControlThread.hpp" #include "gc/shenandoah/shenandoahDegeneratedGC.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahFullGC.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahGenerationalControlThread.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" -#include "gc/shenandoah/shenandoahOldGC.hpp" -#include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" +#include "gc/shenandoah/shenandoahOldGC.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahPacer.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/log.hpp" -#include "memory/metaspaceUtils.hpp" #include "memory/metaspaceStats.hpp" +#include "memory/metaspaceUtils.hpp" #include "runtime/atomic.hpp" #include "utilities/events.hpp" @@ -273,13 +273,9 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest } } - // If this was the requested GC cycle, notify waiters about it - if (ShenandoahCollectorPolicy::is_explicit_gc(request.cause)) { - notify_gc_waiters(); - } - - // If this cycle completed successfully, notify threads waiting to retry allocation + // If this cycle completed successfully, notify threads waiting for gc if (!_heap->cancelled_gc()) { + notify_gc_waiters(); notify_alloc_failure_waiters(); } @@ -486,17 +482,14 @@ bool ShenandoahGenerationalControlThread::resume_concurrent_old_cycle(Shenandoah } if (_heap->cancelled_gc()) { - // It's possible the gc cycle was cancelled after the last time - // the collection checked for cancellation. In which case, the - // old gc cycle is still completed, and we have to deal with this - // cancellation. We set the degeneration point to be outside - // the cycle because if this is an allocation failure, that is - // what must be done (there is no degenerated old cycle). If the - // cancellation was due to a heuristic wanting to start a young - // cycle, then we are not actually going to a degenerated cycle, - // so the degenerated point doesn't matter here. - check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle); - if (cause == GCCause::_shenandoah_concurrent_gc) { + // It's possible the gc cycle was cancelled after the last time the collection checked for cancellation. In which + // case, the old gc cycle is still completed, and we have to deal with this cancellation. We set the degeneration + // point to be outside the cycle because if this is an allocation failure, that is what must be done (there is no + // degenerated old cycle). If the cancellation was due to a heuristic wanting to start a young cycle, then we are + // not actually going to a degenerated cycle, so don't set the degeneration point here. + if (ShenandoahCollectorPolicy::is_allocation_failure(cause)) { + check_cancellation_or_degen(ShenandoahGC::_degenerated_outside_cycle); + } else if (cause == GCCause::_shenandoah_concurrent_gc) { _heap->shenandoah_policy()->record_interrupted_old(); } return false; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp index 6a845afa4fdff..ba9ef5979a89d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -31,8 +31,8 @@ #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahPacer.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" -#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" class ShenandoahConcurrentEvacuator : public ObjectClosure { private: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp index 3387ed9d7a846..142c2d4798948 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp @@ -25,14 +25,14 @@ #include "gc/shared/fullGCForwarding.inline.hpp" #include "gc/shared/preservedMarks.inline.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalFullGC.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" -#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" -#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #ifdef ASSERT void assert_regions_used_not_more_than_capacity(ShenandoahGeneration* generation) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 09b985e3b8d55..1f84feb20e8f4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -40,9 +40,9 @@ #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahRegulatorThread.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahWorkerPolicy.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" -#include "gc/shenandoah/shenandoahUtils.hpp" #include "logging/log.hpp" #include "utilities/events.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp index 230fff162527b..a16a71b81759a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp @@ -22,8 +22,8 @@ * */ -#include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp" +#include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGlobalGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp index d51a77fdf8f88..5857170d4ccc2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp @@ -26,8 +26,8 @@ #define SHARE_VM_GC_SHENANDOAH_SHENANDOAHGLOBALGENERATION_HPP #include "gc/shenandoah/shenandoahGeneration.hpp" -#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" // A "generation" that represents the whole heap. class ShenandoahGlobalGeneration : public ShenandoahGeneration { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 045f485090d2c..41faf3efa2418 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -27,7 +27,6 @@ #include "cds/archiveHeapWriter.hpp" #include "classfile/systemDictionary.hpp" - #include "gc/shared/classUnloadingContext.hpp" #include "gc/shared/fullGCForwarding.hpp" #include "gc/shared/gcArguments.hpp" @@ -37,24 +36,26 @@ #include "gc/shared/memAllocator.hpp" #include "gc/shared/plab.hpp" #include "gc/shared/tlab_globals.hpp" - #include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp" +#include "gc/shenandoah/mode/shenandoahPassiveMode.hpp" +#include "gc/shenandoah/mode/shenandoahSATBMode.hpp" #include "gc/shenandoah/shenandoahAllocRequest.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" +#include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCodeRoots.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentMark.hpp" #include "gc/shenandoah/shenandoahControlThread.hpp" -#include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahGlobalGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahInitLogger.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" @@ -73,18 +74,9 @@ #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahVerifier.hpp" #include "gc/shenandoah/shenandoahVMOperations.hpp" -#include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "gc/shenandoah/shenandoahWorkerPolicy.hpp" +#include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" -#include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp" -#include "gc/shenandoah/mode/shenandoahPassiveMode.hpp" -#include "gc/shenandoah/mode/shenandoahSATBMode.hpp" - -#if INCLUDE_JFR -#include "gc/shenandoah/shenandoahJfrSupport.hpp" -#endif - -#include "memory/allocation.hpp" #include "memory/allocation.hpp" #include "memory/classLoaderMetaspace.hpp" #include "memory/memoryReserver.hpp" @@ -103,9 +95,12 @@ #include "runtime/stackWatermarkSet.hpp" #include "runtime/threads.hpp" #include "runtime/vmThread.hpp" -#include "utilities/globalDefinitions.hpp" #include "utilities/events.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" +#if INCLUDE_JFR +#include "gc/shenandoah/shenandoahJfrSupport.hpp" +#endif class ShenandoahPretouchHeapTask : public WorkerTask { private: @@ -166,7 +161,7 @@ static ReservedSpace reserve(size_t size, size_t preferred_page_size) { size = align_up(size, alignment); } - const ReservedSpace reserved = MemoryReserver::reserve(size, alignment, preferred_page_size); + const ReservedSpace reserved = MemoryReserver::reserve(size, alignment, preferred_page_size, mtGC); if (!reserved.is_reserved()) { vm_exit_during_initialization("Could not reserve space"); } @@ -380,7 +375,7 @@ jint ShenandoahHeap::initialize() { for (uintptr_t addr = min; addr <= max; addr <<= 1u) { char* req_addr = (char*)addr; assert(is_aligned(req_addr, cset_align), "Should be aligned"); - cset_rs = MemoryReserver::reserve(req_addr, cset_size, cset_align, cset_page_size); + cset_rs = MemoryReserver::reserve(req_addr, cset_size, cset_align, cset_page_size, mtGC); if (cset_rs.is_reserved()) { assert(cset_rs.base() == req_addr, "Allocated where requested: " PTR_FORMAT ", " PTR_FORMAT, p2i(cset_rs.base()), addr); _collection_set = new ShenandoahCollectionSet(this, cset_rs, sh_rs.base()); @@ -389,7 +384,7 @@ jint ShenandoahHeap::initialize() { } if (_collection_set == nullptr) { - cset_rs = MemoryReserver::reserve(cset_size, cset_align, os::vm_page_size()); + cset_rs = MemoryReserver::reserve(cset_size, cset_align, os::vm_page_size(), mtGC); if (!cset_rs.is_reserved()) { vm_exit_during_initialization("Cannot reserve memory for collection set"); } @@ -588,7 +583,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : #pragma warning( pop ) #endif -void ShenandoahHeap::print_on(outputStream* st) const { +void ShenandoahHeap::print_heap_on(outputStream* st) const { st->print_cr("Shenandoah Heap"); st->print_cr(" %zu%s max, %zu%s soft max, %zu%s committed, %zu%s used", byte_size_in_proper_unit(max_capacity()), proper_unit_for_byte_size(max_capacity()), @@ -639,7 +634,6 @@ void ShenandoahHeap::print_on(outputStream* st) const { } st->cr(); - MetaspaceUtils::print_on(st); if (Verbose) { st->cr(); @@ -647,9 +641,7 @@ void ShenandoahHeap::print_on(outputStream* st) const { } } -void ShenandoahHeap::print_on_error(outputStream* st) const { - print_on(st); - st->cr(); +void ShenandoahHeap::print_gc_on(outputStream* st) const { print_heap_regions_on(st); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 46f8a1340512a..78d507c0f7dba 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -27,19 +27,19 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHHEAP_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHHEAP_HPP +#include "gc/shared/collectedHeap.hpp" #include "gc/shared/markBitMap.hpp" #include "gc/shared/softRefPolicy.hpp" -#include "gc/shared/collectedHeap.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahAllocRequest.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahController.hpp" -#include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahEvacOOMHandler.hpp" #include "gc/shenandoah/shenandoahEvacTracker.hpp" -#include "gc/shenandoah/shenandoahGenerationType.hpp" #include "gc/shenandoah/shenandoahGenerationSizer.hpp" +#include "gc/shenandoah/shenandoahGenerationType.hpp" +#include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahMmuTracker.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" #include "gc/shenandoah/shenandoahSharedVariables.hpp" #include "gc/shenandoah/shenandoahUnload.hpp" @@ -202,8 +202,8 @@ class ShenandoahHeap : public CollectedHeap { virtual void print_init_logger() const; void initialize_serviceability() override; - void print_on(outputStream* st) const override; - void print_on_error(outputStream *st) const override; + void print_heap_on(outputStream* st) const override; + void print_gc_on(outputStream *st) const override; void print_tracing_info() const override; void print_heap_regions_on(outputStream* st) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index f4ef186743cdf..cf9d808f7ce8f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -30,28 +30,28 @@ #include "gc/shenandoah/shenandoahHeap.hpp" #include "classfile/javaClasses.inline.hpp" -#include "gc/shared/markBitMap.inline.hpp" -#include "gc/shared/threadLocalAllocBuffer.inline.hpp" #include "gc/shared/continuationGCSupport.inline.hpp" +#include "gc/shared/markBitMap.inline.hpp" #include "gc/shared/suspendibleThreadSet.hpp" +#include "gc/shared/threadLocalAllocBuffer.inline.hpp" #include "gc/shared/tlab_globals.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahBarrierSet.inline.hpp" #include "gc/shenandoah/shenandoahCollectionSet.inline.hpp" #include "gc/shenandoah/shenandoahForwarding.inline.hpp" -#include "gc/shenandoah/shenandoahWorkGroup.hpp" -#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" -#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" +#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" +#include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" -#include "runtime/prefetch.inline.hpp" #include "runtime/objectMonitor.inline.hpp" +#include "runtime/prefetch.inline.hpp" #include "utilities/copy.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index a25e2dfd88f67..d00a99ee7289c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -29,14 +29,14 @@ #include "gc/shared/tlab_globals.hpp" #include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" -#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" +#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/shenandoahGeneration.hpp" -#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "jfr/jfrEvents.hpp" #include "memory/allocation.hpp" #include "memory/iterator.inline.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp index 7f29a8628aab5..0df482c1e2dab 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp @@ -27,8 +27,9 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGION_INLINE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGION_INLINE_HPP -#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" + +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahPacer.inline.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp index 360c7d2d649a5..918e6bf1be626 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.cpp @@ -28,8 +28,8 @@ #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" -#include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "gc/shenandoah/shenandoahHeapRegionCounters.hpp" +#include "gc/shenandoah/shenandoahHeapRegionSet.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "runtime/atomic.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.hpp index c139980af4136..508b40e49a80e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionCounters.hpp @@ -26,8 +26,8 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGIONCOUNTERS_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGIONCOUNTERS_HPP -#include "memory/allocation.hpp" #include "logging/logFileStreamOutput.hpp" +#include "memory/allocation.hpp" /** * This provides the following in JVMStat: diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp index 46d54c70fe159..368738fe5ead7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.cpp @@ -23,9 +23,9 @@ * */ -#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "runtime/atomic.hpp" #include "utilities/copy.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.hpp index d933fda60b177..f81bf77d26e82 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegionSet.hpp @@ -25,10 +25,10 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGIONSET_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGIONSET_HPP -#include "memory/allocation.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" +#include "memory/allocation.hpp" #include "utilities/globalDefinitions.hpp" class ShenandoahHeapRegionSet; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp b/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp index b4ea327965b34..b5e5e6fd69894 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp @@ -24,11 +24,11 @@ * */ +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahInitLogger.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "logging/log.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp index 47a144a638f0e..fcfe0d1d5d649 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahLock.cpp @@ -23,12 +23,11 @@ */ -#include "runtime/os.hpp" - #include "gc/shenandoah/shenandoahLock.hpp" #include "runtime/atomic.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.hpp" +#include "runtime/os.hpp" #include "runtime/os.inline.hpp" void ShenandoahLock::contended_lock(bool allow_block_for_safepoint) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index ae8d52a3d0e8f..4aef14f2c9aba 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -29,9 +29,9 @@ #include "gc/shared/ageTable.hpp" #include "gc/shared/stringdedup/stringDedup.hpp" #include "gc/shared/taskTerminator.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationType.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" -#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahTaskqueue.hpp" enum StringDedupMode { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp index 9986afc6f2019..34e6af41b427c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp @@ -24,8 +24,8 @@ * */ -#include "gc/shenandoah/shenandoahMarkBitMap.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahMarkBitMap.inline.hpp" #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp index 399db525cf95c..0babeaffd3e0e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.cpp @@ -27,7 +27,6 @@ #include "gc/shared/markBitMap.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.hpp" - #include "shenandoahGlobalGeneration.hpp" ShenandoahMarkingContext::ShenandoahMarkingContext(MemRegion heap_region, MemRegion bitmap_region, size_t num_regions) : diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp index d9bddd5fbb6b9..e3ba774283c18 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp @@ -28,6 +28,7 @@ #define SHARE_GC_SHENANDOAH_SHENANDOAHMARKINGCONTEXT_INLINE_HPP #include "gc/shenandoah/shenandoahMarkingContext.hpp" + #include "gc/shenandoah/shenandoahMarkBitMap.inline.hpp" #include "logging/log.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp index e9aa69b5555c5..ebfe5267160fb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp @@ -25,8 +25,8 @@ */ #include "gc/shenandoah/shenandoahMemoryPool.hpp" -#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" ShenandoahMemoryPool::ShenandoahMemoryPool(ShenandoahHeap* heap, const char* name) : diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp index 32386e6aec052..edd4f875be4eb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp @@ -23,10 +23,10 @@ * */ -#include "gc/shenandoah/shenandoahMetrics.hpp" +#include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" -#include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahMetrics.hpp" ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot() { _heap = ShenandoahHeap::heap(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.cpp index 663864b1294ce..5867478d73426 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMmuTracker.cpp @@ -24,8 +24,8 @@ */ #include "gc/shenandoah/shenandoahAsserts.hpp" -#include "gc/shenandoah/shenandoahMmuTracker.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahMmuTracker.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/log.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMonitoringSupport.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMonitoringSupport.cpp index 31265addda886..6b72cbdd62bba 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMonitoringSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMonitoringSupport.cpp @@ -25,9 +25,9 @@ #include "gc/shared/collectorCounters.hpp" #include "gc/shared/generationCounters.hpp" #include "gc/shared/hSpaceCounters.hpp" -#include "gc/shenandoah/shenandoahMonitoringSupport.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegionCounters.hpp" +#include "gc/shenandoah/shenandoahMonitoringSupport.hpp" #include "memory/metaspaceCounters.hpp" #include "services/memoryService.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp index 7c28378bf2455..1724fc2849f76 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -25,11 +25,11 @@ #include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" #include "gc/shenandoah/shenandoahOldGC.hpp" -#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "prims/jvmtiTagMap.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index b0c42c7b40feb..35d963f1801d6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -26,6 +26,7 @@ #include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahCardTable.hpp" +#include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" @@ -35,7 +36,6 @@ #include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" -#include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp index e16275b480a27..62a25881b5a60 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp @@ -25,11 +25,11 @@ #include "gc/shared/workerDataArray.inline.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" -#include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "runtime/orderAccess.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index 9100ad2b22077..0a45615131814 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -26,9 +26,9 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHPHASETIMINGS_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHPHASETIMINGS_HPP -#include "jfr/jfrEvents.hpp" -#include "gc/shenandoah/shenandoahNumberSeq.hpp" #include "gc/shared/workerDataArray.hpp" +#include "gc/shenandoah/shenandoahNumberSeq.hpp" +#include "jfr/jfrEvents.hpp" #include "memory/allocation.hpp" class ShenandoahCollectorPolicy; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index 0e10e8c819f2e..2bbce179af8d7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -26,14 +26,14 @@ #include "classfile/javaClasses.hpp" #include "gc/shared/workerThread.hpp" -#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" -#include "runtime/atomic.hpp" #include "logging/log.hpp" +#include "runtime/atomic.hpp" static ReferenceType reference_type(oop reference) { return InstanceKlass::cast(reference->klass())->reference_type(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp index bf309af9743e3..774c4f7d9413e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp @@ -67,22 +67,18 @@ void ShenandoahRegulatorThread::regulate_young_and_old_cycles() { _global_heuristics->cancel_trigger_request(); } } else { - if (_young_heuristics->should_start_gc()) { - // Give the old generation a chance to run. The old generation cycle - // begins with a 'bootstrap' cycle that will also collect young. - if (start_old_cycle()) { - log_debug(gc)("Heuristics request for old collection accepted"); - _young_heuristics->cancel_trigger_request(); - _old_heuristics->cancel_trigger_request(); - } else if (request_concurrent_gc(_heap->young_generation())) { - log_debug(gc)("Heuristics request for young collection accepted"); - _young_heuristics->cancel_trigger_request(); - } - } else if (_old_heuristics->should_resume_old_cycle() || _old_heuristics->should_start_gc()) { + if (_old_heuristics->should_resume_old_cycle()) { if (request_concurrent_gc(_heap->old_generation())) { _old_heuristics->cancel_trigger_request(); log_debug(gc)("Heuristics request to resume old collection accepted"); } + } else if (start_old_cycle()) { + log_debug(gc)("Heuristics request for old collection accepted"); + _young_heuristics->cancel_trigger_request(); + _old_heuristics->cancel_trigger_request(); + } else if (start_young_cycle()) { + log_debug(gc)("Heuristics request for young collection accepted"); + _young_heuristics->cancel_trigger_request(); } } } else if (mode == ShenandoahGenerationalControlThread::servicing_old) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp index b3b5109f6b376..8693046297d5a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp @@ -26,9 +26,9 @@ #include "classfile/classLoaderData.hpp" #include "code/nmethod.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" -#include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" +#include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahStackWatermark.hpp" #include "memory/iterator.hpp" #include "memory/resourceArea.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp index f1544c1762ea8..fa3fa90b2f50b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp @@ -29,11 +29,11 @@ #include "classfile/classLoaderDataGraph.hpp" #include "gc/shared/oopStorageSetParState.inline.hpp" +#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" -#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" #include "memory/resourceArea.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp index 4c7ae68fdbe02..11ff92cd9ccf3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp @@ -28,16 +28,16 @@ #include "classfile/classLoaderDataGraph.hpp" #include "code/codeCache.hpp" +#include "gc/shared/oopStorage.inline.hpp" +#include "gc/shared/oopStorageSet.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" -#include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahRootVerifier.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahStringDedup.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" -#include "gc/shared/oopStorage.inline.hpp" -#include "gc/shared/oopStorageSet.hpp" #include "runtime/javaThread.hpp" #include "runtime/jniHandles.hpp" #include "runtime/threads.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp index 2984debd9f861..d2a5f71bca608 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp @@ -26,8 +26,8 @@ #include "gc/shenandoah/shenandoahBarrierSetClone.inline.hpp" #include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" -#include "runtime/interfaceSupport.inline.hpp" #include "oops/oop.inline.hpp" +#include "runtime/interfaceSupport.inline.hpp" #include "utilities/copy.hpp" JRT_LEAF(void, ShenandoahRuntime::arraycopy_barrier_oop(oop* src, oop* dst, size_t length)) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp index b0fc55631e067..68bec5c2071bc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp @@ -26,18 +26,19 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBEREDINLINE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHSCANREMEMBEREDINLINE_HPP -#include "memory/iterator.hpp" -#include "oops/oop.hpp" -#include "oops/objArrayOop.hpp" +#include "gc/shenandoah/shenandoahScanRemembered.hpp" + #include "gc/shared/collectorCounters.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahCardStats.hpp" #include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" -#include "gc/shenandoah/shenandoahScanRemembered.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "logging/log.hpp" +#include "memory/iterator.hpp" +#include "oops/objArrayOop.hpp" +#include "oops/oop.hpp" // Process all objects starting within count clusters beginning with first_cluster and for which the start address is // less than end_of_range. For any non-array object whose header lies on a dirty card, scan the entire object, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp index 55d21b06e4bbd..3a4cb8cf742fc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp @@ -25,10 +25,10 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSIMPLEBITMAP_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHSIMPLEBITMAP_HPP -#include - #include "gc/shenandoah/shenandoahAsserts.hpp" +#include + // TODO: Merge the enhanced capabilities of ShenandoahSimpleBitMap into src/hotspot/share/utilities/bitMap.hpp // and deprecate ShenandoahSimpleBitMap. The key enhanced capabilities to be integrated include: // diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp index 042143254bc4c..1559dd81849eb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp @@ -25,9 +25,10 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_INLINE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_INLINE_HPP +#include "gc/shenandoah/shenandoahStringDedup.hpp" + #include "classfile/javaClasses.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "gc/shenandoah/shenandoahStringDedup.hpp" #include "oops/markWord.hpp" bool ShenandoahStringDedup::is_string_candidate(oop obj) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index 10887ad8c19d6..af661fd1dc436 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -26,8 +26,8 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHTASKQUEUE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHTASKQUEUE_HPP -#include "gc/shared/taskTerminator.hpp" #include "gc/shared/taskqueue.hpp" +#include "gc/shared/taskTerminator.hpp" #include "gc/shenandoah/shenandoahPadding.hpp" #include "nmt/memTag.hpp" #include "runtime/atomic.hpp" @@ -309,14 +309,14 @@ class ParallelClaimableQueueSet: public GenericTaskQueueSet { volatile jint _claimed_index; shenandoah_padding(1); - debug_only(uint _reserved; ) + DEBUG_ONLY(uint _reserved; ) public: using GenericTaskQueueSet::size; public: ParallelClaimableQueueSet(int n) : GenericTaskQueueSet(n), _claimed_index(0) { - debug_only(_reserved = 0; ) + DEBUG_ONLY(_reserved = 0; ) } void clear_claimed() { _claimed_index = 0; } @@ -326,10 +326,10 @@ class ParallelClaimableQueueSet: public GenericTaskQueueSet { void reserve(uint n) { assert(n <= size(), "Sanity"); _claimed_index = (jint)n; - debug_only(_reserved = n;) + DEBUG_ONLY(_reserved = n;) } - debug_only(uint get_reserved() const { return (uint)_reserved; }) + DEBUG_ONLY(uint get_reserved() const { return (uint)_reserved; }) }; template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp index 933cc501562f1..c1cebdf1ddef4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp @@ -26,16 +26,16 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHTHREADLOCALDATA_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHTHREADLOCALDATA_HPP -#include "gc/shared/plab.hpp" -#include "gc/shared/gcThreadLocalData.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcThreadLocalData.hpp" +#include "gc/shared/plab.hpp" +#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahCardTable.hpp" #include "gc/shenandoah/shenandoahCodeRoots.hpp" -#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahEvacTracker.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" #include "gc/shenandoah/shenandoahSATBMarkQueueSet.hpp" -#include "gc/shenandoah/mode/shenandoahMode.hpp" #include "runtime/javaThread.hpp" #include "utilities/debug.hpp" #include "utilities/sizes.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp index 6bd50154b4fde..83151313f7565 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUnload.cpp @@ -29,11 +29,11 @@ #include "code/codeBehaviours.hpp" #include "code/codeCache.hpp" #include "code/dependencyContext.hpp" -#include "gc/shared/gcBehaviours.hpp" #include "gc/shared/classUnloadingContext.hpp" +#include "gc/shared/gcBehaviours.hpp" #include "gc/shared/suspendibleThreadSet.hpp" -#include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "gc/shenandoah/shenandoahLock.hpp" +#include "gc/shenandoah/shenandoahNMethod.inline.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahRootProcessor.hpp" #include "gc/shenandoah/shenandoahUnload.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp index ecca550d5532c..176baa133c840 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp @@ -24,7 +24,6 @@ */ -#include "jfr/jfrEvents.hpp" #include "gc/shared/gcCause.hpp" #include "gc/shared/gcTrace.hpp" #include "gc/shared/gcWhen.hpp" @@ -36,6 +35,7 @@ #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" +#include "jfr/jfrEvents.hpp" #include "utilities/debug.hpp" ShenandoahPhaseTimings::Phase ShenandoahTimingsTracker::_current_phase = ShenandoahPhaseTimings::_invalid_phase; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp index fd30279d318a2..8a508c4afd84f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp @@ -37,8 +37,8 @@ #include "jfr/jfrEvents.hpp" #include "memory/allocation.hpp" #include "runtime/safepoint.hpp" -#include "runtime/vmThread.hpp" #include "runtime/vmOperations.hpp" +#include "runtime/vmThread.hpp" #include "services/memoryService.hpp" class GCTimer; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 37951c311ed99..cdf7848520765 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -27,11 +27,11 @@ #include "gc/shared/tlab_globals.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahForwarding.inline.hpp" -#include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahRootProcessor.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.cpp b/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.cpp index d6c47339f28a9..f2431a5ad0afc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahWorkGroup.cpp @@ -25,10 +25,9 @@ #include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahTaskqueue.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/shenandoahWorkGroup.hpp" -#include "gc/shenandoah/shenandoahTaskqueue.hpp" - #include "logging/log.hpp" #include "runtime/threads.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp index 8663515d019d0..daf5d456af5ea 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp @@ -22,13 +22,13 @@ * */ +#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" #include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegionClosures.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" -#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" ShenandoahYoungGeneration::ShenandoahYoungGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity) : ShenandoahGeneration(YOUNG, max_queues, max_capacity, soft_max_capacity), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp index 1237e28c06ef8..a8ebab507b6cc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp @@ -25,8 +25,8 @@ #ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP #define SHARE_VM_GC_SHENANDOAH_SHENANDOAHYOUNGGENERATION_HPP -#include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp" +#include "gc/shenandoah/shenandoahGeneration.hpp" class ShenandoahYoungGeneration : public ShenandoahGeneration { private: diff --git a/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp b/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp index 851fa39472237..a245f91fa71e9 100644 --- a/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp +++ b/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp @@ -24,9 +24,9 @@ #ifndef SHARE_GC_SHENANDOAH_VMSTRUCTS_SHENANDOAH_HPP #define SHARE_GC_SHENANDOAH_VMSTRUCTS_SHENANDOAH_HPP -#include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" diff --git a/src/hotspot/share/gc/z/c1/zBarrierSetC1.cpp b/src/hotspot/share/gc/z/c1/zBarrierSetC1.cpp index f3643c0a3251d..7bd2495591091 100644 --- a/src/hotspot/share/gc/z/c1/zBarrierSetC1.cpp +++ b/src/hotspot/share/gc/z/c1/zBarrierSetC1.cpp @@ -21,12 +21,12 @@ * questions. */ +#include "c1/c1_CodeStubs.hpp" #include "c1/c1_FrameMap.hpp" #include "c1/c1_LIR.hpp" #include "c1/c1_LIRAssembler.hpp" #include "c1/c1_LIRGenerator.hpp" #include "c1/c1_MacroAssembler.hpp" -#include "c1/c1_CodeStubs.hpp" #include "gc/z/c1/zBarrierSetC1.hpp" #include "gc/z/zBarrierSet.hpp" #include "gc/z/zBarrierSetAssembler.hpp" diff --git a/src/hotspot/share/gc/z/zArguments.cpp b/src/hotspot/share/gc/z/zArguments.cpp index 67b9f6f0bb918..2c70d8417c95d 100644 --- a/src/hotspot/share/gc/z/zArguments.cpp +++ b/src/hotspot/share/gc/z/zArguments.cpp @@ -121,9 +121,19 @@ void ZArguments::select_max_gc_threads() { void ZArguments::initialize() { GCArguments::initialize(); - // Enable NUMA by default - if (FLAG_IS_DEFAULT(UseNUMA) && FLAG_IS_DEFAULT(ZFakeNUMA)) { - FLAG_SET_DEFAULT(UseNUMA, true); + // NUMA settings + if (FLAG_IS_DEFAULT(ZFakeNUMA)) { + // Enable NUMA by default + if (FLAG_IS_DEFAULT(UseNUMA)) { + FLAG_SET_DEFAULT(UseNUMA, true); + } + } else { + if (UseNUMA) { + if (!FLAG_IS_DEFAULT(UseNUMA)) { + warning("ZFakeNUMA is enabled; turning off UseNUMA"); + } + FLAG_SET_ERGO(UseNUMA, false); + } } select_max_gc_threads(); diff --git a/src/hotspot/share/gc/z/zCollectedHeap.cpp b/src/hotspot/share/gc/z/zCollectedHeap.cpp index 642ad42a1d7bb..a94e50e2a52ff 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.cpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.cpp @@ -355,16 +355,12 @@ void ZCollectedHeap::prepare_for_verify() { // Does nothing } -void ZCollectedHeap::print_on(outputStream* st) const { - StreamAutoIndentor auto_indentor(st); - - _heap.print_on(st); +void ZCollectedHeap::print_heap_on(outputStream* st) const { + _heap.print_usage_on(st); } -void ZCollectedHeap::print_on_error(outputStream* st) const { - StreamAutoIndentor auto_indentor(st); - - _heap.print_on_error(st); +void ZCollectedHeap::print_gc_on(outputStream* st) const { + _heap.print_gc_on(st); } void ZCollectedHeap::print_tracing_info() const { diff --git a/src/hotspot/share/gc/z/zCollectedHeap.hpp b/src/hotspot/share/gc/z/zCollectedHeap.hpp index 08aa7d40a3f9d..9ae3da39dd9c9 100644 --- a/src/hotspot/share/gc/z/zCollectedHeap.hpp +++ b/src/hotspot/share/gc/z/zCollectedHeap.hpp @@ -115,8 +115,8 @@ class ZCollectedHeap : public CollectedHeap { void pin_object(JavaThread* thread, oop obj) override; void unpin_object(JavaThread* thread, oop obj) override; - void print_on(outputStream* st) const override; - void print_on_error(outputStream* st) const override; + void print_heap_on(outputStream* st) const override; + void print_gc_on(outputStream* st) const override; void print_tracing_info() const override; bool print_location(outputStream* st, void* addr) const override; diff --git a/src/hotspot/share/gc/z/zForwarding.hpp b/src/hotspot/share/gc/z/zForwarding.hpp index ee37508903f6c..c58c479984f9c 100644 --- a/src/hotspot/share/gc/z/zForwarding.hpp +++ b/src/hotspot/share/gc/z/zForwarding.hpp @@ -59,8 +59,8 @@ class ZForwarding { const size_t _object_alignment_shift; const AttachedArray _entries; ZPage* const _page; - ZPageAge _from_age; - ZPageAge _to_age; + const ZPageAge _from_age; + const ZPageAge _to_age; volatile bool _claimed; mutable ZConditionLock _ref_lock; volatile int32_t _ref_count; diff --git a/src/hotspot/share/gc/z/zHeap.cpp b/src/hotspot/share/gc/z/zHeap.cpp index 90f8a86713532..ddfa5775fb233 100644 --- a/src/hotspot/share/gc/z/zHeap.cpp +++ b/src/hotspot/share/gc/z/zHeap.cpp @@ -314,33 +314,18 @@ ZServiceabilityCounters* ZHeap::serviceability_counters() { return _serviceability.counters(); } -void ZHeap::print_on(outputStream* st) const { - streamIndentor indentor(st, 1); - _page_allocator.print_on(st); - - // Metaspace printing prepends spaces instead of using outputStream indentation - streamIndentor indentor_back(st, -1); - MetaspaceUtils::print_on(st); +void ZHeap::print_usage_on(outputStream* st) const { + _page_allocator.print_usage_on(st); } -void ZHeap::print_on_error(outputStream* st) const { - { - streamIndentor indentor(st, 1); - _page_allocator.print_on_error(st); - - // Metaspace printing prepends spaces instead of using outputStream indentation - streamIndentor indentor_back(st, -1); - MetaspaceUtils::print_on(st); - } - st->cr(); - +void ZHeap::print_gc_on(outputStream* st) const { print_globals_on(st); st->cr(); print_page_table_on(st); st->cr(); - _page_allocator.print_extended_on_error(st); + _page_allocator.print_cache_extended_on(st); } void ZHeap::print_globals_on(outputStream* st) const { @@ -373,12 +358,12 @@ void ZHeap::print_page_table_on(outputStream* st) const { // Print all pages st->print_cr("ZGC Page Table:"); - { - streamIndentor indentor(st, 1); - ZPageTableIterator iter(&_page_table); - for (ZPage* page; iter.next(&page);) { - page->print_on(st); - } + + StreamAutoIndentor indentor(st, 1); + + ZPageTableIterator iter(&_page_table); + for (ZPage* page; iter.next(&page);) { + page->print_on(st); } // Allow pages to be deleted diff --git a/src/hotspot/share/gc/z/zHeap.hpp b/src/hotspot/share/gc/z/zHeap.hpp index 823fc009b2ca0..64be61cc873ef 100644 --- a/src/hotspot/share/gc/z/zHeap.hpp +++ b/src/hotspot/share/gc/z/zHeap.hpp @@ -130,8 +130,8 @@ class ZHeap { ZServiceabilityCounters* serviceability_counters(); // Printing - void print_on(outputStream* st) const; - void print_on_error(outputStream* st) const; + void print_usage_on(outputStream* st) const; + void print_gc_on(outputStream* st) const; void print_globals_on(outputStream* st) const; void print_page_table_on(outputStream* st) const; bool print_location(outputStream* st, uintptr_t addr) const; diff --git a/src/hotspot/share/gc/z/zHeapIterator.hpp b/src/hotspot/share/gc/z/zHeapIterator.hpp index fb58e3abe5fd6..2b570e98f1c71 100644 --- a/src/hotspot/share/gc/z/zHeapIterator.hpp +++ b/src/hotspot/share/gc/z/zHeapIterator.hpp @@ -25,8 +25,8 @@ #define SHARE_GC_Z_ZHEAPITERATOR_HPP #include "gc/shared/collectedHeap.hpp" -#include "gc/shared/taskTerminator.hpp" #include "gc/shared/taskqueue.hpp" +#include "gc/shared/taskTerminator.hpp" #include "gc/z/zGranuleMap.hpp" #include "gc/z/zLock.hpp" #include "gc/z/zRootsIterator.hpp" diff --git a/src/hotspot/share/gc/z/zMappedCache.cpp b/src/hotspot/share/gc/z/zMappedCache.cpp index c1c6e9edee9ed..f2bea439df2d5 100644 --- a/src/hotspot/share/gc/z/zMappedCache.cpp +++ b/src/hotspot/share/gc/z/zMappedCache.cpp @@ -30,6 +30,7 @@ #include "utilities/align.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" #include "utilities/powerOfTwo.hpp" class ZMappedCacheEntry { @@ -575,7 +576,7 @@ void ZMappedCache::print_on(outputStream* st) const { // class lists. const size_t entry_count = Atomic::load(&_entry_count); - st->print("Cache"); + st->print("Cache "); st->fill_to(17); st->print_cr("%zuM (%zu)", _size / M, entry_count); @@ -591,9 +592,9 @@ void ZMappedCache::print_on(outputStream* st) const { } // Print information on size classes - streamIndentor indentor(st, 1); + StreamAutoIndentor indentor(st, 1); - st->print("size classes"); + st->print("size classes "); st->fill_to(17); // Print the number of entries smaller than the min size class's size diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp index e0c68213ca3e8..9846f1244ecea 100644 --- a/src/hotspot/share/gc/z/zMark.cpp +++ b/src/hotspot/share/gc/z/zMark.cpp @@ -27,7 +27,6 @@ #include "code/nmethod.hpp" #include "gc/shared/continuationGCSupport.inline.hpp" #include "gc/shared/gc_globals.hpp" -#include "gc/shared/stringdedup/stringDedup.hpp" #include "gc/shared/suspendibleThreadSet.hpp" #include "gc/shared/workerThread.hpp" #include "gc/z/zAbort.inline.hpp" @@ -389,25 +388,6 @@ void ZMark::follow_object(oop obj, bool finalizable) { } } -static void try_deduplicate(ZMarkContext* context, oop obj) { - if (!StringDedup::is_enabled()) { - // Not enabled - return; - } - - if (!java_lang_String::is_instance(obj)) { - // Not a String object - return; - } - - if (java_lang_String::test_and_set_deduplication_requested(obj)) { - // Already requested deduplication - return; - } - - // Request deduplication - context->string_dedup_requests()->add(obj); -} void ZMark::mark_and_follow(ZMarkContext* context, ZMarkStackEntry entry) { // Decode flags @@ -449,13 +429,7 @@ void ZMark::mark_and_follow(ZMarkContext* context, ZMarkStackEntry entry) { if (is_array(addr)) { follow_array_object(objArrayOop(to_oop(addr)), finalizable); } else { - const oop obj = to_oop(addr); - follow_object(obj, finalizable); - - if (!finalizable) { - // Try deduplicate - try_deduplicate(context, obj); - } + follow_object(to_oop(addr), finalizable); } } } diff --git a/src/hotspot/share/gc/z/zMarkContext.hpp b/src/hotspot/share/gc/z/zMarkContext.hpp index 009252e524da6..12f56c2f7a597 100644 --- a/src/hotspot/share/gc/z/zMarkContext.hpp +++ b/src/hotspot/share/gc/z/zMarkContext.hpp @@ -25,7 +25,6 @@ #define SHARE_GC_Z_ZMARKCONTEXT_HPP #include "gc/z/zMarkCache.hpp" -#include "gc/shared/stringdedup/stringDedup.hpp" #include "memory/allocation.hpp" class ZMarkStripe; @@ -37,7 +36,6 @@ class ZMarkContext : public StackObj { ZMarkStripe* _stripe; ZMarkThreadLocalStacks* const _stacks; size_t _nstripes; - StringDedup::Requests _string_dedup_requests; public: ZMarkContext(size_t nstripes, @@ -48,7 +46,6 @@ class ZMarkContext : public StackObj { ZMarkStripe* stripe(); void set_stripe(ZMarkStripe* stripe); ZMarkThreadLocalStacks* stacks(); - StringDedup::Requests* string_dedup_requests(); size_t nstripes(); void set_nstripes(size_t nstripes); diff --git a/src/hotspot/share/gc/z/zMarkContext.inline.hpp b/src/hotspot/share/gc/z/zMarkContext.inline.hpp index fe1d4bbbcb6ab..7273a87a8c600 100644 --- a/src/hotspot/share/gc/z/zMarkContext.inline.hpp +++ b/src/hotspot/share/gc/z/zMarkContext.inline.hpp @@ -32,8 +32,7 @@ inline ZMarkContext::ZMarkContext(size_t nstripes, : _cache(nstripes), _stripe(stripe), _stacks(stacks), - _nstripes(nstripes), - _string_dedup_requests() {} + _nstripes(nstripes) {} inline ZMarkCache* ZMarkContext::cache() { return &_cache; @@ -51,10 +50,6 @@ inline ZMarkThreadLocalStacks* ZMarkContext::stacks() { return _stacks; } -inline StringDedup::Requests* ZMarkContext::string_dedup_requests() { - return &_string_dedup_requests; -} - inline size_t ZMarkContext::nstripes() { return _nstripes; } diff --git a/src/hotspot/share/gc/z/zMarkingSMR.hpp b/src/hotspot/share/gc/z/zMarkingSMR.hpp index a6c5afe76c941..26670d959598a 100644 --- a/src/hotspot/share/gc/z/zMarkingSMR.hpp +++ b/src/hotspot/share/gc/z/zMarkingSMR.hpp @@ -26,8 +26,8 @@ #include "gc/z/zArray.hpp" #include "gc/z/zValue.hpp" -#include "utilities/globalDefinitions.hpp" #include "memory/allocation.hpp" +#include "utilities/globalDefinitions.hpp" class ZMarkStackListNode; diff --git a/src/hotspot/share/gc/z/zNMT.cpp b/src/hotspot/share/gc/z/zNMT.cpp index 76e164308dd0c..1019bcfdd961b 100644 --- a/src/hotspot/share/gc/z/zNMT.cpp +++ b/src/hotspot/share/gc/z/zNMT.cpp @@ -24,9 +24,9 @@ #include "gc/z/zAddress.inline.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zNMT.hpp" +#include "nmt/memoryFileTracker.hpp" #include "nmt/memTag.hpp" #include "nmt/memTracker.hpp" -#include "nmt/memoryFileTracker.hpp" #include "utilities/nativeCallStack.hpp" MemoryFileTracker::MemoryFile* ZNMT::_device = nullptr; diff --git a/src/hotspot/share/gc/z/zNMT.hpp b/src/hotspot/share/gc/z/zNMT.hpp index b5b1aa07870f3..5fea74ee8ae6b 100644 --- a/src/hotspot/share/gc/z/zNMT.hpp +++ b/src/hotspot/share/gc/z/zNMT.hpp @@ -27,8 +27,8 @@ #include "gc/z/zAddress.hpp" #include "gc/z/zGlobals.hpp" #include "memory/allStatic.hpp" -#include "nmt/memTracker.hpp" #include "nmt/memoryFileTracker.hpp" +#include "nmt/memTracker.hpp" #include "utilities/globalDefinitions.hpp" class ZNMT : public AllStatic { diff --git a/src/hotspot/share/gc/z/zNMethod.cpp b/src/hotspot/share/gc/z/zNMethod.cpp index 4ae440ea23123..bf592c20fa296 100644 --- a/src/hotspot/share/gc/z/zNMethod.cpp +++ b/src/hotspot/share/gc/z/zNMethod.cpp @@ -22,8 +22,8 @@ */ #include "code/codeCache.hpp" -#include "code/relocInfo.hpp" #include "code/nmethod.hpp" +#include "code/relocInfo.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/barrierSetNMethod.hpp" #include "gc/shared/classUnloadingContext.hpp" diff --git a/src/hotspot/share/gc/z/zNMethodTable.cpp b/src/hotspot/share/gc/z/zNMethodTable.cpp index 0aec0d5a9c7f4..bbc8f56b654ac 100644 --- a/src/hotspot/share/gc/z/zNMethodTable.cpp +++ b/src/hotspot/share/gc/z/zNMethodTable.cpp @@ -21,8 +21,8 @@ * questions. */ -#include "code/relocInfo.hpp" #include "code/nmethod.hpp" +#include "code/relocInfo.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/barrierSetNMethod.hpp" #include "gc/z/zHash.inline.hpp" diff --git a/src/hotspot/share/gc/z/zObjArrayAllocator.cpp b/src/hotspot/share/gc/z/zObjArrayAllocator.cpp index a4484ba10237b..ddb0ca4927849 100644 --- a/src/hotspot/share/gc/z/zObjArrayAllocator.cpp +++ b/src/hotspot/share/gc/z/zObjArrayAllocator.cpp @@ -21,8 +21,8 @@ * questions. */ -#include "gc/z/zThreadLocalData.hpp" #include "gc/z/zObjArrayAllocator.hpp" +#include "gc/z/zThreadLocalData.hpp" #include "gc/z/zUtils.inline.hpp" #include "oops/arrayKlass.hpp" #include "runtime/interfaceSupport.inline.hpp" diff --git a/src/hotspot/share/gc/z/zPageAllocator.cpp b/src/hotspot/share/gc/z/zPageAllocator.cpp index 7bb1dcdcf81d4..ef4ea76f22f1a 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.cpp +++ b/src/hotspot/share/gc/z/zPageAllocator.cpp @@ -1155,12 +1155,12 @@ void ZPartition::threads_do(ThreadClosure* tc) const { } void ZPartition::print_on(outputStream* st) const { - st->print("Partition %u", _numa_id); + st->print("Partition %u ", _numa_id); st->fill_to(17); st->print_cr("used %zuM, capacity %zuM, max capacity %zuM", _used / M, _capacity / M, _max_capacity / M); - streamIndentor indentor(st, 1); + StreamAutoIndentor indentor(st, 1); print_cache_on(st); } @@ -1168,11 +1168,10 @@ void ZPartition::print_cache_on(outputStream* st) const { _cache.print_on(st); } -void ZPartition::print_extended_on_error(outputStream* st) const { +void ZPartition::print_cache_extended_on(outputStream* st) const { st->print_cr("Partition %u", _numa_id); - streamIndentor indentor(st, 1); - + StreamAutoIndentor indentor(st, 1); _cache.print_extended_on(st); } @@ -2394,11 +2393,6 @@ void ZPageAllocator::threads_do(ThreadClosure* tc) const { } } -void ZPageAllocator::print_on(outputStream* st) const { - ZLocker lock(&_lock); - print_on_inner(st); -} - static bool try_lock_on_error(ZLock* lock) { if (VMError::is_error_reported() && VMError::is_error_reported_in_current_thread()) { return lock->try_lock(); @@ -2409,64 +2403,67 @@ static bool try_lock_on_error(ZLock* lock) { return true; } -void ZPageAllocator::print_extended_on_error(outputStream* st) const { - st->print_cr("ZMappedCache:"); - - streamIndentor indentor(st, 1); - - if (!try_lock_on_error(&_lock)) { - // We can't print without taking the lock since printing the contents of - // the cache requires iterating over the nodes in the cache's tree, which - // is not thread-safe. - st->print_cr(""); - - return; - } - - // Print each partition's cache content - ZPartitionConstIterator iter = partition_iterator(); - for (const ZPartition* partition; iter.next(&partition);) { - partition->print_extended_on_error(st); - } - - _lock.unlock(); -} - -void ZPageAllocator::print_on_error(outputStream* st) const { +void ZPageAllocator::print_usage_on(outputStream* st) const { const bool locked = try_lock_on_error(&_lock); if (!locked) { st->print_cr(""); } - // Print information even though we have not successfully taken the lock. + // Print information even though we may not have successfully taken the lock. // This is thread-safe, but may produce inconsistent results. - print_on_inner(st); + + print_total_usage_on(st); + + StreamAutoIndentor indentor(st, 1); + print_partition_usage_on(st); if (locked) { _lock.unlock(); } } -void ZPageAllocator::print_on_inner(outputStream* st) const { - // Print total usage - st->print("ZHeap"); +void ZPageAllocator::print_total_usage_on(outputStream* st) const { + st->print("ZHeap "); st->fill_to(17); st->print_cr("used %zuM, capacity %zuM, max capacity %zuM", used() / M, capacity() / M, max_capacity() / M); +} - // Print per-partition - - streamIndentor indentor(st, 1); - +void ZPageAllocator::print_partition_usage_on(outputStream* st) const { if (_partitions.count() == 1) { - // The summary printing is redundant if we only have one partition + // Partition usage is redundant if we only have one partition. Only + // print the cache. _partitions.get(0).print_cache_on(st); return; } + // Print all partitions ZPartitionConstIterator iter = partition_iterator(); for (const ZPartition* partition; iter.next(&partition);) { partition->print_on(st); } } + +void ZPageAllocator::print_cache_extended_on(outputStream* st) const { + st->print_cr("ZMappedCache:"); + + StreamAutoIndentor indentor(st, 1); + + if (!try_lock_on_error(&_lock)) { + // We can't print without taking the lock since printing the contents of + // the cache requires iterating over the nodes in the cache's tree, which + // is not thread-safe. + st->print_cr(""); + + return; + } + + // Print each partition's cache content + ZPartitionConstIterator iter = partition_iterator(); + for (const ZPartition* partition; iter.next(&partition);) { + partition->print_cache_extended_on(st); + } + + _lock.unlock(); +} diff --git a/src/hotspot/share/gc/z/zPageAllocator.hpp b/src/hotspot/share/gc/z/zPageAllocator.hpp index 5cd08aee94cda..ba83686a87d26 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.hpp +++ b/src/hotspot/share/gc/z/zPageAllocator.hpp @@ -140,7 +140,7 @@ class ZPartition { void print_on(outputStream* st) const; void print_cache_on(outputStream* st) const; - void print_extended_on_error(outputStream* st) const; + void print_cache_extended_on(outputStream* st) const; }; using ZPartitionIterator = ZPerNUMAIterator; @@ -238,8 +238,6 @@ class ZPageAllocator { void update_collection_stats(ZGenerationId id); ZPageAllocatorStats stats_inner(ZGeneration* generation) const; - void print_on_inner(outputStream* st) const; - public: ZPageAllocator(size_t min_capacity, size_t initial_capacity, @@ -285,9 +283,10 @@ class ZPageAllocator { void threads_do(ThreadClosure* tc) const; - void print_on(outputStream* st) const; - void print_extended_on_error(outputStream* st) const; - void print_on_error(outputStream* st) const; + void print_usage_on(outputStream* st) const; + void print_total_usage_on(outputStream* st) const; + void print_partition_usage_on(outputStream* st) const; + void print_cache_extended_on(outputStream* st) const; }; class ZPageAllocatorStats { diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp index b4ae78bc8d984..df2dd8a01cb10 100644 --- a/src/hotspot/share/gc/z/zRelocate.cpp +++ b/src/hotspot/share/gc/z/zRelocate.cpp @@ -40,6 +40,7 @@ #include "gc/z/zRootsIterator.hpp" #include "gc/z/zStackWatermark.hpp" #include "gc/z/zStat.hpp" +#include "gc/z/zStringDedup.inline.hpp" #include "gc/z/zTask.hpp" #include "gc/z/zUncoloredRoot.inline.hpp" #include "gc/z/zVerify.hpp" @@ -559,12 +560,14 @@ class ZRelocateMediumAllocator { template class ZRelocateWork : public StackObj { private: - Allocator* const _allocator; - ZForwarding* _forwarding; - ZPage* _target[ZAllocator::_relocation_allocators]; - ZGeneration* const _generation; - size_t _other_promoted; - size_t _other_compacted; + Allocator* const _allocator; + ZForwarding* _forwarding; + ZPage* _target[ZAllocator::_relocation_allocators]; + ZGeneration* const _generation; + size_t _other_promoted; + size_t _other_compacted; + ZStringDedupContext _string_dedup_context; + ZPage* target(ZPageAge age) { return _target[static_cast(age) - 1]; @@ -795,6 +798,13 @@ class ZRelocateWork : public StackObj { update_remset_promoted(to_addr); } + void maybe_string_dedup(zaddress to_addr) { + if (_forwarding->is_promotion()) { + // Only deduplicate promoted objects, and let short-lived strings simply die instead. + _string_dedup_context.request(to_oop(to_addr)); + } + } + bool try_relocate_object(zaddress from_addr) { const zaddress to_addr = try_relocate_object_inner(from_addr); @@ -804,6 +814,8 @@ class ZRelocateWork : public StackObj { update_remset_for_fields(from_addr, to_addr); + maybe_string_dedup(to_addr); + return true; } @@ -1176,10 +1188,15 @@ class ZRelocateAddRemsetForFlipPromoted : public ZRestartableTask { virtual void work() { SuspendibleThreadSetJoiner sts_joiner; + ZStringDedupContext string_dedup_context; for (ZPage* page; _iter.next(&page);) { page->object_iterate([&](oop obj) { + // Remap oops and add remset if needed ZIterator::basic_oop_iterate_safe(obj, remap_and_maybe_add_remset); + + // String dedup + string_dedup_context.request(obj); }); SuspendibleThreadSet::yield(); diff --git a/src/hotspot/share/gc/z/zRuntimeWorkers.cpp b/src/hotspot/share/gc/z/zRuntimeWorkers.cpp index f19e5cd54697a..c8c4cde9ba0fc 100644 --- a/src/hotspot/share/gc/z/zRuntimeWorkers.cpp +++ b/src/hotspot/share/gc/z/zRuntimeWorkers.cpp @@ -21,8 +21,8 @@ * questions. */ -#include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/gc_globals.hpp" +#include "gc/shared/gcLogPrecious.hpp" #include "gc/z/zRuntimeWorkers.hpp" #include "runtime/java.hpp" diff --git a/src/hotspot/share/gc/z/zStat.cpp b/src/hotspot/share/gc/z/zStat.cpp index 558ccdf105acd..a6b2bd0930d83 100644 --- a/src/hotspot/share/gc/z/zStat.cpp +++ b/src/hotspot/share/gc/z/zStat.cpp @@ -24,9 +24,9 @@ #include "gc/shared/gc_globals.hpp" #include "gc/z/zAbort.inline.hpp" #include "gc/z/zCollectedHeap.hpp" +#include "gc/z/zCPU.inline.hpp" #include "gc/z/zDirector.hpp" #include "gc/z/zDriver.hpp" -#include "gc/z/zCPU.inline.hpp" #include "gc/z/zGeneration.inline.hpp" #include "gc/z/zGlobals.hpp" #include "gc/z/zNMethodTable.hpp" diff --git a/src/hotspot/share/gc/z/zStringDedup.hpp b/src/hotspot/share/gc/z/zStringDedup.hpp new file mode 100644 index 0000000000000..68471fa607530 --- /dev/null +++ b/src/hotspot/share/gc/z/zStringDedup.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef SHARE_GC_Z_ZSTRINGDEDUP_HPP +#define SHARE_GC_Z_ZSTRINGDEDUP_HPP + +#include "gc/shared/stringdedup/stringDedup.hpp" +#include "gc/z/zAddress.hpp" +#include "oops/oopsHierarchy.hpp" + +class ZStringDedupContext { +private: + StringDedup::Requests _requests; + +public: + void request(oop obj); +}; + +#endif // SHARE_GC_Z_ZSTRINGDEDUP_HPP diff --git a/src/hotspot/share/gc/z/zStringDedup.inline.hpp b/src/hotspot/share/gc/z/zStringDedup.inline.hpp new file mode 100644 index 0000000000000..2df617f0ce738 --- /dev/null +++ b/src/hotspot/share/gc/z/zStringDedup.inline.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef SHARE_GC_Z_ZSTRINGDEDUP_INLINE_HPP +#define SHARE_GC_Z_ZSTRINGDEDUP_INLINE_HPP + +#include "gc/z/zStringDedup.hpp" + +#include "classfile/javaClasses.inline.hpp" +#include "gc/shared/gc_globals.hpp" + +inline void ZStringDedupContext::request(oop obj) { + if (!StringDedup::is_enabled()) { + // Not enabled + return; + } + + if (!java_lang_String::is_instance(obj)) { + // Not a String object + return; + } + + if (java_lang_String::test_and_set_deduplication_requested(obj)) { + // Already requested deduplication + return; + } + + // Request deduplication + _requests.add(obj); +} + +#endif // SHARE_GC_Z_ZSTRINGDEDUP_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zUncoloredRoot.inline.hpp b/src/hotspot/share/gc/z/zUncoloredRoot.inline.hpp index f0b3dfcb42c76..0d9fccde87c56 100644 --- a/src/hotspot/share/gc/z/zUncoloredRoot.inline.hpp +++ b/src/hotspot/share/gc/z/zUncoloredRoot.inline.hpp @@ -27,9 +27,9 @@ #include "gc/z/zUncoloredRoot.hpp" #include "gc/z/zAddress.inline.hpp" +#include "gc/z/zBarrier.hpp" #include "gc/z/zBarrier.inline.hpp" #include "gc/z/zHeap.inline.hpp" -#include "gc/z/zBarrier.hpp" #include "oops/oop.hpp" template diff --git a/src/hotspot/share/gc/z/zUtils.inline.hpp b/src/hotspot/share/gc/z/zUtils.inline.hpp index 07a49f144e912..b83f42d18e674 100644 --- a/src/hotspot/share/gc/z/zUtils.inline.hpp +++ b/src/hotspot/share/gc/z/zUtils.inline.hpp @@ -86,11 +86,10 @@ inline void ZUtils::sort(T* array, size_t count, Comparator comparator) { using SortType = int(const void*, const void*); using ComparatorType = int(const T*, const T*); - static constexpr bool IsComparatorCompatible = std::is_assignable::value; - static_assert(IsComparatorCompatible, "Incompatible Comparator, must decay to plain function pointer"); + ComparatorType* const comparator_fn_ptr = comparator; // We rely on ABI compatibility between ComparatorType and SortType - qsort(array, count, sizeof(T), reinterpret_cast(static_cast(comparator))); + qsort(array, count, sizeof(T), reinterpret_cast(comparator_fn_ptr)); } template diff --git a/src/hotspot/share/gc/z/zVerify.cpp b/src/hotspot/share/gc/z/zVerify.cpp index 117d27997eed4..68290c2c009de 100644 --- a/src/hotspot/share/gc/z/zVerify.cpp +++ b/src/hotspot/share/gc/z/zVerify.cpp @@ -32,8 +32,8 @@ #include "gc/z/zResurrection.hpp" #include "gc/z/zRootsIterator.hpp" #include "gc/z/zStackWatermark.hpp" -#include "gc/z/zStoreBarrierBuffer.inline.hpp" #include "gc/z/zStat.hpp" +#include "gc/z/zStoreBarrierBuffer.inline.hpp" #include "gc/z/zVerify.hpp" #include "memory/allocation.hpp" #include "memory/iterator.inline.hpp" diff --git a/src/hotspot/share/gc/z/zVirtualMemoryManager.inline.hpp b/src/hotspot/share/gc/z/zVirtualMemoryManager.inline.hpp index 78f966d0f845e..27159b4eff8e5 100644 --- a/src/hotspot/share/gc/z/zVirtualMemoryManager.inline.hpp +++ b/src/hotspot/share/gc/z/zVirtualMemoryManager.inline.hpp @@ -26,8 +26,8 @@ #include "gc/z/zVirtualMemoryManager.hpp" -#include "utilities/globalDefinitions.hpp" #include "gc/z/zRangeRegistry.inline.hpp" +#include "utilities/globalDefinitions.hpp" inline bool ZVirtualMemoryManager::is_multi_partition_enabled() const { diff --git a/src/hotspot/share/include/cds.h b/src/hotspot/share/include/cds.h index 8819e4c00beca..685fba4734909 100644 --- a/src/hotspot/share/include/cds.h +++ b/src/hotspot/share/include/cds.h @@ -35,12 +35,12 @@ // // Also, this is a C header file. Do not use C++ here. -#define NUM_CDS_REGIONS 4 // this must be the same as MetaspaceShared::n_regions +#define NUM_CDS_REGIONS 5 // this must be the same as MetaspaceShared::n_regions #define CDS_ARCHIVE_MAGIC 0xf00baba2 #define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8 #define CDS_PREIMAGE_ARCHIVE_MAGIC 0xcafea07c #define CDS_GENERIC_HEADER_SUPPORTED_MIN_VERSION 13 -#define CURRENT_CDS_ARCHIVE_VERSION 18 +#define CURRENT_CDS_ARCHIVE_VERSION 19 typedef struct CDSFileMapRegion { int _crc; // CRC checksum of this region. diff --git a/src/hotspot/share/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp index 62ce860594b9b..e577ce42c1ea1 100644 --- a/src/hotspot/share/interpreter/oopMapCache.cpp +++ b/src/hotspot/share/interpreter/oopMapCache.cpp @@ -311,7 +311,7 @@ void OopMapCacheEntry::deallocate_bit_mask() { assert(!Thread::current()->resource_area()->contains((void*)_bit_mask[0]), "This bit mask should not be in the resource area"); FREE_C_HEAP_ARRAY(uintptr_t, _bit_mask[0]); - debug_only(_bit_mask[0] = 0;) + DEBUG_ONLY(_bit_mask[0] = 0;) } } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp index 8dbc3d4fcea26..7736d3f456574 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp @@ -504,7 +504,7 @@ void JfrCheckpointManager::begin_epoch_shift() { void JfrCheckpointManager::end_epoch_shift() { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); - debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();) + DEBUG_ONLY(const u1 current_epoch = JfrTraceIdEpoch::current();) JfrTraceIdEpoch::end_epoch_shift(); assert(current_epoch != JfrTraceIdEpoch::current(), "invariant"); JfrStringPool::on_epoch_shift(); diff --git a/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp b/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp index a7a21f65944bc..ca54b297b38d9 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp @@ -398,7 +398,7 @@ static void assert_flush_large_precondition(ConstBufferPtr cur, const u1* const #endif // ASSERT BufferPtr JfrStorage::flush(BufferPtr cur, size_t used, size_t req, bool native, Thread* t) { - debug_only(assert_flush_precondition(cur, used, native, t);) + DEBUG_ONLY(assert_flush_precondition(cur, used, native, t);) const u1* const cur_pos = cur->pos(); req += used; // requested size now encompass the outstanding used size @@ -407,7 +407,7 @@ BufferPtr JfrStorage::flush(BufferPtr cur, size_t used, size_t req, bool native, } BufferPtr JfrStorage::flush_regular(BufferPtr cur, const u1* const cur_pos, size_t used, size_t req, bool native, Thread* t) { - debug_only(assert_flush_regular_precondition(cur, cur_pos, used, req, t);) + DEBUG_ONLY(assert_flush_regular_precondition(cur, cur_pos, used, req, t);) // A flush is needed before memmove since a non-large buffer is thread stable // (thread local). The flush will not modify memory in addresses above pos() // which is where the "used / uncommitted" data resides. It is therefore both @@ -450,7 +450,7 @@ static BufferPtr restore_shelved_buffer(bool native, Thread* t) { } BufferPtr JfrStorage::flush_large(BufferPtr cur, const u1* const cur_pos, size_t used, size_t req, bool native, Thread* t) { - debug_only(assert_flush_large_precondition(cur, cur_pos, used, req, native, t);) + DEBUG_ONLY(assert_flush_large_precondition(cur, cur_pos, used, req, native, t);) // Can the "regular" buffer (now shelved) accommodate the requested size? BufferPtr shelved = t->jfr_thread_local()->shelved_buffer(); assert(shelved != nullptr, "invariant"); @@ -480,7 +480,7 @@ static BufferPtr large_fail(BufferPtr cur, bool native, JfrStorage& storage_inst // even though it might be smaller than the requested size. // Caller needs to ensure if the size was successfully accommodated. BufferPtr JfrStorage::provision_large(BufferPtr cur, const u1* const cur_pos, size_t used, size_t req, bool native, Thread* t) { - debug_only(assert_provision_large_precondition(cur, used, req, t);) + DEBUG_ONLY(assert_provision_large_precondition(cur, used, req, t);) assert(t->jfr_thread_local()->shelved_buffer() != nullptr, "invariant"); BufferPtr const buffer = acquire_large(req, t); if (buffer == nullptr) { diff --git a/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp b/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp index 97f2b8a1990b0..0ba54fc79bed3 100644 --- a/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp +++ b/src/hotspot/share/jfr/recorder/storage/jfrVirtualMemory.cpp @@ -106,7 +106,8 @@ bool JfrVirtualMemorySegment::initialize(size_t reservation_size_request_bytes) assert(is_aligned(reservation_size_request_bytes, os::vm_allocation_granularity()), "invariant"); _rs = MemoryReserver::reserve(reservation_size_request_bytes, os::vm_allocation_granularity(), - os::vm_page_size()); + os::vm_page_size(), + mtTracing); if (!_rs.is_reserved()) { return false; } diff --git a/src/hotspot/share/jfr/utilities/jfrAllocation.cpp b/src/hotspot/share/jfr/utilities/jfrAllocation.cpp index f97838c786957..faf0baf2bcb2c 100644 --- a/src/hotspot/share/jfr/utilities/jfrAllocation.cpp +++ b/src/hotspot/share/jfr/utilities/jfrAllocation.cpp @@ -83,7 +83,7 @@ static void hook_memory_allocation(const char* allocation, size_t alloc_size) { vm_exit_out_of_memory(alloc_size, OOM_MALLOC_ERROR, "AllocateHeap"); } } - debug_only(add(alloc_size)); + DEBUG_ONLY(add(alloc_size)); } void JfrCHeapObj::on_memory_allocation(const void* allocation, size_t size) { @@ -111,12 +111,12 @@ void* JfrCHeapObj::operator new [](size_t size, const std::nothrow_t& nothrow_c } void JfrCHeapObj::operator delete(void* p, size_t size) { - debug_only(hook_memory_deallocation(size);) + DEBUG_ONLY(hook_memory_deallocation(size);) CHeapObj::operator delete(p); } void JfrCHeapObj::operator delete[](void* p, size_t size) { - debug_only(hook_memory_deallocation(size);) + DEBUG_ONLY(hook_memory_deallocation(size);) CHeapObj::operator delete[](p); } @@ -127,7 +127,7 @@ char* JfrCHeapObj::realloc_array(char* old, size_t size) { } void JfrCHeapObj::free(void* p, size_t size) { - debug_only(hook_memory_deallocation(size);) + DEBUG_ONLY(hook_memory_deallocation(size);) FreeHeap(p); } diff --git a/src/hotspot/share/jfr/utilities/jfrDoublyLinkedList.hpp b/src/hotspot/share/jfr/utilities/jfrDoublyLinkedList.hpp index 4e66af7f47832..814f15017e021 100644 --- a/src/hotspot/share/jfr/utilities/jfrDoublyLinkedList.hpp +++ b/src/hotspot/share/jfr/utilities/jfrDoublyLinkedList.hpp @@ -205,7 +205,7 @@ void JfrDoublyLinkedList::append_list(T* const head_node, T* const tail_node, } *lt = tail_node; const T* node = head_node; - debug_only(validate_count_param(node, count);) + DEBUG_ONLY(validate_count_param(node, count);) _count += count; assert(tail() == tail_node, "invariant"); assert(in_list(tail_node), "not in list error"); diff --git a/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp b/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp index 8d381a646ee07..4b5ca80470ef2 100644 --- a/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -33,11 +33,7 @@ class JfrSpinlockHelper { public: JfrSpinlockHelper(volatile int* lock) : _lock(lock) { - Thread::SpinAcquire(_lock, nullptr); - } - - JfrSpinlockHelper(volatile int* const lock, const char* name) : _lock(lock) { - Thread::SpinAcquire(_lock, name); + Thread::SpinAcquire(_lock); } ~JfrSpinlockHelper() { diff --git a/src/hotspot/share/jfr/writers/jfrMemoryWriterHost.hpp b/src/hotspot/share/jfr/writers/jfrMemoryWriterHost.hpp index ad47c954fd75a..af87f2bc5fb2f 100644 --- a/src/hotspot/share/jfr/writers/jfrMemoryWriterHost.hpp +++ b/src/hotspot/share/jfr/writers/jfrMemoryWriterHost.hpp @@ -45,7 +45,7 @@ class ExclusiveAccessAssert { template class MemoryWriterHost : public StorageHost { - debug_only(AccessAssert _access;) + DEBUG_ONLY(AccessAssert _access;) public: typedef typename Adapter::StorageType StorageType; protected: @@ -53,7 +53,7 @@ class MemoryWriterHost : public StorageHost { MemoryWriterHost(StorageType* storage, Thread* thread); MemoryWriterHost(StorageType* storage, size_t size); MemoryWriterHost(Thread* thread); - debug_only(bool is_acquired() const;) + DEBUG_ONLY(bool is_acquired() const;) public: void acquire(); void release(); diff --git a/src/hotspot/share/jfr/writers/jfrMemoryWriterHost.inline.hpp b/src/hotspot/share/jfr/writers/jfrMemoryWriterHost.inline.hpp index 55229e5d732db..ad16b7abde351 100644 --- a/src/hotspot/share/jfr/writers/jfrMemoryWriterHost.inline.hpp +++ b/src/hotspot/share/jfr/writers/jfrMemoryWriterHost.inline.hpp @@ -52,18 +52,18 @@ inline MemoryWriterHost::MemoryWriterHost(Thread* thr template inline void MemoryWriterHost::acquire() { - debug_only(_access.acquire();) + DEBUG_ONLY(_access.acquire();) if (!this->is_valid()) { this->flush(); } - debug_only(is_acquired();) + DEBUG_ONLY(is_acquired();) } template inline void MemoryWriterHost::release() { - debug_only(is_acquired();) + DEBUG_ONLY(is_acquired();) StorageHost::release(); - debug_only(_access.release();) + DEBUG_ONLY(_access.release();) } #ifdef ASSERT diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index cffac62c1c8b8..a4a8f3bb1d037 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -173,7 +173,7 @@ Handle JavaArgumentUnboxer::next_arg(BasicType expectedType) { ThreadInVMfromNative __tiv(thread); \ HandleMarkCleaner __hm(thread); \ JavaThread* THREAD = thread; \ - debug_only(VMNativeEntryWrapper __vew;) + DEBUG_ONLY(VMNativeEntryWrapper __vew;) // Native method block that transitions current thread to '_thread_in_vm'. // Note: CompilerThreadCanCallJava must precede JVMCIENV_FROM_JNI so that diff --git a/src/hotspot/share/jvmci/jvmciEnv.cpp b/src/hotspot/share/jvmci/jvmciEnv.cpp index a23b09c3c1e70..5a0efef0bd32b 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.cpp +++ b/src/hotspot/share/jvmci/jvmciEnv.cpp @@ -46,6 +46,7 @@ #include "runtime/javaCalls.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/os.hpp" +#include "utilities/permitForbiddenFunctions.hpp" JVMCICompileState::JVMCICompileState(CompileTask* task, JVMCICompiler* compiler): _task(task), @@ -612,7 +613,7 @@ JVMCIEnv::~JVMCIEnv() { if (_init_error_msg != nullptr) { // The memory allocated in libjvmci was not allocated with os::malloc // so must not be freed with os::free. - ALLOW_C_FUNCTION(::free, ::free((void*) _init_error_msg);) + permit_forbidden_function::free((void*)_init_error_msg); } if (_init_error != JNI_OK) { return; diff --git a/src/hotspot/share/jvmci/jvmci_globals.cpp b/src/hotspot/share/jvmci/jvmci_globals.cpp index dfc7e6b597046..a41351218f8f4 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.cpp +++ b/src/hotspot/share/jvmci/jvmci_globals.cpp @@ -144,8 +144,9 @@ bool JVMCIGlobals::check_jvmci_flags_are_consistent() { JVMCI_FLAG_CHECKED(UseMulAddIntrinsic) JVMCI_FLAG_CHECKED(UseMontgomeryMultiplyIntrinsic) JVMCI_FLAG_CHECKED(UseMontgomerySquareIntrinsic) - JVMCI_FLAG_CHECKED(UseVectorStubs) #endif // !COMPILER2 + // + JVMCI_FLAG_CHECKED(UseVectorStubs) #ifndef PRODUCT #define JVMCI_CHECK4(type, name, value, ...) assert(name##checked, #name " flag not checked"); diff --git a/src/hotspot/share/jvmci/jvmci_globals.hpp b/src/hotspot/share/jvmci/jvmci_globals.hpp index 4da49b24e6ef9..5bcead9ff7b32 100644 --- a/src/hotspot/share/jvmci/jvmci_globals.hpp +++ b/src/hotspot/share/jvmci/jvmci_globals.hpp @@ -184,8 +184,8 @@ class fileStream; NOT_COMPILER2(product(bool, EnableVectorAggressiveReboxing, false, EXPERIMENTAL, \ "Enables aggressive reboxing of vectors")) \ \ - NOT_COMPILER2(product(bool, UseVectorStubs, false, EXPERIMENTAL, \ - "Use stubs for vector transcendental operations")) \ + product(bool, UseVectorStubs, false, EXPERIMENTAL, \ + "Use stubs for vector transcendental operations") \ // end of JVMCI_FLAGS diff --git a/src/hotspot/share/logging/logConfiguration.cpp b/src/hotspot/share/logging/logConfiguration.cpp index 662c1f1c2bb79..b883dbccacff3 100644 --- a/src/hotspot/share/logging/logConfiguration.cpp +++ b/src/hotspot/share/logging/logConfiguration.cpp @@ -59,17 +59,17 @@ class ConfigurationLock : public StackObj { private: // Semaphore used as lock static Semaphore _semaphore; - debug_only(static intx _locking_thread_id;) + DEBUG_ONLY(static intx _locking_thread_id;) public: ConfigurationLock() { _semaphore.wait(); - debug_only(_locking_thread_id = os::current_thread_id()); + DEBUG_ONLY(_locking_thread_id = os::current_thread_id()); } ~ConfigurationLock() { - debug_only(_locking_thread_id = -1); + DEBUG_ONLY(_locking_thread_id = -1); _semaphore.signal(); } - debug_only(static bool current_thread_has_lock();) + DEBUG_ONLY(static bool current_thread_has_lock();) }; Semaphore ConfigurationLock::_semaphore(1); diff --git a/src/hotspot/share/logging/logTagSet.cpp b/src/hotspot/share/logging/logTagSet.cpp index f7b0e01331c91..4719d0a926ee4 100644 --- a/src/hotspot/share/logging/logTagSet.cpp +++ b/src/hotspot/share/logging/logTagSet.cpp @@ -34,6 +34,7 @@ #include "memory/allocation.inline.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" +#include "utilities/permitForbiddenFunctions.hpp" LogTagSet* LogTagSet::_list = nullptr; size_t LogTagSet::_ntagsets = 0; @@ -149,7 +150,7 @@ void LogTagSet::vwrite(LogLevelType level, const char* fmt, va_list args) { } else { // Buffer too small, allocate a large enough buffer using malloc/free to avoid circularity. // Since logging is a very basic function, conceivably used within NMT itself, avoid os::malloc/free - ALLOW_C_FUNCTION(::malloc, char* newbuf = (char*)::malloc(newbuf_len * sizeof(char));) + char* newbuf = (char*)permit_forbidden_function::malloc(newbuf_len * sizeof(char)); if (newbuf != nullptr) { prefix_len = _write_prefix(newbuf, newbuf_len); ret = os::vsnprintf(newbuf + prefix_len, newbuf_len - prefix_len, fmt, saved_args); @@ -159,7 +160,7 @@ void LogTagSet::vwrite(LogLevelType level, const char* fmt, va_list args) { if (ret < 0) { log(level, "Log message newbuf issue"); } - ALLOW_C_FUNCTION(::free, ::free(newbuf);) + permit_forbidden_function::free(newbuf); } else { // Native OOM, use buf to output the least message. At this moment buf is full of either // truncated prefix or truncated prefix + string. Put trunc_msg at the end of buf. diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index b67dcd43e4d40..93fe4f2e99bc2 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -314,7 +314,9 @@ class MetaspaceObj { f(ConstantPoolCache) \ f(Annotations) \ f(MethodCounters) \ - f(RecordComponent) + f(RecordComponent) \ + f(AdapterHandlerEntry) \ + f(AdapterFingerPrint) #define METASPACE_OBJ_TYPE_DECLARE(name) name ## Type, #define METASPACE_OBJ_TYPE_NAME_CASE(name) case name ## Type: return #name; diff --git a/src/hotspot/share/memory/allocation.inline.hpp b/src/hotspot/share/memory/allocation.inline.hpp index 8d531c6dd2318..01af1616ce18f 100644 --- a/src/hotspot/share/memory/allocation.inline.hpp +++ b/src/hotspot/share/memory/allocation.inline.hpp @@ -58,7 +58,7 @@ template E* MmapArrayAllocator::allocate_or_null(size_t length, MemTag mem_tag) { size_t size = size_for(length); - char* addr = os::reserve_memory(size, !ExecMem, mem_tag); + char* addr = os::reserve_memory(size, mem_tag); if (addr == nullptr) { return nullptr; } @@ -75,7 +75,7 @@ template E* MmapArrayAllocator::allocate(size_t length, MemTag mem_tag) { size_t size = size_for(length); - char* addr = os::reserve_memory(size, !ExecMem, mem_tag); + char* addr = os::reserve_memory(size, mem_tag); if (addr == nullptr) { vm_exit_out_of_memory(size, OOM_MMAP_ERROR, "Allocator (reserve)"); } diff --git a/src/hotspot/share/memory/memoryReserver.cpp b/src/hotspot/share/memory/memoryReserver.cpp index 43cc71355ffce..457818139cda9 100644 --- a/src/hotspot/share/memory/memoryReserver.cpp +++ b/src/hotspot/share/memory/memoryReserver.cpp @@ -90,13 +90,13 @@ static char* reserve_memory_inner(char* requested_address, assert(is_aligned(requested_address, alignment), "Requested address " PTR_FORMAT " must be aligned to %zu", p2i(requested_address), alignment); - return os::attempt_reserve_memory_at(requested_address, size, exec, mem_tag); + return os::attempt_reserve_memory_at(requested_address, size, mem_tag, exec); } // Optimistically assume that the OS returns an aligned base pointer. // When reserving a large address range, most OSes seem to align to at // least 64K. - char* base = os::reserve_memory(size, exec, mem_tag); + char* base = os::reserve_memory(size, mem_tag, exec); if (is_aligned(base, alignment)) { return base; } @@ -107,7 +107,7 @@ static char* reserve_memory_inner(char* requested_address, } // Map using the requested alignment. - return os::reserve_memory_aligned(size, alignment, exec); + return os::reserve_memory_aligned(size, alignment, mem_tag, exec); } ReservedSpace MemoryReserver::reserve_memory(char* requested_address, @@ -261,7 +261,7 @@ static char* map_memory_to_file(char* requested_address, // Optimistically assume that the OS returns an aligned base pointer. // When reserving a large address range, most OSes seem to align to at // least 64K. - char* base = os::map_memory_to_file(size, fd); + char* base = os::map_memory_to_file(size, fd, mem_tag); if (is_aligned(base, alignment)) { return base; } diff --git a/src/hotspot/share/memory/memoryReserver.hpp b/src/hotspot/share/memory/memoryReserver.hpp index 1e16ec252a9de..f8f642cca9537 100644 --- a/src/hotspot/share/memory/memoryReserver.hpp +++ b/src/hotspot/share/memory/memoryReserver.hpp @@ -58,12 +58,12 @@ class MemoryReserver : AllStatic { size_t size, size_t alignment, size_t page_size, - MemTag mem_tag = mtNone); + MemTag mem_tag); static ReservedSpace reserve(size_t size, size_t alignment, size_t page_size, - MemTag mem_tag = mtNone); + MemTag mem_tag); static ReservedSpace reserve(size_t size, MemTag mem_tag); diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 04e5d807f3244..c28360e55531c 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -63,6 +63,7 @@ #include "utilities/debug.hpp" #include "utilities/formatBuffer.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" using metaspace::ChunkManager; using metaspace::CommitLimiter; @@ -214,10 +215,11 @@ void MetaspaceUtils::print_report(outputStream* out, size_t scale) { void MetaspaceUtils::print_on(outputStream* out) { - // Used from all GCs. It first prints out totals, then, separately, the class space portion. + // First prints out totals, then, separately, the class space portion. MetaspaceCombinedStats stats = get_combined_statistics(); - out->print_cr(" Metaspace " - "used %zuK, " + out->print("Metaspace "); + out->fill_to(17); + out->print_cr("used %zuK, " "committed %zuK, " "reserved %zuK", stats.used()/K, @@ -225,8 +227,10 @@ void MetaspaceUtils::print_on(outputStream* out) { stats.reserved()/K); if (Metaspace::using_class_space()) { - out->print_cr(" class space " - "used %zuK, " + StreamAutoIndentor indentor(out, 1); + out->print("class space "); + out->fill_to(17); + out->print_cr("used %zuK, " "committed %zuK, " "reserved %zuK", stats.class_space_stats().used()/K, @@ -594,7 +598,7 @@ ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t siz if (result == nullptr) { // Fallback: reserve anywhere log_debug(metaspace, map)("Trying anywhere..."); - result = os::reserve_memory_aligned(size, Metaspace::reserve_alignment(), false); + result = os::reserve_memory_aligned(size, Metaspace::reserve_alignment(), mtClass); } // Wrap resulting range in ReservedSpace @@ -767,7 +771,8 @@ void Metaspace::global_initialize() { rs = MemoryReserver::reserve((char*)base, size, Metaspace::reserve_alignment(), - os::vm_page_size()); + os::vm_page_size(), + mtClass); if (rs.is_reserved()) { log_info(metaspace)("Successfully forced class space address to " PTR_FORMAT, p2i(base)); diff --git a/src/hotspot/share/memory/metaspace/testHelpers.cpp b/src/hotspot/share/memory/metaspace/testHelpers.cpp index 76fa1e36c4523..f06f6c855bed5 100644 --- a/src/hotspot/share/memory/metaspace/testHelpers.cpp +++ b/src/hotspot/share/memory/metaspace/testHelpers.cpp @@ -82,7 +82,7 @@ MetaspaceTestContext::MetaspaceTestContext(const char* name, size_t commit_limit reserve_limit, Metaspace::reserve_alignment_words()); if (reserve_limit > 0) { // have reserve limit -> non-expandable context - _rs = MemoryReserver::reserve(reserve_limit * BytesPerWord, Metaspace::reserve_alignment(), os::vm_page_size()); + _rs = MemoryReserver::reserve(reserve_limit * BytesPerWord, Metaspace::reserve_alignment(), os::vm_page_size(), mtTest); _context = MetaspaceContext::create_nonexpandable_context(name, _rs, &_commit_limiter); } else { // no reserve limit -> expandable vslist @@ -142,4 +142,3 @@ size_t MetaspaceTestContext::reserved_words() const { } } // namespace metaspace - diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index 66644c805a938..bb59192cf16cb 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -256,7 +256,7 @@ VirtualSpaceNode* VirtualSpaceNode::create_node(size_t word_size, ReservedSpace rs = MemoryReserver::reserve(word_size * BytesPerWord, Settings::virtual_space_node_reserve_alignment_words() * BytesPerWord, - os::vm_page_size()); + os::vm_page_size(), mtMetaspace); if (!rs.is_reserved()) { vm_exit_out_of_memory(word_size * BytesPerWord, OOM_MMAP_ERROR, "Failed to reserve memory for metaspace"); } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 6f415ab93a46f..156c96d621f8d 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -165,8 +165,8 @@ uintx Universe::_the_array_interfaces_bitmap = 0; uintx Universe::_the_empty_klass_bitmap = 0; // These variables are guarded by FullGCALot_lock. -debug_only(OopHandle Universe::_fullgc_alot_dummy_array;) -debug_only(int Universe::_fullgc_alot_dummy_next = 0;) +DEBUG_ONLY(OopHandle Universe::_fullgc_alot_dummy_array;) +DEBUG_ONLY(int Universe::_fullgc_alot_dummy_next = 0;) // Heap int Universe::_verify_count = 0; @@ -1159,7 +1159,10 @@ void Universe::compute_base_vtable_size() { void Universe::print_on(outputStream* st) { GCMutexLocker hl(Heap_lock); // Heap_lock might be locked by caller thread. st->print_cr("Heap"); - heap()->print_on(st); + + StreamAutoIndentor indentor(st, 1); + heap()->print_heap_on(st); + MetaspaceUtils::print_on(st); } void Universe::print_heap_at_SIGBREAK() { diff --git a/src/hotspot/share/memory/universe.hpp b/src/hotspot/share/memory/universe.hpp index 69f8642d6dafe..35c31330f082e 100644 --- a/src/hotspot/share/memory/universe.hpp +++ b/src/hotspot/share/memory/universe.hpp @@ -117,8 +117,8 @@ class Universe: AllStatic { static intptr_t _non_oop_bits; // array of dummy objects used with +FullGCAlot - debug_only(static OopHandle _fullgc_alot_dummy_array;) - debug_only(static int _fullgc_alot_dummy_next;) + DEBUG_ONLY(static OopHandle _fullgc_alot_dummy_array;) + DEBUG_ONLY(static int _fullgc_alot_dummy_next;) // Compiler/dispatch support static int _base_vtable_size; // Java vtbl size of klass Object (in words) @@ -357,7 +357,7 @@ class Universe: AllStatic { // Change the number of dummy objects kept reachable by the full gc dummy // array; this should trigger relocation in a sliding compaction collector. - debug_only(static bool release_fullgc_alot_dummy();) + DEBUG_ONLY(static bool release_fullgc_alot_dummy();) // The non-oop pattern (see compiledIC.hpp, etc) static void* non_oop_word(); static bool contains_non_oop_word(void* p); diff --git a/src/hotspot/share/memory/virtualspace.cpp b/src/hotspot/share/memory/virtualspace.cpp index 2ea90a6c9ddda..2c2d629f03261 100644 --- a/src/hotspot/share/memory/virtualspace.cpp +++ b/src/hotspot/share/memory/virtualspace.cpp @@ -200,7 +200,7 @@ static bool commit_expanded(char* start, size_t size, size_t alignment, bool pre return true; } - debug_only(warning( + DEBUG_ONLY(warning( "INFO: os::commit_memory(" PTR_FORMAT ", " PTR_FORMAT " size=%zu, executable=%d) failed", p2i(start), p2i(start + size), size, executable);) @@ -371,7 +371,7 @@ void VirtualSpace::shrink_by(size_t size) { aligned_upper_new_high + upper_needs <= upper_high_boundary(), "must not shrink beyond region"); if (!os::uncommit_memory(aligned_upper_new_high, upper_needs, _executable)) { - debug_only(warning("os::uncommit_memory failed")); + DEBUG_ONLY(warning("os::uncommit_memory failed")); return; } else { _upper_high -= upper_needs; @@ -382,7 +382,7 @@ void VirtualSpace::shrink_by(size_t size) { aligned_middle_new_high + middle_needs <= middle_high_boundary(), "must not shrink beyond region"); if (!os::uncommit_memory(aligned_middle_new_high, middle_needs, _executable)) { - debug_only(warning("os::uncommit_memory failed")); + DEBUG_ONLY(warning("os::uncommit_memory failed")); return; } else { _middle_high -= middle_needs; @@ -393,7 +393,7 @@ void VirtualSpace::shrink_by(size_t size) { aligned_lower_new_high + lower_needs <= lower_high_boundary(), "must not shrink beyond region"); if (!os::uncommit_memory(aligned_lower_new_high, lower_needs, _executable)) { - debug_only(warning("os::uncommit_memory failed")); + DEBUG_ONLY(warning("os::uncommit_memory failed")); return; } else { _lower_high -= lower_needs; @@ -422,13 +422,13 @@ void VirtualSpace::check_for_contiguity() { } void VirtualSpace::print_on(outputStream* out) const { - out->print ("Virtual space:"); - if (special()) out->print(" (pinned in memory)"); - out->cr(); - out->print_cr(" - committed: %zu", committed_size()); - out->print_cr(" - reserved: %zu", reserved_size()); - out->print_cr(" - [low, high]: [" PTR_FORMAT ", " PTR_FORMAT "]", p2i(low()), p2i(high())); - out->print_cr(" - [low_b, high_b]: [" PTR_FORMAT ", " PTR_FORMAT "]", p2i(low_boundary()), p2i(high_boundary())); + out->print_cr("Virtual space:%s", special() ? " (pinned in memory)" : ""); + + StreamAutoIndentor indentor(out, 1); + out->print_cr("- committed: %zu", committed_size()); + out->print_cr("- reserved: %zu", reserved_size()); + out->print_cr("- [low, high]: [" PTR_FORMAT ", " PTR_FORMAT "]", p2i(low()), p2i(high())); + out->print_cr("- [low_b, high_b]: [" PTR_FORMAT ", " PTR_FORMAT "]", p2i(low_boundary()), p2i(high_boundary())); } void VirtualSpace::print() const { @@ -436,3 +436,8 @@ void VirtualSpace::print() const { } #endif + +void VirtualSpace::print_space_boundaries_on(outputStream* out) const { + out->print_cr("[" PTR_FORMAT ", " PTR_FORMAT ", " PTR_FORMAT ")", + p2i(low_boundary()), p2i(high()), p2i(high_boundary())); +} diff --git a/src/hotspot/share/memory/virtualspace.hpp b/src/hotspot/share/memory/virtualspace.hpp index d5abf13b720fd..abc760ffeb357 100644 --- a/src/hotspot/share/memory/virtualspace.hpp +++ b/src/hotspot/share/memory/virtualspace.hpp @@ -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 @@ -124,6 +124,8 @@ class VirtualSpace { // Debugging void print_on(outputStream* out) const PRODUCT_RETURN; void print() const; + + void print_space_boundaries_on(outputStream* out) const; }; #endif // SHARE_MEMORY_VIRTUALSPACE_HPP diff --git a/src/hotspot/share/nmt/mallocSiteTable.cpp b/src/hotspot/share/nmt/mallocSiteTable.cpp index 71e75551e2fb2..55fa5f0b173ed 100644 --- a/src/hotspot/share/nmt/mallocSiteTable.cpp +++ b/src/hotspot/share/nmt/mallocSiteTable.cpp @@ -25,6 +25,8 @@ #include "memory/allocation.inline.hpp" #include "nmt/mallocSiteTable.hpp" #include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/permitForbiddenFunctions.hpp" // Malloc site hashtable buckets MallocSiteHashtableEntry** MallocSiteTable::_table = nullptr; @@ -40,9 +42,7 @@ const MallocSiteHashtableEntry* MallocSiteTable::_hash_entry_allocation_site = n * time, it is in single-threaded mode from JVM perspective. */ bool MallocSiteTable::initialize() { - - ALLOW_C_FUNCTION(::calloc, - _table = (MallocSiteHashtableEntry**)::calloc(table_size, sizeof(MallocSiteHashtableEntry*));) + _table = (MallocSiteHashtableEntry**)permit_forbidden_function::calloc(table_size, sizeof(MallocSiteHashtableEntry*)); if (_table == nullptr) { return false; } diff --git a/src/hotspot/share/nmt/memMapPrinter.cpp b/src/hotspot/share/nmt/memMapPrinter.cpp index db67321fb1d45..804a03f45e5d0 100644 --- a/src/hotspot/share/nmt/memMapPrinter.cpp +++ b/src/hotspot/share/nmt/memMapPrinter.cpp @@ -42,6 +42,7 @@ #include "runtime/vmThread.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" +#include "utilities/permitForbiddenFunctions.hpp" // Note: throughout this code we will use the term "VMA" for OS system level memory mapping @@ -95,8 +96,8 @@ class CachedNMTInformation : public VirtualMemoryWalker { _count(0), _capacity(0), _last(0) {} ~CachedNMTInformation() { - ALLOW_C_FUNCTION(free, ::free(_ranges);) - ALLOW_C_FUNCTION(free, ::free(_mem_tags);) + permit_forbidden_function::free(_ranges); + permit_forbidden_function::free(_mem_tags); } bool add(const void* from, const void* to, MemTag mem_tag) { @@ -111,8 +112,8 @@ class CachedNMTInformation : public VirtualMemoryWalker { // Enlarge if needed const size_t new_capacity = MAX2((size_t)4096, 2 * _capacity); // Unfortunately, we need to allocate manually, raw, since we must prevent NMT deadlocks (ThreadCritical). - ALLOW_C_FUNCTION(realloc, _ranges = (Range*)::realloc(_ranges, new_capacity * sizeof(Range));) - ALLOW_C_FUNCTION(realloc, _mem_tags = (MemTag*)::realloc(_mem_tags, new_capacity * sizeof(MemTag));) + _ranges = (Range*)permit_forbidden_function::realloc(_ranges, new_capacity * sizeof(Range)); + _mem_tags = (MemTag*)permit_forbidden_function::realloc(_mem_tags, new_capacity * sizeof(MemTag)); if (_ranges == nullptr || _mem_tags == nullptr) { // In case of OOM lets make no fuss. Just return. return false; diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index c7327782a4ad7..512e197592962 100644 --- a/src/hotspot/share/nmt/memReporter.cpp +++ b/src/hotspot/share/nmt/memReporter.cpp @@ -249,7 +249,7 @@ void MemSummaryReporter::report_summary_of_tag(MemTag mem_tag, // report malloc'd memory if (amount_in_current_scale(MAX2(malloc_memory->malloc_size(), pk_malloc)) > 0) { - print_malloc(malloc_memory->malloc_counter()); + print_malloc(malloc_memory->malloc_counter(), mem_tag); out->cr(); } diff --git a/src/hotspot/share/nmt/memReporter.hpp b/src/hotspot/share/nmt/memReporter.hpp index 05b1588f38b34..2238d42f15fbd 100644 --- a/src/hotspot/share/nmt/memReporter.hpp +++ b/src/hotspot/share/nmt/memReporter.hpp @@ -108,7 +108,7 @@ class MemReporterBase : public StackObj { // Print summary total, malloc and virtual memory void print_total(size_t reserved, size_t committed, size_t peak = 0) const; - void print_malloc(const MemoryCounter* c, MemTag mem_tag = mtNone) const; + void print_malloc(const MemoryCounter* c, MemTag mem_tag) const; void print_virtual_memory(size_t reserved, size_t committed, size_t peak) const; void print_arena(const MemoryCounter* c) const; diff --git a/src/hotspot/share/nmt/memTracker.hpp b/src/hotspot/share/nmt/memTracker.hpp index 981e991a41ef4..3918e81dab72b 100644 --- a/src/hotspot/share/nmt/memTracker.hpp +++ b/src/hotspot/share/nmt/memTracker.hpp @@ -127,7 +127,7 @@ class MemTracker : AllStatic { // (we do not do any reservations before that). static inline void record_virtual_memory_reserve(void* addr, size_t size, const NativeCallStack& stack, - MemTag mem_tag = mtNone) { + MemTag mem_tag) { assert_post_init(); if (!enabled()) return; if (addr != nullptr) { @@ -153,7 +153,7 @@ class MemTracker : AllStatic { } static inline void record_virtual_memory_reserve_and_commit(void* addr, size_t size, - const NativeCallStack& stack, MemTag mem_tag = mtNone) { + const NativeCallStack& stack, MemTag mem_tag) { assert_post_init(); if (!enabled()) return; if (addr != nullptr) { diff --git a/src/hotspot/share/nmt/nmtPreInit.cpp b/src/hotspot/share/nmt/nmtPreInit.cpp index 34a2d8dd7d2ee..3b9150366dc73 100644 --- a/src/hotspot/share/nmt/nmtPreInit.cpp +++ b/src/hotspot/share/nmt/nmtPreInit.cpp @@ -29,12 +29,13 @@ #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" +#include "utilities/permitForbiddenFunctions.hpp" // Obviously we cannot use os::malloc for any dynamic allocation during pre-NMT-init, so we must use // raw malloc; to make this very clear, wrap them. -static void* raw_malloc(size_t s) { ALLOW_C_FUNCTION(::malloc, return ::malloc(s);) } -static void* raw_realloc(void* old, size_t s) { ALLOW_C_FUNCTION(::realloc, return ::realloc(old, s);) } -static void raw_free(void* p) { ALLOW_C_FUNCTION(::free, ::free(p);) } +static void* raw_malloc(size_t s) { return permit_forbidden_function::malloc(s); } +static void* raw_realloc(void* old, size_t s) { return permit_forbidden_function::realloc(old, s); } +static void raw_free(void* p) { permit_forbidden_function::free(p); } // To keep matters simple we just raise a fatal error on OOM. Since preinit allocation // is just used for pre-VM-initialization mallocs, none of which are optional, we don't diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp index 9e5d0a18c10a0..e6fac91f2450b 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp @@ -396,6 +396,11 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, return true; } + if (reserved_rgn->mem_tag() == mtCode) { + assert(reserved_rgn->contain_region(base_addr, size), "Reserved code region should contain this mapping region"); + return true; + } + // Print some more details. Don't use UL here to avoid circularities. tty->print_cr("Error: existing region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), memory tag %u.\n" " new region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), memory tag %u.", diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.hpp b/src/hotspot/share/nmt/virtualMemoryTracker.hpp index 74d299e6637f6..2b3b572257114 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.hpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.hpp @@ -297,7 +297,7 @@ class ReservedMemoryRegion : public VirtualMemoryRegion { public: ReservedMemoryRegion(address base, size_t size, const NativeCallStack& stack, - MemTag mem_tag = mtNone) : + MemTag mem_tag) : VirtualMemoryRegion(base, size), _stack(stack), _mem_tag(mem_tag) { } @@ -380,7 +380,7 @@ class VirtualMemoryTracker : AllStatic { public: static bool initialize(NMT_TrackingLevel level); - static bool add_reserved_region (address base_addr, size_t size, const NativeCallStack& stack, MemTag mem_tag = mtNone); + static bool add_reserved_region (address base_addr, size_t size, const NativeCallStack& stack, MemTag mem_tag); static bool add_committed_region (address base_addr, size_t size, const NativeCallStack& stack); static bool remove_uncommitted_region (address base_addr, size_t size); diff --git a/src/hotspot/share/oops/cpCache.hpp b/src/hotspot/share/oops/cpCache.hpp index 6490a012e9aeb..83af4b88e322f 100644 --- a/src/hotspot/share/oops/cpCache.hpp +++ b/src/hotspot/share/oops/cpCache.hpp @@ -76,7 +76,7 @@ class ConstantPoolCache: public MetaspaceObj { Array* _resolved_method_entries; // Sizing - debug_only(friend class ClassVerifier;) + DEBUG_ONLY(friend class ClassVerifier;) public: // specific but defiinitions for ldc diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index a3db976046dbc..a17d1ca4e3704 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -2027,7 +2027,7 @@ void GenerateOopMap::ret_jump_targets_do(BytecodeStream *bcs, jmpFct_t jmpFct, i int target_bci = rtEnt->jsrs(i); // Make sure a jrtRet does not set the changed bit for dead basicblock. BasicBlock* jsr_bb = get_basic_block_containing(target_bci - 1); - debug_only(BasicBlock* target_bb = &jsr_bb[1];) + DEBUG_ONLY(BasicBlock* target_bb = &jsr_bb[1];) assert(target_bb == get_basic_block_at(target_bci), "wrong calc. of successor basicblock"); bool alive = jsr_bb->is_alive(); if (TraceNewOopMapGeneration) { diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 715c8f473d087..00ef75d8486c5 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1321,7 +1321,7 @@ void InstanceKlass::initialize_impl(TRAPS) { // Step 9 if (!HAS_PENDING_EXCEPTION) { set_initialization_state_and_notify(fully_initialized, CHECK); - debug_only(vtable().verify(tty, true);) + DEBUG_ONLY(vtable().verify(tty, true);) } else { // Step 10 and 11 @@ -2501,6 +2501,7 @@ void InstanceKlass::mark_dependent_nmethods(DeoptimizationScope* deopt_scope, Kl } void InstanceKlass::add_dependent_nmethod(nmethod* nm) { + assert_lock_strong(CodeCache_lock); dependencies().add_dependent_nmethod(nm); } @@ -4191,7 +4192,7 @@ JNIid::JNIid(Klass* holder, int offset, JNIid* next) { _holder = holder; _offset = offset; _next = next; - debug_only(_is_static_field_id = false;) + DEBUG_ONLY(_is_static_field_id = false;) } diff --git a/src/hotspot/share/oops/instanceRefKlass.cpp b/src/hotspot/share/oops/instanceRefKlass.cpp index eb507495cf554..b83274924937e 100644 --- a/src/hotspot/share/oops/instanceRefKlass.cpp +++ b/src/hotspot/share/oops/instanceRefKlass.cpp @@ -71,10 +71,10 @@ void InstanceRefKlass::update_nonstatic_oop_maps(Klass* k) { InstanceKlass* ik = InstanceKlass::cast(k); // Check that we have the right class - debug_only(static bool first_time = true); + DEBUG_ONLY(static bool first_time = true); assert(k == vmClasses::Reference_klass() && first_time, "Invalid update of maps"); - debug_only(first_time = false); + DEBUG_ONLY(first_time = false); assert(ik->nonstatic_oop_map_count() == 1, "just checking"); OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 71da5e1da095b..b0d2e84335e1f 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -678,7 +678,7 @@ void Klass::append_to_sibling_list() { if (Universe::is_fully_initialized()) { assert_locked_or_safepoint(Compile_lock); } - debug_only(verify();) + DEBUG_ONLY(verify();) // add ourselves to superklass' subklass list InstanceKlass* super = superklass(); if (super == nullptr) return; // special case: class Object @@ -703,7 +703,7 @@ void Klass::append_to_sibling_list() { return; } } - debug_only(verify();) + DEBUG_ONLY(verify();) } void Klass::clean_subklass() { @@ -830,8 +830,14 @@ void Klass::remove_java_mirror() { oop orig_mirror = src_k->java_mirror(); if (orig_mirror == nullptr) { assert(CDSConfig::is_dumping_final_static_archive(), "sanity"); - assert(is_instance_klass(), "sanity"); - assert(InstanceKlass::cast(this)->is_shared_unregistered_class(), "sanity"); + if (is_instance_klass()) { + assert(InstanceKlass::cast(this)->is_shared_unregistered_class(), "sanity"); + } else { + precond(is_objArray_klass()); + Klass *k = ObjArrayKlass::cast(this)->bottom_klass(); + precond(k->is_instance_klass()); + assert(InstanceKlass::cast(k)->is_shared_unregistered_class(), "sanity"); + } } else { oop scratch_mirror = HeapShared::scratch_java_mirror(orig_mirror); if (scratch_mirror != nullptr) { diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index 0c4430b44c3f5..ce7d77a38ccc2 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -31,6 +31,7 @@ #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmClasses.hpp" +#include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" #include "code/debugInfoRec.hpp" #include "compiler/compilationPolicy.hpp" @@ -392,6 +393,7 @@ void Method::metaspace_pointers_do(MetaspaceClosure* it) { } else { it->push(&_constMethod); } + it->push(&_adapter); it->push(&_method_data); it->push(&_method_counters); NOT_PRODUCT(it->push(&_name);) @@ -405,11 +407,18 @@ void Method::metaspace_pointers_do(MetaspaceClosure* it) { void Method::remove_unshareable_info() { unlink_method(); + if (AOTCodeCache::is_dumping_adapters() && _adapter != nullptr) { + _adapter->remove_unshareable_info(); + } JFR_ONLY(REMOVE_METHOD_ID(this);) } void Method::restore_unshareable_info(TRAPS) { assert(is_method() && is_valid_method(this), "ensure C++ vtable is restored"); + if (_adapter != nullptr) { + assert(_adapter->is_linked(), "must be"); + _from_compiled_entry = _adapter->get_c2i_entry(); + } assert(!queued_for_compilation(), "method's queued_for_compilation flag should not be set"); } #endif @@ -1137,7 +1146,9 @@ void Method::unlink_code() { void Method::unlink_method() { assert(CDSConfig::is_dumping_archive(), "sanity"); _code = nullptr; - _adapter = nullptr; + if (!AOTCodeCache::is_dumping_adapters() || AdapterHandlerLibrary::is_abstract_method_adapter(_adapter)) { + _adapter = nullptr; + } _i2i_entry = nullptr; _from_compiled_entry = nullptr; _from_interpreted_entry = nullptr; @@ -1178,14 +1189,18 @@ void Method::link_method(const methodHandle& h_method, TRAPS) { // If the code cache is full, we may reenter this function for the // leftover methods that weren't linked. if (adapter() != nullptr) { - return; + if (adapter()->is_shared()) { + assert(adapter()->is_linked(), "Adapter is shared but not linked"); + } else { + return; + } } assert( _code == nullptr, "nothing compiled yet" ); // Setup interpreter entrypoint assert(this == h_method(), "wrong h_method()" ); - assert(adapter() == nullptr, "init'd to null"); + assert(adapter() == nullptr || adapter()->is_linked(), "init'd to null or restored from cache"); address entry = Interpreter::entry_for_method(h_method); assert(entry != nullptr, "interpreter entry must be non-null"); // Sets both _i2i_entry and _from_interpreted_entry @@ -1206,7 +1221,10 @@ void Method::link_method(const methodHandle& h_method, TRAPS) { // called from the vtable. We need adapters on such methods that get loaded // later. Ditto for mega-morphic itable calls. If this proves to be a // problem we'll make these lazily later. - (void) make_adapters(h_method, CHECK); + if (_adapter == nullptr) { + (void) make_adapters(h_method, CHECK); + assert(adapter()->is_linked(), "Adapter must have been linked"); + } // ONLY USE the h_method now as make_adapter may have blocked @@ -1256,7 +1274,7 @@ address Method::make_adapters(const methodHandle& mh, TRAPS) { // or adapter that it points to is still live and valid. // This function must not hit a safepoint! address Method::verified_code_entry() { - debug_only(NoSafepointVerifier nsv;) + DEBUG_ONLY(NoSafepointVerifier nsv;) assert(_from_compiled_entry != nullptr, "must be set"); return _from_compiled_entry; } @@ -1482,6 +1500,9 @@ methodHandle Method::make_method_handle_intrinsic(vmIntrinsics::ID iid, #if INCLUDE_CDS void Method::restore_archived_method_handle_intrinsic(methodHandle m, TRAPS) { + if (m->adapter() != nullptr) { + m->set_from_compiled_entry(m->adapter()->get_c2i_entry()); + } m->link_method(m, CHECK); if (m->intrinsic_id() == vmIntrinsics::_linkToNative) { diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 5d4ac47130335..c33f656047d5e 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -42,7 +42,7 @@ void Block_Array::grow( uint i ) { if (i < Max()) { return; // No need to grow } - debug_only(_limit = i+1); + DEBUG_ONLY(_limit = i+1); if( i < _size ) return; if( !_size ) { _size = 1; diff --git a/src/hotspot/share/opto/block.hpp b/src/hotspot/share/opto/block.hpp index 4ac6399d3a089..5baa72dfffba6 100644 --- a/src/hotspot/share/opto/block.hpp +++ b/src/hotspot/share/opto/block.hpp @@ -48,7 +48,7 @@ struct Tarjan; // allocation I do not need a destructor to reclaim storage. class Block_Array : public ArenaObj { uint _size; // allocated size, as opposed to formal limit - debug_only(uint _limit;) // limit to formal domain + DEBUG_ONLY(uint _limit;) // limit to formal domain Arena *_arena; // Arena to allocate in ReallocMark _nesting; // Safety checks for arena reallocation protected: @@ -57,7 +57,7 @@ class Block_Array : public ArenaObj { public: Block_Array(Arena *a) : _size(OptoBlockListSize), _arena(a) { - debug_only(_limit=0); + DEBUG_ONLY(_limit=0); _blocks = NEW_ARENA_ARRAY( a, Block *, OptoBlockListSize ); for( int i = 0; i < OptoBlockListSize; i++ ) { _blocks[i] = nullptr; @@ -69,7 +69,7 @@ class Block_Array : public ArenaObj { { assert( i < Max(), "oob" ); return _blocks[i]; } // Extend the mapping: index i maps to Block *n. void map( uint i, Block *n ) { grow(i); _blocks[i] = n; } - uint Max() const { debug_only(return _limit); return _size; } + uint Max() const { DEBUG_ONLY(return _limit); return _size; } }; diff --git a/src/hotspot/share/opto/buildOopMap.cpp b/src/hotspot/share/opto/buildOopMap.cpp index f135df211145e..675113163e842 100644 --- a/src/hotspot/share/opto/buildOopMap.cpp +++ b/src/hotspot/share/opto/buildOopMap.cpp @@ -191,7 +191,7 @@ void OopFlow::clone( OopFlow *flow, int max_size ) { OopFlow *OopFlow::make( Arena *A, int max_size, Compile* C ) { short *callees = NEW_ARENA_ARRAY(A,short,max_size+1); Node **defs = NEW_ARENA_ARRAY(A,Node*,max_size+1); - debug_only( memset(defs,0,(max_size+1)*sizeof(Node*)) ); + DEBUG_ONLY( memset(defs,0,(max_size+1)*sizeof(Node*)) ); OopFlow *flow = new (A) OopFlow(callees+1, defs+1, C); assert( &flow->_callees[OptoReg::Bad] == callees, "Ok to index at OptoReg::Bad" ); assert( &flow->_defs [OptoReg::Bad] == defs , "Ok to index at OptoReg::Bad" ); @@ -209,7 +209,7 @@ static void clr_live_bit( int *live, int reg ) { OopMap *OopFlow::build_oop_map( Node *n, int max_reg, PhaseRegAlloc *regalloc, int* live ) { int framesize = regalloc->_framesize; int max_inarg_slot = OptoReg::reg2stack(regalloc->_matcher._new_SP); - debug_only( char *dup_check = NEW_RESOURCE_ARRAY(char,OptoReg::stack0()); + DEBUG_ONLY( char *dup_check = NEW_RESOURCE_ARRAY(char,OptoReg::stack0()); memset(dup_check,0,OptoReg::stack0()) ); OopMap *omap = new OopMap( framesize, max_inarg_slot ); @@ -351,7 +351,7 @@ OopMap *OopFlow::build_oop_map( Node *n, int max_reg, PhaseRegAlloc *regalloc, i } else if( OptoReg::is_valid(_callees[reg])) { // callee-save? // It's a callee-save value assert( dup_check[_callees[reg]]==0, "trying to callee save same reg twice" ); - debug_only( dup_check[_callees[reg]]=1; ) + DEBUG_ONLY( dup_check[_callees[reg]]=1; ) VMReg callee = OptoReg::as_VMReg(OptoReg::Name(_callees[reg])); omap->set_callee_saved(r, callee); diff --git a/src/hotspot/share/opto/c2_MacroAssembler.hpp b/src/hotspot/share/opto/c2_MacroAssembler.hpp index 41347313a8cf1..1fb7714153dd8 100644 --- a/src/hotspot/share/opto/c2_MacroAssembler.hpp +++ b/src/hotspot/share/opto/c2_MacroAssembler.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,8 @@ #include "utilities/macros.hpp" class C2EntryBarrierStub; +class TypeInt; +class TypeLong; class C2_MacroAssembler: public MacroAssembler { public: diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 41b081610974a..2246bd62f4eb6 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -669,6 +669,16 @@ develop(bool, VerifyAliases, false, \ "perform extra checks on the results of alias analysis") \ \ + product(uint, VerifyConstraintCasts, 0, DIAGNOSTIC, \ + "Perform runtime checks to verify the value of a " \ + "ConstraintCast lies inside its type" \ + "0 = does not perform any verification, " \ + "1 = perform verification on ConstraintCastNodes that are " \ + "present during code emission, " \ + "2 = Do not do widening of ConstraintCastNodes so that we can " \ + "have more verification coverage") \ + range(0, 2) \ + \ product(intx, MaxInlineLevel, 15, \ "maximum number of nested calls that are inlined by high tier " \ "compiler") \ @@ -750,9 +760,6 @@ product(bool, EnableVectorAggressiveReboxing, false, EXPERIMENTAL, \ "Enables aggressive reboxing of vectors") \ \ - product(bool, UseVectorStubs, false, EXPERIMENTAL, \ - "Use stubs for vector transcendental operations") \ - \ product(bool, UseTypeSpeculation, true, \ "Speculatively propagate types from profiles") \ \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index f39937b9cdd3b..272692446ae61 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -848,6 +848,9 @@ bool C2Compiler::is_intrinsic_supported(vmIntrinsics::ID id) { case vmIntrinsics::_IndexVector: case vmIntrinsics::_IndexPartiallyInUpperRange: return EnableVectorSupport; + case vmIntrinsics::_VectorUnaryLibOp: + case vmIntrinsics::_VectorBinaryLibOp: + return EnableVectorSupport && Matcher::supports_vector_calling_convention(); case vmIntrinsics::_blackhole: #if INCLUDE_JVMTI case vmIntrinsics::_notifyJvmtiVThreadStart: diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index bb30dcb2976a7..3527e46c24b7c 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -266,8 +266,8 @@ JVMState::JVMState(ciMethod* method, JVMState* caller) : assert(method != nullptr, "must be valid call site"); _bci = InvocationEntryBci; _reexecute = Reexecute_Undefined; - debug_only(_bci = -99); // random garbage value - debug_only(_map = (SafePointNode*)-1); + DEBUG_ONLY(_bci = -99); // random garbage value + DEBUG_ONLY(_map = (SafePointNode*)-1); _caller = caller; _depth = 1 + (caller == nullptr ? 0 : caller->depth()); _locoff = TypeFunc::Parms; @@ -281,7 +281,7 @@ JVMState::JVMState(int stack_size) : _method(nullptr) { _bci = InvocationEntryBci; _reexecute = Reexecute_Undefined; - debug_only(_map = (SafePointNode*)-1); + DEBUG_ONLY(_map = (SafePointNode*)-1); _caller = nullptr; _depth = 1; _locoff = TypeFunc::Parms; @@ -323,14 +323,14 @@ bool JVMState::same_calls_as(const JVMState* that) const { //------------------------------debug_start------------------------------------ uint JVMState::debug_start() const { - debug_only(JVMState* jvmroot = of_depth(1)); + DEBUG_ONLY(JVMState* jvmroot = of_depth(1)); assert(jvmroot->locoff() <= this->locoff(), "youngest JVMState must be last"); return of_depth(1)->locoff(); } //-------------------------------debug_end------------------------------------- uint JVMState::debug_end() const { - debug_only(JVMState* jvmroot = of_depth(1)); + DEBUG_ONLY(JVMState* jvmroot = of_depth(1)); assert(jvmroot->endoff() <= this->endoff(), "youngest JVMState must be last"); return endoff(); } @@ -1465,7 +1465,7 @@ void SafePointNode::push_monitor(const FastLockNode *lock) { void SafePointNode::pop_monitor() { // Delete last monitor from debug info - debug_only(int num_before_pop = jvms()->nof_monitors()); + DEBUG_ONLY(int num_before_pop = jvms()->nof_monitors()); const int MonitorEdges = 2; assert(JVMState::logMonitorEdges == exact_log2(MonitorEdges), "correct MonitorEdges"); int scloff = jvms()->scloff(); diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 198634af71bba..6b3058d94f6ff 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -531,6 +531,18 @@ const Type* ConstraintCastNode::widen_type(const PhaseGVN* phase, const Type* re if (!phase->C->post_loop_opts_phase()) { return res; } + + // At VerifyConstraintCasts == 1, we verify the ConstraintCastNodes that are present during code + // emission. This allows us detecting possible mis-scheduling due to these nodes being pinned at + // the wrong control nodes. + // At VerifyConstraintCasts == 2, we do not perform widening so that we can verify the + // correctness of more ConstraintCastNodes. This further helps us detect possible + // mis-transformations that may happen due to these nodes being pinned at the wrong control + // nodes. + if (VerifyConstraintCasts > 1) { + return res; + } + const TypeInteger* this_type = res->is_integer(bt); const TypeInteger* in_type = phase->type(in(1))->isa_integer(bt); if (in_type != nullptr && diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 4885d44a13df7..92f6c938dbace 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2233,7 +2233,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } // One unique input. - debug_only(Node* ident = Identity(phase)); + DEBUG_ONLY(Node* ident = Identity(phase)); // The unique input must eventually be detected by the Identity call. #ifdef ASSERT if (ident != uin && !ident->is_top() && !must_wait_for_region_in_irreducible_loop(phase)) { diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index 11e1797d03429..7832cbd42f824 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -1323,7 +1323,7 @@ void PhaseChaitin::Simplify( ) { bool bound = lrgs(lo_score)._is_bound; // Find cheapest guy - debug_only( int lo_no_simplify=0; ); + DEBUG_ONLY( int lo_no_simplify=0; ); for (uint i = _hi_degree; i; i = lrgs(i)._next) { assert(!_ifg->_yanked->test(i), ""); // It's just vaguely possible to move hi-degree to lo-degree without @@ -1335,7 +1335,7 @@ void PhaseChaitin::Simplify( ) { lo_score = i; break; } - debug_only( if( lrgs(i)._was_lo ) lo_no_simplify=i; ); + DEBUG_ONLY( if( lrgs(i)._was_lo ) lo_no_simplify=i; ); double iscore = lrgs(i).score(); double iarea = lrgs(i)._area; double icost = lrgs(i)._cost; @@ -1577,7 +1577,7 @@ uint PhaseChaitin::Select( ) { // Remove neighbor colors IndexSet *s = _ifg->neighbors(lidx); - debug_only(RegMask orig_mask = lrg->mask();) + DEBUG_ONLY(RegMask orig_mask = lrg->mask();) if (!s->is_empty()) { IndexSetIterator elements(s); @@ -1706,8 +1706,8 @@ uint PhaseChaitin::Select( ) { ttyLocker ttyl; tty->print("L%d spilling with neighbors: ", lidx); s->dump(); - debug_only(tty->print(" original mask: ")); - debug_only(orig_mask.dump()); + DEBUG_ONLY(tty->print(" original mask: ")); + DEBUG_ONLY(orig_mask.dump()); dump_lrg(lidx); } #endif diff --git a/src/hotspot/share/opto/chaitin.hpp b/src/hotspot/share/opto/chaitin.hpp index 4b74420f9960b..cc3d3479c81fd 100644 --- a/src/hotspot/share/opto/chaitin.hpp +++ b/src/hotspot/share/opto/chaitin.hpp @@ -82,11 +82,11 @@ class LRG : public ResourceObj { // set makes it not valid. void set_degree( uint degree ) { _eff_degree = degree; - debug_only(_degree_valid = 1;) + DEBUG_ONLY(_degree_valid = 1;) assert(!_mask.is_AllStack() || (_mask.is_AllStack() && lo_degree()), "_eff_degree can't be bigger than AllStack_size - _num_regs if the mask supports stack registers"); } // Made a change that hammered degree - void invalid_degree() { debug_only(_degree_valid=0;) } + void invalid_degree() { DEBUG_ONLY(_degree_valid=0;) } // Incrementally modify degree. If it was correct, it should remain correct void inc_degree( uint mod ) { _eff_degree += mod; @@ -128,15 +128,15 @@ class LRG : public ResourceObj { // count of bits in the current mask. int get_invalid_mask_size() const { return _mask_size; } const RegMask &mask() const { return _mask; } - void set_mask( const RegMask &rm ) { _mask = rm; debug_only(_msize_valid=0;)} - void AND( const RegMask &rm ) { _mask.AND(rm); debug_only(_msize_valid=0;)} - void SUBTRACT( const RegMask &rm ) { _mask.SUBTRACT(rm); debug_only(_msize_valid=0;)} - void Clear() { _mask.Clear() ; debug_only(_msize_valid=1); _mask_size = 0; } - void Set_All() { _mask.Set_All(); debug_only(_msize_valid=1); _mask_size = RegMask::CHUNK_SIZE; } - - void Insert( OptoReg::Name reg ) { _mask.Insert(reg); debug_only(_msize_valid=0;) } - void Remove( OptoReg::Name reg ) { _mask.Remove(reg); debug_only(_msize_valid=0;) } - void clear_to_sets() { _mask.clear_to_sets(_num_regs); debug_only(_msize_valid=0;) } + void set_mask( const RegMask &rm ) { _mask = rm; DEBUG_ONLY(_msize_valid=0;)} + void AND( const RegMask &rm ) { _mask.AND(rm); DEBUG_ONLY(_msize_valid=0;)} + void SUBTRACT( const RegMask &rm ) { _mask.SUBTRACT(rm); DEBUG_ONLY(_msize_valid=0;)} + void Clear() { _mask.Clear() ; DEBUG_ONLY(_msize_valid=1); _mask_size = 0; } + void Set_All() { _mask.Set_All(); DEBUG_ONLY(_msize_valid=1); _mask_size = RegMask::CHUNK_SIZE; } + + void Insert( OptoReg::Name reg ) { _mask.Insert(reg); DEBUG_ONLY(_msize_valid=0;) } + void Remove( OptoReg::Name reg ) { _mask.Remove(reg); DEBUG_ONLY(_msize_valid=0;) } + void clear_to_sets() { _mask.clear_to_sets(_num_regs); DEBUG_ONLY(_msize_valid=0;) } private: // Number of registers this live range uses when it colors diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index e4e82cbddabeb..effc7e2135477 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -474,7 +474,7 @@ void Compile::disconnect_useless_nodes(Unique_Node_List& useful, Unique_Node_Lis remove_useless_late_inlines( &_string_late_inlines, useful); remove_useless_late_inlines( &_boxing_late_inlines, useful); remove_useless_late_inlines(&_vector_reboxing_late_inlines, useful); - debug_only(verify_graph_edges(true /*check for no_dead_code*/, root_and_safepoints);) + DEBUG_ONLY(verify_graph_edges(true /*check for no_dead_code*/, root_and_safepoints);) } // ============================================================================ diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index decc43a212a21..3a6a81f656e91 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -2826,14 +2826,14 @@ int ConnectionGraph::find_init_values_null(JavaObjectNode* pta, PhaseValues* pha offsets_worklist.append(offset); Node* value = nullptr; if (ini != nullptr) { - // StoreP::memory_type() == T_ADDRESS + // StoreP::value_basic_type() == T_ADDRESS BasicType ft = UseCompressedOops ? T_NARROWOOP : T_ADDRESS; Node* store = ini->find_captured_store(offset, type2aelembytes(ft, true), phase); // Make sure initializing store has the same type as this AddP. // This AddP may reference non existing field because it is on a // dead branch of bimorphic call which is not eliminated yet. if (store != nullptr && store->is_Store() && - store->as_Store()->memory_type() == ft) { + store->as_Store()->value_basic_type() == ft) { value = store->in(MemNode::ValueIn); #ifdef ASSERT if (VerifyConnectionGraph) { @@ -4567,7 +4567,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist, } } } else { - debug_only(n->dump();) + DEBUG_ONLY(n->dump();) assert(false, "EA: unexpected node"); continue; } diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index df9177a493833..20feca26ede55 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -72,8 +72,8 @@ GraphKit::GraphKit() { _exceptions = nullptr; set_map(nullptr); - debug_only(_sp = -99); - debug_only(set_bci(-99)); + DEBUG_ONLY(_sp = -99); + DEBUG_ONLY(set_bci(-99)); } @@ -196,7 +196,7 @@ bool GraphKit::has_exception_handler() { void GraphKit::set_saved_ex_oop(SafePointNode* ex_map, Node* ex_oop) { assert(!has_saved_ex_oop(ex_map), "clear ex-oop before setting again"); ex_map->add_req(ex_oop); - debug_only(verify_exception_state(ex_map)); + DEBUG_ONLY(verify_exception_state(ex_map)); } inline static Node* common_saved_ex_oop(SafePointNode* ex_map, bool clear_it) { @@ -296,7 +296,7 @@ JVMState* GraphKit::transfer_exceptions_into_jvms() { _map = clone_map(); _map->set_next_exception(nullptr); clear_saved_ex_oop(_map); - debug_only(verify_map()); + DEBUG_ONLY(verify_map()); } else { // ...or created from scratch JVMState* jvms = new (C) JVMState(_method, nullptr); @@ -672,7 +672,7 @@ ciInstance* GraphKit::builtin_throw_exception(Deoptimization::DeoptReason reason //----------------------------PreserveJVMState--------------------------------- PreserveJVMState::PreserveJVMState(GraphKit* kit, bool clone_map) { - debug_only(kit->verify_map()); + DEBUG_ONLY(kit->verify_map()); _kit = kit; _map = kit->map(); // preserve the map _sp = kit->sp(); @@ -780,7 +780,7 @@ void GraphKit::set_map_clone(SafePointNode* m) { _map = m; _map = clone_map(); _map->set_next_exception(nullptr); - debug_only(verify_map()); + DEBUG_ONLY(verify_map()); } @@ -1537,7 +1537,7 @@ Node* GraphKit::memory(uint alias_idx) { Node* GraphKit::reset_memory() { Node* mem = map()->memory(); // do not use this node for any more parsing! - debug_only( map()->set_memory((Node*)nullptr) ); + DEBUG_ONLY( map()->set_memory((Node*)nullptr) ); return _gvn.transform( mem ); } @@ -1574,7 +1574,7 @@ Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, int adr_idx = C->get_alias_index(_gvn.type(adr)->isa_ptr()); assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" ); const TypePtr* adr_type = nullptr; // debug-mode-only argument - debug_only(adr_type = C->get_adr_type(adr_idx)); + DEBUG_ONLY(adr_type = C->get_adr_type(adr_idx)); Node* mem = memory(adr_idx); Node* ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, mo, control_dependency, require_atomic_access, unaligned, mismatched, unsafe, barrier_data); ld = _gvn.transform(ld); @@ -1602,7 +1602,7 @@ Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, int adr_idx = C->get_alias_index(_gvn.type(adr)->isa_ptr()); assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); const TypePtr* adr_type = nullptr; - debug_only(adr_type = C->get_adr_type(adr_idx)); + DEBUG_ONLY(adr_type = C->get_adr_type(adr_idx)); Node *mem = memory(adr_idx); Node* st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, mo, require_atomic_access); if (unaligned) { diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index d1b58526a6dc1..28773d75333ec 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -145,7 +145,7 @@ class GraphKit : public Phase { _sp = jvms->sp(); _bci = jvms->bci(); _method = jvms->has_method() ? jvms->method() : nullptr; } - void set_map(SafePointNode* m) { _map = m; debug_only(verify_map()); } + void set_map(SafePointNode* m) { _map = m; DEBUG_ONLY(verify_map()); } void set_sp(int sp) { assert(sp >= 0, "sp must be non-negative: %d", sp); _sp = sp; } void clean_stack(int from_sp); // clear garbage beyond from_sp to top @@ -226,14 +226,14 @@ class GraphKit : public Phase { if (ex_map != nullptr) { _exceptions = ex_map->next_exception(); ex_map->set_next_exception(nullptr); - debug_only(verify_exception_state(ex_map)); + DEBUG_ONLY(verify_exception_state(ex_map)); } return ex_map; } // Add an exception, using the given JVM state, without commoning. void push_exception_state(SafePointNode* ex_map) { - debug_only(verify_exception_state(ex_map)); + DEBUG_ONLY(verify_exception_state(ex_map)); ex_map->set_next_exception(_exceptions); _exceptions = ex_map; } diff --git a/src/hotspot/share/opto/idealKit.cpp b/src/hotspot/share/opto/idealKit.cpp index 8c26e1cc39db5..dd7e9ae52b778 100644 --- a/src/hotspot/share/opto/idealKit.cpp +++ b/src/hotspot/share/opto/idealKit.cpp @@ -354,7 +354,7 @@ Node* IdealKit::load(Node* ctl, assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" ); const TypePtr* adr_type = nullptr; // debug-mode-only argument - debug_only(adr_type = C->get_adr_type(adr_idx)); + DEBUG_ONLY(adr_type = C->get_adr_type(adr_idx)); Node* mem = memory(adr_idx); Node* ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, mo, control_dependency, require_atomic_access); return transform(ld); @@ -366,7 +366,7 @@ Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, bool mismatched) { assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory"); const TypePtr* adr_type = nullptr; - debug_only(adr_type = C->get_adr_type(adr_idx)); + DEBUG_ONLY(adr_type = C->get_adr_type(adr_idx)); Node *mem = memory(adr_idx); Node* st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, mo, require_atomic_access); if (mismatched) { diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 5d349caf103d2..8d810e4202fab 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -2219,13 +2219,13 @@ void ParsePredicateNode::dump_spec(outputStream* st) const { st->print("Loop "); break; case Deoptimization::DeoptReason::Reason_profile_predicate: - st->print("Profiled Loop "); + st->print("Profiled_Loop "); break; case Deoptimization::DeoptReason::Reason_auto_vectorization_check: st->print("Auto_Vectorization_Check "); break; case Deoptimization::DeoptReason::Reason_loop_limit_check: - st->print("Loop Limit Check "); + st->print("Loop_Limit_Check "); break; default: fatal("unknown kind"); diff --git a/src/hotspot/share/opto/indexSet.cpp b/src/hotspot/share/opto/indexSet.cpp index 7f02f01c83fbb..367f5b78af29d 100644 --- a/src/hotspot/share/opto/indexSet.cpp +++ b/src/hotspot/share/opto/indexSet.cpp @@ -121,7 +121,7 @@ IndexSet::BitBlock *IndexSet::alloc_block_containing(uint element) { // Add a BitBlock to the free list. void IndexSet::free_block(uint i) { - debug_only(check_watch("free block", i)); + DEBUG_ONLY(check_watch("free block", i)); assert(i < _max_blocks, "block index too large"); BitBlock *block = _blocks[i]; assert(block != &_empty_block, "cannot free the empty block"); diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 017564173a847..65635001e131d 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -721,6 +721,10 @@ bool LibraryCallKit::try_to_inline(int predicate) { return inline_vector_nary_operation(1); case vmIntrinsics::_VectorBinaryOp: return inline_vector_nary_operation(2); + case vmIntrinsics::_VectorUnaryLibOp: + return inline_vector_call(1); + case vmIntrinsics::_VectorBinaryLibOp: + return inline_vector_call(2); case vmIntrinsics::_VectorTernaryOp: return inline_vector_nary_operation(3); case vmIntrinsics::_VectorFromBitsCoerced: diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index cb755267ec57e..1be08df32aea8 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -369,6 +369,7 @@ class LibraryCallKit : public GraphKit { // Vector API support bool inline_vector_nary_operation(int n); + bool inline_vector_call(int arity); bool inline_vector_frombits_coerced(); bool inline_vector_mask_operation(); bool inline_vector_mem_operation(bool is_store); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 7de53466e21e9..f7783e19c9c67 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1299,6 +1299,22 @@ Node *PhaseIdealLoop::clone_up_backedge_goo(Node *back_ctrl, Node *preheader_ctr return n; } +// When a counted loop is created, the loop phi type may be narrowed down. As a consequence, the control input of some +// nodes may be cleared: in particular in the case of a division by the loop iv, the Div node would lose its control +// dependency if the loop phi is never zero. After pre/main/post loops are created (and possibly unrolling), the +// loop phi type is only correct if the loop is indeed reachable: there's an implicit dependency between the loop phi +// type and the zero trip guard for the main or post loop and as a consequence a dependency between the Div node and the +// zero trip guard. This makes the dependency explicit by adding a CastII for the loop entry input of the loop phi. If +// the backedge of the main or post loop is removed, a Div node won't be able to float above the zero trip guard of the +// loop and can't execute even if the loop is not reached. +void PhaseIdealLoop::cast_incr_before_loop(Node* incr, Node* ctrl, CountedLoopNode* loop) { + Node* castii = new CastIINode(ctrl, incr, TypeInt::INT, ConstraintCastNode::UnconditionalDependency); + register_new_node(castii, ctrl); + Node* phi = loop->phi(); + assert(phi->in(LoopNode::EntryControl) == incr, "replacing wrong input?"); + _igvn.replace_input_of(phi, LoopNode::EntryControl, castii); +} + #ifdef ASSERT void PhaseIdealLoop::ensure_zero_trip_guard_proj(Node* node, bool is_main_loop) { assert(node->is_IfProj(), "must be the zero trip guard If node"); @@ -1453,6 +1469,8 @@ void PhaseIdealLoop::insert_pre_post_loops(IdealLoopTree *loop, Node_List &old_n initialize_assertion_predicates_for_main_loop(pre_head, main_head, first_node_index_in_pre_loop_body, last_node_index_in_pre_loop_body, DEBUG_ONLY(last_node_index_from_backedge_goo COMMA) old_new); + // CastII for the main loop: + cast_incr_before_loop(pre_incr, min_taken, main_head); // Step B4: Shorten the pre-loop to run only 1 iteration (for now). // RCE and alignment may change this later. @@ -1593,7 +1611,7 @@ void PhaseIdealLoop::insert_vector_post_loop(IdealLoopTree *loop, Node_List &old // Insert post loops. Add a post loop to the given loop passed. Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, CountedLoopNode* main_head, CountedLoopEndNode* main_end, - Node*& incr, Node* limit, CountedLoopNode*& post_head) { + Node* incr, Node* limit, CountedLoopNode*& post_head) { IfNode* outer_main_end = main_end; IdealLoopTree* outer_loop = loop; if (main_head->is_strip_mined()) { @@ -1682,6 +1700,7 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, DEBUG_ONLY(ensure_zero_trip_guard_proj(post_head->in(LoopNode::EntryControl), false);) initialize_assertion_predicates_for_post_loop(main_head, post_head, first_node_index_in_cloned_loop_body); + cast_incr_before_loop(zer_opaq->in(1), zer_taken, post_head); return new_main_exit; } @@ -1777,8 +1796,8 @@ void PhaseIdealLoop::create_assertion_predicates_at_main_or_post_loop(CountedLoo // to do because these control dependent nodes on the old target loop entry created by clone_up_backedge_goo() were // pinned on the loop backedge before. The Assertion Predicates are not control dependent on these nodes in any way. void PhaseIdealLoop::rewire_old_target_loop_entry_dependency_to_new_entry( - LoopNode* target_loop_head, const Node* old_target_loop_entry, - const uint node_index_before_new_assertion_predicate_nodes) { + CountedLoopNode* target_loop_head, const Node* old_target_loop_entry, + const uint node_index_before_new_assertion_predicate_nodes) { Node* new_main_loop_entry = target_loop_head->skip_strip_mined()->in(LoopNode::EntryControl); if (new_main_loop_entry == old_target_loop_entry) { // No Assertion Predicates added. @@ -1788,6 +1807,7 @@ void PhaseIdealLoop::rewire_old_target_loop_entry_dependency_to_new_entry( for (DUIterator_Fast imax, i = old_target_loop_entry->fast_outs(imax); i < imax; i++) { Node* out = old_target_loop_entry->fast_out(i); if (!out->is_CFG() && out->_idx < node_index_before_new_assertion_predicate_nodes) { + assert(out != target_loop_head->init_trip(), "CastII on loop entry?"); _igvn.replace_input_of(out, 0, new_main_loop_entry); set_ctrl(out, new_main_loop_entry); --i; @@ -2677,7 +2697,8 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { if (b_test._test == BoolTest::lt) { // Range checks always use lt // The underflow and overflow limits: 0 <= scale*I+offset < limit add_constraint(stride_con, lscale_con, offset, zero, limit, next_limit_ctrl, &pre_limit, &main_limit); - Node* init = cl->init_trip(); + Node* init = cl->uncasted_init_trip(true); + Node* opaque_init = new OpaqueLoopInitNode(C, init); register_new_node(opaque_init, loop_entry); diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index b0ed29d856730..195d48b37c631 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -359,7 +359,7 @@ void PhaseIdealLoop::insert_loop_limit_check_predicate(ParsePredicateSuccessProj // for this loop if (TraceLoopLimitCheck) { tty->print_cr("Counted Loop Limit Check generated:"); - debug_only( bol->dump(2); ) + DEBUG_ONLY( bol->dump(2); ) } #endif } @@ -6058,6 +6058,24 @@ CountedLoopEndNode* CountedLoopNode::find_pre_loop_end() { return pre_end; } +Node* CountedLoopNode::uncasted_init_trip(bool uncast) { + Node* init = init_trip(); + if (uncast && init->is_CastII()) { + // skip over the cast added by PhaseIdealLoop::cast_incr_before_loop() when pre/post/main loops are created because + // it can get in the way of type propagation. For instance, the index tested by an Assertion Predicate, if the cast + // is not skipped over, could be (1): + // (AddI (CastII (AddI pre_loop_iv -2) int) 1) + // while without the cast, it is (2): + // (AddI (AddI pre_loop_iv -2) 1) + // which is be transformed to (3): + // (AddI pre_loop_iv -1) + // The compiler may be able to constant fold the Assertion Predicate condition for (3) but not (1) + assert(init->as_CastII()->carry_dependency() && skip_assertion_predicates_with_halt() == init->in(0), "casted iv phi from pre loop expected"); + init = init->in(1); + } + return init; +} + //------------------------------get_late_ctrl---------------------------------- // Compute latest legal control. Node *PhaseIdealLoop::get_late_ctrl( Node *n, Node *early ) { diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index ef958ced8c333..5a1fceacf9e89 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -365,6 +365,8 @@ class CountedLoopNode : public BaseCountedLoopNode { Node* is_canonical_loop_entry(); CountedLoopEndNode* find_pre_loop_end(); + Node* uncasted_init_trip(bool uncasted); + #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; #endif @@ -973,6 +975,8 @@ class PhaseIdealLoop : public PhaseTransform { return ctrl; } + void cast_incr_before_loop(Node* incr, Node* ctrl, CountedLoopNode* loop); + #ifdef ASSERT static void ensure_zero_trip_guard_proj(Node* node, bool is_main_loop); #endif @@ -998,7 +1002,7 @@ class PhaseIdealLoop : public PhaseTransform { CountedLoopNode* target_loop_head, const NodeInLoopBody& _node_in_loop_body, bool kill_old_template); - void rewire_old_target_loop_entry_dependency_to_new_entry(LoopNode* target_loop_head, + void rewire_old_target_loop_entry_dependency_to_new_entry(CountedLoopNode* target_loop_head, const Node* old_target_loop_entry, uint node_index_before_new_assertion_predicate_nodes); void insert_loop_limit_check_predicate(ParsePredicateSuccessProj* loop_limit_check_parse_proj, Node* cmp_limit, @@ -1352,7 +1356,7 @@ class PhaseIdealLoop : public PhaseTransform { // Add post loop after the given loop. Node *insert_post_loop(IdealLoopTree* loop, Node_List& old_new, CountedLoopNode* main_head, CountedLoopEndNode* main_end, - Node*& incr, Node* limit, CountedLoopNode*& post_head); + Node* incr, Node* limit, CountedLoopNode*& post_head); // Add a vector post loop between a vector main loop and the current post loop void insert_vector_post_loop(IdealLoopTree *loop, Node_List &old_new); @@ -1580,8 +1584,6 @@ class PhaseIdealLoop : public PhaseTransform { // Attempt to use a conditional move instead of a phi/branch Node *conditional_move( Node *n ); - bool split_thru_phi_could_prevent_vectorization(Node* n, Node* n_blk); - // Check for aggressive application of 'split-if' optimization, // using basic block level info. void split_if_with_blocks ( VectorSet &visited, Node_Stack &nstack); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index e896196c6cf1c..b8bbaa964e899 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1089,25 +1089,6 @@ void PhaseIdealLoop::try_move_store_after_loop(Node* n) { } } -// Split some nodes that take a counted loop phi as input at a counted -// loop can cause vectorization of some expressions to fail -bool PhaseIdealLoop::split_thru_phi_could_prevent_vectorization(Node* n, Node* n_blk) { - if (!n_blk->is_CountedLoop()) { - return false; - } - - int opcode = n->Opcode(); - - if (opcode != Op_AndI && - opcode != Op_MulI && - opcode != Op_RotateRight && - opcode != Op_RShiftI) { - return false; - } - - return n->in(1) == n_blk->as_BaseCountedLoop()->phi(); -} - //------------------------------split_if_with_blocks_pre----------------------- // Do the real work in a non-recursive function. Data nodes want to be // cloned in the pre-order so they can feed each other nicely. @@ -1194,10 +1175,6 @@ Node *PhaseIdealLoop::split_if_with_blocks_pre( Node *n ) { return n; } - if (split_thru_phi_could_prevent_vectorization(n, n_blk)) { - return n; - } - // Check for having no control input; not pinned. Allow // dominating control. if (n->in(0)) { diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 1bd35f9dcc980..1fe59c9156b2f 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -225,7 +225,7 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me if (!ClearArrayNode::step_through(&mem, alloc->_idx, phase)) { // Can not bypass initialization of the instance // we are looking. - debug_only(intptr_t offset;) + DEBUG_ONLY(intptr_t offset;) assert(alloc == AllocateNode::Ideal_allocation(mem->in(3), phase, offset), "sanity"); InitializeNode* init = alloc->as_Allocate()->initialization(); // We are looking for stored value, return Initialize node @@ -1328,7 +1328,7 @@ void PhaseMacroExpand::expand_allocate_common( // No initial test, just fall into next case assert(allocation_has_use || !expand_fast_path, "Should already have been handled"); toobig_false = ctrl; - debug_only(slow_region = NodeSentinel); + DEBUG_ONLY(slow_region = NodeSentinel); } // If we are here there are several possibilities diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index e34a43cc1e2f6..0849b40ad7e38 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -128,7 +128,7 @@ Matcher::Matcher() idealreg2mhdebugmask[Op_RegFlags] = nullptr; idealreg2mhdebugmask[Op_RegVectMask] = nullptr; - debug_only(_mem_node = nullptr;) // Ideal memory node consumed by mach node + DEBUG_ONLY(_mem_node = nullptr;) // Ideal memory node consumed by mach node } //------------------------------warp_incoming_stk_arg------------------------ @@ -1184,7 +1184,7 @@ Node *Matcher::xform( Node *n, int max_stack ) { n->_idx); C->set_node_notes_at(m->_idx, nn); } - debug_only(match_alias_type(C, n, m)); + DEBUG_ONLY(match_alias_type(C, n, m)); } n = m; // n is now a new-space node mstack.set_node(n); @@ -1591,7 +1591,7 @@ MachNode *Matcher::match_tree( const Node *n ) { } } - debug_only( _mem_node = save_mem_node; ) + DEBUG_ONLY( _mem_node = save_mem_node; ) return m; } @@ -1965,9 +1965,9 @@ void Matcher::ReduceInst_Chain_Rule(State* s, int rule, Node* &mem, MachNode* ma assert(newrule >= _LAST_MACH_OPER, "Do NOT chain from internal operand"); mach->_opnds[1] = s->MachOperGenerator(_reduceOp[catch_op]); Node *mem1 = (Node*)1; - debug_only(Node *save_mem_node = _mem_node;) + DEBUG_ONLY(Node *save_mem_node = _mem_node;) mach->add_req( ReduceInst(s, newrule, mem1) ); - debug_only(_mem_node = save_mem_node;) + DEBUG_ONLY(_mem_node = save_mem_node;) } return; } @@ -1979,7 +1979,7 @@ uint Matcher::ReduceInst_Interior( State *s, int rule, Node *&mem, MachNode *mac if( s->_leaf->is_Load() ) { Node *mem2 = s->_leaf->in(MemNode::Memory); assert( mem == (Node*)1 || mem == mem2, "multiple Memories being matched at once?" ); - debug_only( if( mem == (Node*)1 ) _mem_node = s->_leaf;) + DEBUG_ONLY( if( mem == (Node*)1 ) _mem_node = s->_leaf;) mem = mem2; } if( s->_leaf->in(0) != nullptr && s->_leaf->req() > 1) { @@ -2023,9 +2023,9 @@ uint Matcher::ReduceInst_Interior( State *s, int rule, Node *&mem, MachNode *mac // --> ReduceInst( newrule ) mach->_opnds[num_opnds++] = s->MachOperGenerator(_reduceOp[catch_op]); Node *mem1 = (Node*)1; - debug_only(Node *save_mem_node = _mem_node;) + DEBUG_ONLY(Node *save_mem_node = _mem_node;) mach->add_req( ReduceInst( newstate, newrule, mem1 ) ); - debug_only(_mem_node = save_mem_node;) + DEBUG_ONLY(_mem_node = save_mem_node;) } } assert( mach->_opnds[num_opnds-1], "" ); @@ -2056,7 +2056,7 @@ void Matcher::ReduceOper( State *s, int rule, Node *&mem, MachNode *mach ) { if( s->_leaf->is_Load() ) { assert( mem == (Node*)1, "multiple Memories being matched at once?" ); mem = s->_leaf->in(MemNode::Memory); - debug_only(_mem_node = s->_leaf;) + DEBUG_ONLY(_mem_node = s->_leaf;) } handle_precedence_edges(s->_leaf, mach); @@ -2085,9 +2085,9 @@ void Matcher::ReduceOper( State *s, int rule, Node *&mem, MachNode *mach ) { // Reduce the instruction, and add a direct pointer from this // machine instruction to the newly reduced one. Node *mem1 = (Node*)1; - debug_only(Node *save_mem_node = _mem_node;) + DEBUG_ONLY(Node *save_mem_node = _mem_node;) mach->add_req( ReduceInst( kid, newrule, mem1 ) ); - debug_only(_mem_node = save_mem_node;) + DEBUG_ONLY(_mem_node = save_mem_node;) } } } diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 9cd3dcfee41df..24b81b894cb1a 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1213,12 +1213,12 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseValues* phase) const { // (This is one of the few places where a generic PhaseTransform // can create new nodes. Think of it as lazily manifesting // virtually pre-existing constants.) - if (memory_type() != T_VOID) { + if (value_basic_type() != T_VOID) { if (ReduceBulkZeroing || find_array_copy_clone(ld_alloc, in(MemNode::Memory)) == nullptr) { // If ReduceBulkZeroing is disabled, we need to check if the allocation does not belong to an // ArrayCopyNode clone. If it does, then we cannot assume zero since the initialization is done // by the ArrayCopyNode. - return phase->zerocon(memory_type()); + return phase->zerocon(value_basic_type()); } } else { // TODO: materialize all-zero vector constant @@ -2047,7 +2047,7 @@ const Type* LoadNode::Value(PhaseGVN* phase) const { int stable_dimension = (ary->stable_dimension() > 0 ? ary->stable_dimension() - 1 : 0); const Type* con_type = Type::make_constant_from_array_element(aobj->as_array(), off, stable_dimension, - memory_type(), is_unsigned()); + value_basic_type(), is_unsigned()); if (con_type != nullptr) { return con_type; } @@ -2115,7 +2115,7 @@ const Type* LoadNode::Value(PhaseGVN* phase) const { const TypeInstPtr* tinst = tp->is_instptr(); ciObject* const_oop = tinst->const_oop(); if (!is_mismatched_access() && off != Type::OffsetBot && const_oop != nullptr && const_oop->is_instance()) { - const Type* con_type = Type::make_constant_from_field(const_oop->as_instance(), off, is_unsigned(), memory_type()); + const Type* con_type = Type::make_constant_from_field(const_oop->as_instance(), off, is_unsigned(), value_basic_type()); if (con_type != nullptr) { return con_type; } diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index f17db05e68362..a751022f752d4 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -71,7 +71,7 @@ class MemNode : public Node { _unsafe_access(false), _barrier_data(0) { init_class_id(Class_Mem); - debug_only(_adr_type=at; adr_type();) + DEBUG_ONLY(_adr_type=at; adr_type();) } MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3 ) : Node(c0,c1,c2,c3), @@ -80,7 +80,7 @@ class MemNode : public Node { _unsafe_access(false), _barrier_data(0) { init_class_id(Class_Mem); - debug_only(_adr_type=at; adr_type();) + DEBUG_ONLY(_adr_type=at; adr_type();) } MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3, Node *c4) : Node(c0,c1,c2,c3,c4), @@ -89,7 +89,7 @@ class MemNode : public Node { _unsafe_access(false), _barrier_data(0) { init_class_id(Class_Mem); - debug_only(_adr_type=at; adr_type();) + DEBUG_ONLY(_adr_type=at; adr_type();) } virtual Node* find_previous_arraycopy(PhaseValues* phase, Node* ld_alloc, Node*& mem, bool can_see_stored_value) const { return nullptr; } @@ -134,12 +134,16 @@ class MemNode : public Node { virtual int store_Opcode() const { return -1; } // What is the type of the value in memory? (T_VOID mean "unspecified".) - virtual BasicType memory_type() const = 0; + // The returned type is a property of the value that is loaded/stored and + // not the memory that is accessed. For mismatched memory accesses + // they might differ. For instance, a value of type 'short' may be stored + // into an array of elements of type 'long'. + virtual BasicType value_basic_type() const = 0; virtual int memory_size() const { #ifdef ASSERT - return type2aelembytes(memory_type(), true); + return type2aelembytes(value_basic_type(), true); #else - return type2aelembytes(memory_type()); + return type2aelembytes(value_basic_type()); #endif } @@ -269,7 +273,7 @@ class LoadNode : public MemNode { // Following method is copied from TypeNode: void set_type(const Type* t) { assert(t != nullptr, "sanity"); - debug_only(uint check_hash = (VerifyHashTableKeys && _hash_lock) ? hash() : NO_HASH); + DEBUG_ONLY(uint check_hash = (VerifyHashTableKeys && _hash_lock) ? hash() : NO_HASH); *(const Type**)&_type = t; // cast away const-ness // If this node is in the hash table, make sure it doesn't need a rehash. assert(check_hash == NO_HASH || check_hash == hash(), "type change must preserve hash code"); @@ -337,7 +341,7 @@ class LoadBNode : public LoadNode { virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type* Value(PhaseGVN* phase) const; virtual int store_Opcode() const { return Op_StoreB; } - virtual BasicType memory_type() const { return T_BYTE; } + virtual BasicType value_basic_type() const { return T_BYTE; } }; //------------------------------LoadUBNode------------------------------------- @@ -351,7 +355,7 @@ class LoadUBNode : public LoadNode { virtual Node* Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type* Value(PhaseGVN* phase) const; virtual int store_Opcode() const { return Op_StoreB; } - virtual BasicType memory_type() const { return T_BYTE; } + virtual BasicType value_basic_type() const { return T_BYTE; } }; //------------------------------LoadUSNode------------------------------------- @@ -365,7 +369,7 @@ class LoadUSNode : public LoadNode { virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type* Value(PhaseGVN* phase) const; virtual int store_Opcode() const { return Op_StoreC; } - virtual BasicType memory_type() const { return T_CHAR; } + virtual BasicType value_basic_type() const { return T_CHAR; } }; //------------------------------LoadSNode-------------------------------------- @@ -379,7 +383,7 @@ class LoadSNode : public LoadNode { virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type* Value(PhaseGVN* phase) const; virtual int store_Opcode() const { return Op_StoreC; } - virtual BasicType memory_type() const { return T_SHORT; } + virtual BasicType value_basic_type() const { return T_SHORT; } }; //------------------------------LoadINode-------------------------------------- @@ -391,7 +395,7 @@ class LoadINode : public LoadNode { virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual int store_Opcode() const { return Op_StoreI; } - virtual BasicType memory_type() const { return T_INT; } + virtual BasicType value_basic_type() const { return T_INT; } }; //------------------------------LoadRangeNode---------------------------------- @@ -424,7 +428,7 @@ class LoadLNode : public LoadNode { virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegL; } virtual int store_Opcode() const { return Op_StoreL; } - virtual BasicType memory_type() const { return T_LONG; } + virtual BasicType value_basic_type() const { return T_LONG; } bool require_atomic_access() const { return _require_atomic_access; } #ifndef PRODUCT @@ -453,7 +457,7 @@ class LoadFNode : public LoadNode { virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegF; } virtual int store_Opcode() const { return Op_StoreF; } - virtual BasicType memory_type() const { return T_FLOAT; } + virtual BasicType value_basic_type() const { return T_FLOAT; } }; //------------------------------LoadDNode-------------------------------------- @@ -474,7 +478,7 @@ class LoadDNode : public LoadNode { virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegD; } virtual int store_Opcode() const { return Op_StoreD; } - virtual BasicType memory_type() const { return T_DOUBLE; } + virtual BasicType value_basic_type() const { return T_DOUBLE; } bool require_atomic_access() const { return _require_atomic_access; } #ifndef PRODUCT @@ -503,7 +507,7 @@ class LoadPNode : public LoadNode { virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } virtual int store_Opcode() const { return Op_StoreP; } - virtual BasicType memory_type() const { return T_ADDRESS; } + virtual BasicType value_basic_type() const { return T_ADDRESS; } }; @@ -516,7 +520,7 @@ class LoadNNode : public LoadNode { virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegN; } virtual int store_Opcode() const { return Op_StoreN; } - virtual BasicType memory_type() const { return T_NARROWOOP; } + virtual BasicType value_basic_type() const { return T_NARROWOOP; } }; //------------------------------LoadKlassNode---------------------------------- @@ -555,7 +559,7 @@ class LoadNKlassNode : public LoadNNode { virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegN; } virtual int store_Opcode() const { return Op_StoreNKlass; } - virtual BasicType memory_type() const { return T_NARROWKLASS; } + virtual BasicType value_basic_type() const { return T_NARROWKLASS; } virtual const Type* Value(PhaseGVN* phase) const; virtual Node* Identity(PhaseGVN* phase); @@ -666,7 +670,7 @@ class StoreBNode : public StoreNode { : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual BasicType memory_type() const { return T_BYTE; } + virtual BasicType value_basic_type() const { return T_BYTE; } }; //------------------------------StoreCNode------------------------------------- @@ -677,7 +681,7 @@ class StoreCNode : public StoreNode { : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual BasicType memory_type() const { return T_CHAR; } + virtual BasicType value_basic_type() const { return T_CHAR; } }; //------------------------------StoreINode------------------------------------- @@ -687,7 +691,7 @@ class StoreINode : public StoreNode { StoreINode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, MemOrd mo) : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; - virtual BasicType memory_type() const { return T_INT; } + virtual BasicType value_basic_type() const { return T_INT; } }; //------------------------------StoreLNode------------------------------------- @@ -705,7 +709,7 @@ class StoreLNode : public StoreNode { StoreLNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, MemOrd mo, bool require_atomic_access = false) : StoreNode(c, mem, adr, at, val, mo), _require_atomic_access(require_atomic_access) {} virtual int Opcode() const; - virtual BasicType memory_type() const { return T_LONG; } + virtual BasicType value_basic_type() const { return T_LONG; } bool require_atomic_access() const { return _require_atomic_access; } #ifndef PRODUCT @@ -723,7 +727,7 @@ class StoreFNode : public StoreNode { StoreFNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, MemOrd mo) : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; - virtual BasicType memory_type() const { return T_FLOAT; } + virtual BasicType value_basic_type() const { return T_FLOAT; } }; //------------------------------StoreDNode------------------------------------- @@ -741,7 +745,7 @@ class StoreDNode : public StoreNode { MemOrd mo, bool require_atomic_access = false) : StoreNode(c, mem, adr, at, val, mo), _require_atomic_access(require_atomic_access) {} virtual int Opcode() const; - virtual BasicType memory_type() const { return T_DOUBLE; } + virtual BasicType value_basic_type() const { return T_DOUBLE; } bool require_atomic_access() const { return _require_atomic_access; } #ifndef PRODUCT @@ -760,7 +764,7 @@ class StorePNode : public StoreNode { StorePNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, MemOrd mo) : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; - virtual BasicType memory_type() const { return T_ADDRESS; } + virtual BasicType value_basic_type() const { return T_ADDRESS; } }; //------------------------------StoreNNode------------------------------------- @@ -770,7 +774,7 @@ class StoreNNode : public StoreNode { StoreNNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, MemOrd mo) : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; - virtual BasicType memory_type() const { return T_NARROWOOP; } + virtual BasicType value_basic_type() const { return T_NARROWOOP; } }; //------------------------------StoreNKlassNode-------------------------------------- @@ -780,7 +784,7 @@ class StoreNKlassNode : public StoreNNode { StoreNKlassNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, MemOrd mo) : StoreNNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; - virtual BasicType memory_type() const { return T_NARROWKLASS; } + virtual BasicType value_basic_type() const { return T_NARROWKLASS; } }; //------------------------------SCMemProjNode--------------------------------------- @@ -1493,7 +1497,7 @@ class MergeMemStream : public StackObj { MergeMemStream(MergeMemNode* mm) { mm->iteration_setup(); init(mm); - debug_only(_cnt2 = 999); + DEBUG_ONLY(_cnt2 = 999); } // iterate in parallel over two merges // only iterates through non-empty elements of mm2 diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 1d046583ee04b..72562a657ea61 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -924,13 +924,15 @@ Node *AndLNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( t12 && t12->is_con() ) { // Shift is by a constant int shift = t12->get_con(); shift &= BitsPerJavaLong - 1; // semantics of Java shifts - const julong sign_bits_mask = ~(((julong)CONST64(1) << (julong)(BitsPerJavaLong - shift)) -1); - // If the AND'ing of the 2 masks has no bits, then only original shifted - // bits survive. NO sign-extension bits survive the maskings. - if( (sign_bits_mask & mask) == 0 ) { - // Use zero-fill shift instead - Node *zshift = phase->transform(new URShiftLNode(in1->in(1), in1->in(2))); - return new AndLNode(zshift, in(2)); + if (shift != 0) { + const julong sign_bits_mask = ~(((julong)CONST64(1) << (julong)(BitsPerJavaLong - shift)) -1); + // If the AND'ing of the 2 masks has no bits, then only original shifted + // bits survive. NO sign-extension bits survive the maskings. + if( (sign_bits_mask & mask) == 0 ) { + // Use zero-fill shift instead + Node *zshift = phase->transform(new URShiftLNode(in1->in(1), in1->in(2))); + return new AndLNode(zshift, in(2)); + } } } } diff --git a/src/hotspot/share/opto/multnode.hpp b/src/hotspot/share/opto/multnode.hpp index 25dad70a50aa7..dff2caed38d16 100644 --- a/src/hotspot/share/opto/multnode.hpp +++ b/src/hotspot/share/opto/multnode.hpp @@ -71,7 +71,7 @@ class ProjNode : public Node { // Optimistic setting. Need additional checks in Node::is_dead_loop_safe(). if (con != TypeFunc::Memory || src->is_Start()) init_flags(Flag_is_dead_loop_safe); - debug_only(check_con()); + DEBUG_ONLY(check_con()); } const uint _con; // The field in the tuple we are projecting const bool _is_io_use; // Used to distinguish between the projections diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 76e193609101c..a635abebec1bb 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -322,7 +322,7 @@ Node::Node(uint req) #endif { assert( req < Compile::current()->max_node_limit() - NodeLimitFudgeFactor, "Input limit exceeded" ); - debug_only( verify_construction() ); + DEBUG_ONLY( verify_construction() ); NOT_PRODUCT(nodes_created++); if (req == 0) { _in = nullptr; @@ -341,7 +341,7 @@ Node::Node(Node *n0) , _parse_idx(_idx) #endif { - debug_only( verify_construction() ); + DEBUG_ONLY( verify_construction() ); NOT_PRODUCT(nodes_created++); assert( is_not_dead(n0), "can not use dead node"); _in[0] = n0; if (n0 != nullptr) n0->add_out((Node *)this); @@ -354,7 +354,7 @@ Node::Node(Node *n0, Node *n1) , _parse_idx(_idx) #endif { - debug_only( verify_construction() ); + DEBUG_ONLY( verify_construction() ); NOT_PRODUCT(nodes_created++); assert( is_not_dead(n0), "can not use dead node"); assert( is_not_dead(n1), "can not use dead node"); @@ -369,7 +369,7 @@ Node::Node(Node *n0, Node *n1, Node *n2) , _parse_idx(_idx) #endif { - debug_only( verify_construction() ); + DEBUG_ONLY( verify_construction() ); NOT_PRODUCT(nodes_created++); assert( is_not_dead(n0), "can not use dead node"); assert( is_not_dead(n1), "can not use dead node"); @@ -386,7 +386,7 @@ Node::Node(Node *n0, Node *n1, Node *n2, Node *n3) , _parse_idx(_idx) #endif { - debug_only( verify_construction() ); + DEBUG_ONLY( verify_construction() ); NOT_PRODUCT(nodes_created++); assert( is_not_dead(n0), "can not use dead node"); assert( is_not_dead(n1), "can not use dead node"); @@ -405,7 +405,7 @@ Node::Node(Node *n0, Node *n1, Node *n2, Node *n3, Node *n4) , _parse_idx(_idx) #endif { - debug_only( verify_construction() ); + DEBUG_ONLY( verify_construction() ); NOT_PRODUCT(nodes_created++); assert( is_not_dead(n0), "can not use dead node"); assert( is_not_dead(n1), "can not use dead node"); @@ -427,7 +427,7 @@ Node::Node(Node *n0, Node *n1, Node *n2, Node *n3, , _parse_idx(_idx) #endif { - debug_only( verify_construction() ); + DEBUG_ONLY( verify_construction() ); NOT_PRODUCT(nodes_created++); assert( is_not_dead(n0), "can not use dead node"); assert( is_not_dead(n1), "can not use dead node"); @@ -451,7 +451,7 @@ Node::Node(Node *n0, Node *n1, Node *n2, Node *n3, , _parse_idx(_idx) #endif { - debug_only( verify_construction() ); + DEBUG_ONLY( verify_construction() ); NOT_PRODUCT(nodes_created++); assert( is_not_dead(n0), "can not use dead node"); assert( is_not_dead(n1), "can not use dead node"); @@ -489,7 +489,7 @@ Node *Node::clone() const { n->_outcnt = 0; n->_outmax = 0; // Unlock this guy, since he is not in any hash table. - debug_only(n->_hash_lock = 0); + DEBUG_ONLY(n->_hash_lock = 0); // Walk the old node's input list to duplicate its edges uint i; for( i = 0; i < len(); i++ ) { @@ -525,11 +525,11 @@ Node *Node::clone() const { n->set_idx(C->next_unique()); // Get new unique index as well NOT_PRODUCT(n->_igv_idx = C->next_igv_idx()); - debug_only( n->verify_construction() ); + DEBUG_ONLY( n->verify_construction() ); NOT_PRODUCT(nodes_created++); // Do not patch over the debug_idx of a clone, because it makes it // impossible to break on the clone's moment of creation. - //debug_only( n->set_debug_idx( debug_idx() ) ); + //DEBUG_ONLY( n->set_debug_idx( debug_idx() ) ); C->copy_node_notes_to(n, (Node*) this); @@ -942,7 +942,7 @@ void Node::disconnect_inputs(Compile* C) { #endif // Node::destruct requires all out edges be deleted first - // debug_only(destruct();) // no reuse benefit expected + // DEBUG_ONLY(destruct();) // no reuse benefit expected C->record_dead_node(_idx); } diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 1cb9009ef276b..e1a4ee22661f7 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -427,11 +427,11 @@ class Node { assert(_outcnt > 0,"oob"); #if OPTO_DU_ITERATOR_ASSERT // Record that a change happened here. - debug_only(_last_del = _out[i]; ++_del_tick); + DEBUG_ONLY(_last_del = _out[i]; ++_del_tick); #endif _out[i] = _out[--_outcnt]; // Smash the old edge so it can't be used accidentally. - debug_only(_out[_outcnt] = (Node *)(uintptr_t)0xdeadbeef); + DEBUG_ONLY(_out[_outcnt] = (Node *)(uintptr_t)0xdeadbeef); } #ifdef ASSERT @@ -533,10 +533,10 @@ class Node { } while (*--outp != n); *outp = _out[--_outcnt]; // Smash the old edge so it can't be used accidentally. - debug_only(_out[_outcnt] = (Node *)(uintptr_t)0xdeadbeef); + DEBUG_ONLY(_out[_outcnt] = (Node *)(uintptr_t)0xdeadbeef); // Record that a change happened here. #if OPTO_DU_ITERATOR_ASSERT - debug_only(_last_del = n; ++_del_tick); + DEBUG_ONLY(_last_del = n; ++_del_tick); #endif } // Close gap after removing edge. @@ -593,7 +593,7 @@ class Node { } // Swap input edge order. (Edge indexes i1 and i2 are usually 1 and 2.) void swap_edges(uint i1, uint i2) { - debug_only(uint check_hash = (VerifyHashTableKeys && _hash_lock) ? hash() : NO_HASH); + DEBUG_ONLY(uint check_hash = (VerifyHashTableKeys && _hash_lock) ? hash() : NO_HASH); // Def-Use info is unchanged Node* n1 = in(i1); Node* n2 = in(i2); @@ -1431,15 +1431,15 @@ class DUIterator : public DUIterator_Common { #endif DUIterator(const Node* node, int dummy_to_avoid_conversion) - { _idx = 0; debug_only(sample(node)); } + { _idx = 0; DEBUG_ONLY(sample(node)); } public: // initialize to garbage; clear _vdui to disable asserts DUIterator() - { /*initialize to garbage*/ debug_only(_vdui = false); } + { /*initialize to garbage*/ DEBUG_ONLY(_vdui = false); } DUIterator(const DUIterator& that) - { _idx = that._idx; debug_only(_vdui = false; reset(that)); } + { _idx = that._idx; DEBUG_ONLY(_vdui = false; reset(that)); } void operator++(int dummy_to_specify_postfix_op) { _idx++; VDUI_ONLY(verify_increment()); } @@ -1451,7 +1451,7 @@ class DUIterator : public DUIterator_Common { { VDUI_ONLY(verify_finish()); } void operator=(const DUIterator& that) - { _idx = that._idx; debug_only(reset(that)); } + { _idx = that._idx; DEBUG_ONLY(reset(that)); } }; DUIterator Node::outs() const @@ -1461,7 +1461,7 @@ DUIterator& Node::refresh_out_pos(DUIterator& i) const bool Node::has_out(DUIterator& i) const { I_VDUI_ONLY(i, i.verify(this,true));return i._idx < _outcnt; } Node* Node::out(DUIterator& i) const - { I_VDUI_ONLY(i, i.verify(this)); return debug_only(i._last=) _out[i._idx]; } + { I_VDUI_ONLY(i, i.verify(this)); return DEBUG_ONLY(i._last=) _out[i._idx]; } // Faster DU iterator. Disallows insertions into the out array. @@ -1496,15 +1496,15 @@ class DUIterator_Fast : public DUIterator_Common { // Note: offset must be signed, since -1 is sometimes passed DUIterator_Fast(const Node* node, ptrdiff_t offset) - { _outp = node->_out + offset; debug_only(sample(node)); } + { _outp = node->_out + offset; DEBUG_ONLY(sample(node)); } public: // initialize to garbage; clear _vdui to disable asserts DUIterator_Fast() - { /*initialize to garbage*/ debug_only(_vdui = false); } + { /*initialize to garbage*/ DEBUG_ONLY(_vdui = false); } DUIterator_Fast(const DUIterator_Fast& that) - { _outp = that._outp; debug_only(_vdui = false; reset(that)); } + { _outp = that._outp; DEBUG_ONLY(_vdui = false; reset(that)); } void operator++(int dummy_to_specify_postfix_op) { _outp++; VDUI_ONLY(verify(_node, true)); } @@ -1522,7 +1522,7 @@ class DUIterator_Fast : public DUIterator_Common { } void operator=(const DUIterator_Fast& that) - { _outp = that._outp; debug_only(reset(that)); } + { _outp = that._outp; DEBUG_ONLY(reset(that)); } }; DUIterator_Fast Node::fast_outs(DUIterator_Fast& imax) const { @@ -1533,7 +1533,7 @@ DUIterator_Fast Node::fast_outs(DUIterator_Fast& imax) const { } Node* Node::fast_out(DUIterator_Fast& i) const { I_VDUI_ONLY(i, i.verify(this)); - return debug_only(i._last=) *i._outp; + return DEBUG_ONLY(i._last=) *i._outp; } @@ -1591,7 +1591,7 @@ DUIterator_Last Node::last_outs(DUIterator_Last& imin) const { } Node* Node::last_out(DUIterator_Last& i) const { I_VDUI_ONLY(i, i.verify(this)); - return debug_only(i._last=) *i._outp; + return DEBUG_ONLY(i._last=) *i._outp; } #endif //OPTO_DU_ITERATOR_ASSERT @@ -2035,7 +2035,7 @@ class TypeNode : public Node { public: void set_type(const Type* t) { assert(t != nullptr, "sanity"); - debug_only(uint check_hash = (VerifyHashTableKeys && _hash_lock) ? hash() : NO_HASH); + DEBUG_ONLY(uint check_hash = (VerifyHashTableKeys && _hash_lock) ? hash() : NO_HASH); *(const Type**)&_type = t; // cast away const-ness // If this node is in the hash table, make sure it doesn't need a rehash. assert(check_hash == NO_HASH || check_hash == hash(), "type change must preserve hash code"); diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 9fe5ad562b0f2..1cd6ebabc4bf7 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -2971,7 +2971,7 @@ void Scheduling::anti_do_def( Block *b, Node *def, OptoReg::Name def_reg, int is } Node *kill = def; // Rename 'def' to more descriptive 'kill' - debug_only( def = (Node*)((intptr_t)0xdeadbeef); ) + DEBUG_ONLY( def = (Node*)((intptr_t)0xdeadbeef); ) // After some number of kills there _may_ be a later def Node *later_def = nullptr; diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index f7330d10df3a7..6fa0b0f497d5e 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1835,10 +1835,10 @@ void Parse::merge_common(Parse::Block* target, int pnum) { // Now _gvn will join that with the meet of current inputs. // BOTTOM is never permissible here, 'cause pessimistically // Phis of pointers cannot lose the basic pointer type. - debug_only(const Type* bt1 = phi->bottom_type()); + DEBUG_ONLY(const Type* bt1 = phi->bottom_type()); assert(bt1 != Type::BOTTOM, "should not be building conflict phis"); map()->set_req(j, _gvn.transform(phi)); - debug_only(const Type* bt2 = phi->bottom_type()); + DEBUG_ONLY(const Type* bt2 = phi->bottom_type()); assert(bt2->higher_equal_speculative(bt1), "must be consistent with type-flow"); record_for_igvn(phi); } @@ -1936,7 +1936,7 @@ void Parse::ensure_phis_everywhere() { // Ensure a phi on all currently known memories. for (MergeMemStream mms(merged_memory()); mms.next_non_empty(); ) { ensure_memory_phi(mms.alias_idx()); - debug_only(mms.set_memory()); // keep the iterator happy + DEBUG_ONLY(mms.set_memory()); // keep the iterator happy } // Note: This is our only chance to create phis for memory slices. diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index f0c8f9d76494f..82e5b3f9d85a1 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -123,7 +123,7 @@ Node *NodeHash::hash_find_insert( Node *n ) { if( !k ) { // ?Miss? NOT_PRODUCT( _lookup_misses++ ); _table[key] = n; // Insert into table! - debug_only(n->enter_hash_lock()); // Lock down the node while in the table. + DEBUG_ONLY(n->enter_hash_lock()); // Lock down the node while in the table. check_grow(); // Grow table if insert hit limit return nullptr; // Miss! } @@ -152,7 +152,7 @@ Node *NodeHash::hash_find_insert( Node *n ) { NOT_PRODUCT( _lookup_misses++ ); key = (first_sentinel == 0) ? key : first_sentinel; // ?saw sentinel? _table[key] = n; // Insert into table! - debug_only(n->enter_hash_lock()); // Lock down the node while in the table. + DEBUG_ONLY(n->enter_hash_lock()); // Lock down the node while in the table. check_grow(); // Grow table if insert hit limit return nullptr; // Miss! } @@ -188,7 +188,7 @@ void NodeHash::hash_insert( Node *n ) { key = (key + stride) & (_max-1); // Stride through table w/ relative prime } _table[key] = n; // Insert into table! - debug_only(n->enter_hash_lock()); // Lock down the node while in the table. + DEBUG_ONLY(n->enter_hash_lock()); // Lock down the node while in the table. // if( conflict ) { n->dump(); } } @@ -203,9 +203,9 @@ bool NodeHash::hash_delete( const Node *n ) { } uint key = hash & (_max-1); uint stride = key | 0x01; - debug_only( uint counter = 0; ); + DEBUG_ONLY( uint counter = 0; ); for( ; /* (k != nullptr) && (k != _sentinel) */; ) { - debug_only( counter++ ); + DEBUG_ONLY( counter++ ); NOT_PRODUCT( _delete_probes++ ); k = _table[key]; // Get hashed value if( !k ) { // Miss? @@ -215,7 +215,7 @@ bool NodeHash::hash_delete( const Node *n ) { else if( n == k ) { NOT_PRODUCT( _delete_hits++ ); _table[key] = _sentinel; // Hit! Label as deleted entry - debug_only(((Node*)n)->exit_hash_lock()); // Unlock the node upon removal from table. + DEBUG_ONLY(((Node*)n)->exit_hash_lock()); // Unlock the node upon removal from table. return true; } else { @@ -257,7 +257,7 @@ void NodeHash::grow() { for( uint i = 0; i < old_max; i++ ) { Node *m = *old_table++; if( !m || m == _sentinel ) continue; - debug_only(m->exit_hash_lock()); // Unlock the node upon removal from old table. + DEBUG_ONLY(m->exit_hash_lock()); // Unlock the node upon removal from old table. hash_insert(m); } } @@ -289,7 +289,7 @@ void NodeHash::remove_useless_nodes(VectorSet &useful) { for( uint i = 0; i < max; ++i ) { Node *n = at(i); if(n != nullptr && n != sentinel_node && !useful.test(n->_idx)) { - debug_only(n->exit_hash_lock()); // Unlock the node when removed + DEBUG_ONLY(n->exit_hash_lock()); // Unlock the node when removed _table[i] = sentinel_node; // Replace with placeholder } } @@ -1728,6 +1728,34 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } } } + + /* AndNode has a special handling when one of the operands is a LShiftNode: + * (LHS << s) & RHS + * if RHS fits in less than s bits, the value of this expression is 0. + * The difficulty is that there might be a conversion node (ConvI2L) between + * the LShiftINode and the AndLNode, like so: + * AndLNode(ConvI2L(LShiftI(LHS, s)), RHS) + * This case is handled by And[IL]Node::Value(PhaseGVN*) + * (see `AndIL_min_trailing_zeros`). + * + * But, when the shift is updated during IGVN, pushing the user (ConvI2L) + * is not enough: there might be no update happening there. We need to + * directly push the And[IL]Node on the worklist, jumping over ConvI2L. + * + * Moreover we can have ConstraintCasts in between. It may look like + * ConstraintCast+ -> ConvI2L -> ConstraintCast+ -> And + * and And[IL]Node::Value(PhaseGVN*) still handles that by looking through casts. + * So we must deal with that as well. + */ + if (use->is_ConstraintCast() || use_op == Op_ConvI2L) { + auto is_boundary = [](Node* n){ return !n->is_ConstraintCast() && n->Opcode() != Op_ConvI2L; }; + auto push_and_to_worklist = [&worklist](Node* n){ + if (n->Opcode() == Op_AndL || n->Opcode() == Op_AndI) { + worklist.push(n); + } + }; + use->visit_uses(push_and_to_worklist, is_boundary); + } } /** diff --git a/src/hotspot/share/opto/predicates.cpp b/src/hotspot/share/opto/predicates.cpp index 90df1ff05b4f2..d6078e50d69ae 100644 --- a/src/hotspot/share/opto/predicates.cpp +++ b/src/hotspot/share/opto/predicates.cpp @@ -29,6 +29,7 @@ #include "opto/node.hpp" #include "opto/predicates.hpp" #include "opto/rootnode.hpp" +#include "runtime/deoptimization.hpp" // Walk over all Initialized Assertion Predicates and return the entry into the first Initialized Assertion Predicate // (i.e. not belonging to an Initialized Assertion Predicate anymore) @@ -757,7 +758,7 @@ void AssertionPredicateIfCreator::create_halt_node(IfFalseNode* fail_proj, Ideal } OpaqueLoopInitNode* TemplateAssertionPredicateCreator::create_opaque_init(Node* new_control) const { - OpaqueLoopInitNode* opaque_init = new OpaqueLoopInitNode(_phase->C, _loop_head->init_trip()); + OpaqueLoopInitNode* opaque_init = new OpaqueLoopInitNode(_phase->C, _loop_head->uncasted_init_trip(_loop_head->is_main_loop())); _phase->register_new_node(opaque_init, new_control); return opaque_init; } @@ -1093,11 +1094,19 @@ CloneUnswitchedLoopPredicatesVisitor::CloneUnswitchedLoopPredicatesVisitor( PhaseIdealLoop* phase) : _clone_predicate_to_true_path_loop(true_path_loop_head, node_in_true_path_loop_body, phase), _clone_predicate_to_false_path_loop(false_path_loop_head, node_in_false_path_loop_body, phase), - _phase(phase) {} + _phase(phase), + _is_counted_loop(true_path_loop_head->is_CountedLoop()) {} -// Keep track of whether we are in the correct Predicate Block where Template Assertion Predicates can be found. // The PredicateIterator will always start at the loop entry and first visits the Loop Limit Check Predicate Block. +// Does not clone a Loop Limit Check Parse Predicate if a counted loop is unswitched, because it most likely will not be +// used anymore (it could only be used when both unswitched loop versions die and the Loop Limit Check Parse Predicate +// ends up at a LoopNode without Loop Limit Check Parse Predicate directly following the unswitched loop that can then +// be speculatively converted to a counted loop - this is rather rare). void CloneUnswitchedLoopPredicatesVisitor::visit(const ParsePredicate& parse_predicate) { + Deoptimization::DeoptReason deopt_reason = parse_predicate.head()->deopt_reason(); + if (_is_counted_loop && deopt_reason == Deoptimization::Reason_loop_limit_check) { + return; + } _clone_predicate_to_true_path_loop.clone_parse_predicate(parse_predicate, false); _clone_predicate_to_false_path_loop.clone_parse_predicate(parse_predicate, true); parse_predicate.kill(_phase->igvn()); diff --git a/src/hotspot/share/opto/predicates.hpp b/src/hotspot/share/opto/predicates.hpp index 5cea5598392b9..f19b2d73c5668 100644 --- a/src/hotspot/share/opto/predicates.hpp +++ b/src/hotspot/share/opto/predicates.hpp @@ -1176,6 +1176,7 @@ class CloneUnswitchedLoopPredicatesVisitor : public PredicateVisitor { ClonePredicateToTargetLoop _clone_predicate_to_false_path_loop; PhaseIdealLoop* const _phase; + const bool _is_counted_loop; public: CloneUnswitchedLoopPredicatesVisitor(LoopNode* true_path_loop_head, diff --git a/src/hotspot/share/opto/regalloc.hpp b/src/hotspot/share/opto/regalloc.hpp index 86877e183254c..c724406a168c3 100644 --- a/src/hotspot/share/opto/regalloc.hpp +++ b/src/hotspot/share/opto/regalloc.hpp @@ -60,12 +60,12 @@ class PhaseRegAlloc : public Phase { // Get the register associated with the Node OptoReg::Name get_reg_first( const Node *n ) const { - debug_only( if( n->_idx >= _node_regs_max_index ) n->dump(); ); + DEBUG_ONLY( if( n->_idx >= _node_regs_max_index ) n->dump(); ); assert( n->_idx < _node_regs_max_index, "Exceeded _node_regs array"); return _node_regs[n->_idx].first(); } OptoReg::Name get_reg_second( const Node *n ) const { - debug_only( if( n->_idx >= _node_regs_max_index ) n->dump(); ); + DEBUG_ONLY( if( n->_idx >= _node_regs_max_index ) n->dump(); ); assert( n->_idx < _node_regs_max_index, "Exceeded _node_regs array"); return _node_regs[n->_idx].second(); } diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 6d24fb26cd2f4..fcb0ac38ace68 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -1944,7 +1944,7 @@ address OptoRuntime::handle_exception_C(JavaThread* current) { #ifndef PRODUCT SharedRuntime::_find_handler_ctr++; // find exception handler #endif - debug_only(NoHandleMark __hm;) + DEBUG_ONLY(NoHandleMark __hm;) nmethod* nm = nullptr; address handler_address = nullptr; { diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index fd7fc796aa4b5..633cced00909c 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -113,6 +113,7 @@ enum class OptoStubId :int { class OptoRuntime : public AllStatic { friend class Matcher; // allow access to stub names + friend class AOTCodeAddressTable; private: // declare opto stub address/blob holder static fields diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 0746715f065a3..5e34249aee810 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -158,7 +158,7 @@ void SuperWord::unrolling_analysis(const VLoop &vloop, int &local_loop_unroll_fa // Ignore nodes with non-primitive type. BasicType bt; if (n->is_Mem()) { - bt = n->as_Mem()->memory_type(); + bt = n->as_Mem()->value_basic_type(); } else { bt = n->bottom_type()->basic_type(); } @@ -191,7 +191,7 @@ void SuperWord::unrolling_analysis(const VLoop &vloop, int &local_loop_unroll_fa BasicType bt; Node* n = lpt->_body.at(i); if (n->is_Mem()) { - bt = n->as_Mem()->memory_type(); + bt = n->as_Mem()->value_basic_type(); } else { bt = n->bottom_type()->basic_type(); } @@ -564,7 +564,7 @@ void SuperWord::collect_valid_memops(GrowableArray& memops) const { const VPointer& p = vpointer(mem); if (p.is_valid() && !mem->is_LoadStore() && - is_java_primitive(mem->memory_type())) { + is_java_primitive(mem->value_basic_type())) { memops.append(MemOp(mem, &p, original_index++)); } }); @@ -764,8 +764,8 @@ bool SuperWord::are_adjacent_refs(Node* s1, Node* s2) const { if (!in_bb(s1) || !in_bb(s2)) return false; // Do not use superword for non-primitives - if (!is_java_primitive(s1->as_Mem()->memory_type()) || - !is_java_primitive(s2->as_Mem()->memory_type())) { + if (!is_java_primitive(s1->as_Mem()->value_basic_type()) || + !is_java_primitive(s2->as_Mem()->value_basic_type())) { return false; } @@ -2593,7 +2593,7 @@ void VLoopTypes::compute_vector_element_type() { const Type* VLoopTypes::container_type(Node* n) const { int opc = n->Opcode(); if (n->is_Mem()) { - BasicType bt = n->as_Mem()->memory_type(); + BasicType bt = n->as_Mem()->value_basic_type(); if (n->is_Store() && (bt == T_CHAR)) { // Use T_SHORT type instead of T_CHAR for stored values because any // preceding arithmetic operation extends values to signed Int. diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index 4556555aceade..163f94ee95933 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -756,7 +756,7 @@ void Type::Initialize(Compile* current) { // delete the current Type and return the existing Type. Otherwise stick the // current Type in the Type table. const Type *Type::hashcons(void) { - debug_only(base()); // Check the assertion in Type::base(). + DEBUG_ONLY(base()); // Check the assertion in Type::base(). // Look up the Type in the Type dictionary Dict *tdic = type_dict(); Type* old = (Type*)(tdic->Insert(this, this, false)); diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index e33d7b1968682..13acc0469eb29 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -366,17 +366,11 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { int num_elem = vlen->get_con(); int opc = VectorSupport::vop2ideal(opr->get_con(), elem_bt); int sopc = has_scalar_op ? VectorNode::opcode(opc, elem_bt) : opc; - if ((opc != Op_CallLeafVector) && (sopc == 0)) { - log_if_needed(" ** operation not supported: opc=%s bt=%s", NodeClassNames[opc], type2name(elem_bt)); + if (sopc == 0 || num_elem == 1) { + log_if_needed(" ** operation not supported: arity=%d opc=%s[%d] vlen=%d etype=%s", + n, NodeClassNames[opc], opc, num_elem, type2name(elem_bt)); return false; // operation not supported } - if (num_elem == 1) { - if (opc != Op_CallLeafVector || elem_bt != T_DOUBLE) { - log_if_needed(" ** not a svml call: arity=%d opc=%d vlen=%d etype=%s", - n, opc, num_elem, type2name(elem_bt)); - return false; - } - } ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); @@ -384,22 +378,6 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { assert(!is_masked_op, "mask operations do not need mask to control"); } - if (opc == Op_CallLeafVector) { - if (!UseVectorStubs) { - log_if_needed(" ** vector stubs support is disabled"); - return false; - } - if (!Matcher::supports_vector_calling_convention()) { - log_if_needed(" ** no vector calling conventions supported"); - return false; - } - if (!Matcher::vector_size_supported(elem_bt, num_elem)) { - log_if_needed(" ** vector size (vlen=%d, etype=%s) is not supported", - num_elem, type2name(elem_bt)); - return false; - } - } - // When using mask, mask use type needs to be VecMaskUseLoad. VectorMaskUseType mask_use_type = is_vector_mask(vbox_klass) ? VecMaskUseAll : is_masked_op ? VecMaskUseLoad : VecMaskNotUsed; @@ -464,30 +442,18 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { } Node* operation = nullptr; - if (opc == Op_CallLeafVector) { - assert(UseVectorStubs, "sanity"); - operation = gen_call_to_vector_math(opr->get_con(), elem_bt, num_elem, opd1, opd2); - if (operation == nullptr) { - log_if_needed(" ** Vector math call failed for %s_%s_%d", - (elem_bt == T_FLOAT) ? "float" : "double", - VectorSupport::mathname[opr->get_con() - VectorSupport::VECTOR_OP_MATH_START], - num_elem * type2aelembytes(elem_bt)); - return false; - } - } else { - const TypeVect* vt = TypeVect::make(elem_bt, num_elem, is_vector_mask(vbox_klass)); - switch (n) { - case 1: - case 2: { - operation = VectorNode::make(sopc, opd1, opd2, vt, is_vector_mask(vbox_klass), VectorNode::is_shift_opcode(opc), is_unsigned); - break; - } - case 3: { - operation = VectorNode::make(sopc, opd1, opd2, opd3, vt); - break; - } - default: fatal("unsupported arity: %d", n); + const TypeVect* vt = TypeVect::make(elem_bt, num_elem, is_vector_mask(vbox_klass)); + switch (n) { + case 1: + case 2: { + operation = VectorNode::make(sopc, opd1, opd2, vt, is_vector_mask(vbox_klass), VectorNode::is_shift_opcode(opc), is_unsigned); + break; + } + case 3: { + operation = VectorNode::make(sopc, opd1, opd2, opd3, vt); + break; } + default: fatal("unsupported arity: %d", n); } if (is_masked_op && mask != nullptr) { @@ -510,6 +476,107 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { } // public static +// , E> +// V libraryUnaryOp(long address, Class vClass, Class elementType, int length, String debugName, +// V v, +// UnaryOperation defaultImpl) +// +// public static +// +// V libraryBinaryOp(long address, Class vClass, Class elementType, int length, String debugName, +// V v1, V v2, +// BinaryOperation defaultImpl) +bool LibraryCallKit::inline_vector_call(int arity) { + assert(Matcher::supports_vector_calling_convention(), "required"); + + const TypeLong* entry = gvn().type(argument(0))->isa_long(); + const TypeInstPtr* vector_klass = gvn().type(argument(2))->isa_instptr(); + const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr(); + const TypeInt* vlen = gvn().type(argument(4))->isa_int(); + const TypeInstPtr* debug_name_oop = gvn().type(argument(5))->isa_instptr(); + + if (entry == nullptr || !entry->is_con() || + vector_klass == nullptr || vector_klass->const_oop() == nullptr || + elem_klass == nullptr || elem_klass->const_oop() == nullptr || + vlen == nullptr || !vlen->is_con() || + debug_name_oop == nullptr || debug_name_oop->const_oop() == nullptr) { + log_if_needed(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s debug_name=%s", + NodeClassNames[argument(0)->Opcode()], + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()], + NodeClassNames[argument(4)->Opcode()], + NodeClassNames[argument(5)->Opcode()]); + return false; // not enough info for intrinsification + } + + if (entry->get_con() == 0) { + log_if_needed(" ** missing entry point"); + return false; + } + + ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); + if (!elem_type->is_primitive_type()) { + log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); + return false; // should be primitive type + } + if (!is_klass_initialized(vector_klass)) { + log_if_needed(" ** klass argument not initialized"); + return false; + } + + BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + if (!Matcher::vector_size_supported(elem_bt, num_elem)) { + log_if_needed(" ** vector size (vlen=%d, etype=%s) is not supported", + num_elem, type2name(elem_bt)); + return false; + } + + ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); + const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); + + Node* opd1 = unbox_vector(argument(6), vbox_type, elem_bt, num_elem); + if (opd1 == nullptr) { + log_if_needed(" ** unbox failed v1=%s", NodeClassNames[argument(6)->Opcode()]); + return false; + } + + Node* opd2 = nullptr; + if (arity > 1) { + opd2 = unbox_vector(argument(7), vbox_type, elem_bt, num_elem); + if (opd2 == nullptr) { + log_if_needed(" ** unbox failed v2=%s", NodeClassNames[argument(7)->Opcode()]); + return false; + } + } + assert(arity == 1 || arity == 2, "arity %d not supported", arity); + const TypeVect* vt = TypeVect::make(elem_bt, num_elem); + const TypeFunc* call_type = OptoRuntime::Math_Vector_Vector_Type(arity, vt, vt); + address entry_addr = (address)entry->get_con(); + + const char* debug_name = ""; + if (!debug_name_oop->const_oop()->is_null_object()) { + size_t buflen = 100; + char* buf = NEW_ARENA_ARRAY(C->comp_arena(), char, buflen); + debug_name = debug_name_oop->const_oop()->as_instance()->java_lang_String_str(buf, buflen); + } + Node* vcall = make_runtime_call(RC_VECTOR, + call_type, + entry_addr, + debug_name, + TypePtr::BOTTOM, + opd1, + opd2); + + vcall = gvn().transform(new ProjNode(gvn().transform(vcall), TypeFunc::Parms)); + + // Wrap it up in VectorBox to keep object type information. + Node* vbox = box_vector(vcall, vbox_type, elem_bt, num_elem); + set_result(vbox); + C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); + return true; +} + // // long maskReductionCoerced(int oper, Class maskClass, Class elemClass, // int length, M m, VectorMaskOp defaultImpl) @@ -1844,50 +1911,6 @@ bool LibraryCallKit::inline_vector_rearrange() { return true; } -static address get_vector_math_address(int vop, int bits, BasicType bt, char* name_ptr, int name_len) { - address addr = nullptr; - assert(UseVectorStubs, "sanity"); - assert(name_ptr != nullptr, "unexpected"); - assert((vop >= VectorSupport::VECTOR_OP_MATH_START) && (vop <= VectorSupport::VECTOR_OP_MATH_END), "unexpected"); - int op = vop - VectorSupport::VECTOR_OP_MATH_START; - - switch(bits) { - case 64: //fallthough - case 128: //fallthough - case 256: //fallthough - case 512: - if (bt == T_FLOAT) { - snprintf(name_ptr, name_len, "vector_%s_float_%dbits_fixed", VectorSupport::mathname[op], bits); - addr = StubRoutines::_vector_f_math[exact_log2(bits/64)][op]; - } else { - assert(bt == T_DOUBLE, "must be FP type only"); - snprintf(name_ptr, name_len, "vector_%s_double_%dbits_fixed", VectorSupport::mathname[op], bits); - addr = StubRoutines::_vector_d_math[exact_log2(bits/64)][op]; - } - break; - default: - if (!Matcher::supports_scalable_vector() || !Matcher::vector_size_supported(bt, bits/type2aelembytes(bt)) ) { - snprintf(name_ptr, name_len, "invalid"); - addr = nullptr; - Unimplemented(); - } - break; - } - - if (addr == nullptr && Matcher::supports_scalable_vector()) { - if (bt == T_FLOAT) { - snprintf(name_ptr, name_len, "vector_%s_float_%dbits_scalable", VectorSupport::mathname[op], bits); - addr = StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_SCALABLE][op]; - } else { - assert(bt == T_DOUBLE, "must be FP type only"); - snprintf(name_ptr, name_len, "vector_%s_double_%dbits_scalable", VectorSupport::mathname[op], bits); - addr = StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_SCALABLE][op]; - } - } - - return addr; -} - // public static // , // M extends VectorMask, @@ -2044,32 +2067,6 @@ bool LibraryCallKit::inline_vector_select_from() { return true; } -Node* LibraryCallKit::gen_call_to_vector_math(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2) { - assert(UseVectorStubs, "sanity"); - assert(vector_api_op_id >= VectorSupport::VECTOR_OP_MATH_START && vector_api_op_id <= VectorSupport::VECTOR_OP_MATH_END, "need valid op id"); - assert(opd1 != nullptr, "must not be null"); - const TypeVect* vt = TypeVect::make(bt, num_elem); - const TypeFunc* call_type = OptoRuntime::Math_Vector_Vector_Type(opd2 != nullptr ? 2 : 1, vt, vt); - char name[100] = ""; - - // Get address for vector math method. - address addr = get_vector_math_address(vector_api_op_id, vt->length_in_bytes() * BitsPerByte, bt, name, 100); - - if (addr == nullptr) { - return nullptr; - } - - assert(name[0] != '\0', "name must not be null"); - Node* operation = make_runtime_call(RC_VECTOR, - call_type, - addr, - name, - TypePtr::BOTTOM, - opd1, - opd2); - return gvn().transform(new ProjNode(gvn().transform(operation), TypeFunc::Parms)); -} - // public static // , // M extends VectorMask, diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 084b70a690653..05ef64af70432 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -2237,6 +2237,72 @@ bool MulVLNode::has_uint_inputs() const { has_vector_elements_fit_uint(in(2)); } +static Node* UMinMaxV_Ideal(Node* n, PhaseGVN* phase, bool can_reshape) { + int vopc = n->Opcode(); + assert(vopc == Op_UMinV || vopc == Op_UMaxV, "Unexpected opcode"); + + Node* umin = nullptr; + Node* umax = nullptr; + int lopc = n->in(1)->Opcode(); + int ropc = n->in(2)->Opcode(); + + if (lopc == Op_UMinV && ropc == Op_UMaxV) { + umin = n->in(1); + umax = n->in(2); + } else if (lopc == Op_UMaxV && ropc == Op_UMinV) { + umin = n->in(2); + umax = n->in(1); + } else { + return nullptr; + } + + // UMin (UMin(a, b), UMax(a, b)) => UMin(a, b) + // UMin (UMax(a, b), UMin(b, a)) => UMin(a, b) + // UMax (UMin(a, b), UMax(a, b)) => UMax(a, b) + // UMax (UMax(a, b), UMin(b, a)) => UMax(a, b) + if (umin != nullptr && umax != nullptr) { + if ((umin->in(1) == umax->in(1) && umin->in(2) == umax->in(2)) || + (umin->in(2) == umax->in(1) && umin->in(1) == umax->in(2))) { + if (vopc == Op_UMinV) { + return new UMinVNode(umax->in(1), umax->in(2), n->bottom_type()->is_vect()); + } else { + return new UMaxVNode(umax->in(1), umax->in(2), n->bottom_type()->is_vect()); + } + } + } + + return nullptr; +} + +Node* UMinVNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* progress = UMinMaxV_Ideal(this, phase, can_reshape); + if (progress != nullptr) return progress; + + return VectorNode::Ideal(phase, can_reshape); +} + +Node* UMinVNode::Identity(PhaseGVN* phase) { + // UMin (a, a) => a + if (in(1) == in(2)) { + return in(1); + } + return this; +} + +Node* UMaxVNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* progress = UMinMaxV_Ideal(this, phase, can_reshape); + if (progress != nullptr) return progress; + + return VectorNode::Ideal(phase, can_reshape); +} + +Node* UMaxVNode::Identity(PhaseGVN* phase) { + // UMax (a, a) => a + if (in(1) == in(2)) { + return in(1); + } + return this; +} #ifndef PRODUCT void VectorBoxAllocateNode::dump_spec(outputStream *st) const { CallStaticJavaNode::dump_spec(st); diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index 36706a7b7a14b..e72c3880c7983 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -675,9 +675,12 @@ class UMinVNode : public VectorNode { UMinVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2 ,vt) { assert(is_integral_type(vt->element_basic_type()), ""); } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); virtual int Opcode() const; }; + //------------------------------MaxVNode-------------------------------------- // Vector Max class MaxVNode : public VectorNode { @@ -691,6 +694,8 @@ class UMaxVNode : public VectorNode { UMaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) { assert(is_integral_type(vt->element_basic_type()), ""); } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual Node* Identity(PhaseGVN* phase); virtual int Opcode() const; }; @@ -1084,7 +1089,7 @@ class LoadVectorNode : public LoadNode { virtual int Opcode() const; virtual uint ideal_reg() const { return Matcher::vector_ideal_reg(memory_size()); } - virtual BasicType memory_type() const { return T_VOID; } + virtual BasicType value_basic_type() const { return T_VOID; } virtual int memory_size() const { return vect_type()->length_in_bytes(); } virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); @@ -1157,7 +1162,7 @@ class StoreVectorNode : public StoreNode { virtual int Opcode() const; virtual uint ideal_reg() const { return Matcher::vector_ideal_reg(memory_size()); } - virtual BasicType memory_type() const { return T_VOID; } + virtual BasicType value_basic_type() const { return T_VOID; } virtual int memory_size() const { return vect_type()->length_in_bytes(); } virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index d2916fad1850d..228244bbd0528 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -215,7 +215,7 @@ intptr_t jfieldIDWorkaround::encode_klass_hash(Klass* k, int offset) { field_klass = super_klass; // super contains the field also super_klass = field_klass->super(); } - debug_only(NoSafepointVerifier nosafepoint;) + DEBUG_ONLY(NoSafepointVerifier nosafepoint;) uintptr_t klass_hash = field_klass->identity_hash(); return ((klass_hash & klass_mask) << klass_shift) | checked_mask_in_place; } else { @@ -235,7 +235,7 @@ bool jfieldIDWorkaround::klass_hash_ok(Klass* k, jfieldID id) { uintptr_t as_uint = (uintptr_t) id; intptr_t klass_hash = (as_uint >> klass_shift) & klass_mask; do { - debug_only(NoSafepointVerifier nosafepoint;) + DEBUG_ONLY(NoSafepointVerifier nosafepoint;) // Could use a non-blocking query for identity_hash here... if ((k->identity_hash() & klass_mask) == klass_hash) return true; @@ -410,7 +410,7 @@ JNI_ENTRY(jfieldID, jni_FromReflectedField(JNIEnv *env, jobject field)) int offset = InstanceKlass::cast(k1)->field_offset( slot ); JNIid* id = InstanceKlass::cast(k1)->jni_id_for(offset); assert(id != nullptr, "corrupt Field object"); - debug_only(id->set_is_static_field_id();) + DEBUG_ONLY(id->set_is_static_field_id();) // A jfieldID for a static field is a JNIid specifying the field holder and the offset within the Klass* ret = jfieldIDWorkaround::to_static_jfieldID(id); return ret; @@ -472,7 +472,7 @@ JNI_ENTRY(jclass, jni_GetSuperclass(JNIEnv *env, jclass sub)) // return mirror for superclass Klass* super = k->java_super(); // super2 is the value computed by the compiler's getSuperClass intrinsic: - debug_only(Klass* super2 = ( k->is_array_klass() + DEBUG_ONLY(Klass* super2 = ( k->is_array_klass() ? vmClasses::Object_klass() : k->super() ) ); assert(super == super2, @@ -906,7 +906,7 @@ static void jni_invoke_nonstatic(JNIEnv *env, JavaValue* result, jobject receive selected_method = m; } else if (!m->has_itable_index()) { // non-interface call -- for that little speed boost, don't handlize - debug_only(NoSafepointVerifier nosafepoint;) + DEBUG_ONLY(NoSafepointVerifier nosafepoint;) // jni_GetMethodID makes sure class is linked and initialized // so m should have a valid vtable index. assert(m->valid_vtable_index(), "no valid vtable index"); @@ -1995,9 +1995,9 @@ JNI_ENTRY(jfieldID, jni_GetStaticFieldID(JNIEnv *env, jclass clazz, // A jfieldID for a static field is a JNIid specifying the field holder and the offset within the Klass* JNIid* id = fd.field_holder()->jni_id_for(fd.offset()); - debug_only(id->set_is_static_field_id();) + DEBUG_ONLY(id->set_is_static_field_id();) - debug_only(id->verify(fd.field_holder())); + DEBUG_ONLY(id->verify(fd.field_holder())); ret = jfieldIDWorkaround::to_static_jfieldID(id); return ret; @@ -2400,7 +2400,7 @@ static char* get_bad_address() { static char* bad_address = nullptr; if (bad_address == nullptr) { size_t size = os::vm_allocation_granularity(); - bad_address = os::reserve_memory(size, false, mtInternal); + bad_address = os::reserve_memory(size, mtInternal); if (bad_address != nullptr) { os::protect_memory(bad_address, size, os::MEM_PROT_READ, /*is_committed*/false); diff --git a/src/hotspot/share/prims/jniCheck.cpp b/src/hotspot/share/prims/jniCheck.cpp index aa158490eabdd..14d9c36c9fd51 100644 --- a/src/hotspot/share/prims/jniCheck.cpp +++ b/src/hotspot/share/prims/jniCheck.cpp @@ -2320,7 +2320,7 @@ struct JNINativeInterface_* jni_functions_check() { // make sure the last pointer in the checked table is not null, indicating // an addition to the JNINativeInterface_ structure without initializing // it in the checked table. - debug_only(intptr_t *lastPtr = (intptr_t *)((char *)&checked_jni_NativeInterface + \ + DEBUG_ONLY(intptr_t *lastPtr = (intptr_t *)((char *)&checked_jni_NativeInterface + \ sizeof(*unchecked_jni_NativeInterface) - sizeof(char *));) assert(*lastPtr != 0, "Mismatched JNINativeInterface tables, check for new entries"); diff --git a/src/hotspot/share/prims/jvmtiEnter.xsl b/src/hotspot/share/prims/jvmtiEnter.xsl index bbca1ccc6e19a..d1274158be465 100644 --- a/src/hotspot/share/prims/jvmtiEnter.xsl +++ b/src/hotspot/share/prims/jvmtiEnter.xsl @@ -444,7 +444,7 @@ struct jvmtiInterface_1_ jvmti , current_thread) - debug_only(VMNativeEntryWrapper __vew;) + DEBUG_ONLY(VMNativeEntryWrapper __vew;) PreserveExceptionMark __em(this_thread); diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp index 7a0c9a428c5a3..b2af12b08a6ad 100644 --- a/src/hotspot/share/prims/jvmtiEnv.cpp +++ b/src/hotspot/share/prims/jvmtiEnv.cpp @@ -199,7 +199,7 @@ JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) { MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current_thread)); ThreadInVMfromNative __tiv(current_thread); VM_ENTRY_BASE(jvmtiError, JvmtiEnv::GetThreadLocalStorage , current_thread) - debug_only(VMNativeEntryWrapper __vew;) + DEBUG_ONLY(VMNativeEntryWrapper __vew;) JvmtiVTMSTransitionDisabler disabler(thread); ThreadsListHandle tlh(current_thread); diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 32dd6fc13e7d2..13822f73f772e 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -384,7 +384,7 @@ JvmtiExport::get_jvmti_interface(JavaVM *jvm, void **penv, jint version) { MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current_thread)); ThreadInVMfromNative __tiv(current_thread); VM_ENTRY_BASE(jvmtiEnv*, JvmtiExport::get_jvmti_interface, current_thread) - debug_only(VMNativeEntryWrapper __vew;) + DEBUG_ONLY(VMNativeEntryWrapper __vew;) JvmtiEnv *jvmti_env = JvmtiEnv::create_a_jvmti(version); *penv = jvmti_env->jvmti_external(); // actual type is jvmtiEnv* -- not to be confused with JvmtiEnv* diff --git a/src/hotspot/share/prims/jvmtiThreadState.cpp b/src/hotspot/share/prims/jvmtiThreadState.cpp index e9d496fc69684..b4119825bd36e 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.cpp +++ b/src/hotspot/share/prims/jvmtiThreadState.cpp @@ -102,7 +102,7 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop) { // The thread state list manipulation code must not have safepoints. // See periodic_clean_up(). - debug_only(NoSafepointVerifier nosafepoint;) + DEBUG_ONLY(NoSafepointVerifier nosafepoint;) _prev = nullptr; _next = _head; @@ -154,7 +154,7 @@ JvmtiThreadState::~JvmtiThreadState() { { // The thread state list manipulation code must not have safepoints. // See periodic_clean_up(). - debug_only(NoSafepointVerifier nosafepoint;) + DEBUG_ONLY(NoSafepointVerifier nosafepoint;) if (_prev == nullptr) { assert(_head == this, "sanity check"); @@ -759,7 +759,7 @@ void JvmtiThreadState::add_env(JvmtiEnvBase *env) { // add this environment thread state to the end of the list (order is important) { // list deallocation (which occurs at a safepoint) cannot occur simultaneously - debug_only(NoSafepointVerifier nosafepoint;) + DEBUG_ONLY(NoSafepointVerifier nosafepoint;) JvmtiEnvThreadStateIterator it(this); JvmtiEnvThreadState* previous_ets = nullptr; diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index 246e2cdbb132d..1ab5d6ab7f7ba 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -929,8 +929,7 @@ void MethodHandles::expand_MemberName(Handle mname, int suppress, TRAPS) { } void MethodHandles::add_dependent_nmethod(oop call_site, nmethod* nm) { - assert_locked_or_safepoint(CodeCache_lock); - + assert_lock_strong(CodeCache_lock); DependencyContext deps = java_lang_invoke_CallSite::vmdependencies(call_site); deps.add_dependent_nmethod(nm); } diff --git a/src/hotspot/share/prims/perf.cpp b/src/hotspot/share/prims/perf.cpp index f5f91e4614f51..0b051bbc60f2d 100644 --- a/src/hotspot/share/prims/perf.cpp +++ b/src/hotspot/share/prims/perf.cpp @@ -111,7 +111,7 @@ PERF_ENTRY(jobject, Perf_CreateLong(JNIEnv *env, jobject perf, jstring name, char* name_utf = nullptr; if (units <= 0 || units > PerfData::U_Last) { - debug_only(warning("unexpected units argument, units = %d", units)); + DEBUG_ONLY(warning("unexpected units argument, units = %d", units)); THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } @@ -150,7 +150,7 @@ PERF_ENTRY(jobject, Perf_CreateLong(JNIEnv *env, jobject perf, jstring name, break; default: /* Illegal Argument */ - debug_only(warning("unexpected variability value: %d", variability)); + DEBUG_ONLY(warning("unexpected variability value: %d", variability)); THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); break; } @@ -179,14 +179,14 @@ PERF_ENTRY(jobject, Perf_CreateByteArray(JNIEnv *env, jobject perf, // check for valid variability classification if (variability != PerfData::V_Constant && variability != PerfData::V_Variable) { - debug_only(warning("unexpected variability value: %d", variability)); + DEBUG_ONLY(warning("unexpected variability value: %d", variability)); THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } // check for valid units if (units != PerfData::U_String) { // only String based ByteArray objects are currently supported - debug_only(warning("unexpected units value: %d", variability)); + DEBUG_ONLY(warning("unexpected units value: %d", variability)); THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } diff --git a/src/hotspot/share/prims/upcallLinker.cpp b/src/hotspot/share/prims/upcallLinker.cpp index 911f0f3ad2c6c..bc6a56dab055b 100644 --- a/src/hotspot/share/prims/upcallLinker.cpp +++ b/src/hotspot/share/prims/upcallLinker.cpp @@ -104,7 +104,7 @@ JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) { context->jfa.copy(thread->frame_anchor()); thread->frame_anchor()->clear(); - debug_only(thread->inc_java_call_counter()); + DEBUG_ONLY(thread->inc_java_call_counter()); thread->set_active_handles(context->new_handles); // install new handle block and reset Java frame linkage return thread; @@ -118,7 +118,7 @@ void UpcallLinker::on_exit(UpcallStub::FrameData* context) { // restore previous handle block thread->set_active_handles(context->old_handles); - debug_only(thread->dec_java_call_counter()); + DEBUG_ONLY(thread->dec_java_call_counter()); thread->frame_anchor()->copy(&context->jfa); diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp index a00656f30ee1e..c907ddb48857e 100644 --- a/src/hotspot/share/prims/vectorSupport.cpp +++ b/src/hotspot/share/prims/vectorSupport.cpp @@ -39,31 +39,9 @@ #include "runtime/stackValue.hpp" #ifdef COMPILER2 #include "opto/matcher.hpp" +#include "opto/vectornode.hpp" #endif // COMPILER2 -#ifdef COMPILER2 -const char* VectorSupport::mathname[VectorSupport::NUM_VECTOR_OP_MATH] = { - "tan", - "tanh", - "sin", - "sinh", - "cos", - "cosh", - "asin", - "acos", - "atan", - "atan2", - "cbrt", - "log", - "log10", - "log1p", - "pow", - "exp", - "expm1", - "hypot", -}; -#endif - bool VectorSupport::is_vector(Klass* klass) { return klass->is_subclass_of(vmClasses::vector_VectorPayload_klass()); } @@ -615,25 +593,6 @@ int VectorSupport::vop2ideal(jint id, BasicType bt) { break; } - case VECTOR_OP_TAN: - case VECTOR_OP_TANH: - case VECTOR_OP_SIN: - case VECTOR_OP_SINH: - case VECTOR_OP_COS: - case VECTOR_OP_COSH: - case VECTOR_OP_ASIN: - case VECTOR_OP_ACOS: - case VECTOR_OP_ATAN: - case VECTOR_OP_ATAN2: - case VECTOR_OP_CBRT: - case VECTOR_OP_LOG: - case VECTOR_OP_LOG10: - case VECTOR_OP_LOG1P: - case VECTOR_OP_POW: - case VECTOR_OP_EXP: - case VECTOR_OP_EXPM1: - case VECTOR_OP_HYPOT: - return Op_CallLeafVector; default: fatal("unknown op: %d", vop); } return 0; // Unimplemented @@ -655,16 +614,26 @@ JVM_ENTRY(jint, VectorSupport_GetMaxLaneCount(JNIEnv *env, jclass vsclazz, jobje return -1; } JVM_END +JVM_ENTRY(jstring, VectorSupport_GetCPUFeatures(JNIEnv* env, jclass ignored)) + const char* features_string = VM_Version::features_string(); + assert(features_string != nullptr, "missing cpu features info"); + + oop result = java_lang_String::create_oop_from_str(features_string, CHECK_NULL); + return (jstring) JNIHandles::make_local(THREAD, result); +JVM_END + // JVM_RegisterVectorSupportMethods #define LANG "Ljava/lang/" #define CLS LANG "Class;" +#define LSTR LANG "String;" #define CC (char*) /*cast a literal from (const char*)*/ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) static JNINativeMethod jdk_internal_vm_vector_VectorSupport_methods[] = { - {CC "getMaxLaneCount", CC "(" CLS ")I", FN_PTR(VectorSupport_GetMaxLaneCount)} + {CC "getMaxLaneCount", CC "(" CLS ")I", FN_PTR(VectorSupport_GetMaxLaneCount)}, + {CC "getCPUFeatures", CC "()" LSTR, FN_PTR(VectorSupport_GetCPUFeatures)} }; #undef CC @@ -672,6 +641,7 @@ static JNINativeMethod jdk_internal_vm_vector_VectorSupport_methods[] = { #undef LANG #undef CLS +#undef LSTR // This function is exported, used by NativeLookup. diff --git a/src/hotspot/share/prims/vectorSupport.hpp b/src/hotspot/share/prims/vectorSupport.hpp index 688fb595099f6..5ba18cdfaa85e 100644 --- a/src/hotspot/share/prims/vectorSupport.hpp +++ b/src/hotspot/share/prims/vectorSupport.hpp @@ -101,36 +101,12 @@ class VectorSupport : AllStatic { VECTOR_OP_COMPRESS_BITS = 33, VECTOR_OP_EXPAND_BITS = 34, - // Vector Math Library - VECTOR_OP_TAN = 101, - VECTOR_OP_TANH = 102, - VECTOR_OP_SIN = 103, - VECTOR_OP_SINH = 104, - VECTOR_OP_COS = 105, - VECTOR_OP_COSH = 106, - VECTOR_OP_ASIN = 107, - VECTOR_OP_ACOS = 108, - VECTOR_OP_ATAN = 109, - VECTOR_OP_ATAN2 = 110, - VECTOR_OP_CBRT = 111, - VECTOR_OP_LOG = 112, - VECTOR_OP_LOG10 = 113, - VECTOR_OP_LOG1P = 114, - VECTOR_OP_POW = 115, - VECTOR_OP_EXP = 116, - VECTOR_OP_EXPM1 = 117, - VECTOR_OP_HYPOT = 118, - VECTOR_OP_SADD = 119, VECTOR_OP_SSUB = 120, VECTOR_OP_SUADD = 121, VECTOR_OP_SUSUB = 122, VECTOR_OP_UMIN = 123, VECTOR_OP_UMAX = 124, - - VECTOR_OP_MATH_START = VECTOR_OP_TAN, - VECTOR_OP_MATH_END = VECTOR_OP_HYPOT, - NUM_VECTOR_OP_MATH = VECTOR_OP_MATH_END - VECTOR_OP_MATH_START + 1 }; enum { @@ -147,8 +123,6 @@ class VectorSupport : AllStatic { MODE_BITS_COERCED_LONG_TO_MASK = 1 }; - static const char* mathname[VectorSupport::NUM_VECTOR_OP_MATH]; - static int vop2ideal(jint vop, BasicType bt); static bool has_scalar_op(jint id); static bool is_unsigned_op(jint id); diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index e08a5ba5ebd12..2e277ffadabd0 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -728,11 +728,11 @@ WB_ENTRY(void, WB_NMTFree(JNIEnv* env, jobject o, jlong mem)) WB_END WB_ENTRY(jlong, WB_NMTReserveMemory(JNIEnv* env, jobject o, jlong size)) - return (jlong)(uintptr_t)os::reserve_memory(size, false, mtTest); + return (jlong)(uintptr_t)os::reserve_memory(size, mtTest); WB_END WB_ENTRY(jlong, WB_NMTAttemptReserveMemoryAt(JNIEnv* env, jobject o, jlong addr, jlong size)) - return (jlong)(uintptr_t)os::attempt_reserve_memory_at((char*)(uintptr_t)addr, (size_t)size, false, mtTest); + return (jlong)(uintptr_t)os::attempt_reserve_memory_at((char*)(uintptr_t)addr, (size_t)size, mtTest); WB_END WB_ENTRY(void, WB_NMTCommitMemory(JNIEnv* env, jobject o, jlong addr, jlong size)) @@ -1524,7 +1524,7 @@ WB_ENTRY(void, WB_ReadReservedMemory(JNIEnv* env, jobject o)) static char c; static volatile char* p; - p = os::reserve_memory(os::vm_allocation_granularity()); + p = os::reserve_memory(os::vm_allocation_granularity(), mtTest); if (p == nullptr) { THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "Failed to reserve memory"); } @@ -1533,7 +1533,7 @@ WB_ENTRY(void, WB_ReadReservedMemory(JNIEnv* env, jobject o)) WB_END WB_ENTRY(jstring, WB_GetCPUFeatures(JNIEnv* env, jobject o)) - const char* features = VM_Version::features_string(); + const char* features = VM_Version::cpu_info_string(); ThreadToNativeFromVM ttn(thread); jstring features_string = env->NewStringUTF(features); diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp index e95c96b4e9c25..c0667d739ce7f 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.cpp +++ b/src/hotspot/share/runtime/abstract_vm_version.cpp @@ -34,6 +34,7 @@ const char* Abstract_VM_Version::_s_internal_vm_info_string = Abstract_VM_Versio uint64_t Abstract_VM_Version::_features = 0; const char* Abstract_VM_Version::_features_string = ""; +const char* Abstract_VM_Version::_cpu_info_string = ""; uint64_t Abstract_VM_Version::_cpu_features = 0; #ifndef SUPPORTS_NATIVE_CX8 @@ -340,6 +341,19 @@ void Abstract_VM_Version::insert_features_names(char* buf, size_t buflen, const } } +const char* Abstract_VM_Version::extract_features_string(const char* cpu_info_string, + size_t cpu_info_string_len, + size_t features_offset) { + assert(features_offset <= cpu_info_string_len, ""); + if (features_offset < cpu_info_string_len) { + assert(cpu_info_string[features_offset + 0] == ',', ""); + assert(cpu_info_string[features_offset + 1] == ' ', ""); + return cpu_info_string + features_offset + 2; // skip initial ", " + } else { + return ""; // empty + } +} + bool Abstract_VM_Version::print_matching_lines_from_file(const char* filename, outputStream* st, const char* keywords_to_match[]) { char line[500]; FILE* fp = os::fopen(filename, "r"); diff --git a/src/hotspot/share/runtime/abstract_vm_version.hpp b/src/hotspot/share/runtime/abstract_vm_version.hpp index 8cfc7031f97ae..6f1b886bc9878 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.hpp +++ b/src/hotspot/share/runtime/abstract_vm_version.hpp @@ -58,6 +58,8 @@ class Abstract_VM_Version: AllStatic { static uint64_t _features; static const char* _features_string; + static const char* _cpu_info_string; + // Original CPU feature flags, not affected by VM settings. static uint64_t _cpu_features; @@ -128,7 +130,11 @@ class Abstract_VM_Version: AllStatic { static uint64_t features() { return _features; } static const char* features_string() { return _features_string; } + static const char* cpu_info_string() { return _cpu_info_string; } static void insert_features_names(char* buf, size_t buflen, const char* features_names[]); + static const char* extract_features_string(const char* cpu_info_string, + size_t cpu_info_string_len, + size_t features_offset); static VirtualizationType get_detected_virtualization() { return _detected_virtualization; diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index b3e0600df3458..69d37a11e45ab 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -332,7 +332,6 @@ bool Arguments::internal_module_property_helper(const char* property, bool check if (strncmp(property, MODULE_PROPERTY_PREFIX, MODULE_PROPERTY_PREFIX_LEN) == 0) { const char* property_suffix = property + MODULE_PROPERTY_PREFIX_LEN; if (matches_property_suffix(property_suffix, ADDREADS, ADDREADS_LEN) || - matches_property_suffix(property_suffix, ADDOPENS, ADDOPENS_LEN) || matches_property_suffix(property_suffix, PATCH, PATCH_LEN) || matches_property_suffix(property_suffix, LIMITMODS, LIMITMODS_LEN) || matches_property_suffix(property_suffix, UPGRADE_PATH, UPGRADE_PATH_LEN) || @@ -343,6 +342,7 @@ bool Arguments::internal_module_property_helper(const char* property, bool check if (!check_for_cds) { // CDS notes: these properties are supported by CDS archived full module graph. if (matches_property_suffix(property_suffix, ADDEXPORTS, ADDEXPORTS_LEN) || + matches_property_suffix(property_suffix, ADDOPENS, ADDOPENS_LEN) || matches_property_suffix(property_suffix, PATH, PATH_LEN) || matches_property_suffix(property_suffix, ADDMODS, ADDMODS_LEN) || matches_property_suffix(property_suffix, ENABLE_NATIVE_ACCESS, ENABLE_NATIVE_ACCESS_LEN)) { @@ -3786,11 +3786,6 @@ jint Arguments::apply_ergo() { } } FLAG_SET_DEFAULT(EnableVectorAggressiveReboxing, false); - - if (!FLAG_IS_DEFAULT(UseVectorStubs) && UseVectorStubs) { - warning("Disabling UseVectorStubs since EnableVectorSupport is turned off."); - } - FLAG_SET_DEFAULT(UseVectorStubs, false); } #endif // COMPILER2_OR_JVMCI diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 12a75c6c62e76..d20cfde09cab0 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -565,10 +565,50 @@ void FreezeBase::copy_to_chunk(intptr_t* from, intptr_t* to, int size) { #endif } +static void assert_frames_in_continuation_are_safe(JavaThread* thread) { +#ifdef ASSERT + StackWatermark* watermark = StackWatermarkSet::get(thread, StackWatermarkKind::gc); + if (watermark == nullptr) { + return; + } + ContinuationEntry* ce = thread->last_continuation(); + RegisterMap map(thread, + RegisterMap::UpdateMap::include, + RegisterMap::ProcessFrames::skip, + RegisterMap::WalkContinuation::skip); + map.set_include_argument_oops(false); + for (frame f = thread->last_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map)) { + watermark->assert_is_frame_safe(f); + } +#endif // ASSERT +} + +#ifdef ASSERT +static bool monitors_on_stack(JavaThread* thread) { + assert_frames_in_continuation_are_safe(thread); + ContinuationEntry* ce = thread->last_continuation(); + RegisterMap map(thread, + RegisterMap::UpdateMap::include, + RegisterMap::ProcessFrames::skip, + RegisterMap::WalkContinuation::skip); + map.set_include_argument_oops(false); + for (frame f = thread->last_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map)) { + if ((f.is_interpreted_frame() && ContinuationHelper::InterpretedFrame::is_owning_locks(f)) || + (f.is_compiled_frame() && ContinuationHelper::CompiledFrame::is_owning_locks(map.thread(), &map, f)) || + (f.is_native_frame() && ContinuationHelper::NativeFrame::is_owning_locks(map.thread(), f))) { + return true; + } + } + return false; +} +#endif // ASSERT + // Called _after_ the last possible safepoint during the freeze operation (chunk allocation) void FreezeBase::unwind_frames() { ContinuationEntry* entry = _cont.entry(); entry->flush_stack_processing(_thread); + assert_frames_in_continuation_are_safe(_thread); + assert(LockingMode != LM_LEGACY || !monitors_on_stack(_thread), "unexpected monitors on stack"); set_anchor_to_entry(_thread, entry); } @@ -1621,23 +1661,6 @@ static void jvmti_mount_end(JavaThread* current, ContinuationWrapper& cont, fram #endif // INCLUDE_JVMTI #ifdef ASSERT -static bool monitors_on_stack(JavaThread* thread) { - ContinuationEntry* ce = thread->last_continuation(); - RegisterMap map(thread, - RegisterMap::UpdateMap::include, - RegisterMap::ProcessFrames::include, - RegisterMap::WalkContinuation::skip); - map.set_include_argument_oops(false); - for (frame f = thread->last_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map)) { - if ((f.is_interpreted_frame() && ContinuationHelper::InterpretedFrame::is_owning_locks(f)) || - (f.is_compiled_frame() && ContinuationHelper::CompiledFrame::is_owning_locks(map.thread(), &map, f)) || - (f.is_native_frame() && ContinuationHelper::NativeFrame::is_owning_locks(map.thread(), f))) { - return true; - } - } - return false; -} - // There are no interpreted frames if we're not called from the interpreter and we haven't ancountered an i2c // adapter or called Deoptimization::unpack_frames. As for native frames, upcalls from JNI also go through the // interpreter (see JavaCalls::call_helper), while the UpcallLinker explicitly sets cont_fastpath. @@ -1714,8 +1737,6 @@ static inline freeze_result freeze_internal(JavaThread* current, intptr_t* const assert(entry->is_virtual_thread() == (entry->scope(current) == java_lang_VirtualThread::vthread_scope()), ""); - assert(LockingMode != LM_LEGACY || (monitors_on_stack(current) == ((current->held_monitor_count() - current->jni_monitor_count()) > 0)), - "Held monitor count and locks on stack invariant: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count()); assert(LockingMode == LM_LEGACY || (current->held_monitor_count() == 0 && current->jni_monitor_count() == 0), "Held monitor count should only be used for LM_LEGACY: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count()); diff --git a/src/hotspot/share/runtime/flags/flagSetting.hpp b/src/hotspot/share/runtime/flags/flagSetting.hpp index 6611081235e20..0f75636621a7d 100644 --- a/src/hotspot/share/runtime/flags/flagSetting.hpp +++ b/src/hotspot/share/runtime/flags/flagSetting.hpp @@ -40,6 +40,7 @@ typedef AutoModifyRestore FlagSetting; typedef AutoModifyRestore UIntFlagSetting; +typedef AutoModifyRestore IntFlagSetting; // Legacy use of FLAG_GUARD. Retained in the code to help identify use-cases // that should be addressed when this file is removed. diff --git a/src/hotspot/share/runtime/handles.cpp b/src/hotspot/share/runtime/handles.cpp index f747b4dc76e57..cd3bd3eb3e0c9 100644 --- a/src/hotspot/share/runtime/handles.cpp +++ b/src/hotspot/share/runtime/handles.cpp @@ -133,7 +133,7 @@ void HandleMark::initialize(Thread* thread) { _hwm = _area->_hwm; _max = _area->_max; _size_in_bytes = _area->_size_in_bytes; - debug_only(_area->_handle_mark_nesting++); + DEBUG_ONLY(_area->_handle_mark_nesting++); assert(_area->_handle_mark_nesting > 0, "must stack allocate HandleMarks"); // Link this in the thread diff --git a/src/hotspot/share/runtime/handles.hpp b/src/hotspot/share/runtime/handles.hpp index 8ed16d33a2f06..d2020e34121cc 100644 --- a/src/hotspot/share/runtime/handles.hpp +++ b/src/hotspot/share/runtime/handles.hpp @@ -188,8 +188,8 @@ class HandleArea: public Arena { public: // Constructor HandleArea(MemTag mem_tag, HandleArea* prev) : Arena(mem_tag, Tag::tag_ha, Chunk::tiny_size) { - debug_only(_handle_mark_nesting = 0); - debug_only(_no_handle_mark_nesting = 0); + DEBUG_ONLY(_handle_mark_nesting = 0); + DEBUG_ONLY(_no_handle_mark_nesting = 0); _prev = prev; } @@ -212,7 +212,7 @@ class HandleArea: public Arena { // Garbage collection support void oops_do(OopClosure* f); - debug_only(bool no_handle_mark_active() { return _no_handle_mark_nesting > 0; }) + DEBUG_ONLY(bool no_handle_mark_active() { return _no_handle_mark_nesting > 0; }) }; diff --git a/src/hotspot/share/runtime/handles.inline.hpp b/src/hotspot/share/runtime/handles.inline.hpp index 669a940eca961..4d3dd527c0d8d 100644 --- a/src/hotspot/share/runtime/handles.inline.hpp +++ b/src/hotspot/share/runtime/handles.inline.hpp @@ -80,7 +80,7 @@ inline void HandleMark::push() { // This is intentionally a NOP. pop_and_restore will reset // values to the HandleMark further down the stack, typically // in JavaCalls::call_helper. - debug_only(_area->_handle_mark_nesting++); + DEBUG_ONLY(_area->_handle_mark_nesting++); } inline void HandleMark::pop_and_restore() { @@ -95,7 +95,7 @@ inline void HandleMark::pop_and_restore() { _area->_chunk = _chunk; _area->_hwm = _hwm; _area->_max = _max; - debug_only(_area->_handle_mark_nesting--); + DEBUG_ONLY(_area->_handle_mark_nesting--); } inline HandleMarkCleaner::HandleMarkCleaner(Thread* thread) { diff --git a/src/hotspot/share/runtime/icache.cpp b/src/hotspot/share/runtime/icache.cpp index 4f5006963ab43..34d5a7e055b47 100644 --- a/src/hotspot/share/runtime/icache.cpp +++ b/src/hotspot/share/runtime/icache.cpp @@ -25,22 +25,35 @@ #include "code/codeBlob.hpp" #include "memory/resourceArea.hpp" #include "runtime/icache.hpp" +#include "runtime/java.hpp" #include "utilities/align.hpp" // The flush stub function address AbstractICache::flush_icache_stub_t AbstractICache::_flush_icache_stub = nullptr; -void AbstractICache::initialize() { +void AbstractICache::initialize(int phase) { // Making this stub must be FIRST use of assembler ResourceMark rm; - BufferBlob* b = BufferBlob::create("flush_icache_stub", ICache::stub_size); + const char* stub_name = nullptr; + switch (phase) { + case 1: + stub_name = "flush_icache_initial_stub"; + break; + case 2: + stub_name = "flush_icache_final_stub"; + break; + default: + ShouldNotReachHere(); + } + + BufferBlob* b = BufferBlob::create(stub_name, ICache::stub_size); if (b == nullptr) { - vm_exit_out_of_memory(ICache::stub_size, OOM_MALLOC_ERROR, "CodeCache: no space for flush_icache_stub"); + vm_exit_out_of_memory(ICache::stub_size, OOM_MALLOC_ERROR, "CodeCache: no space for %s", stub_name); } CodeBuffer c(b); - ICacheStubGenerator g(&c); + ICacheStubGenerator g(stub_name, &c); g.generate_icache_flush(&_flush_icache_stub); // The first use of flush_icache_stub must apply it to itself. @@ -106,5 +119,9 @@ void AbstractICache::invalidate_range(address start, int nbytes) { // For init.cpp void icache_init() { - ICache::initialize(); + ICache::initialize(1); +} + +void icache_init2() { + ICache::initialize(2); } diff --git a/src/hotspot/share/runtime/icache.hpp b/src/hotspot/share/runtime/icache.hpp index d273372a877d6..e442c4d88e00a 100644 --- a/src/hotspot/share/runtime/icache.hpp +++ b/src/hotspot/share/runtime/icache.hpp @@ -60,7 +60,13 @@ class AbstractICache : AllStatic { log2_line_size = 0 // log2(line_size) }; - static void initialize(); + // Initialization phases: + // 1 = Initial phase, nothing is known about the machine features. + // The stub generated at this phase must use the most basic mechanism, + // until optimized final stub is generated. + // 2 = Final stub that uses the optimized flush mechanism. Happens after + // CPU feature detection determines which mechanism is usable. + static void initialize(int phase); static void invalidate_word(address addr); static void invalidate_range(address start, int nbytes); }; @@ -72,8 +78,11 @@ class AbstractICache : AllStatic { #include CPU_HEADER(icache) class ICacheStubGenerator : public StubCodeGenerator { + private: + const char* _stub_name; + public: - ICacheStubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} + ICacheStubGenerator(const char* stub_name, CodeBuffer *c) : StubCodeGenerator(c), _stub_name(stub_name) {} // Generate the icache flush stub. // diff --git a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp index 417bba6c3fa0b..48e01f82025b6 100644 --- a/src/hotspot/share/runtime/init.cpp +++ b/src/hotspot/share/runtime/init.cpp @@ -24,6 +24,7 @@ #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" +#include "code/aotCodeCache.hpp" #include "compiler/compiler_globals.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gcHeapSummary.hpp" @@ -65,6 +66,7 @@ void classLoader_init1(); void compilationPolicy_init(); void codeCache_init(); void VM_Version_init(); +void icache_init2(); void initial_stubs_init(); jint universe_init(); // depends on codeCache_init and initial_stubs_init @@ -125,6 +127,7 @@ jint init_globals() { compilationPolicy_init(); codeCache_init(); VM_Version_init(); // depends on codeCache_init for emitting code + icache_init2(); // depends on VM_Version for choosing the mechanism // stub routines in initial blob are referenced by later generated code initial_stubs_init(); // stack overflow exception blob is referenced by the interpreter @@ -141,7 +144,7 @@ jint init_globals() { LSAN_REGISTER_ROOT_REGION(summary.start(), summary.reserved_size()); } #endif // LEAK_SANITIZER - + AOTCodeCache::init2(); // depends on universe_init AsyncLogWriter::initialize(); gc_barrier_stubs_init(); // depends on universe_init, must be before interpreter_init continuations_init(); // must precede continuation stub generation @@ -154,6 +157,8 @@ jint init_globals() { InterfaceSupport_init(); VMRegImpl::set_regName(); // need this before generate_stubs (for printing oop maps). SharedRuntime::generate_stubs(); + AOTCodeCache::init_shared_blobs_table(); // need this after generate_stubs + SharedRuntime::init_adapter_library(); // do this after AOTCodeCache::init_shared_blobs_table return JNI_OK; } diff --git a/src/hotspot/share/runtime/interfaceSupport.inline.hpp b/src/hotspot/share/runtime/interfaceSupport.inline.hpp index 403ff1d9ea292..c52c2664faa51 100644 --- a/src/hotspot/share/runtime/interfaceSupport.inline.hpp +++ b/src/hotspot/share/runtime/interfaceSupport.inline.hpp @@ -259,12 +259,12 @@ class VMNativeEntryWrapper { // in the codecache. #define VM_LEAF_BASE(result_type, header) \ - debug_only(NoHandleMark __hm;) \ + DEBUG_ONLY(NoHandleMark __hm;) \ os::verify_stack_alignment(); \ /* begin of body */ #define VM_ENTRY_BASE_FROM_LEAF(result_type, header, thread) \ - debug_only(ResetNoHandleMark __rnhm;) \ + DEBUG_ONLY(ResetNoHandleMark __rnhm;) \ HandleMarkCleaner __hm(thread); \ JavaThread* THREAD = thread; /* For exception macros. */ \ os::verify_stack_alignment(); \ @@ -286,7 +286,7 @@ class VMNativeEntryWrapper { MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \ ThreadInVMfromJava __tiv(current); \ VM_ENTRY_BASE(result_type, header, current) \ - debug_only(VMEntryWrapper __vew;) + DEBUG_ONLY(VMEntryWrapper __vew;) // JRT_LEAF currently can be called from either _thread_in_Java or // _thread_in_native mode. @@ -305,7 +305,7 @@ class VMNativeEntryWrapper { #define JRT_LEAF(result_type, header) \ result_type header { \ VM_LEAF_BASE(result_type, header) \ - debug_only(NoSafepointVerifier __nsv;) + DEBUG_ONLY(NoSafepointVerifier __nsv;) #define JRT_ENTRY_NO_ASYNC(result_type, header) \ @@ -314,7 +314,7 @@ class VMNativeEntryWrapper { MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \ ThreadInVMfromJava __tiv(current, false /* check asyncs */); \ VM_ENTRY_BASE(result_type, header, current) \ - debug_only(VMEntryWrapper __vew;) + DEBUG_ONLY(VMEntryWrapper __vew;) // Same as JRT Entry but allows for return value after the safepoint // to get back into Java from the VM @@ -329,14 +329,14 @@ class VMNativeEntryWrapper { assert(current == JavaThread::current(), "Must be"); \ ThreadInVMfromJava __tiv(current); \ JavaThread* THREAD = current; /* For exception macros. */ \ - debug_only(VMEntryWrapper __vew;) + DEBUG_ONLY(VMEntryWrapper __vew;) #define JRT_BLOCK_NO_ASYNC \ { \ assert(current == JavaThread::current(), "Must be"); \ ThreadInVMfromJava __tiv(current, false /* check asyncs */); \ JavaThread* THREAD = current; /* For exception macros. */ \ - debug_only(VMEntryWrapper __vew;) + DEBUG_ONLY(VMEntryWrapper __vew;) #define JRT_BLOCK_END } @@ -360,7 +360,7 @@ extern "C" { \ assert(thread == Thread::current(), "JNIEnv is only valid in same thread"); \ MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \ ThreadInVMfromNative __tiv(thread); \ - debug_only(VMNativeEntryWrapper __vew;) \ + DEBUG_ONLY(VMNativeEntryWrapper __vew;) \ VM_ENTRY_BASE(result_type, header, thread) @@ -385,7 +385,7 @@ extern "C" { \ JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \ ThreadInVMfromNative __tiv(thread); \ - debug_only(VMNativeEntryWrapper __vew;) \ + DEBUG_ONLY(VMNativeEntryWrapper __vew;) \ VM_ENTRY_BASE(result_type, header, thread) @@ -395,7 +395,7 @@ extern "C" { \ JavaThread* thread = JavaThread::current(); \ MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \ ThreadInVMfromNative __tiv(thread); \ - debug_only(VMNativeEntryWrapper __vew;) \ + DEBUG_ONLY(VMNativeEntryWrapper __vew;) \ VM_ENTRY_BASE(result_type, header, thread) diff --git a/src/hotspot/share/runtime/javaCalls.cpp b/src/hotspot/share/runtime/javaCalls.cpp index 012cea19bb0f4..04bd08710739d 100644 --- a/src/hotspot/share/runtime/javaCalls.cpp +++ b/src/hotspot/share/runtime/javaCalls.cpp @@ -91,7 +91,7 @@ JavaCallWrapper::JavaCallWrapper(const methodHandle& callee_method, Handle recei _anchor.copy(_thread->frame_anchor()); _thread->frame_anchor()->clear(); - debug_only(_thread->inc_java_call_counter()); + DEBUG_ONLY(_thread->inc_java_call_counter()); _thread->set_active_handles(new_handles); // install new handle block and reset Java frame linkage MACOS_AARCH64_ONLY(_thread->enable_wx(WXExec)); @@ -109,7 +109,7 @@ JavaCallWrapper::~JavaCallWrapper() { _thread->frame_anchor()->zap(); - debug_only(_thread->dec_java_call_counter()); + DEBUG_ONLY(_thread->dec_java_call_counter()); // Old thread-local info. has been restored. We are not back in the VM. ThreadStateTransition::transition_from_java(_thread, _thread_in_vm); diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index e4f377a121799..8a6da52d762d7 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -553,7 +553,7 @@ void JavaThread::interrupt() { // All callers should have 'this' thread protected by a // ThreadsListHandle so that it cannot terminate and deallocate // itself. - debug_only(check_for_dangling_thread_pointer(this);) + DEBUG_ONLY(check_for_dangling_thread_pointer(this);) // For Windows _interrupt_event WINDOWS_ONLY(osthread()->set_interrupted(true);) @@ -569,7 +569,7 @@ void JavaThread::interrupt() { } bool JavaThread::is_interrupted(bool clear_interrupted) { - debug_only(check_for_dangling_thread_pointer(this);) + DEBUG_ONLY(check_for_dangling_thread_pointer(this);) if (_threadObj.peek() == nullptr) { // If there is no j.l.Thread then it is impossible to have diff --git a/src/hotspot/share/runtime/jfieldIDWorkaround.hpp b/src/hotspot/share/runtime/jfieldIDWorkaround.hpp index e44fb0648134c..68db2e36d4575 100644 --- a/src/hotspot/share/runtime/jfieldIDWorkaround.hpp +++ b/src/hotspot/share/runtime/jfieldIDWorkaround.hpp @@ -157,7 +157,7 @@ class jfieldIDWorkaround: AllStatic { static jfieldID to_jfieldID(InstanceKlass* k, int offset, bool is_static) { if (is_static) { JNIid *id = k->jni_id_for(offset); - debug_only(id->set_is_static_field_id()); + DEBUG_ONLY(id->set_is_static_field_id()); return jfieldIDWorkaround::to_static_jfieldID(id); } else { return jfieldIDWorkaround::to_instance_jfieldID(k, offset); diff --git a/src/hotspot/share/runtime/jniHandles.cpp b/src/hotspot/share/runtime/jniHandles.cpp index b4fc32947dd3e..e7564467a812f 100644 --- a/src/hotspot/share/runtime/jniHandles.cpp +++ b/src/hotspot/share/runtime/jniHandles.cpp @@ -342,9 +342,9 @@ JNIHandleBlock* JNIHandleBlock::allocate_block(JavaThread* thread, AllocFailType block->_next = nullptr; block->_pop_frame_link = nullptr; // _last, _free_list & _allocate_before_rebuild initialized in allocate_handle - debug_only(block->_last = nullptr); - debug_only(block->_free_list = nullptr); - debug_only(block->_allocate_before_rebuild = -1); + DEBUG_ONLY(block->_last = nullptr); + DEBUG_ONLY(block->_free_list = nullptr); + DEBUG_ONLY(block->_allocate_before_rebuild = -1); return block; } diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index faed0d5f95299..8cb239636d2d0 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -123,6 +123,8 @@ Mutex* CodeHeapStateAnalytics_lock = nullptr; Mutex* ExternalsRecorder_lock = nullptr; +Mutex* AOTCodeCStrings_lock = nullptr; + Monitor* ContinuationRelativize_lock = nullptr; Mutex* Metaspace_lock = nullptr; @@ -319,6 +321,8 @@ void mutex_init() { // tty_lock is held when printing nmethod and its relocations which use this lock. MUTEX_DEFL(ExternalsRecorder_lock , PaddedMutex , tty_lock); + MUTEX_DEFL(AOTCodeCStrings_lock , PaddedMutex , tty_lock); + MUTEX_DEFL(Threads_lock , PaddedMonitor, CompileThread_lock, true); MUTEX_DEFL(Compile_lock , PaddedMutex , MethodCompileQueue_lock); MUTEX_DEFL(JNICritical_lock , PaddedMonitor, AdapterHandlerLibrary_lock); // used for JNI critical regions diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 3bd01b575c4ea..c307f70ba808a 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -148,6 +148,8 @@ extern Mutex* CodeHeapStateAnalytics_lock; // lock print functions against extern Mutex* ExternalsRecorder_lock; // used to guard access to the external addresses table +extern Mutex* AOTCodeCStrings_lock; // used to guard access to the AOT code C strings table + extern Monitor* ContinuationRelativize_lock; #if INCLUDE_JVMCI diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 48e068714f59e..4489922569135 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -1805,7 +1805,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // returns because of a timeout of interrupt. Contention is exceptionally rare // so we use a simple spin-lock instead of a heavier-weight blocking lock. - Thread::SpinAcquire(&_wait_set_lock, "wait_set - add"); + Thread::SpinAcquire(&_wait_set_lock); add_waiter(&node); Thread::SpinRelease(&_wait_set_lock); @@ -1864,7 +1864,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // That is, we fail toward safety. if (node.TState == ObjectWaiter::TS_WAIT) { - Thread::SpinAcquire(&_wait_set_lock, "wait_set - unlink"); + Thread::SpinAcquire(&_wait_set_lock); if (node.TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(&node); // unlink from wait_set assert(!node._notified, "invariant"); @@ -1980,7 +1980,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { bool ObjectMonitor::notify_internal(JavaThread* current) { bool did_notify = false; - Thread::SpinAcquire(&_wait_set_lock, "wait_set - notify"); + Thread::SpinAcquire(&_wait_set_lock); ObjectWaiter* iterator = dequeue_waiter(); if (iterator != nullptr) { guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant"); @@ -2120,7 +2120,7 @@ void ObjectMonitor::vthread_wait(JavaThread* current, jlong millis) { // returns because of a timeout or interrupt. Contention is exceptionally rare // so we use a simple spin-lock instead of a heavier-weight blocking lock. - Thread::SpinAcquire(&_wait_set_lock, "wait_set - add"); + Thread::SpinAcquire(&_wait_set_lock); add_waiter(node); Thread::SpinRelease(&_wait_set_lock); @@ -2143,7 +2143,7 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node // need to check if we were interrupted or the wait timed-out, and // in that case remove ourselves from the _wait_set queue. if (node->TState == ObjectWaiter::TS_WAIT) { - Thread::SpinAcquire(&_wait_set_lock, "wait_set - unlink"); + Thread::SpinAcquire(&_wait_set_lock); if (node->TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(node); // unlink from wait_set assert(!node->_notified, "invariant"); diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index e4bab3410c1a7..8e85c0a8c021f 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -76,7 +76,9 @@ #include "utilities/defaultStream.hpp" #include "utilities/events.hpp" #include "utilities/fastrand.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include "utilities/powerOfTwo.hpp" #ifdef LINUX @@ -117,7 +119,7 @@ int os::snprintf_checked(char* buf, size_t len, const char* fmt, ...) { } int os::vsnprintf(char* buf, size_t len, const char* fmt, va_list args) { - ALLOW_C_FUNCTION(::vsnprintf, int result = ::vsnprintf(buf, len, fmt, args);) + int result = permit_forbidden_function::vsnprintf(buf, len, fmt, args); // If an encoding error occurred (result < 0) then it's not clear // whether the buffer is NUL terminated, so ensure it is. if ((result < 0) && (len > 0)) { @@ -241,7 +243,7 @@ char* os::iso8601_time(jlong milliseconds_since_19700101, char* buffer, size_t b } OSReturn os::set_priority(Thread* thread, ThreadPriority p) { - debug_only(Thread::check_for_dangling_thread_pointer(thread);) + DEBUG_ONLY(Thread::check_for_dangling_thread_pointer(thread);) if ((p >= MinPriority && p <= MaxPriority) || (p == CriticalPriority && thread->is_ConcurrentGC_thread())) { @@ -596,7 +598,7 @@ char *os::strdup(const char *str, MemTag mem_tag) { size_t size = strlen(str); char *dup_str = (char *)malloc(size + 1, mem_tag); if (dup_str == nullptr) return nullptr; - strcpy(dup_str, str); + memcpy(dup_str, str, size + 1); return dup_str; } @@ -654,7 +656,7 @@ void* os::malloc(size_t size, MemTag mem_tag, const NativeCallStack& stack) { return nullptr; } - ALLOW_C_FUNCTION(::malloc, void* const outer_ptr = ::malloc(outer_size);) + void* const outer_ptr = permit_forbidden_function::malloc(outer_size); if (outer_ptr == nullptr) { return nullptr; } @@ -721,7 +723,7 @@ void* os::realloc(void *memblock, size_t size, MemTag mem_tag, const NativeCallS header->mark_block_as_dead(); // the real realloc - ALLOW_C_FUNCTION(::realloc, void* const new_outer_ptr = ::realloc(header, new_outer_size);) + void* const new_outer_ptr = permit_forbidden_function::realloc(header, new_outer_size); if (new_outer_ptr == nullptr) { // realloc(3) failed and the block still exists. @@ -749,7 +751,7 @@ void* os::realloc(void *memblock, size_t size, MemTag mem_tag, const NativeCallS } else { // NMT disabled. - ALLOW_C_FUNCTION(::realloc, rc = ::realloc(memblock, size);) + rc = permit_forbidden_function::realloc(memblock, size); if (rc == nullptr) { return nullptr; } @@ -777,7 +779,7 @@ void os::free(void *memblock) { // When NMT is enabled this checks for heap overwrites, then deaccounts the old block. void* const old_outer_ptr = MemTracker::record_free(memblock); - ALLOW_C_FUNCTION(::free, ::free(old_outer_ptr);) + permit_forbidden_function::free(old_outer_ptr); } void os::init_random(unsigned int initval) { @@ -1166,7 +1168,7 @@ void os::print_cpu_info(outputStream* st, char* buf, size_t buflen) { // We access the raw value here because the assert in the accessor will // fail if the crash occurs before initialization of this value. st->print(" (initial active %d)", _initial_active_processor_count); - st->print(" %s", VM_Version::features_string()); + st->print(" %s", VM_Version::cpu_info_string()); st->cr(); pd_print_cpu_info(st, buf, buflen); } @@ -1966,7 +1968,7 @@ bool os::create_stack_guard_pages(char* addr, size_t bytes) { return os::pd_create_stack_guard_pages(addr, bytes); } -char* os::reserve_memory(size_t bytes, bool executable, MemTag mem_tag) { +char* os::reserve_memory(size_t bytes, MemTag mem_tag, bool executable) { char* result = pd_reserve_memory(bytes, executable); if (result != nullptr) { MemTracker::record_virtual_memory_reserve(result, bytes, CALLER_PC, mem_tag); @@ -1977,7 +1979,7 @@ char* os::reserve_memory(size_t bytes, bool executable, MemTag mem_tag) { return result; } -char* os::attempt_reserve_memory_at(char* addr, size_t bytes, bool executable, MemTag mem_tag) { +char* os::attempt_reserve_memory_at(char* addr, size_t bytes, MemTag mem_tag, bool executable) { char* result = SimulateFullAddressSpace ? nullptr : pd_attempt_reserve_memory_at(addr, bytes, executable); if (result != nullptr) { MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC, mem_tag); @@ -2183,7 +2185,7 @@ char* os::attempt_reserve_memory_between(char* min, char* max, size_t bytes, siz assert(is_aligned(result, alignment), "alignment invalid (" ERRFMT ")", ERRFMTARGS); log_trace(os, map)(ERRFMT, ERRFMTARGS); log_debug(os, map)("successfully attached at " PTR_FORMAT, p2i(result)); - MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); + MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC, mtNone); } else { log_debug(os, map)("failed to attach anywhere in [" PTR_FORMAT "-" PTR_FORMAT ")", p2i(min), p2i(max)); } @@ -2251,6 +2253,11 @@ void os::commit_memory_or_exit(char* addr, size_t size, size_t alignment_hint, MemTracker::record_virtual_memory_commit((address)addr, size, CALLER_PC); } +// The scope of NmtVirtualMemoryLocker covers both pd_uncommit_memory and record_virtual_memory_uncommit because +// these operations must happen atomically to avoid races causing NMT to fall out os sync with the OS reality. +// We do not have the same lock protection for pd_commit_memory and record_virtual_memory_commit. +// We assume that there is some external synchronization that prevents a region from being uncommitted +// before it is finished being committed. bool os::uncommit_memory(char* addr, size_t bytes, bool executable) { assert_nonempty_range(addr, bytes); bool res; @@ -2273,6 +2280,11 @@ bool os::uncommit_memory(char* addr, size_t bytes, bool executable) { return res; } +// The scope of NmtVirtualMemoryLocker covers both pd_release_memory and record_virtual_memory_release because +// these operations must happen atomically to avoid races causing NMT to fall out os sync with the OS reality. +// We do not have the same lock protection for pd_reserve_memory and record_virtual_memory_reserve. +// We assume that there is some external synchronization that prevents a region from being released +// before it is finished being reserved. bool os::release_memory(char* addr, size_t bytes) { assert_nonempty_range(addr, bytes); bool res; @@ -2350,8 +2362,8 @@ char* os::attempt_map_memory_to_file_at(char* addr, size_t bytes, int file_desc, } char* os::map_memory(int fd, const char* file_name, size_t file_offset, - char *addr, size_t bytes, bool read_only, - bool allow_exec, MemTag mem_tag) { + char *addr, size_t bytes, MemTag mem_tag, + bool read_only, bool allow_exec) { char* result = pd_map_memory(fd, file_name, file_offset, addr, bytes, read_only, allow_exec); if (result != nullptr) { MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC, mem_tag); @@ -2389,7 +2401,7 @@ char* os::reserve_memory_special(size_t size, size_t alignment, size_t page_size char* result = pd_reserve_memory_special(size, alignment, page_size, addr, executable); if (result != nullptr) { // The memory is committed - MemTracker::record_virtual_memory_reserve_and_commit((address)result, size, CALLER_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)result, size, CALLER_PC, mtNone); log_debug(os, map)("Reserved and committed " RANGEFMT, RANGEFMTARGS(result, size)); } else { log_info(os, map)("Reserve and commit failed (%zu bytes)", size); diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 16a74cd7ea957..dde80806912f5 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -457,14 +457,14 @@ class os: AllStatic { inline static size_t cds_core_region_alignment(); // Reserves virtual memory. - static char* reserve_memory(size_t bytes, bool executable = false, MemTag mem_tag = mtNone); + static char* reserve_memory(size_t bytes, MemTag mem_tag, bool executable = false); // Reserves virtual memory that starts at an address that is aligned to 'alignment'. - static char* reserve_memory_aligned(size_t size, size_t alignment, bool executable = false); + static char* reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag, bool executable = false); // Attempts to reserve the virtual memory at [addr, addr + bytes). // Does not overwrite existing mappings. - static char* attempt_reserve_memory_at(char* addr, size_t bytes, bool executable = false, MemTag mem_tag = mtNone); + static char* attempt_reserve_memory_at(char* addr, size_t bytes, MemTag mem_tag, bool executable = false); // Given an address range [min, max), attempts to reserve memory within this area, with the given alignment. // If randomize is true, the location will be randomized. @@ -516,16 +516,16 @@ class os: AllStatic { static int create_file_for_heap(const char* dir); // Map memory to the file referred by fd. This function is slightly different from map_memory() // and is added to be used for implementation of -XX:AllocateHeapAt - static char* map_memory_to_file(size_t size, int fd, MemTag mem_tag = mtNone); - static char* map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MemTag mem_tag = mtNone); + static char* map_memory_to_file(size_t size, int fd, MemTag mem_tag); + static char* map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MemTag mem_tag); static char* map_memory_to_file(char* base, size_t size, int fd); - static char* attempt_map_memory_to_file_at(char* base, size_t size, int fd, MemTag mem_tag = mtNone); + static char* attempt_map_memory_to_file_at(char* base, size_t size, int fd, MemTag mem_tag); // Replace existing reserved memory with file mapping static char* replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd); static char* map_memory(int fd, const char* file_name, size_t file_offset, - char *addr, size_t bytes, bool read_only = false, - bool allow_exec = false, MemTag mem_tag = mtNone); + char *addr, size_t bytes, MemTag mem_tag, bool read_only = false, + bool allow_exec = false); static bool unmap_memory(char *addr, size_t bytes); static void disclaim_memory(char *addr, size_t bytes); static void realign_memory(char *addr, size_t bytes, size_t alignment_hint); diff --git a/src/hotspot/share/runtime/park.cpp b/src/hotspot/share/runtime/park.cpp index a5a67686f2c21..37dfe6fcc3d07 100644 --- a/src/hotspot/share/runtime/park.cpp +++ b/src/hotspot/share/runtime/park.cpp @@ -60,7 +60,7 @@ ParkEvent * ParkEvent::Allocate (Thread * t) { // Using a spin lock since we are part of the mutex impl. // 8028280: using concurrent free list without memory management can leak // pretty badly it turns out. - Thread::SpinAcquire(&ListLock, "ParkEventFreeListAllocate"); + Thread::SpinAcquire(&ListLock); { ev = FreeList; if (ev != nullptr) { @@ -88,7 +88,7 @@ void ParkEvent::Release (ParkEvent * ev) { ev->AssociatedWith = nullptr ; // Note that if we didn't have the TSM/immortal constraint, then // when reattaching we could trim the list. - Thread::SpinAcquire(&ListLock, "ParkEventFreeListRelease"); + Thread::SpinAcquire(&ListLock); { ev->FreeNext = FreeList; FreeList = ev; diff --git a/src/hotspot/share/runtime/park.hpp b/src/hotspot/share/runtime/park.hpp index 6d8f67edb9b35..f353ce34b7411 100644 --- a/src/hotspot/share/runtime/park.hpp +++ b/src/hotspot/share/runtime/park.hpp @@ -117,10 +117,6 @@ class ParkEvent : public PlatformEvent { // Current association Thread * AssociatedWith ; - public: - volatile int TState ; - volatile int Notified ; // for native monitor construct - private: static ParkEvent * volatile FreeList ; static volatile int ListLock ; @@ -137,8 +133,6 @@ class ParkEvent : public PlatformEvent { ParkEvent() : PlatformEvent() { AssociatedWith = nullptr ; FreeNext = nullptr ; - TState = 0 ; - Notified = 0 ; } // We use placement-new to force ParkEvent instances to be diff --git a/src/hotspot/share/runtime/safepointMechanism.cpp b/src/hotspot/share/runtime/safepointMechanism.cpp index 51038d764bb63..71224bbff4c88 100644 --- a/src/hotspot/share/runtime/safepointMechanism.cpp +++ b/src/hotspot/share/runtime/safepointMechanism.cpp @@ -57,7 +57,7 @@ void SafepointMechanism::default_initialize() { // Polling page const size_t page_size = os::vm_page_size(); const size_t allocation_size = 2 * page_size; - char* polling_page = os::reserve_memory(allocation_size, !ExecMem, mtSafepoint); + char* polling_page = os::reserve_memory(allocation_size, mtSafepoint); os::commit_memory_or_exit(polling_page, allocation_size, !ExecMem, "Unable to commit Safepoint polling page"); char* bad_page = polling_page; diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 3695d9dfe76dc..733e8a78d903e 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -22,11 +22,14 @@ * */ +#include "cds/archiveBuilder.hpp" +#include "cds/archiveUtils.inline.hpp" #include "classfile/classLoader.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/stringTable.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" +#include "code/aotCodeCache.hpp" #include "code/codeCache.hpp" #include "code/compiledIC.hpp" #include "code/nmethod.inline.hpp" @@ -147,8 +150,6 @@ void SharedRuntime::generate_stubs() { generate_throw_exception(SharedStubId::throw_NullPointerException_at_call_id, CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException_at_call)); - AdapterHandlerLibrary::initialize(); - #if COMPILER2_OR_JVMCI // Vectors are generated only by C2 and JVMCI. bool support_wide = is_wide_vector(MaxVectorSize); @@ -168,6 +169,10 @@ void SharedRuntime::generate_stubs() { generate_deopt_blob(); } +void SharedRuntime::init_adapter_library() { + AdapterHandlerLibrary::initialize(); +} + #if INCLUDE_JFR //------------------------------generate jfr runtime stubs ------ void SharedRuntime::generate_jfr_stubs() { @@ -2170,13 +2175,14 @@ void SharedRuntime::print_call_statistics(uint64_t comp_total) { #ifndef PRODUCT static int _lookups; // number of calls to lookup static int _equals; // number of buckets checked with matching hash -static int _hits; // number of successful lookups +static int _archived_hits; // number of successful lookups in archived table +static int _runtime_hits; // number of successful lookups in runtime table static int _compact; // number of equals calls with compact signature #endif // A simple wrapper class around the calling convention information // that allows sharing of adapters for the same calling convention. -class AdapterFingerPrint : public CHeapObj { +class AdapterFingerPrint : public MetaspaceObj { private: enum { _basic_type_bits = 4, @@ -2187,12 +2193,29 @@ class AdapterFingerPrint : public CHeapObj { // TO DO: Consider integrating this with a more global scheme for compressing signatures. // For now, 4 bits per components (plus T_VOID gaps after double/long) is not excessive. - union { - int _compact[_compact_int_count]; - int* _fingerprint; - } _value; - int _length; // A negative length indicates the fingerprint is in the compact form, - // Otherwise _value._fingerprint is the array. + int _length; + int _value[_compact_int_count]; + + // Private construtor. Use allocate() to get an instance. + AdapterFingerPrint(int total_args_passed, BasicType* sig_bt) { + // Pack the BasicTypes with 8 per int + _length = (total_args_passed + (_basic_types_per_int-1)) / _basic_types_per_int; + int sig_index = 0; + for (int index = 0; index < _length; index++) { + int value = 0; + for (int byte = 0; sig_index < total_args_passed && byte < _basic_types_per_int; byte++) { + int bt = adapter_encoding(sig_bt[sig_index++]); + assert((bt & _basic_type_mask) == bt, "must fit in 4 bits"); + value = (value << _basic_type_bits) | bt; + } + _value[index] = value; + } + } + + // Call deallocate instead + ~AdapterFingerPrint() { + FreeHeap(this); + } // Remap BasicTypes that are handled equivalently by the adapters. // These are correct for the current system but someday it might be @@ -2229,64 +2252,63 @@ class AdapterFingerPrint : public CHeapObj { } } - public: - AdapterFingerPrint(int total_args_passed, BasicType* sig_bt) { - // The fingerprint is based on the BasicType signature encoded - // into an array of ints with eight entries per int. - int* ptr; - int len = (total_args_passed + (_basic_types_per_int-1)) / _basic_types_per_int; - if (len <= _compact_int_count) { - assert(_compact_int_count == 3, "else change next line"); - _value._compact[0] = _value._compact[1] = _value._compact[2] = 0; - // Storing the signature encoded as signed chars hits about 98% - // of the time. - _length = -len; - ptr = _value._compact; - } else { - _length = len; - _value._fingerprint = NEW_C_HEAP_ARRAY(int, _length, mtCode); - ptr = _value._fingerprint; - } + void* operator new(size_t size, size_t fp_size) throw() { + assert(fp_size >= size, "sanity check"); + void* p = AllocateHeap(fp_size, mtCode); + memset(p, 0, fp_size); + return p; + } - // Now pack the BasicTypes with 8 per int - int sig_index = 0; - for (int index = 0; index < len; index++) { - int value = 0; - for (int byte = 0; sig_index < total_args_passed && byte < _basic_types_per_int; byte++) { - int bt = adapter_encoding(sig_bt[sig_index++]); - assert((bt & _basic_type_mask) == bt, "must fit in 4 bits"); - value = (value << _basic_type_bits) | bt; + template + void iterate_args(Function function) { + for (int i = 0; i < length(); i++) { + unsigned val = (unsigned)value(i); + // args are packed so that first/lower arguments are in the highest + // bits of each int value, so iterate from highest to the lowest + for (int j = 32 - _basic_type_bits; j >= 0; j -= _basic_type_bits) { + unsigned v = (val >> j) & _basic_type_mask; + if (v == 0) { + continue; + } + function(v); } - ptr[index] = value; } } - ~AdapterFingerPrint() { - if (_length > 0) { - FREE_C_HEAP_ARRAY(int, _value._fingerprint); - } + public: + static int allocation_size(int total_args_passed, BasicType* sig_bt) { + int len = (total_args_passed + (_basic_types_per_int-1)) / _basic_types_per_int; + return sizeof(AdapterFingerPrint) + (len > _compact_int_count ? (len - _compact_int_count) * sizeof(int) : 0); + } + + static AdapterFingerPrint* allocate(int total_args_passed, BasicType* sig_bt) { + int size_in_bytes = allocation_size(total_args_passed, sig_bt); + return new (size_in_bytes) AdapterFingerPrint(total_args_passed, sig_bt); + } + + static void deallocate(AdapterFingerPrint* fp) { + fp->~AdapterFingerPrint(); } int value(int index) { - if (_length < 0) { - return _value._compact[index]; - } - return _value._fingerprint[index]; + return _value[index]; } + int length() { if (_length < 0) return -_length; return _length; } bool is_compact() { - return _length <= 0; + return _length <= _compact_int_count; } unsigned int compute_hash() { int hash = 0; for (int i = 0; i < length(); i++) { int v = value(i); - hash = (hash << 8) ^ v ^ (hash >> 5); + //Add arithmetic operation to the hash, like +3 to improve hashing + hash = ((hash << 8) ^ v ^ (hash >> 5)) + 3; } return (unsigned int)hash; } @@ -2300,59 +2322,90 @@ class AdapterFingerPrint : public CHeapObj { return st.as_string(); } -#ifndef PRODUCT - // Reconstitutes the basic type arguments from the fingerprint, - // producing strings like LIJDF const char* as_basic_args_string() { stringStream st; bool long_prev = false; - for (int i = 0; i < length(); i++) { - unsigned val = (unsigned)value(i); - // args are packed so that first/lower arguments are in the highest - // bits of each int value, so iterate from highest to the lowest - for (int j = 32 - _basic_type_bits; j >= 0; j -= _basic_type_bits) { - unsigned v = (val >> j) & _basic_type_mask; - if (v == 0) { - assert(i == length() - 1, "Only expect zeroes in the last word"); - continue; - } - if (long_prev) { - long_prev = false; - if (v == T_VOID) { - st.print("J"); - } else { - st.print("L"); - } - } - switch (v) { - case T_INT: st.print("I"); break; - case T_LONG: long_prev = true; break; - case T_FLOAT: st.print("F"); break; - case T_DOUBLE: st.print("D"); break; - case T_VOID: break; - default: ShouldNotReachHere(); + iterate_args([&] (int arg) { + if (long_prev) { + long_prev = false; + if (arg == T_VOID) { + st.print("J"); + } else { + st.print("L"); } } - } + switch (arg) { + case T_INT: st.print("I"); break; + case T_LONG: long_prev = true; break; + case T_FLOAT: st.print("F"); break; + case T_DOUBLE: st.print("D"); break; + case T_VOID: break; + default: ShouldNotReachHere(); + } + }); if (long_prev) { st.print("L"); } return st.as_string(); } -#endif // !product + + BasicType* as_basic_type(int& nargs) { + nargs = 0; + GrowableArray btarray; + bool long_prev = false; + + iterate_args([&] (int arg) { + if (long_prev) { + long_prev = false; + if (arg == T_VOID) { + btarray.append(T_LONG); + } else { + btarray.append(T_OBJECT); // it could be T_ARRAY; it shouldn't matter + } + } + switch (arg) { + case T_INT: // fallthrough + case T_FLOAT: // fallthrough + case T_DOUBLE: + case T_VOID: + btarray.append((BasicType)arg); + break; + case T_LONG: + long_prev = true; + break; + default: ShouldNotReachHere(); + } + }); + + if (long_prev) { + btarray.append(T_OBJECT); + } + + nargs = btarray.length(); + BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, nargs); + int index = 0; + GrowableArrayIterator iter = btarray.begin(); + while (iter != btarray.end()) { + sig_bt[index++] = *iter; + ++iter; + } + assert(index == btarray.length(), "sanity check"); +#ifdef ASSERT + { + AdapterFingerPrint* compare_fp = AdapterFingerPrint::allocate(nargs, sig_bt); + assert(this->equals(compare_fp), "sanity check"); + AdapterFingerPrint::deallocate(compare_fp); + } +#endif + return sig_bt; + } bool equals(AdapterFingerPrint* other) { if (other->_length != _length) { return false; - } - if (_length < 0) { - assert(_compact_int_count == 3, "else change next line"); - return _value._compact[0] == other->_value._compact[0] && - _value._compact[1] == other->_value._compact[1] && - _value._compact[2] == other->_value._compact[2]; } else { for (int i = 0; i < _length; i++) { - if (_value._fingerprint[i] != other->_value._fingerprint[i]) { + if (_value[i] != other->_value[i]) { return false; } } @@ -2360,6 +2413,11 @@ class AdapterFingerPrint : public CHeapObj { return true; } + // methods required by virtue of being a MetaspaceObj + void metaspace_pointers_do(MetaspaceClosure* it) { return; /* nothing to do here */ } + int size() const { return (int)heap_word_size(sizeof(AdapterFingerPrint) + (_length > _compact_int_count ? (_length - _compact_int_count) * sizeof(int) : 0)); } + MetaspaceObj::Type type() const { return AdapterFingerPrintType; } + static bool equals(AdapterFingerPrint* const& fp1, AdapterFingerPrint* const& fp2) { NOT_PRODUCT(_equals++); return fp1->equals(fp2); @@ -2370,27 +2428,63 @@ class AdapterFingerPrint : public CHeapObj { } }; +#if INCLUDE_CDS +static inline bool adapter_fp_equals_compact_hashtable_entry(AdapterHandlerEntry* entry, AdapterFingerPrint* fp, int len_unused) { + return AdapterFingerPrint::equals(entry->fingerprint(), fp); +} + +class ArchivedAdapterTable : public OffsetCompactHashtable< + AdapterFingerPrint*, + AdapterHandlerEntry*, + adapter_fp_equals_compact_hashtable_entry> {}; +#endif // INCLUDE_CDS + // A hashtable mapping from AdapterFingerPrints to AdapterHandlerEntries using AdapterHandlerTable = ResourceHashtable; static AdapterHandlerTable* _adapter_handler_table; +static GrowableArray* _adapter_handler_list = nullptr; // Find a entry with the same fingerprint if it exists -static AdapterHandlerEntry* lookup(int total_args_passed, BasicType* sig_bt) { +AdapterHandlerEntry* AdapterHandlerLibrary::lookup(int total_args_passed, BasicType* sig_bt) { NOT_PRODUCT(_lookups++); assert_lock_strong(AdapterHandlerLibrary_lock); - AdapterFingerPrint fp(total_args_passed, sig_bt); - AdapterHandlerEntry** entry = _adapter_handler_table->get(&fp); - if (entry != nullptr) { + AdapterFingerPrint* fp = AdapterFingerPrint::allocate(total_args_passed, sig_bt); + AdapterHandlerEntry* entry = nullptr; +#if INCLUDE_CDS + // if we are building the archive then the archived adapter table is + // not valid and we need to use the ones added to the runtime table + if (!AOTCodeCache::is_dumping_adapters()) { + // Search archived table first. It is read-only table so can be searched without lock + entry = _aot_adapter_handler_table.lookup(fp, fp->compute_hash(), 0 /* unused */); + if (entry != nullptr) { #ifndef PRODUCT - if (fp.is_compact()) _compact++; - _hits++; + if (fp->is_compact()) { + _compact++; + } + _archived_hits++; #endif - return *entry; + } } - return nullptr; +#endif // INCLUDE_CDS + if (entry == nullptr) { + assert_lock_strong(AdapterHandlerLibrary_lock); + AdapterHandlerEntry** entry_p = _adapter_handler_table->get(fp); + if (entry_p != nullptr) { + entry = *entry_p; + assert(entry->fingerprint()->equals(fp), "fingerprint mismatch key fp %s %s (hash=%d) != found fp %s %s (hash=%d)", + entry->fingerprint()->as_basic_args_string(), entry->fingerprint()->as_string(), entry->fingerprint()->compute_hash(), + fp->as_basic_args_string(), fp->as_string(), fp->compute_hash()); + #ifndef PRODUCT + if (fp->is_compact()) _compact++; + _runtime_hits++; + #endif + } + } + AdapterFingerPrint::deallocate(fp); + return entry; } #ifndef PRODUCT @@ -2402,8 +2496,9 @@ static void print_table_statistics() { ts.print(tty, "AdapterHandlerTable"); tty->print_cr("AdapterHandlerTable (table_size=%d, entries=%d)", _adapter_handler_table->table_size(), _adapter_handler_table->number_of_entries()); - tty->print_cr("AdapterHandlerTable: lookups %d equals %d hits %d compact %d", - _lookups, _equals, _hits, _compact); + int total_hits = _archived_hits + _runtime_hits; + tty->print_cr("AdapterHandlerTable: lookups %d equals %d hits %d (archived=%d+runtime=%d) compact %d", + _lookups, _equals, total_hits, _archived_hits, _runtime_hits, _compact); } #endif @@ -2415,10 +2510,14 @@ AdapterHandlerEntry* AdapterHandlerLibrary::_int_arg_handler = nullptr; AdapterHandlerEntry* AdapterHandlerLibrary::_obj_arg_handler = nullptr; AdapterHandlerEntry* AdapterHandlerLibrary::_obj_int_arg_handler = nullptr; AdapterHandlerEntry* AdapterHandlerLibrary::_obj_obj_arg_handler = nullptr; -const int AdapterHandlerLibrary_size = 16*K; +#if INCLUDE_CDS +ArchivedAdapterTable AdapterHandlerLibrary::_aot_adapter_handler_table; +#endif // INCLUDE_CDS +static const int AdapterHandlerLibrary_size = 16*K; BufferBlob* AdapterHandlerLibrary::_buffer = nullptr; BufferBlob* AdapterHandlerLibrary::buffer_blob() { + assert(_buffer != nullptr, "should be initialized"); return _buffer; } @@ -2441,7 +2540,39 @@ static void post_adapter_creation(const AdapterBlob* new_adapter, } } +void AdapterHandlerLibrary::create_abstract_method_handler() { + assert_lock_strong(AdapterHandlerLibrary_lock); + // Create a special handler for abstract methods. Abstract methods + // are never compiled so an i2c entry is somewhat meaningless, but + // throw AbstractMethodError just in case. + // Pass wrong_method_abstract for the c2i transitions to return + // AbstractMethodError for invalid invocations. + address wrong_method_abstract = SharedRuntime::get_handle_wrong_method_abstract_stub(); + _abstract_method_handler = AdapterHandlerLibrary::new_entry(AdapterFingerPrint::allocate(0, nullptr)); + _abstract_method_handler->set_entry_points(SharedRuntime::throw_AbstractMethodError_entry(), + wrong_method_abstract, + wrong_method_abstract, + nullptr); +} + void AdapterHandlerLibrary::initialize() { + { + ResourceMark rm; + MutexLocker mu(AdapterHandlerLibrary_lock); + _adapter_handler_table = new (mtCode) AdapterHandlerTable(); + _buffer = BufferBlob::create("adapters", AdapterHandlerLibrary_size); + create_abstract_method_handler(); + } + +#if INCLUDE_CDS + // Link adapters in AOT Cache to their code in AOT Code Cache + if (!_aot_adapter_handler_table.empty()) { + link_aot_adapters(); + lookup_simple_adapters(); + return; + } +#endif // INCLUDE_CDS + ResourceMark rm; AdapterBlob* no_arg_blob = nullptr; AdapterBlob* int_arg_blob = nullptr; @@ -2449,39 +2580,27 @@ void AdapterHandlerLibrary::initialize() { AdapterBlob* obj_int_arg_blob = nullptr; AdapterBlob* obj_obj_arg_blob = nullptr; { - _adapter_handler_table = new (mtCode) AdapterHandlerTable(); MutexLocker mu(AdapterHandlerLibrary_lock); - // Create a special handler for abstract methods. Abstract methods - // are never compiled so an i2c entry is somewhat meaningless, but - // throw AbstractMethodError just in case. - // Pass wrong_method_abstract for the c2i transitions to return - // AbstractMethodError for invalid invocations. - address wrong_method_abstract = SharedRuntime::get_handle_wrong_method_abstract_stub(); - _abstract_method_handler = AdapterHandlerLibrary::new_entry(new AdapterFingerPrint(0, nullptr), - SharedRuntime::throw_AbstractMethodError_entry(), - wrong_method_abstract, wrong_method_abstract); - - _buffer = BufferBlob::create("adapters", AdapterHandlerLibrary_size); - _no_arg_handler = create_adapter(no_arg_blob, 0, nullptr, true); + _no_arg_handler = create_adapter(no_arg_blob, 0, nullptr); BasicType obj_args[] = { T_OBJECT }; - _obj_arg_handler = create_adapter(obj_arg_blob, 1, obj_args, true); + _obj_arg_handler = create_adapter(obj_arg_blob, 1, obj_args); BasicType int_args[] = { T_INT }; - _int_arg_handler = create_adapter(int_arg_blob, 1, int_args, true); + _int_arg_handler = create_adapter(int_arg_blob, 1, int_args); BasicType obj_int_args[] = { T_OBJECT, T_INT }; - _obj_int_arg_handler = create_adapter(obj_int_arg_blob, 2, obj_int_args, true); + _obj_int_arg_handler = create_adapter(obj_int_arg_blob, 2, obj_int_args); BasicType obj_obj_args[] = { T_OBJECT, T_OBJECT }; - _obj_obj_arg_handler = create_adapter(obj_obj_arg_blob, 2, obj_obj_args, true); + _obj_obj_arg_handler = create_adapter(obj_obj_arg_blob, 2, obj_obj_args); assert(no_arg_blob != nullptr && - obj_arg_blob != nullptr && - int_arg_blob != nullptr && - obj_int_arg_blob != nullptr && - obj_obj_arg_blob != nullptr, "Initial adapters must be properly created"); + obj_arg_blob != nullptr && + int_arg_blob != nullptr && + obj_int_arg_blob != nullptr && + obj_obj_arg_blob != nullptr, "Initial adapters must be properly created"); } // Outside of the lock @@ -2492,14 +2611,8 @@ void AdapterHandlerLibrary::initialize() { post_adapter_creation(obj_obj_arg_blob, _obj_obj_arg_handler); } -AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint, - address i2c_entry, - address c2i_entry, - address c2i_unverified_entry, - address c2i_no_clinit_check_entry) { - // Insert an entry into the table - return new AdapterHandlerEntry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, - c2i_no_clinit_check_entry); +AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint) { + return AdapterHandlerEntry::allocate(fingerprint); } AdapterHandlerEntry* AdapterHandlerLibrary::get_simple_adapter(const methodHandle& method) { @@ -2583,6 +2696,22 @@ class AdapterSignatureIterator : public SignatureIterator { } }; + +const char* AdapterHandlerEntry::_entry_names[] = { + "i2c", "c2i", "c2i_unverified", "c2i_no_clinit_check" +}; + +#ifdef ASSERT +void AdapterHandlerLibrary::verify_adapter_sharing(int total_args_passed, BasicType* sig_bt, AdapterHandlerEntry* cached_entry) { + AdapterBlob* comparison_blob = nullptr; + AdapterHandlerEntry* comparison_entry = create_adapter(comparison_blob, total_args_passed, sig_bt, true); + assert(comparison_blob == nullptr, "no blob should be created when creating an adapter for comparison"); + assert(comparison_entry->compare_code(cached_entry), "code must match"); + // Release the one just created + AdapterHandlerEntry::deallocate(comparison_entry); +} +#endif /* ASSERT*/ + AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& method) { // Use customized signature handler. Need to lock around updates to // the _adapter_handler_table (it is not safe for concurrent readers @@ -2596,7 +2725,7 @@ AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& meth } ResourceMark rm; - AdapterBlob* new_adapter = nullptr; + AdapterBlob* adapter_blob = nullptr; // Fill in the signature array, for the calling-convention call. int total_args_passed = method->size_of_parameters(); // All args on stack @@ -2612,110 +2741,291 @@ AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& meth entry = lookup(total_args_passed, sig_bt); if (entry != nullptr) { + assert(entry->is_linked(), "AdapterHandlerEntry must have been linked"); #ifdef ASSERT - if (VerifyAdapterSharing) { - AdapterBlob* comparison_blob = nullptr; - AdapterHandlerEntry* comparison_entry = create_adapter(comparison_blob, total_args_passed, sig_bt, false); - assert(comparison_blob == nullptr, "no blob should be created when creating an adapter for comparison"); - assert(comparison_entry->compare_code(entry), "code must match"); - // Release the one just created and return the original - delete comparison_entry; + if (!entry->is_shared() && VerifyAdapterSharing) { + verify_adapter_sharing(total_args_passed, sig_bt, entry); } #endif - return entry; + } else { + entry = create_adapter(adapter_blob, total_args_passed, sig_bt); } - - entry = create_adapter(new_adapter, total_args_passed, sig_bt, /* allocate_code_blob */ true); } // Outside of the lock - if (new_adapter != nullptr) { - post_adapter_creation(new_adapter, entry); + if (adapter_blob != nullptr) { + post_adapter_creation(adapter_blob, entry); } return entry; } -AdapterHandlerEntry* AdapterHandlerLibrary::create_adapter(AdapterBlob*& new_adapter, - int total_args_passed, - BasicType* sig_bt, - bool allocate_code_blob) { +AdapterBlob* AdapterHandlerLibrary::lookup_aot_cache(AdapterHandlerEntry* handler) { + ResourceMark rm; + const char* name = AdapterHandlerLibrary::name(handler->fingerprint()); + const uint32_t id = AdapterHandlerLibrary::id(handler->fingerprint()); + int offsets[AdapterHandlerEntry::ENTRIES_COUNT]; + + AdapterBlob* adapter_blob = nullptr; + CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::Adapter, id, name, AdapterHandlerEntry::ENTRIES_COUNT, offsets); + if (blob != nullptr) { + adapter_blob = blob->as_adapter_blob(); + address i2c_entry = adapter_blob->content_begin(); + assert(offsets[0] == 0, "sanity check"); + handler->set_entry_points(i2c_entry, i2c_entry + offsets[1], i2c_entry + offsets[2], i2c_entry + offsets[3]); + } + return adapter_blob; +} + +#ifndef PRODUCT +void AdapterHandlerLibrary::print_adapter_handler_info(outputStream* st, AdapterHandlerEntry* handler, AdapterBlob* adapter_blob) { + ttyLocker ttyl; + ResourceMark rm; + int insts_size = adapter_blob->code_size(); + handler->print_adapter_on(tty); + st->print_cr("i2c argument handler for: %s %s (%d bytes generated)", + handler->fingerprint()->as_basic_args_string(), + handler->fingerprint()->as_string(), insts_size); + st->print_cr("c2i argument handler starts at " INTPTR_FORMAT, p2i(handler->get_c2i_entry())); + if (Verbose || PrintStubCode) { + address first_pc = handler->base_address(); + if (first_pc != nullptr) { + Disassembler::decode(first_pc, first_pc + insts_size, st, &adapter_blob->asm_remarks()); + st->cr(); + } + } +} +#endif // PRODUCT + +bool AdapterHandlerLibrary::generate_adapter_code(AdapterBlob*& adapter_blob, + AdapterHandlerEntry* handler, + int total_args_passed, + BasicType* sig_bt, + bool is_transient) { if (log_is_enabled(Info, perf, class, link)) { ClassLoader::perf_method_adapters_count()->inc(); } - // StubRoutines::_final_stubs_code is initialized after this function can be called. As a result, - // VerifyAdapterCalls and VerifyAdapterSharing can fail if we re-use code that generated prior - // to all StubRoutines::_final_stubs_code being set. Checks refer to runtime range checks generated - // in an I2C stub that ensure that an I2C stub is called from an interpreter frame or stubs. - bool contains_all_checks = StubRoutines::final_stubs_code() != nullptr; - - VMRegPair stack_regs[16]; - VMRegPair* regs = (total_args_passed <= 16) ? stack_regs : NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed); - - // Get a description of the compiled java calling convention and the largest used (VMReg) stack slot usage - int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed); BufferBlob* buf = buffer_blob(); // the temporary code buffer in CodeCache CodeBuffer buffer(buf); short buffer_locs[20]; buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs, - sizeof(buffer_locs)/sizeof(relocInfo)); - - // Make a C heap allocated version of the fingerprint to store in the adapter - AdapterFingerPrint* fingerprint = new AdapterFingerPrint(total_args_passed, sig_bt); - MacroAssembler _masm(&buffer); - AdapterHandlerEntry* entry = SharedRuntime::generate_i2c2i_adapters(&_masm, - total_args_passed, - comp_args_on_stack, - sig_bt, - regs, - fingerprint); + sizeof(buffer_locs)/sizeof(relocInfo)); + MacroAssembler masm(&buffer); + VMRegPair stack_regs[16]; + VMRegPair* regs = (total_args_passed <= 16) ? stack_regs : NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed); + // Get a description of the compiled java calling convention and the largest used (VMReg) stack slot usage + int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed); + SharedRuntime::generate_i2c2i_adapters(&masm, + total_args_passed, + comp_args_on_stack, + sig_bt, + regs, + handler); #ifdef ASSERT if (VerifyAdapterSharing) { - entry->save_code(buf->code_begin(), buffer.insts_size()); - if (!allocate_code_blob) { - return entry; + handler->save_code(buf->code_begin(), buffer.insts_size()); + if (is_transient) { + return true; } } #endif - new_adapter = AdapterBlob::create(&buffer); - NOT_PRODUCT(int insts_size = buffer.insts_size()); - if (new_adapter == nullptr) { + adapter_blob = AdapterBlob::create(&buffer); + if (adapter_blob == nullptr) { // CodeCache is full, disable compilation // Ought to log this but compile log is only per compile thread // and we're some non descript Java thread. - return nullptr; + return false; } - entry->relocate(new_adapter->content_begin()); + if (!is_transient && AOTCodeCache::is_dumping_adapters()) { + // try to save generated code + const char* name = AdapterHandlerLibrary::name(handler->fingerprint()); + const uint32_t id = AdapterHandlerLibrary::id(handler->fingerprint()); + int entry_offset[AdapterHandlerEntry::ENTRIES_COUNT]; + assert(AdapterHandlerEntry::ENTRIES_COUNT == 4, "sanity"); + address i2c_entry = handler->get_i2c_entry(); + entry_offset[0] = 0; // i2c_entry offset + entry_offset[1] = handler->get_c2i_entry() - i2c_entry; + entry_offset[2] = handler->get_c2i_unverified_entry() - i2c_entry; + entry_offset[3] = handler->get_c2i_no_clinit_check_entry() - i2c_entry; + bool success = AOTCodeCache::store_code_blob(*adapter_blob, AOTCodeEntry::Adapter, id, name, AdapterHandlerEntry::ENTRIES_COUNT, entry_offset); + assert(success || !AOTCodeCache::is_dumping_adapters(), "caching of adapter must be disabled"); + } + handler->relocate(adapter_blob->content_begin()); #ifndef PRODUCT // debugging support if (PrintAdapterHandlers || PrintStubCode) { - ttyLocker ttyl; - entry->print_adapter_on(tty); - tty->print_cr("i2c argument handler #%d for: %s %s (%d bytes generated)", - _adapter_handler_table->number_of_entries(), fingerprint->as_basic_args_string(), - fingerprint->as_string(), insts_size); - tty->print_cr("c2i argument handler starts at " INTPTR_FORMAT, p2i(entry->get_c2i_entry())); - if (Verbose || PrintStubCode) { - address first_pc = entry->base_address(); - if (first_pc != nullptr) { - Disassembler::decode(first_pc, first_pc + insts_size, tty - NOT_PRODUCT(COMMA &new_adapter->asm_remarks())); - tty->cr(); + print_adapter_handler_info(tty, handler, adapter_blob); + } +#endif + return true; +} + +AdapterHandlerEntry* AdapterHandlerLibrary::create_adapter(AdapterBlob*& adapter_blob, + int total_args_passed, + BasicType* sig_bt, + bool is_transient) { + AdapterFingerPrint* fp = AdapterFingerPrint::allocate(total_args_passed, sig_bt); + AdapterHandlerEntry* handler = AdapterHandlerLibrary::new_entry(fp); + if (!generate_adapter_code(adapter_blob, handler, total_args_passed, sig_bt, is_transient)) { + AdapterHandlerEntry::deallocate(handler); + return nullptr; + } + if (!is_transient) { + assert_lock_strong(AdapterHandlerLibrary_lock); + _adapter_handler_table->put(fp, handler); + } + return handler; +} + +#if INCLUDE_CDS +void AdapterHandlerEntry::remove_unshareable_info() { +#ifdef ASSERT + _saved_code = nullptr; + _saved_code_length = 0; +#endif // ASSERT + set_entry_points(nullptr, nullptr, nullptr, nullptr, false); +} + +class CopyAdapterTableToArchive : StackObj { +private: + CompactHashtableWriter* _writer; + ArchiveBuilder* _builder; +public: + CopyAdapterTableToArchive(CompactHashtableWriter* writer) : _writer(writer), + _builder(ArchiveBuilder::current()) + {} + + bool do_entry(AdapterFingerPrint* fp, AdapterHandlerEntry* entry) { + LogStreamHandle(Trace, cds) lsh; + if (ArchiveBuilder::current()->has_been_archived((address)entry)) { + assert(ArchiveBuilder::current()->has_been_archived((address)fp), "must be"); + AdapterFingerPrint* buffered_fp = ArchiveBuilder::current()->get_buffered_addr(fp); + assert(buffered_fp != nullptr,"sanity check"); + AdapterHandlerEntry* buffered_entry = ArchiveBuilder::current()->get_buffered_addr(entry); + assert(buffered_entry != nullptr,"sanity check"); + + uint hash = fp->compute_hash(); + u4 delta = _builder->buffer_to_offset_u4((address)buffered_entry); + _writer->add(hash, delta); + if (lsh.is_enabled()) { + address fp_runtime_addr = (address)buffered_fp + ArchiveBuilder::current()->buffer_to_requested_delta(); + address entry_runtime_addr = (address)buffered_entry + ArchiveBuilder::current()->buffer_to_requested_delta(); + log_trace(cds)("Added fp=%p (%s), entry=%p to the archived adater table", buffered_fp, buffered_fp->as_basic_args_string(), buffered_entry); + } + } else { + if (lsh.is_enabled()) { + log_trace(cds)("Skipping adapter handler %p (fp=%s) as it is not archived", entry, fp->as_basic_args_string()); } } + return true; + } +}; + +void AdapterHandlerLibrary::dump_aot_adapter_table() { + CompactHashtableStats stats; + CompactHashtableWriter writer(_adapter_handler_table->number_of_entries(), &stats); + CopyAdapterTableToArchive copy(&writer); + _adapter_handler_table->iterate(©); + writer.dump(&_aot_adapter_handler_table, "archived adapter table"); +} + +void AdapterHandlerLibrary::serialize_shared_table_header(SerializeClosure* soc) { + _aot_adapter_handler_table.serialize_header(soc); +} + +AdapterBlob* AdapterHandlerLibrary::link_aot_adapter_handler(AdapterHandlerEntry* handler) { +#ifdef ASSERT + if (TestAOTAdapterLinkFailure) { + return nullptr; } #endif + AdapterBlob* blob = lookup_aot_cache(handler); +#ifndef PRODUCT + // debugging support + if ((blob != nullptr) && (PrintAdapterHandlers || PrintStubCode)) { + print_adapter_handler_info(tty, handler, blob); + } +#endif + return blob; +} - // Add the entry only if the entry contains all required checks (see sharedRuntime_xxx.cpp) - // The checks are inserted only if -XX:+VerifyAdapterCalls is specified. - if (contains_all_checks || !VerifyAdapterCalls) { - assert_lock_strong(AdapterHandlerLibrary_lock); - _adapter_handler_table->put(fingerprint, entry); +// This method is used during production run to link archived adapters (stored in AOT Cache) +// to their code in AOT Code Cache +void AdapterHandlerEntry::link() { + AdapterBlob* adapter_blob = nullptr; + ResourceMark rm; + assert(_fingerprint != nullptr, "_fingerprint must not be null"); + bool generate_code = false; + // Generate code only if AOTCodeCache is not available, or + // caching adapters is disabled, or we fail to link + // the AdapterHandlerEntry to its code in the AOTCodeCache + if (AOTCodeCache::is_using_adapters()) { + adapter_blob = AdapterHandlerLibrary::link_aot_adapter_handler(this); + if (adapter_blob == nullptr) { + log_warning(cds)("Failed to link AdapterHandlerEntry (fp=%s) to its code in the AOT code cache", _fingerprint->as_basic_args_string()); + generate_code = true; + } + } else { + generate_code = true; + } + if (generate_code) { + int nargs; + BasicType* bt = _fingerprint->as_basic_type(nargs); + if (!AdapterHandlerLibrary::generate_adapter_code(adapter_blob, this, nargs, bt, /* is_transient */ false)) { + // Don't throw exceptions during VM initialization because java.lang.* classes + // might not have been initialized, causing problems when constructing the + // Java exception object. + vm_exit_during_initialization("Out of space in CodeCache for adapters"); + } } - return entry; + // Outside of the lock + if (adapter_blob != nullptr) { + post_adapter_creation(adapter_blob, this); + } + assert(_linked, "AdapterHandlerEntry must now be linked"); +} + +void AdapterHandlerLibrary::link_aot_adapters() { + _aot_adapter_handler_table.iterate([](AdapterHandlerEntry* entry) { + assert(!entry->is_linked(), "AdapterHandlerEntry is already linked!"); + entry->link(); + }); +} + +// This method is called during production run to lookup simple adapters +// in the archived adapter handler table +void AdapterHandlerLibrary::lookup_simple_adapters() { + assert(!_aot_adapter_handler_table.empty(), "archived adapter handler table is empty"); + + MutexLocker mu(AdapterHandlerLibrary_lock); + _no_arg_handler = lookup(0, nullptr); + + BasicType obj_args[] = { T_OBJECT }; + _obj_arg_handler = lookup(1, obj_args); + + BasicType int_args[] = { T_INT }; + _int_arg_handler = lookup(1, int_args); + + BasicType obj_int_args[] = { T_OBJECT, T_INT }; + _obj_int_arg_handler = lookup(2, obj_int_args); + + BasicType obj_obj_args[] = { T_OBJECT, T_OBJECT }; + _obj_obj_arg_handler = lookup(2, obj_obj_args); + + assert(_no_arg_handler != nullptr && + _obj_arg_handler != nullptr && + _int_arg_handler != nullptr && + _obj_int_arg_handler != nullptr && + _obj_obj_arg_handler != nullptr, "Initial adapters not found in archived adapter handler table"); + assert(_no_arg_handler->is_linked() && + _obj_arg_handler->is_linked() && + _int_arg_handler->is_linked() && + _obj_int_arg_handler->is_linked() && + _obj_obj_arg_handler->is_linked(), "Initial adapters not in linked state"); } +#endif // INCLUDE_CDS address AdapterHandlerEntry::base_address() { address base = _i2c_entry; @@ -2741,12 +3051,24 @@ void AdapterHandlerEntry::relocate(address new_base) { assert(base_address() == new_base, ""); } +void AdapterHandlerEntry::metaspace_pointers_do(MetaspaceClosure* it) { + LogStreamHandle(Trace, cds) lsh; + if (lsh.is_enabled()) { + lsh.print("Iter(AdapterHandlerEntry): %p(%s)", this, _fingerprint->as_basic_args_string()); + lsh.cr(); + } + it->push(&_fingerprint); +} AdapterHandlerEntry::~AdapterHandlerEntry() { - delete _fingerprint; + if (_fingerprint != nullptr) { + AdapterFingerPrint::deallocate(_fingerprint); + _fingerprint = nullptr; + } #ifdef ASSERT FREE_C_HEAP_ARRAY(unsigned char, _saved_code); #endif + FreeHeap(this); } @@ -3051,28 +3373,61 @@ JRT_END bool AdapterHandlerLibrary::contains(const CodeBlob* b) { bool found = false; - auto findblob = [&] (AdapterFingerPrint* key, AdapterHandlerEntry* a) { - return (found = (b == CodeCache::find_blob(a->get_i2c_entry()))); +#if INCLUDE_CDS + auto findblob_archived_table = [&] (AdapterHandlerEntry* handler) { + return (found = (b == CodeCache::find_blob(handler->get_i2c_entry()))); }; - assert_locked_or_safepoint(AdapterHandlerLibrary_lock); - _adapter_handler_table->iterate(findblob); + _aot_adapter_handler_table.iterate(findblob_archived_table); +#endif // INCLUDE_CDS + if (!found) { + auto findblob_runtime_table = [&] (AdapterFingerPrint* key, AdapterHandlerEntry* a) { + return (found = (b == CodeCache::find_blob(a->get_i2c_entry()))); + }; + assert_locked_or_safepoint(AdapterHandlerLibrary_lock); + _adapter_handler_table->iterate(findblob_runtime_table); + } return found; } +const char* AdapterHandlerLibrary::name(AdapterFingerPrint* fingerprint) { + return fingerprint->as_basic_args_string(); +} + +uint32_t AdapterHandlerLibrary::id(AdapterFingerPrint* fingerprint) { + unsigned int hash = fingerprint->compute_hash(); + return hash; +} + void AdapterHandlerLibrary::print_handler_on(outputStream* st, const CodeBlob* b) { bool found = false; - auto findblob = [&] (AdapterFingerPrint* key, AdapterHandlerEntry* a) { - if (b == CodeCache::find_blob(a->get_i2c_entry())) { +#if INCLUDE_CDS + auto findblob_archived_table = [&] (AdapterHandlerEntry* handler) { + if (b == CodeCache::find_blob(handler->get_i2c_entry())) { found = true; st->print("Adapter for signature: "); - a->print_adapter_on(st); + handler->print_adapter_on(st); return true; } else { return false; // keep looking + } }; - assert_locked_or_safepoint(AdapterHandlerLibrary_lock); - _adapter_handler_table->iterate(findblob); + _aot_adapter_handler_table.iterate(findblob_archived_table); +#endif // INCLUDE_CDS + if (!found) { + auto findblob_runtime_table = [&] (AdapterFingerPrint* key, AdapterHandlerEntry* a) { + if (b == CodeCache::find_blob(a->get_i2c_entry())) { + found = true; + st->print("Adapter for signature: "); + a->print_adapter_on(st); + return true; + } else { + return false; // keep looking + } + }; + assert_locked_or_safepoint(AdapterHandlerLibrary_lock); + _adapter_handler_table->iterate(findblob_runtime_table); + } assert(found, "Should have found handler"); } @@ -3101,6 +3456,13 @@ void AdapterHandlerLibrary::print_statistics() { #endif /* PRODUCT */ +bool AdapterHandlerLibrary::is_abstract_method_adapter(AdapterHandlerEntry* entry) { + if (entry == _abstract_method_handler) { + return true; + } + return false; +} + JRT_LEAF(void, SharedRuntime::enable_stack_reserved_zone(JavaThread* current)) assert(current == JavaThread::current(), "pre-condition"); StackOverflow* overflow_state = current->stack_overflow_state(); diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index 94cbc460cad66..de16a33fdcc71 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -25,10 +25,12 @@ #ifndef SHARE_RUNTIME_SHAREDRUNTIME_HPP #define SHARE_RUNTIME_SHAREDRUNTIME_HPP +#include "classfile/compactHashtable.hpp" #include "code/codeBlob.hpp" #include "code/vmreg.hpp" #include "interpreter/linkResolver.hpp" #include "memory/allStatic.hpp" +#include "memory/metaspaceClosure.hpp" #include "memory/resourceArea.hpp" #include "runtime/stubDeclarations.hpp" #include "utilities/macros.hpp" @@ -114,6 +116,7 @@ class SharedRuntime: AllStatic { // For c2: call to runtime to return a buffer lease. static RuntimeStub* generate_jfr_return_lease(); #endif + static void init_adapter_library(); static const char *stub_name(SharedStubId id) { assert(id > SharedStubId::NO_STUBID && id < SharedStubId::NUM_STUBIDS, "stub id out of range"); @@ -464,12 +467,12 @@ class SharedRuntime: AllStatic { // pointer as needed. This means the i2c adapter code doesn't need any special // handshaking path with compiled code to keep the stack walking correct. - static AdapterHandlerEntry* generate_i2c2i_adapters(MacroAssembler *_masm, - int total_args_passed, - int max_arg, - const BasicType *sig_bt, - const VMRegPair *regs, - AdapterFingerPrint* fingerprint); + static void generate_i2c2i_adapters(MacroAssembler *_masm, + int total_args_passed, + int max_arg, + const BasicType *sig_bt, + const VMRegPair *regs, + AdapterHandlerEntry* handler); static void gen_i2c_adapter(MacroAssembler *_masm, int total_args_passed, @@ -666,15 +669,21 @@ class SharedRuntime: AllStatic { // used by the adapters. The code generation happens here because it's very // similar to what the adapters have to do. -class AdapterHandlerEntry : public CHeapObj { +class AdapterHandlerEntry : public MetaspaceObj { friend class AdapterHandlerLibrary; + public: + static const int ENTRIES_COUNT = 4; + private: AdapterFingerPrint* _fingerprint; address _i2c_entry; address _c2i_entry; address _c2i_unverified_entry; address _c2i_no_clinit_check_entry; + bool _linked; + + static const char *_entry_names[]; #ifdef ASSERT // Captures code and signature used to generate this adapter when @@ -683,27 +692,58 @@ class AdapterHandlerEntry : public CHeapObj { int _saved_code_length; #endif - AdapterHandlerEntry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, - address c2i_unverified_entry, - address c2i_no_clinit_check_entry) : + AdapterHandlerEntry(AdapterFingerPrint* fingerprint) : _fingerprint(fingerprint), - _i2c_entry(i2c_entry), - _c2i_entry(c2i_entry), - _c2i_unverified_entry(c2i_unverified_entry), - _c2i_no_clinit_check_entry(c2i_no_clinit_check_entry) + _i2c_entry(nullptr), + _c2i_entry(nullptr), + _c2i_unverified_entry(nullptr), + _c2i_no_clinit_check_entry(nullptr), + _linked(false) #ifdef ASSERT - , _saved_code_length(0) + , _saved_code(nullptr), + _saved_code_length(0) #endif { } ~AdapterHandlerEntry(); + // Allocate on CHeap instead of metaspace (see JDK-8331086). + // Dummy argument is used to avoid C++ warning about using + // deleted opearator MetaspaceObj::delete(). + void* operator new(size_t size, size_t dummy) throw() { + void* p = AllocateHeap(size, mtCode); + memset(p, 0, size); + return p; + } + public: + static AdapterHandlerEntry* allocate(AdapterFingerPrint* fingerprint) { + return new(0) AdapterHandlerEntry(fingerprint); + } + + static void deallocate(AdapterHandlerEntry *handler) { + handler->~AdapterHandlerEntry(); + } + + void set_entry_points(address i2c_entry, address c2i_entry, address c2i_unverified_entry, address c2i_no_clinit_check_entry, bool linked = true) { + _i2c_entry = i2c_entry; + _c2i_entry = c2i_entry; + _c2i_unverified_entry = c2i_unverified_entry; + _c2i_no_clinit_check_entry = c2i_no_clinit_check_entry; + _linked = linked; + } + address get_i2c_entry() const { return _i2c_entry; } address get_c2i_entry() const { return _c2i_entry; } address get_c2i_unverified_entry() const { return _c2i_unverified_entry; } address get_c2i_no_clinit_check_entry() const { return _c2i_no_clinit_check_entry; } + static const char* entry_name(int i) { + assert(i >=0 && i < ENTRIES_COUNT, "entry id out of range"); + return _entry_names[i]; + } + + bool is_linked() const { return _linked; } address base_address(); void relocate(address new_base); @@ -717,8 +757,19 @@ class AdapterHandlerEntry : public CHeapObj { //virtual void print_on(outputStream* st) const; DO NOT USE void print_adapter_on(outputStream* st) const; + + void metaspace_pointers_do(MetaspaceClosure* it); + int size() const {return (int)heap_word_size(sizeof(AdapterHandlerEntry)); } + MetaspaceObj::Type type() const { return AdapterHandlerEntryType; } + + void remove_unshareable_info() NOT_CDS_RETURN; + void link() NOT_CDS_RETURN; }; +#if INCLUDE_CDS +class ArchivedAdapterTable; +#endif // INCLUDE_CDS + class AdapterHandlerLibrary: public AllStatic { friend class SharedRuntime; private: @@ -729,31 +780,54 @@ class AdapterHandlerLibrary: public AllStatic { static AdapterHandlerEntry* _obj_arg_handler; static AdapterHandlerEntry* _obj_int_arg_handler; static AdapterHandlerEntry* _obj_obj_arg_handler; +#if INCLUDE_CDS + static ArchivedAdapterTable _aot_adapter_handler_table; +#endif // INCLUDE_CDS static BufferBlob* buffer_blob(); static void initialize(); + static AdapterHandlerEntry* get_simple_adapter(const methodHandle& method); + static AdapterBlob* lookup_aot_cache(AdapterHandlerEntry* handler); static AdapterHandlerEntry* create_adapter(AdapterBlob*& new_adapter, int total_args_passed, BasicType* sig_bt, - bool allocate_code_blob); - static AdapterHandlerEntry* get_simple_adapter(const methodHandle& method); + bool is_transient = false); + static void create_abstract_method_handler(); + static void lookup_simple_adapters() NOT_CDS_RETURN; +#ifndef PRODUCT + static void print_adapter_handler_info(outputStream* st, AdapterHandlerEntry* handler, AdapterBlob* adapter_blob); +#endif // PRODUCT public: - static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, - address i2c_entry, - address c2i_entry, - address c2i_unverified_entry, - address c2i_no_clinit_check_entry = nullptr); + static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint); static void create_native_wrapper(const methodHandle& method); static AdapterHandlerEntry* get_adapter(const methodHandle& method); + static AdapterHandlerEntry* lookup(int total_args_passed, BasicType* sig_bt); + static bool generate_adapter_code(AdapterBlob*& adapter_blob, + AdapterHandlerEntry* handler, + int total_args_passed, + BasicType* sig_bt, + bool is_transient); + +#ifdef ASSERT + static void verify_adapter_sharing(int total_args_passed, BasicType* sig_bt, AdapterHandlerEntry* cached); +#endif // ASSERT static void print_handler(const CodeBlob* b) { print_handler_on(tty, b); } static void print_handler_on(outputStream* st, const CodeBlob* b); static bool contains(const CodeBlob* b); + static const char* name(AdapterFingerPrint* fingerprint); + static uint32_t id(AdapterFingerPrint* fingerprint); #ifndef PRODUCT static void print_statistics(); #endif // PRODUCT + static bool is_abstract_method_adapter(AdapterHandlerEntry* adapter); + + static AdapterBlob* link_aot_adapter_handler(AdapterHandlerEntry* handler) NOT_CDS_RETURN_(nullptr); + static void dump_aot_adapter_table() NOT_CDS_RETURN; + static void serialize_shared_table_header(SerializeClosure* soc) NOT_CDS_RETURN; + static void link_aot_adapters() NOT_CDS_RETURN; }; #endif // SHARE_RUNTIME_SHAREDRUNTIME_HPP diff --git a/src/hotspot/share/runtime/stackWatermark.hpp b/src/hotspot/share/runtime/stackWatermark.hpp index 8d51f694a1d71..4f1c65c903c8a 100644 --- a/src/hotspot/share/runtime/stackWatermark.hpp +++ b/src/hotspot/share/runtime/stackWatermark.hpp @@ -102,7 +102,6 @@ class StackWatermark : public CHeapObj { void yield_processing(); static bool has_barrier(const frame& f); void ensure_safe(const frame& f); - void assert_is_frame_safe(const frame& f) NOT_DEBUG_RETURN; bool is_frame_safe(const frame& f); // API for consumers of the stack watermark barrier. @@ -151,6 +150,8 @@ class StackWatermark : public CHeapObj { void on_safepoint(); void start_processing(); void finish_processing(void* context); + + void assert_is_frame_safe(const frame& f) NOT_DEBUG_RETURN; }; #endif // SHARE_RUNTIME_STACKWATERMARK_HPP diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index b1b1f1d60566a..358434938f29a 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -101,8 +101,6 @@ jint StubRoutines::_verify_oop_count = 0; address StubRoutines::_string_indexof_array[4] = { nullptr }; -address StubRoutines::_vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH] = {{nullptr}, {nullptr}}; -address StubRoutines::_vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH] = {{nullptr}, {nullptr}}; const char* StubRoutines::get_blob_name(StubGenBlobId id) { assert(0 <= id && id < StubGenBlobId::NUM_BLOBIDS, "invalid blob id"); diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index 3189415a6c5d6..7548a97ced899 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -305,10 +305,6 @@ class StubRoutines: AllStatic { /* special case: stub employs array of entries */ - // Vector Math Routines - static address _vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH]; - static address _vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH]; - static bool is_stub_code(address addr) { return contains(addr); } // generate code to implement method contains diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index a42576b562d89..400d69ad510a3 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -92,7 +92,7 @@ Thread::Thread(MemTag mem_tag) { new HandleMark(this); // plain initialization - debug_only(_owned_locks = nullptr;) + DEBUG_ONLY(_owned_locks = nullptr;) NOT_PRODUCT(_skip_gcalot = false;) _jvmti_env_iteration_count = 0; set_allocated_bytes(0); @@ -379,7 +379,7 @@ bool Thread::is_JavaThread_protected_by_TLH(const JavaThread* target) { } void Thread::set_priority(Thread* thread, ThreadPriority priority) { - debug_only(check_for_dangling_thread_pointer(thread);) + DEBUG_ONLY(check_for_dangling_thread_pointer(thread);) // Can return an error! (void)os::set_priority(thread, priority); } @@ -488,7 +488,7 @@ void Thread::print_on(outputStream* st, bool print_extended_info) const { } ThreadsSMRSupport::print_info_on(this, st); st->print(" "); - debug_only(if (WizardMode) print_owned_locks_on(st);) + DEBUG_ONLY(if (WizardMode) print_owned_locks_on(st);) } void Thread::print() const { print_on(tty); } @@ -562,7 +562,7 @@ bool Thread::set_as_starting_thread(JavaThread* jt) { // short-duration critical sections where we're concerned // about native mutex_t or HotSpot Mutex:: latency. -void Thread::SpinAcquire(volatile int * adr, const char * LockName) { +void Thread::SpinAcquire(volatile int * adr) { if (Atomic::cmpxchg(adr, 0, 1) == 0) { return; // normal fast-path return } diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index d0c0e4d5f7373..81307c4acab42 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -605,7 +605,7 @@ class Thread: public ThreadShadow { // Low-level leaf-lock primitives used to implement synchronization. // Not for general synchronization use. - static void SpinAcquire(volatile int * Lock, const char * Name); + static void SpinAcquire(volatile int * Lock); static void SpinRelease(volatile int * Lock); #if defined(__APPLE__) && defined(AARCH64) diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index f865380fdb7de..77fae3becb5f3 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -702,6 +702,7 @@ static_field(Abstract_VM_Version, _s_internal_vm_info_string, const char*) \ static_field(Abstract_VM_Version, _features, uint64_t) \ static_field(Abstract_VM_Version, _features_string, const char*) \ + static_field(Abstract_VM_Version, _cpu_info_string, const char*) \ static_field(Abstract_VM_Version, _vm_major_version, int) \ static_field(Abstract_VM_Version, _vm_minor_version, int) \ static_field(Abstract_VM_Version, _vm_security_version, int) \ diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index d257ef61be680..17906520f5cbb 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -37,6 +37,7 @@ #include "compiler/directivesParser.hpp" #include "gc/shared/gcVMOperations.hpp" #include "jvm.h" +#include "memory/metaspaceUtils.hpp" #include "memory/metaspace/metaspaceDCmd.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -411,7 +412,8 @@ void RunFinalizationDCmd::execute(DCmdSource source, TRAPS) { void HeapInfoDCmd::execute(DCmdSource source, TRAPS) { MutexLocker hl(THREAD, Heap_lock); - Universe::heap()->print_on(output()); + Universe::heap()->print_heap_on(output()); + MetaspaceUtils::print_on(output()); } void FinalizerInfoDCmd::execute(DCmdSource source, TRAPS) { diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index 7a2c8d5296985..a042a3909253a 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -455,7 +455,7 @@ class AbstractDumpWriter : public CHeapObj { void AbstractDumpWriter::write_fast(const void* s, size_t len) { assert(!_in_dump_segment || (_sub_record_left >= len), "sub-record too large"); assert(buffer_size() - position() >= len, "Must fit"); - debug_only(_sub_record_left -= len); + DEBUG_ONLY(_sub_record_left -= len); memcpy(buffer() + position(), s, len); set_position(position() + len); } @@ -467,7 +467,7 @@ bool AbstractDumpWriter::can_write_fast(size_t len) { // write raw bytes void AbstractDumpWriter::write_raw(const void* s, size_t len) { assert(!_in_dump_segment || (_sub_record_left >= len), "sub-record too large"); - debug_only(_sub_record_left -= len); + DEBUG_ONLY(_sub_record_left -= len); // flush buffer to make room. while (len > buffer_size() - position()) { @@ -591,8 +591,8 @@ void AbstractDumpWriter::start_sub_record(u1 tag, u4 len) { return; } - debug_only(_sub_record_left = len); - debug_only(_sub_record_ended = false); + DEBUG_ONLY(_sub_record_left = len); + DEBUG_ONLY(_sub_record_ended = false); write_u1(tag); } @@ -601,7 +601,7 @@ void AbstractDumpWriter::end_sub_record() { assert(_in_dump_segment, "must be in dump segment"); assert(_sub_record_left == 0, "sub-record not written completely"); assert(!_sub_record_ended, "Must not have ended yet"); - debug_only(_sub_record_ended = true); + DEBUG_ONLY(_sub_record_ended = true); } // Supports I/O operations for a dump diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 89ee69242c965..d320e17fafbfb 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -228,7 +228,7 @@ void ThreadService::current_thread_exiting(JavaThread* jt, bool daemon) { // FIXME: JVMTI should call this function Handle ThreadService::get_current_contended_monitor(JavaThread* thread) { assert(thread != nullptr, "should be non-null"); - debug_only(Thread::check_for_dangling_thread_pointer(thread);) + DEBUG_ONLY(Thread::check_for_dangling_thread_pointer(thread);) // This function can be called on a target JavaThread that is not // the caller and we are not at a safepoint. So it is possible for diff --git a/src/hotspot/share/utilities/bitMap.cpp b/src/hotspot/share/utilities/bitMap.cpp index 03eb1620cf136..14b1dc8d10f36 100644 --- a/src/hotspot/share/utilities/bitMap.cpp +++ b/src/hotspot/share/utilities/bitMap.cpp @@ -688,7 +688,7 @@ BitMap::idx_t BitMap::count_one_bits(idx_t beg, idx_t end) const { } -void BitMap::print_on_error(outputStream* st, const char* prefix) const { +void BitMap::print_range_on(outputStream* st, const char* prefix) const { st->print_cr("%s[" PTR_FORMAT ", " PTR_FORMAT ")", prefix, p2i(map()), p2i((char*)map() + (size() >> LogBitsPerByte))); } diff --git a/src/hotspot/share/utilities/bitMap.hpp b/src/hotspot/share/utilities/bitMap.hpp index 434993d45b902..af547eb539a59 100644 --- a/src/hotspot/share/utilities/bitMap.hpp +++ b/src/hotspot/share/utilities/bitMap.hpp @@ -393,7 +393,7 @@ class BitMap { void write_to(bm_word_t* buffer, size_t buffer_size_in_bytes) const; // Printing - void print_on_error(outputStream* st, const char* prefix) const; + void print_range_on(outputStream* st, const char* prefix) const; void print_on(outputStream* st) const; }; diff --git a/src/hotspot/share/utilities/compilerWarnings.hpp b/src/hotspot/share/utilities/compilerWarnings.hpp index ddcaf022fa690..bf5ca5b489393 100644 --- a/src/hotspot/share/utilities/compilerWarnings.hpp +++ b/src/hotspot/share/utilities/compilerWarnings.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -62,9 +62,6 @@ #ifndef PRAGMA_FORMAT_NONLITERAL_IGNORED #define PRAGMA_FORMAT_NONLITERAL_IGNORED #endif -#ifndef PRAGMA_FORMAT_IGNORED -#define PRAGMA_FORMAT_IGNORED -#endif #ifndef PRAGMA_STRINGOP_TRUNCATION_IGNORED #define PRAGMA_STRINGOP_TRUNCATION_IGNORED @@ -88,23 +85,61 @@ // Support warnings for use of certain C functions, except where explicitly // permitted. + +// FORBID_C_FUNCTION(Signature, Alternative) +// - Signature: the function that should not normally be used. +// - Alternative: a string literal that may be used in a warning about a use, +// often suggesting an alternative. +// Declares the C-linkage function designated by Signature to be deprecated, +// using the `deprecated` attribute with Alternative as an argument. // -// FORBID_C_FUNCTION(signature, alternative) -// - signature: the function that should not normally be used. -// - alternative: a string that may be used in a warning about a use, typically -// suggesting an alternative. +// The variants with IMPORTED in the name are to deal with Windows +// requirements, using FORBIDDEN_FUNCTION_IMPORT_SPEC. See the Visual +// Studio definition of that macro for more details. The default has +// an empty expansion. The potentially added spec must precede the +// base signature but follow all attributes. // -// ALLOW_C_FUNCTION(name, ... using statement ...) -// - name: the name of a forbidden function whose use is permitted in statement. -// - statement: a use of the otherwise forbidden function. Using a variadic -// tail allows the statement to contain non-nested commas. +// FORBID_NORETURN_C_FUNCTION deals with a clang issue. See the clang +// definition of FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE for more +// details. The default expands to `[[noreturn]]`. +#define FORBID_C_FUNCTION(Signature, Alternative) \ + extern "C" { [[deprecated(Alternative)]] Signature; } -#ifndef FORBID_C_FUNCTION -#define FORBID_C_FUNCTION(signature, alternative) +#ifndef FORBIDDEN_FUNCTION_IMPORT_SPEC +#define FORBIDDEN_FUNCTION_IMPORT_SPEC #endif -#ifndef ALLOW_C_FUNCTION -#define ALLOW_C_FUNCTION(name, ...) __VA_ARGS__ +#ifndef FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE +#define FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE [[noreturn]] #endif +#ifndef FORBIDDEN_FUNCTION_IGNORE_CLANG_FORTIFY_WARNING +#define FORBIDDEN_FUNCTION_IGNORE_CLANG_FORTIFY_WARNING +#endif + +#define FORBID_IMPORTED_C_FUNCTION(Signature, Alternative) \ + FORBID_C_FUNCTION(FORBIDDEN_FUNCTION_IMPORT_SPEC Signature, Alternative) + +#define FORBID_NORETURN_C_FUNCTION(Signature, Alternative) \ + FORBID_C_FUNCTION(FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE Signature, Alternative) + +#define FORBID_IMPORTED_NORETURN_C_FUNCTION(Signature, Alternative) \ + FORBID_NORETURN_C_FUNCTION(FORBIDDEN_FUNCTION_IMPORT_SPEC Signature, Alternative) + +// A BEGIN/END_ALLOW_FORBIDDEN_FUNCTIONS pair establishes a scope in which the +// deprecation warnings used to forbid the use of certain functions are +// suppressed. These macros are not intended for warning suppression at +// individual call sites; see permitForbiddenFunctions.hpp for the approach +// taken for that where needed. Rather, these are used to suppress warnings +// from 3rd-party code included by HotSpot, such as the gtest framework and +// C++ Standard Library headers, which may refer to functions that are +// disallowed in other parts of HotSpot. They are also used in the +// implementation of the "permit" mechanism. +#define BEGIN_ALLOW_FORBIDDEN_FUNCTIONS \ + PRAGMA_DIAG_PUSH \ + PRAGMA_DEPRECATED_IGNORED + +#define END_ALLOW_FORBIDDEN_FUNCTIONS \ + PRAGMA_DIAG_POP + #endif // SHARE_UTILITIES_COMPILERWARNINGS_HPP diff --git a/src/hotspot/share/utilities/compilerWarnings_gcc.hpp b/src/hotspot/share/utilities/compilerWarnings_gcc.hpp index 1dd5892e4c348..c10650598cd21 100644 --- a/src/hotspot/share/utilities/compilerWarnings_gcc.hpp +++ b/src/hotspot/share/utilities/compilerWarnings_gcc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -51,8 +51,6 @@ PRAGMA_DISABLE_GCC_WARNING("-Wformat-nonliteral") \ PRAGMA_DISABLE_GCC_WARNING("-Wformat-security") -#define PRAGMA_FORMAT_IGNORED PRAGMA_DISABLE_GCC_WARNING("-Wformat") - // Disable -Wstringop-truncation which is introduced in GCC 8. // https://gcc.gnu.org/gcc-8/changes.html #if !defined(__clang_major__) && (__GNUC__ >= 8) @@ -70,32 +68,71 @@ #define PRAGMA_ZERO_AS_NULL_POINTER_CONSTANT_IGNORED \ PRAGMA_DISABLE_GCC_WARNING("-Wzero-as-null-pointer-constant") -#if (__GNUC__ >= 10) -// TODO: Re-enable warning attribute for Clang once -// https://github.com/llvm/llvm-project/issues/56519 is fixed and released. -// || (defined(__clang_major__) && (__clang_major__ >= 14)) +#define PRAGMA_DEPRECATED_IGNORED \ + PRAGMA_DISABLE_GCC_WARNING("-Wdeprecated-declarations") + +// This macro is used by the NORETURN variants of FORBID_C_FUNCTION. +// +// The [[noreturn]] attribute requires that the first declaration of a +// function has it if any have it. +// +// gcc, clang, and MSVC all provide compiler-specific alternatives to that +// attribute: __attribute__((noreturn)) for gcc and clang, +// __declspec(noreturn) for MSVC and clang. gcc and MSVC treat their +// respective compiler-specific alternatives as satisfying that requirement. +// clang does not. +// +// So clang warns if we use [[noreturn]] in the forbidding declaration and the +// library header has already been included and uses the compiler-specific +// attribute. Similarly, clang warns if we use the compiler-specific attribute +// while the library uses [[noreturn]] and the library header is included +// after the forbidding declaration. +// +// For now, we're only going to worry about the standard library, and not +// noreturn functions in some other library that we might want to forbid in +// the future. If there's more than one library to be accounted for, then +// things may get more complicated. +// +// There are several ways we could deal with this. +// +// Probably the most robust is to use the same style of noreturn attribute as +// is used by the library providing the function. That way it doesn't matter +// in which order the inclusion of the library header and the forbidding are +// performed. We could use configure to determine which to use and provide a +// macro to select on here. +// +// Another approach is to always use __attribute__ noreturn in the forbidding +// declaration, but ensure the relevant library header has been included +// before the forbidding declaration. Since there are currently only a couple +// of affected functions, this is easier to implement. So this is the +// approach being taken for now. +// +// clang's failure to treat the compiler-specific form as counting toward the +// [[noreturn]] requirement is arguably a clang bug. +// https://github.com/llvm/llvm-project/issues/131700 + +#ifdef __clang__ +#define FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE __attribute__((__noreturn__)) +#endif -// Use "warning" attribute to detect uses of "forbidden" functions. +// This macro is used to suppress a warning for some uses of FORBID_C_FUNCTION. // -// Note: The warning attribute is available since GCC 9, but disabling pragmas -// does not work reliably in ALLOW_C_FUNCTION. GCC 10+ and up work fine. +// libstdc++ provides inline definitions of some functions to support +// _FORTIFY_SOURCE. clang warns about our forbidding declaration adding the +// [[deprecated]] attribute following such a definition: +// "warning: attribute declaration must precede definition [-Wignored-attributes]" +// Use this macro to suppress the warning, not getting protection when using +// that combination. Other build combinations should provide sufficient +// coverage. // -// Note: _FORTIFY_SOURCE transforms calls to certain functions into calls to -// associated "checking" functions, and that transformation seems to occur -// *before* the attribute check. We use fortification in fastdebug builds, -// so uses of functions that are both forbidden and fortified won't cause -// forbidden warnings in such builds. -#define FORBID_C_FUNCTION(signature, alternative) \ - extern "C" __attribute__((__warning__(alternative))) signature; - -// Disable warning attribute over the scope of the affected statement. -// The name serves only to document the intended function. -#define ALLOW_C_FUNCTION(name, ...) \ - PRAGMA_DIAG_PUSH \ - PRAGMA_DISABLE_GCC_WARNING("-Wattribute-warning") \ - __VA_ARGS__ \ - PRAGMA_DIAG_POP - -#endif // gcc10+ +// clang's warning in this case is arguably a clang bug. +// https://github.com/llvm/llvm-project/issues/135481 +// This issue has been fixed, with the fix probably appearing in clang 21. +#if defined(__clang__) && defined(_FORTIFY_SOURCE) +#if _FORTIFY_SOURCE > 0 +#define FORBIDDEN_FUNCTION_IGNORE_CLANG_FORTIFY_WARNING \ + PRAGMA_DISABLE_GCC_WARNING("-Wignored-attributes") +#endif +#endif #endif // SHARE_UTILITIES_COMPILERWARNINGS_GCC_HPP diff --git a/src/hotspot/share/utilities/compilerWarnings_visCPP.hpp b/src/hotspot/share/utilities/compilerWarnings_visCPP.hpp index de76b522c7b41..6321388c48af7 100644 --- a/src/hotspot/share/utilities/compilerWarnings_visCPP.hpp +++ b/src/hotspot/share/utilities/compilerWarnings_visCPP.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -30,39 +30,17 @@ #define PRAGMA_DIAG_PUSH _Pragma("warning(push)") #define PRAGMA_DIAG_POP _Pragma("warning(pop)") -// The Visual Studio implementation of FORBID_C_FUNCTION explicitly does -// nothing, because there doesn't seem to be a way to implement it for Visual -// Studio. What seems the most likely approach is to use deprecation warnings, -// but that runs into problems. -// -// (1) Declaring the function deprecated (using either __declspec(deprecated) -// or the C++14 [[deprecated]] attribute) fails with warnings like this: -// warning C4273: 'exit': inconsistent dll linkage -// It seems attributes are not simply additive with this compiler. -// -// (2) Additionally adding __declspec(dllimport) to deal with (1) fails with -// warnings like this: -// error C2375: 'vsnprintf': redefinition; different linkage -// It seems some functions in the set of interest have different linkage than -// others ("exit" is marked imported while "vsnprintf" is not, for example). -// That makes it difficult to provide a generic macro. -// -// (3) Using __pragma(deprecated(name)) fails with -// warning C4995: 'frobnicate': name was marked as #pragma deprecated -// for a *declaration* (not a use) of a 'frobnicate' function. -// -// ALLOW_C_FUNCTIONS disables deprecation warnings over the statement scope. -// Some of the functions we're interested in allowing are conditionally -// deprecated on Windows, under the control of various preprocessor defines -// such as _CRT_SECURE_NO_WARNINGS. Annotating vetted uses allows those -// warnings to catch unchecked uses. +#define PRAGMA_DEPRECATED_IGNORED PRAGMA_DISABLE_MSVC_WARNING(4996) -#define FORBID_C_FUNCTION(signature, alternative) +// This macro is used by the IMPORTED variants of FORBID_C_FUNCTION. +// +// Some, but not all, functions we want to forbid using must include a +// `__declspec(dllimport)` in the declaration. Failure to do so where needed +// leads to "redefinition; different linkage" errors for the forbidding +// declaration. But including a dllimport specifier if not present in the +// compiler's header leads to the same errors. It seems one must just know +// which are imported and which are not, and use the specifier accordingly. -#define ALLOW_C_FUNCTION(name, ...) \ - PRAGMA_DIAG_PUSH \ - PRAGMA_DISABLE_MSVC_WARNING(4996) \ - __VA_ARGS__ \ - PRAGMA_DIAG_POP +#define FORBIDDEN_FUNCTION_IMPORT_SPEC __declspec(dllimport) #endif // SHARE_UTILITIES_COMPILERWARNINGS_VISCPP_HPP diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 9413f1f72d7a9..abe3d6757b5fb 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -712,7 +712,7 @@ struct TestMultipleStaticAssertFormsInClassScope { // Support for showing register content on asserts/guarantees. #ifdef CAN_SHOW_REGISTERS_ON_ASSERT void initialize_assert_poison() { - char* page = os::reserve_memory(os::vm_page_size(), !ExecMem, mtInternal); + char* page = os::reserve_memory(os::vm_page_size(), mtInternal); if (page) { if (os::commit_memory(page, os::vm_page_size(), !ExecMem) && os::protect_memory(page, os::vm_page_size(), os::MEM_PROT_NONE)) { diff --git a/src/hotspot/share/utilities/events.cpp b/src/hotspot/share/utilities/events.cpp index 5b051305696bf..de86772816aac 100644 --- a/src/hotspot/share/utilities/events.cpp +++ b/src/hotspot/share/utilities/events.cpp @@ -26,14 +26,13 @@ #include "memory/allocation.inline.hpp" #include "oops/instanceKlass.hpp" #include "oops/symbol.hpp" +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/osThread.hpp" -#include "runtime/threadCritical.hpp" #include "runtime/timer.hpp" #include "utilities/events.hpp" - EventLog* Events::_logs = nullptr; StringEventLog* Events::_messages = nullptr; StringEventLog* Events::_memprotect_messages = nullptr; @@ -48,18 +47,19 @@ StringEventLog* Events::_deopt_messages = nullptr; StringEventLog* Events::_dll_messages = nullptr; EventLog::EventLog() { - // This normally done during bootstrap when we're only single - // threaded but use a ThreadCritical to ensure inclusion in case - // some are created slightly late. - ThreadCritical tc; - _next = Events::_logs; - Events::_logs = this; + // This is normally done during bootstrap when we're only single threaded, + // but use lock free add because there are some events that are created later. + EventLog* old_head; + do { + old_head = Atomic::load(&Events::_logs); + _next = old_head; + } while (Atomic::cmpxchg(&Events::_logs, old_head, this) != old_head); } // For each registered event logger, print out the current contents of // the buffer. void Events::print_all(outputStream* out, int max) { - EventLog* log = _logs; + EventLog* log = Atomic::load(&Events::_logs); while (log != nullptr) { log->print_log_on(out, max); log = log->next(); @@ -68,7 +68,7 @@ void Events::print_all(outputStream* out, int max) { // Print a single event log specified by name. void Events::print_one(outputStream* out, const char* log_name, int max) { - EventLog* log = _logs; + EventLog* log = Atomic::load(&Events::_logs); int num_printed = 0; while (log != nullptr) { if (log->matches_name_or_handle(log_name)) { @@ -81,7 +81,7 @@ void Events::print_one(outputStream* out, const char* log_name, int max) { if (num_printed == 0) { out->print_cr("The name \"%s\" did not match any known event log. " "Valid event log names are:", log_name); - EventLog* log = _logs; + EventLog* log = Atomic::load(&Events::_logs); while (log != nullptr) { log->print_names(out); out->cr(); diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index 724797eb9f392..a3ad480f50c8b 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -540,6 +540,7 @@ inline void ExceptionMark::check_no_pending_exception() { if (_thread->has_pending_exception()) { oop exception = _thread->pending_exception(); _thread->clear_pending_exception(); // Needed to avoid infinite recursion + ResourceMark rm; exception->print(); fatal("ExceptionMark constructor expects no pending exceptions"); } @@ -551,6 +552,7 @@ ExceptionMark::~ExceptionMark() { Handle exception(_thread, _thread->pending_exception()); _thread->clear_pending_exception(); // Needed to avoid infinite recursion if (is_init_completed()) { + ResourceMark rm; exception->print(); fatal("ExceptionMark destructor expects no pending exceptions"); } else { diff --git a/src/hotspot/share/utilities/forbiddenFunctions.hpp b/src/hotspot/share/utilities/forbiddenFunctions.hpp new file mode 100644 index 0000000000000..0bc34adf213b3 --- /dev/null +++ b/src/hotspot/share/utilities/forbiddenFunctions.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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_UTILITIES_FORBIDDENFUNCTIONS_HPP +#define SHARE_UTILITIES_FORBIDDENFUNCTIONS_HPP + +#include "utilities/compilerWarnings.hpp" +#include "utilities/macros.hpp" + +// For types used in the signatures. +#include +#include + +// Workaround for noreturn functions: exit, _exit, _Exit - see the clang +// definition of FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE. +#ifdef __clang__ +#include +#endif + +#ifdef _WINDOWS +#include "forbiddenFunctions_windows.hpp" +#else +#include "forbiddenFunctions_posix.hpp" +#endif + +// Forbid the use of various C library functions. Some of these have os:: +// replacements that should be used instead. Others are considered obsolete +// or have security concerns, either with preferred alternatives, or to be +// avoided entirely. + +FORBID_IMPORTED_NORETURN_C_FUNCTION(void exit(int), "use os::exit") +FORBID_IMPORTED_NORETURN_C_FUNCTION(void _Exit(int), "use os::exit") + +// Windows puts _exit in , POSIX in . +FORBID_IMPORTED_NORETURN_C_FUNCTION(void _exit(int), "use os::exit") + +FORBID_IMPORTED_C_FUNCTION(char* strerror(int), "use os::strerror"); +FORBID_IMPORTED_C_FUNCTION(char* strtok(char*, const char*), "use strtok_r"); + +FORBID_C_FUNCTION(int sprintf(char*, const char*, ...), "use os::snprintf"); + +PRAGMA_DIAG_PUSH +FORBIDDEN_FUNCTION_IGNORE_CLANG_FORTIFY_WARNING +FORBID_C_FUNCTION(int vsprintf(char*, const char*, va_list), "use os::vsnprintf"); +FORBID_C_FUNCTION(int vsnprintf(char*, size_t, const char*, va_list), "use os::vsnprintf"); +PRAGMA_DIAG_POP + +// All of the following functions return raw C-heap pointers (sometimes as an +// option, e.g. realpath or getwd) or, in case of free(), take raw C-heap +// pointers. We generally want allocation to be done through NMT. +FORBID_IMPORTED_C_FUNCTION(void* malloc(size_t size), "use os::malloc"); +FORBID_IMPORTED_C_FUNCTION(void free(void *ptr), "use os::free"); +FORBID_IMPORTED_C_FUNCTION(void* calloc(size_t nmemb, size_t size), "use os::malloc and zero out manually"); +FORBID_IMPORTED_C_FUNCTION(void* realloc(void *ptr, size_t size), "use os::realloc"); +FORBID_IMPORTED_C_FUNCTION(char* strdup(const char *s), "use os::strdup"); +FORBID_IMPORTED_C_FUNCTION(wchar_t* wcsdup(const wchar_t *s), "don't use"); + +#endif // SHARE_UTILITIES_FORBIDDENFUNCTIONS_HPP diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 95ebc77d1c988..cc5f3ebb29126 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -27,6 +27,7 @@ #include "utilities/compilerWarnings.hpp" #include "utilities/debug.hpp" +#include "utilities/forbiddenFunctions.hpp" #include "utilities/macros.hpp" // Get constants like JVM_T_CHAR and JVM_SIGNATURE_INT, before pulling in . @@ -173,35 +174,6 @@ inline uintptr_t p2u(const volatile void* p) { #define BOOL_TO_STR(_b_) ((_b_) ? "true" : "false") -//---------------------------------------------------------------------------------------------------- -// Forbid the use of various C library functions. -// Some of these have os:: replacements that should normally be used instead. -// Others are considered security concerns, with preferred alternatives. - -FORBID_C_FUNCTION(void exit(int), "use os::exit"); -FORBID_C_FUNCTION(void _exit(int), "use os::exit"); -FORBID_C_FUNCTION(char* strerror(int), "use os::strerror"); -FORBID_C_FUNCTION(char* strtok(char*, const char*), "use strtok_r"); -FORBID_C_FUNCTION(int sprintf(char*, const char*, ...), "use os::snprintf"); -FORBID_C_FUNCTION(int vsprintf(char*, const char*, va_list), "use os::vsnprintf"); -FORBID_C_FUNCTION(int vsnprintf(char*, size_t, const char*, va_list), "use os::vsnprintf"); - -// All of the following functions return raw C-heap pointers (sometimes as an option, e.g. realpath or getwd) -// or, in case of free(), take raw C-heap pointers. Don't use them unless you are really sure you must. -FORBID_C_FUNCTION(void* malloc(size_t size), "use os::malloc"); -FORBID_C_FUNCTION(void* calloc(size_t nmemb, size_t size), "use os::malloc and zero out manually"); -FORBID_C_FUNCTION(void free(void *ptr), "use os::free"); -FORBID_C_FUNCTION(void* realloc(void *ptr, size_t size), "use os::realloc"); -FORBID_C_FUNCTION(char* strdup(const char *s), "use os::strdup"); -FORBID_C_FUNCTION(char* strndup(const char *s, size_t n), "don't use"); -FORBID_C_FUNCTION(int posix_memalign(void **memptr, size_t alignment, size_t size), "don't use"); -FORBID_C_FUNCTION(void* aligned_alloc(size_t alignment, size_t size), "don't use"); -FORBID_C_FUNCTION(char* realpath(const char* path, char* resolved_path), "use os::realpath"); -FORBID_C_FUNCTION(char* get_current_dir_name(void), "use os::get_current_directory()"); -FORBID_C_FUNCTION(char* getwd(char *buf), "use os::get_current_directory()"); -FORBID_C_FUNCTION(wchar_t* wcsdup(const wchar_t *s), "don't use"); -FORBID_C_FUNCTION(void* reallocf(void *ptr, size_t size), "don't use"); - //---------------------------------------------------------------------------------------------------- // Constants @@ -297,6 +269,9 @@ inline jdouble jdouble_cast(jlong x); const jlong min_jlong = CONST64(0x8000000000000000); const jlong max_jlong = CONST64(0x7fffffffffffffff); +// for timer info max values which include all bits, 0xffffffffffffffff +const jlong all_bits_jlong = ~jlong(0); + //------------------------------------------- // Constant for jdouble const jlong min_jlongDouble = CONST64(0x0000000000000001); @@ -307,7 +282,6 @@ const jdouble max_jdouble = jdouble_cast(max_jlongDouble); const size_t K = 1024; const size_t M = K*K; const size_t G = M*K; -const size_t HWperKB = K / sizeof(HeapWord); // Constants for converting from a base unit to milli-base units. For // example from seconds to milliseconds and microseconds diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 31e797fc1927d..86b7ed5f917ee 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -622,7 +622,7 @@ class GrowableArrayMetadata { uintptr_t _bits; // resource area nesting at creation - debug_only(GrowableArrayNestingCheck _nesting_check;) + DEBUG_ONLY(GrowableArrayNestingCheck _nesting_check;) // Resource allocation static uintptr_t bits() { @@ -645,19 +645,19 @@ class GrowableArrayMetadata { // Resource allocation GrowableArrayMetadata() : _bits(bits()) - debug_only(COMMA _nesting_check(true)) { + DEBUG_ONLY(COMMA _nesting_check(true)) { } // Arena allocation GrowableArrayMetadata(Arena* arena) : _bits(bits(arena)) - debug_only(COMMA _nesting_check(arena)) { + DEBUG_ONLY(COMMA _nesting_check(arena)) { } // CHeap allocation GrowableArrayMetadata(MemTag mem_tag) : _bits(bits(mem_tag)) - debug_only(COMMA _nesting_check(false)) { + DEBUG_ONLY(COMMA _nesting_check(false)) { } #ifdef ASSERT @@ -725,7 +725,7 @@ class GrowableArray : public GrowableArrayWithAllocator> { GrowableArrayMetadata _metadata; - void init_checks() const { debug_only(_metadata.init_checks(this);) } + void init_checks() const { DEBUG_ONLY(_metadata.init_checks(this);) } // Where are we going to allocate memory? bool on_C_heap() const { return _metadata.on_C_heap(); } @@ -734,7 +734,7 @@ class GrowableArray : public GrowableArrayWithAllocator> { E* allocate() { if (on_resource_area()) { - debug_only(_metadata.on_resource_area_alloc_check()); + DEBUG_ONLY(_metadata.on_resource_area_alloc_check()); return allocate(this->_capacity); } @@ -743,7 +743,7 @@ class GrowableArray : public GrowableArrayWithAllocator> { } assert(on_arena(), "Sanity"); - debug_only(_metadata.on_arena_alloc_check()); + DEBUG_ONLY(_metadata.on_arena_alloc_check()); return allocate(this->_capacity, _metadata.arena()); } diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index 5c3cfaa0dd55e..a5caf316aa3b8 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -377,13 +377,10 @@ #define DEBUG_ONLY(code) code #define NOT_DEBUG(code) #define NOT_DEBUG_RETURN /*next token must be ;*/ -// Historical. -#define debug_only(code) code #else // ASSERT #define DEBUG_ONLY(code) #define NOT_DEBUG(code) code #define NOT_DEBUG_RETURN {} -#define debug_only(code) #endif // ASSERT #ifdef _LP64 diff --git a/src/hotspot/share/utilities/ostream.hpp b/src/hotspot/share/utilities/ostream.hpp index 46bd0b673c16a..0a99ee886ada3 100644 --- a/src/hotspot/share/utilities/ostream.hpp +++ b/src/hotspot/share/utilities/ostream.hpp @@ -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 @@ -176,6 +176,7 @@ class outputStream : public CHeapObjBase { extern outputStream* tty; // tty output class streamIndentor : public StackObj { +protected: outputStream* const _str; const int _amount; NONCOPYABLE(streamIndentor); @@ -186,14 +187,13 @@ class streamIndentor : public StackObj { ~streamIndentor() { _str->dec(_amount); } }; -class StreamAutoIndentor : public StackObj { - outputStream* const _os; +class StreamAutoIndentor : public streamIndentor { const bool _old; NONCOPYABLE(StreamAutoIndentor); public: - StreamAutoIndentor(outputStream* os) : - _os(os), _old(os->set_autoindent(true)) {} - ~StreamAutoIndentor() { _os->set_autoindent(_old); } + StreamAutoIndentor(outputStream* os, int indentation = 0) : + streamIndentor(os, indentation), _old(os->set_autoindent(true)) {} + ~StreamAutoIndentor() { _str->set_autoindent(_old); } }; // advisory locking for the shared tty stream: diff --git a/src/hotspot/share/utilities/permitForbiddenFunctions.hpp b/src/hotspot/share/utilities/permitForbiddenFunctions.hpp new file mode 100644 index 0000000000000..1ba42f8e386d8 --- /dev/null +++ b/src/hotspot/share/utilities/permitForbiddenFunctions.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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_UTILITIES_PERMITFORBIDDENFUNCTIONS_HPP +#define SHARE_UTILITIES_PERMITFORBIDDENFUNCTIONS_HPP + +#include "utilities/compilerWarnings.hpp" +#include "utilities/globalDefinitions.hpp" + +#ifdef _WINDOWS +#include "permitForbiddenFunctions_windows.hpp" +#else +#include "permitForbiddenFunctions_posix.hpp" +#endif + +// Provide wrappers for some functions otherwise forbidden from use in HotSpot. +// +// There may be special circumstances where an otherwise forbidden function +// really does need to be used. One example is in the implementation of a +// corresponding os:: function. +// +// Wrapper functions are provided for such forbidden functions. These +// wrappers are defined in a context where the forbidding warnings are +// suppressed. They are defined in a special namespace, to highlight uses as +// unusual and requiring increased scrutiny. +// +// Note that there are several seemingly plausible shorter alternatives to +// these written-out wrapper functions. All that have been tried don't work +// for one reason or another. + +namespace permit_forbidden_function { +BEGIN_ALLOW_FORBIDDEN_FUNCTIONS + +[[noreturn]] inline void exit(int status) { ::exit(status); } +[[noreturn]] inline void _exit(int status) { ::_exit(status); } + +ATTRIBUTE_PRINTF(3, 0) +inline int vsnprintf(char* str, size_t size, const char* format, va_list ap) { + return ::vsnprintf(str, size, format, ap); +} + +inline void* malloc(size_t size) { return ::malloc(size); } +inline void free(void* ptr) { return ::free(ptr); } +inline void* calloc(size_t nmemb, size_t size) { return ::calloc(nmemb, size); } +inline void* realloc(void* ptr, size_t size) { return ::realloc(ptr, size); } + +inline char* strdup(const char* s) { return ::strdup(s); } + +END_ALLOW_FORBIDDEN_FUNCTIONS +} // namespace permit_forbidden_function + +#endif // SHARE_UTILITIES_PERMITFORBIDDENFUNCTIONS_HPP diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index e1940123d678a..ee402b8b894f5 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -1200,7 +1200,15 @@ void VMError::report(outputStream* st, bool _verbose) { if (Universe::heap() != nullptr) { st->print_cr("Heap:"); - Universe::heap()->print_on_error(st); + StreamAutoIndentor indentor(st, 1); + Universe::heap()->print_heap_on(st); + MetaspaceUtils::print_on(st); + st->cr(); + } + + STEP_IF("printing GC information", _verbose) + if (Universe::heap() != nullptr) { + Universe::heap()->print_gc_on(st); st->cr(); } @@ -1382,16 +1390,32 @@ void VMError::print_vm_info(outputStream* st) { } #endif - // STEP("printing heap information") + // Take heap lock over both heap and GC printing so that information is + // consistent. + { + MutexLocker ml(Heap_lock); - if (Universe::is_fully_initialized()) { - MutexLocker hl(Heap_lock); - GCLogPrecious::print_on_error(st); - st->print_cr("Heap:"); - Universe::heap()->print_on_error(st); - st->cr(); - st->print_cr("Polling page: " PTR_FORMAT, p2i(SafepointMechanism::get_polling_page())); - st->cr(); + // STEP("printing heap information") + + if (Universe::is_fully_initialized()) { + GCLogPrecious::print_on_error(st); + + st->print_cr("Heap:"); + StreamAutoIndentor indentor(st, 1); + Universe::heap()->print_heap_on(st); + MetaspaceUtils::print_on(st); + st->cr(); + } + + // STEP("printing GC information") + + if (Universe::is_fully_initialized()) { + Universe::heap()->print_gc_on(st); + st->cr(); + + st->print_cr("Polling page: " PTR_FORMAT, p2i(SafepointMechanism::get_polling_page())); + st->cr(); + } } // STEP("printing metaspace information") diff --git a/src/java.base/aix/native/libsyslookup/syslookup.c b/src/java.base/aix/native/libsyslookup/syslookup.c index 66bd50e01a0ef..95aff2d430011 100644 --- a/src/java.base/aix/native/libsyslookup/syslookup.c +++ b/src/java.base/aix/native/libsyslookup/syslookup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, IBM Corp. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -29,6 +29,10 @@ #include #include +#include "jni_util.h" + +DEF_STATIC_JNI_OnLoad + // Addresses of functions to be referenced using static linking. void* funcs[] = { //string.h diff --git a/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java b/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java index 8af64b11ac88a..98fa21230091b 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/PBEKey.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 @@ -36,6 +36,7 @@ import java.util.Locale; import javax.crypto.SecretKey; import javax.crypto.spec.PBEKeySpec; +import sun.security.util.PBEUtil; import jdk.internal.ref.CleanerFactory; @@ -67,17 +68,7 @@ final class PBEKey implements SecretKey { // Should allow an empty password. passwd = new char[0]; } - // Accept "\0" to signify "zero-length password with no terminator". - if (!(passwd.length == 1 && passwd[0] == 0)) { - for (int i=0; i '\u007E')) { - throw new InvalidKeySpecException("Password is not ASCII"); - } - } - } - this.key = new byte[passwd.length]; - for (int i=0; i The string representation of the specified object is obtained as if - * by calling {@link String#valueOf(Object)}. - * - * @param obj - * An object whose string representation is to be written, - * may be {@code null}. - * - * @return This console - * - * @since 23 - */ - @PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES) - public Console println(Object obj) { - throw newUnsupportedOperationException(); - } - - /** - * Terminates the current line in this console's output stream using - * {@link System#lineSeparator()} and then flushes the console. - * - * @return This console - * - * @since 24 - */ - @PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES) - public Console println() { - return println(""); - } - - /** - * Writes a string representation of the specified object to this console's - * output stream and then flushes the console. - * - *

The string representation of the specified object is obtained as if - * by calling {@link String#valueOf(Object)}. - * - * @param obj - * An object whose string representation is to be written, - * may be {@code null}. - * - * @return This console - * - * @since 23 - */ - @PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES) - public Console print(Object obj) { - throw newUnsupportedOperationException(); - } - - /** - * Writes a prompt as if by calling {@code print}, then reads a single line - * of text from this console. - * - * @param prompt - * A prompt string, may be {@code null}. - * - * @throws IOError - * If an I/O error occurs. - * - * @return A string containing the line read from the console, not - * including any line-termination characters, or {@code null} - * if an end of stream has been reached without having read - * any characters. - * - * @since 23 - */ - @PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES) - public String readln(String prompt) { - throw newUnsupportedOperationException(); - } - - /** - * Reads a single line of text from this console. - * - * @throws IOError - * If an I/O error occurs. - * - * @return A string containing the line read from the console, not - * including any line-termination characters, or {@code null} - * if an end of stream has been reached without having read - * any characters. - * - * @since 24 - */ - @PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES) - public String readln() { - throw newUnsupportedOperationException(); - } - /** * Writes a formatted string to this console's output stream using * the specified format string and arguments with the diff --git a/src/java.base/share/classes/java/io/FilePermission.java b/src/java.base/share/classes/java/io/FilePermission.java index aa6f11e00ee87..883f0410b6dc8 100644 --- a/src/java.base/share/classes/java/io/FilePermission.java +++ b/src/java.base/share/classes/java/io/FilePermission.java @@ -34,11 +34,9 @@ import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; -import jdk.internal.access.JavaIOFilePermissionAccess; -import jdk.internal.access.SharedSecrets; import sun.nio.fs.DefaultFileSystemProvider; -import sun.security.util.FilePermCompat; import sun.security.util.SecurityConstants; +import sun.security.util.SecurityProperties; /** * This class represents access to a file or directory. A FilePermission consists @@ -155,6 +153,26 @@ public final class FilePermission extends Permission implements Serializable { private static final char RECURSIVE_CHAR = '-'; private static final char WILD_CHAR = '*'; + /** + * New behavior? Keep compatibility? + * The new behavior does not use the canonical path normalization + */ + private static final boolean nb = initNb(); + + // Initialize the nb flag from the System property jdk.io.permissionsUseCanonicalPath. + private static boolean initNb() { + String flag = SecurityProperties.getOverridableProperty( + "jdk.io.permissionsUseCanonicalPath"); + return switch (flag) { + case "true" -> false; // compatibility mode to canonicalize paths + case "false" -> true; // do not canonicalize + case null -> true; // default, do not canonicalize + default -> + throw new RuntimeException( + "Invalid jdk.io.permissionsUseCanonicalPath: " + flag); + }; + } + // public String toString() { // StringBuilder sb = new StringBuilder(); // sb.append("*** FilePermission on " + getName() + " ***"); @@ -232,51 +250,49 @@ private static Path altPath(Path in) { } } - static { - SharedSecrets.setJavaIOFilePermissionAccess( - /** - * Creates FilePermission objects with special internals. - * See {@link FilePermCompat#newPermPlusAltPath(Permission)} and - * {@link FilePermCompat#newPermUsingAltPath(Permission)}. - */ - new JavaIOFilePermissionAccess() { - public FilePermission newPermPlusAltPath(FilePermission input) { - if (!input.invalid && input.npath2 == null && !input.allFiles) { - Path npath2 = altPath(input.npath); - if (npath2 != null) { - // Please note the name of the new permission is - // different than the original so that when one is - // added to a FilePermissionCollection it will not - // be merged with the original one. - return new FilePermission(input.getName() + "#plus", - input, - input.npath, - npath2, - input.mask, - input.actions); - } - } - return input; - } - public FilePermission newPermUsingAltPath(FilePermission input) { - if (!input.invalid && !input.allFiles) { - Path npath2 = altPath(input.npath); - if (npath2 != null) { - // New name, see above. - return new FilePermission(input.getName() + "#using", - input, - npath2, - null, - input.mask, - input.actions); - } - } - return null; - } + // Construct a new Permission with altPath + // Used by test FilePermissionCollectionMerge + private FilePermission newPermPlusAltPath() { + System.err.println("PlusAlt path: " + this + ", npath: " + npath); + if (nb && !invalid && npath2 == null && !allFiles) { + Path npath2 = altPath(npath); + if (npath2 != null) { + // Please note the name of the new permission is + // different than the original so that when one is + // added to a FilePermissionCollection it will not + // be merged with the original one. + return new FilePermission(getName() + "#plus", + this, + npath, + npath2, + mask, + actions); } - ); + } + return this; } + // Construct a new Permission adding altPath + // Used by test FilePermissionCollectionMerge + private FilePermission newPermUsingAltPath() { + System.err.println("Alt path: " + this + ", npath: " + npath); + if (!invalid && !allFiles) { + Path npath2 = altPath(npath); + if (npath2 != null) { + // New name, see above. + return new FilePermission(getName() + "#using", + this, + npath2, + null, + mask, + actions); + } + } + return this; +} + + + /** * initialize a FilePermission object. Common to all constructors. * Also called during de-serialization. @@ -291,7 +307,7 @@ private void init(int mask) { if (mask == NONE) throw new IllegalArgumentException("invalid actions mask"); - if (FilePermCompat.nb) { + if (nb) { String name = getName(); if (name == null) @@ -567,7 +583,7 @@ boolean impliesIgnoreMask(FilePermission that) { if (that.allFiles) { return false; } - if (FilePermCompat.nb) { + if (nb) { // Left at least same level of wildness as right if ((this.recursive && that.recursive) != that.recursive || (this.directory && that.directory) != that.directory) { @@ -766,7 +782,7 @@ public boolean equals(Object obj) { if (this.invalid || that.invalid) { return false; } - if (FilePermCompat.nb) { + if (nb) { return (this.mask == that.mask) && (this.allFiles == that.allFiles) && this.npath.equals(that.npath) && @@ -789,7 +805,7 @@ public boolean equals(Object obj) { */ @Override public int hashCode() { - if (FilePermCompat.nb) { + if (nb) { return Objects.hash( mask, allFiles, directory, recursive, npath, npath2, invalid); } else { diff --git a/src/java.base/share/classes/java/io/IO.java b/src/java.base/share/classes/java/io/IO.java deleted file mode 100644 index a49a51041fc04..0000000000000 --- a/src/java.base/share/classes/java/io/IO.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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 java.io; - -import jdk.internal.javac.PreviewFeature; - -/** - * A collection of static convenience methods that provide access to - * {@linkplain System#console() system console} for implicitly declared classes. - * - *

Each of this class' methods throws {@link IOError} if the system console - * is {@code null}; otherwise, the effect is as if a similarly-named method - * had been called on that console. - * - *

Input and output from methods in this class use the character set of - * the system console as specified by {@link Console#charset}. - * - * @since 23 - */ -@PreviewFeature(feature = PreviewFeature.Feature.IMPLICIT_CLASSES) -public final class IO { - - private IO() { - throw new Error("no instances"); - } - - /** - * Writes a string representation of the specified object to the system - * console, terminates the line and then flushes that console. - * - *

The effect is as if {@link Console#println(Object) println(obj)} - * had been called on {@code System.console()}. - * - * @param obj the object to print, may be {@code null} - * - * @throws IOError if {@code System.console()} returns {@code null}, - * or if an I/O error occurs - */ - public static void println(Object obj) { - con().println(obj); - } - - /** - * Terminates the current line on the system console and then flushes - * that console. - * - *

The effect is as if {@link Console#println() println()} - * had been called on {@code System.console()}. - * - * @throws IOError if {@code System.console()} returns {@code null}, - * or if an I/O error occurs - * @since 24 - */ - public static void println() { - con().println(); - } - - /** - * Writes a string representation of the specified object to the system - * console and then flushes that console. - * - *

The effect is as if {@link Console#print(Object) print(obj)} - * had been called on {@code System.console()}. - * - * @param obj the object to print, may be {@code null} - * - * @throws IOError if {@code System.console()} returns {@code null}, - * or if an I/O error occurs - */ - public static void print(Object obj) { - con().print(obj); - } - - /** - * Writes a prompt as if by calling {@code print}, then reads a single line - * of text from the system console. - * - *

The effect is as if {@link Console#readln(String) readln(prompt)} - * had been called on {@code System.console()}. - * - * @param prompt the prompt string, may be {@code null} - * - * @return a string containing the line read from the system console, not - * including any line-termination characters. Returns {@code null} if an - * end of stream has been reached without having read any characters. - * - * @throws IOError if {@code System.console()} returns {@code null}, - * or if an I/O error occurs - */ - public static String readln(String prompt) { - return con().readln(prompt); - } - - /** - * Reads a single line of text from the system console. - * - *

The effect is as if {@link Console#readln() readln()} - * had been called on {@code System.console()}. - * - * @return a string containing the line read from the system console, not - * including any line-termination characters. Returns {@code null} if an - * end of stream has been reached without having read any characters. - * - * @throws IOError if {@code System.console()} returns {@code null}, - * or if an I/O error occurs - * @since 24 - */ - public static String readln() { - return con().readln(); - } - - private static Console con() { - var con = System.console(); - if (con != null) { - return con; - } else { - throw new IOError(null); - } - } -} diff --git a/src/java.base/share/classes/java/io/ProxyingConsole.java b/src/java.base/share/classes/java/io/ProxyingConsole.java index cc5cd92626470..5c8da9f51da19 100644 --- a/src/java.base/share/classes/java/io/ProxyingConsole.java +++ b/src/java.base/share/classes/java/io/ProxyingConsole.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -81,54 +81,6 @@ public Reader reader() { return reader; } - /** - * {@inheritDoc} - */ - @Override - public Console println(Object obj) { - synchronized (writeLock) { - delegate.println(obj); - } - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public Console print(Object obj) { - synchronized (writeLock) { - delegate.print(obj); - } - return this; - } - - /** - * {@inheritDoc} - * - * @throws IOError {@inheritDoc} - */ - @Override - public String readln(String prompt) { - synchronized (writeLock) { - synchronized (readLock) { - return delegate.readln(prompt); - } - } - } - - /** - * {@inheritDoc} - * - * @throws IOError {@inheritDoc} - */ - @Override - public String readln() { - synchronized (readLock) { - return delegate.readln(); - } - } - /** * {@inheritDoc} */ diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index c2f90e228020d..b8d209b8a6e24 100644 --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ import jdk.internal.math.FloatToDecimal; import jdk.internal.util.DecimalDigits; -import java.io.IOException; import java.nio.CharBuffer; import java.util.Arrays; import java.util.Spliterator; @@ -360,8 +359,12 @@ public void setLength(int newLength) { */ @Override public char charAt(int index) { + byte coder = this.coder; + byte[] value = this.value; + // Ensure count is less than or equal to capacity (racy reads and writes can produce inconsistent values) + int count = Math.min(this.count, value.length >> coder); checkIndex(index, count); - if (isLatin1()) { + if (coder == LATIN1) { return (char)(value[index] & 0xff); } return StringUTF16.getChar(value, index); @@ -420,6 +423,7 @@ public int codePointAt(int index) { * of this sequence. */ public int codePointBefore(int index) { + byte[] value = this.value; int i = index - 1; checkIndex(i, count); if (isLatin1()) { @@ -1730,7 +1734,7 @@ private final void putCharsAt(int index, CharSequence s, int off, int end) { } else { inflate(); // store c to make sure it has a UTF16 char - StringUTF16.putChar(this.value, j++, c); + StringUTF16.putCharSB(this.value, j++, c); i++; StringUTF16.putCharsSB(this.value, j, s, i, end); return; @@ -1825,7 +1829,7 @@ private final void appendChars(CharSequence s, int off, int end) { count = j; inflate(); // Store c to make sure sb has a UTF16 char - StringUTF16.putChar(this.value, j++, c); + StringUTF16.putCharSB(this.value, j++, c); count = j; i++; StringUTF16.putCharsSB(this.value, j, s, i, end); diff --git a/src/java.base/share/classes/java/lang/Boolean.java b/src/java.base/share/classes/java/lang/Boolean.java index aa64d799b6655..4c24e98a549ac 100644 --- a/src/java.base/share/classes/java/lang/Boolean.java +++ b/src/java.base/share/classes/java/lang/Boolean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -104,7 +104,7 @@ public final class Boolean implements java.io.Serializable, * Also consider using the final fields {@link #TRUE} and {@link #FALSE} * if possible. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Boolean(boolean value) { this.value = value; } @@ -124,7 +124,7 @@ public Boolean(boolean value) { * {@code boolean} primitive, or use {@link #valueOf(String)} * to convert a string to a {@code Boolean} object. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Boolean(String s) { this(parseBoolean(s)); } diff --git a/src/java.base/share/classes/java/lang/Byte.java b/src/java.base/share/classes/java/lang/Byte.java index 5835f65366fb7..accd448a0cdc5 100644 --- a/src/java.base/share/classes/java/lang/Byte.java +++ b/src/java.base/share/classes/java/lang/Byte.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -346,7 +346,7 @@ public static Byte decode(String nm) throws NumberFormatException { * {@link #valueOf(byte)} is generally a better choice, as it is * likely to yield significantly better space and time performance. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Byte(byte value) { this.value = value; } @@ -369,7 +369,7 @@ public Byte(byte value) { * {@code byte} primitive, or use {@link #valueOf(String)} * to convert a string to a {@code Byte} object. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Byte(String s) throws NumberFormatException { this.value = parseByte(s, 10); } diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index f5a9ab77b5240..a439a90761d48 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -9232,7 +9232,7 @@ public static final UnicodeScript forName(String scriptName) { * {@link #valueOf(char)} is generally a better choice, as it is * likely to yield significantly better space and time performance. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Character(char value) { this.value = value; } @@ -11089,6 +11089,7 @@ public static boolean isIdentifierIgnorable(int codePoint) { * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Emoji; * {@code false} otherwise. + * @spec https://unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmoji(int codePoint) { @@ -11107,6 +11108,7 @@ public static boolean isEmoji(int codePoint) { * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character has the Emoji Presentation * property; {@code false} otherwise. + * @spec https://unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmojiPresentation(int codePoint) { @@ -11125,6 +11127,7 @@ public static boolean isEmojiPresentation(int codePoint) { * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Emoji Modifier; * {@code false} otherwise. + * @spec https://unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmojiModifier(int codePoint) { @@ -11143,6 +11146,7 @@ public static boolean isEmojiModifier(int codePoint) { * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Emoji Modifier Base; * {@code false} otherwise. + * @spec https://unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmojiModifierBase(int codePoint) { @@ -11161,6 +11165,7 @@ public static boolean isEmojiModifierBase(int codePoint) { * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Emoji Component; * {@code false} otherwise. + * @spec https://unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isEmojiComponent(int codePoint) { @@ -11179,6 +11184,7 @@ public static boolean isEmojiComponent(int codePoint) { * @param codePoint the character (Unicode code point) to be tested. * @return {@code true} if the character is an Extended Pictographic; * {@code false} otherwise. + * @spec https://unicode.org/reports/tr51/ Unicode Emoji * @since 21 */ public static boolean isExtendedPictographic(int codePoint) { diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index d86baaac3621a..8be5ae8fa1521 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -1384,10 +1384,8 @@ public Set accessFlags() { isAnonymousClass() || isArray()) ? AccessFlag.Location.INNER_CLASS : AccessFlag.Location.CLASS; - return AccessFlag.maskToAccessFlags((location == AccessFlag.Location.CLASS) ? - getClassAccessFlagsRaw() : - getModifiers(), - location); + return getReflectionFactory().parseAccessFlags((location == AccessFlag.Location.CLASS) ? + getClassAccessFlagsRaw() : getModifiers(), location, this); } /** @@ -4125,7 +4123,7 @@ public boolean isSealed() { * type is returned. If the class is a primitive type then the latest class * file major version is returned and zero is returned for the minor version. */ - private int getClassFileVersion() { + int getClassFileVersion() { Class c = isArray() ? elementType() : this; return c.getClassFileVersion0(); } diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index b890ba51651d4..6082bc53297af 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -2565,22 +2565,38 @@ private void initializeJavaAssertionMaps() { */ private boolean trySetObjectField(String name, Object obj) { Unsafe unsafe = Unsafe.getUnsafe(); - Class k = ClassLoader.class; - long offset; - offset = unsafe.objectFieldOffset(k, name); + long offset = unsafe.objectFieldOffset(ClassLoader.class, name); return unsafe.compareAndSetReference(this, offset, null, obj); } + private void reinitObjectField(String name, Object obj) { + Unsafe unsafe = Unsafe.getUnsafe(); + long offset = unsafe.objectFieldOffset(ClassLoader.class, name); + + // Extra safety: check the types + Object current = unsafe.getReference(this, offset); + if (current.getClass() != obj.getClass()) { + throw new IllegalStateException("Wrong field type"); + } + + unsafe.putReference(this, offset, obj); + } + /** - * Called by the VM, during -Xshare:dump + * Called only by the VM, during -Xshare:dump. + * + * @implNote This is done while the JVM is running in single-threaded mode, + * and at the very end of Java bytecode execution. We know that no more classes + * will be loaded and none of the fields modified by this method will be used again. */ private void resetArchivedStates() { if (parallelLockMap != null) { - parallelLockMap.clear(); + reinitObjectField("parallelLockMap", new ConcurrentHashMap<>()); } - packages.clear(); - package2certs.clear(); + reinitObjectField("packages", new ConcurrentHashMap<>()); + reinitObjectField("package2certs", new ConcurrentHashMap<>()); classes.clear(); + classes.trimToSize(); classLoaderValueMap = null; } } diff --git a/src/java.base/share/classes/java/lang/Double.java b/src/java.base/share/classes/java/lang/Double.java index 54be998cac5fe..5ab6dce080b1a 100644 --- a/src/java.base/share/classes/java/lang/Double.java +++ b/src/java.base/share/classes/java/lang/Double.java @@ -1041,7 +1041,7 @@ public static boolean isFinite(double d) { * {@link #valueOf(double)} is generally a better choice, as it is * likely to yield significantly better space and time performance. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Double(double value) { this.value = value; } @@ -1062,7 +1062,7 @@ public Double(double value) { * {@code double} primitive, or use {@link #valueOf(String)} * to convert a string to a {@code Double} object. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Double(String s) throws NumberFormatException { value = parseDouble(s); } diff --git a/src/java.base/share/classes/java/lang/Float.java b/src/java.base/share/classes/java/lang/Float.java index 746d90db81e20..4344d9657b47a 100644 --- a/src/java.base/share/classes/java/lang/Float.java +++ b/src/java.base/share/classes/java/lang/Float.java @@ -668,7 +668,7 @@ public static boolean isFinite(float f) { * {@link #valueOf(float)} is generally a better choice, as it is * likely to yield significantly better space and time performance. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Float(float value) { this.value = value; } @@ -684,7 +684,7 @@ public Float(float value) { * static factory method {@link #valueOf(float)} method as follows: * {@code Float.valueOf((float)value)}. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Float(double value) { this.value = (float)value; } @@ -705,7 +705,7 @@ public Float(double value) { * {@code float} primitive, or use {@link #valueOf(String)} * to convert a string to a {@code Float} object. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Float(String s) throws NumberFormatException { value = parseFloat(s); } diff --git a/src/java.base/share/classes/java/lang/IO.java b/src/java.base/share/classes/java/lang/IO.java new file mode 100644 index 0000000000000..1f88de7e238ac --- /dev/null +++ b/src/java.base/share/classes/java/lang/IO.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 java.lang; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOError; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * A collection of static methods that provide convenient access to {@link System#in} + * and {@link System#out} for line-oriented input and output. + *

+ * The {@link #readln()} and {@link #readln(String)} methods decode bytes read from + * {@code System.in} into characters. The charset used for decoding is specified by the + * {@link System#getProperties stdin.encoding} property. If this property is not present, + * or if the charset it names cannot be loaded, then UTF-8 is used instead. Decoding + * always replaces malformed and unmappable byte sequences with the charset's default + * replacement string. + *

+ * Charset decoding is set up upon the first call to one of the {@code readln} methods. + * Decoding may buffer additional bytes beyond those that have been decoded to characters + * returned to the application. After the first call to one of the {@code readln} methods, + * any subsequent use of {@code System.in} results in unspecified behavior. + * + * @apiNote + * The expected use case is that certain applications will use only the {@code readln} + * methods to read from the standard input, and they will not mix these calls with + * other techniques for reading from {@code System.in}. + * + * @since 25 + */ +public final class IO { + + /* + * We are deliberately not including printf, at least not initially, for + * the following reasons. First, it introduces a rather cryptic and arcane + * formatting language that isn't really suited to beginners. Second, it + * is inherently localizable, which drags in a whole bunch of issues about + * what locale should be used for formatting, the possible inclusion of + * an overload with an explicit Locale parameter, and so forth. These issues + * are best avoided for the time being. Third, when string templates come + * along, they might offer a better alternative to printf-style formatting, + * so it's best not be saddled with this unnecessarily. + */ + + /* + * Notes on flushing. We want flushing to occur after every call to println + * and print, so that the user can see output immediately. This could be + * important if the user calls print() to issue a prompt before calling + * readln() instead of the readln(prompt) overload. It's also important to + * flush after print() in case the user is relying on print() to emit output + * as sort of a progress indicator. + * + * We rely on System.out to have autoflush enabled, which flushes after every + * println() call, so we needn't flush again. We flush unconditionally after + * calls to print(). Since System.out is doing a lot of flushing anyway, there + * isn't much point trying to make this conditional, for example, only if + * stdout is connected to a terminal. + */ + + private IO() { + throw new Error("no instances"); + } + + /** + * Writes a string representation of the specified object and then writes + * a line separator to the standard output. + * + *

The effect is as if {@link java.io.PrintStream#println(Object) println(obj)} + * had been called on {@code System.out}. + * + * @param obj the object to print, may be {@code null} + */ + public static void println(Object obj) { + System.out.println(obj); + } + + /** + * Writes a line separator to the standard output. + * + *

The effect is as if {@link java.io.PrintStream#println() println()} + * had been called on {@code System.out}. + */ + public static void println() { + System.out.println(); + } + + /** + * Writes a string representation of the specified object to the + * standard output. + * + *

The effect is as if {@link java.io.PrintStream#print(Object) print(obj)} + * had been called on {@code System.out}. + * + * @param obj the object to print, may be {@code null} + */ + public static void print(Object obj) { + var out = System.out; + out.print(obj); + out.flush(); + } + + /** + * Reads a single line of text from the standard input. + *

+ * One line is read from the decoded input as if by + * {@link java.io.BufferedReader#readLine() BufferedReader.readLine()} + * and then the result is returned. + *

+ * If necessary, this method first sets up charset decoding, as described in + * above in the class specification. + * + * @return a string containing the line read from the standard input, not + * including any line separator characters. Returns {@code null} if an + * end of stream has been reached without having read any characters. + * + * @throws IOError if an I/O error occurs + */ + public static String readln() { + try { + return reader().readLine(); + } catch (IOException ioe) { + throw new IOError(ioe); + } + } + + /** + * Writes a prompt and then reads a line of input. + *

+ * Writes a prompt as if by calling {@link #print print}, and then reads a single + * line of text as if by calling {@link #readln() readln}. + *

+ * If necessary, this method first sets up charset decoding, as described in + * above in the class specification. + * + * @param prompt the prompt string, may be {@code null} + * + * @return a string containing the line read from the standard input, not + * including any line separator characters. Returns {@code null} if an + * end of stream has been reached without having read any characters. + * + * @throws IOError if an I/O error occurs + */ + public static String readln(String prompt) { + print(prompt); + return readln(); + } + + /** + * The BufferedReader used by readln(). Initialized under a class lock by + * the reader() method. All access should be through the reader() method. + */ + private static BufferedReader br; + + /** + * On the first call, creates an InputStreamReader to decode characters from + * System.in, wraps it in a BufferedReader, and returns the BufferedReader. + * These objects are cached and returned by subsequent calls. + * + * @return the internal BufferedReader instance + */ + static synchronized BufferedReader reader() { + if (br == null) { + String enc = System.getProperty("stdin.encoding", ""); + Charset cs = Charset.forName(enc, StandardCharsets.UTF_8); + br = new BufferedReader(new InputStreamReader(System.in, cs)); + } + return br; + } +} diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 99b056ec18a3f..1350a66b66c7c 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -1024,7 +1024,7 @@ public static Integer valueOf(int i) { * {@link #valueOf(int)} is generally a better choice, as it is * likely to yield significantly better space and time performance. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Integer(int value) { this.value = value; } @@ -1046,7 +1046,7 @@ public Integer(int value) { * {@code int} primitive, or use {@link #valueOf(String)} * to convert a string to an {@code Integer} object. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Integer(String s) throws NumberFormatException { this.value = parseInt(s, 10); } diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index c401fcd8027e5..3093f37e99ac7 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -1109,7 +1109,7 @@ else if (nm.startsWith("0", index) && nm.length() > 1 + index) { * {@link #valueOf(long)} is generally a better choice, as it is * likely to yield significantly better space and time performance. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Long(long value) { this.value = value; } @@ -1132,7 +1132,7 @@ public Long(long value) { * {@code long} primitive, or use {@link #valueOf(String)} * to convert a string to a {@code Long} object. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Long(String s) throws NumberFormatException { this.value = parseLong(s, 10); } diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index dcc92d012de59..065e1ac46208e 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,6 @@ import java.net.URI; import java.net.URL; import java.security.CodeSource; -import java.security.ProtectionDomain; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -688,7 +687,6 @@ public boolean isOpen(String pn) { return implIsExportedOrOpen(pn, EVERYONE_MODULE, /*open*/true); } - /** * Returns {@code true} if this module exports or opens the given package * to the given module. If the other module is {@code EVERYONE_MODULE} then @@ -707,12 +705,12 @@ private boolean implIsExportedOrOpen(String pn, Module other, boolean open) { if (descriptor.isOpen() || descriptor.isAutomatic()) return descriptor.packages().contains(pn); - // exported/opened via module declaration/descriptor - if (isStaticallyExportedOrOpen(pn, other, open)) + // exported/opened via module declaration/descriptor or CLI options + if (isExplicitlyExportedOrOpened(pn, other, open)) return true; // exported via addExports/addOpens - if (isReflectivelyExportedOrOpen(pn, other, open)) + if (isReflectivelyExportedOrOpened(pn, other, open)) return true; // not exported or open to other @@ -723,7 +721,7 @@ private boolean implIsExportedOrOpen(String pn, Module other, boolean open) { * Returns {@code true} if this module exports or opens a package to * the given module via its module declaration or CLI options. */ - private boolean isStaticallyExportedOrOpen(String pn, Module other, boolean open) { + private boolean isExplicitlyExportedOrOpened(String pn, Module other, boolean open) { // test if package is open to everyone or Map> openPackages = this.openPackages; if (openPackages != null && allows(openPackages.get(pn), other)) { @@ -764,7 +762,7 @@ private boolean allows(Set targets, Module module) { * Returns {@code true} if this module reflectively exports or opens the * given package to the given module. */ - private boolean isReflectivelyExportedOrOpen(String pn, Module other, boolean open) { + private boolean isReflectivelyExportedOrOpened(String pn, Module other, boolean open) { // exported or open to all modules Map exports = ReflectionData.exports.get(this, EVERYONE_MODULE); if (exports != null) { @@ -809,7 +807,7 @@ private boolean isReflectivelyExportedOrOpen(String pn, Module other, boolean op * given package to the given module. */ boolean isReflectivelyExported(String pn, Module other) { - return isReflectivelyExportedOrOpen(pn, other, false); + return isReflectivelyExportedOrOpened(pn, other, false); } /** @@ -817,7 +815,7 @@ boolean isReflectivelyExported(String pn, Module other) { * given package to the given module. */ boolean isReflectivelyOpened(String pn, Module other) { - return isReflectivelyExportedOrOpen(pn, other, true); + return isReflectivelyExportedOrOpened(pn, other, true); } @@ -1033,50 +1031,38 @@ private void implAddExportsOrOpens(String pn, } } - // add package name to exports if absent - Map map = ReflectionData.exports - .computeIfAbsent(this, other, - (m1, m2) -> new ConcurrentHashMap<>()); - if (open) { - map.put(pn, Boolean.TRUE); // may need to promote from FALSE to TRUE - } else { - map.putIfAbsent(pn, Boolean.FALSE); - } - } - - /** - * Updates a module to open all packages in the given sets to all unnamed - * modules. - * - * @apiNote Used during startup to open packages for illegal access. - */ - void implAddOpensToAllUnnamed(Set concealedPkgs, Set exportedPkgs) { - if (jdk.internal.misc.VM.isModuleSystemInited()) { - throw new IllegalStateException("Module system already initialized"); - } - - // replace this module's openPackages map with a new map that opens - // the packages to all unnamed modules. - Map> openPackages = this.openPackages; - if (openPackages == null) { - openPackages = HashMap.newHashMap(concealedPkgs.size() + exportedPkgs.size()); + if (VM.isBooted()) { + // add package name to ReflectionData.exports if absent + Map map = ReflectionData.exports + .computeIfAbsent(this, other, + (m1, m2) -> new ConcurrentHashMap<>()); + if (open) { + map.put(pn, Boolean.TRUE); // may need to promote from FALSE to TRUE + } else { + map.putIfAbsent(pn, Boolean.FALSE); + } } else { - openPackages = new HashMap<>(openPackages); - } - implAddOpensToAllUnnamed(concealedPkgs, openPackages); - implAddOpensToAllUnnamed(exportedPkgs, openPackages); - this.openPackages = openPackages; - } - - private void implAddOpensToAllUnnamed(Set pkgs, Map> openPackages) { - for (String pn : pkgs) { - Set prev = openPackages.putIfAbsent(pn, ALL_UNNAMED_MODULE_SET); - if (prev != null) { - prev.add(ALL_UNNAMED_MODULE); + // export/open packages during startup (--add-exports and --add-opens) + Map> packageToTargets = (open) ? openPackages : exportedPackages; + if (packageToTargets != null) { + // copy existing map + packageToTargets = new HashMap<>(packageToTargets); + packageToTargets.compute(pn, (_, values) -> { + var targets = new HashSet(); + if (values != null) { + targets.addAll(values); + } + targets.add(other); + return targets; + }); + } else { + packageToTargets = Map.of(pn, Set.of(other)); + } + if (open) { + this.openPackages = packageToTargets; + } else { + this.exportedPackages = packageToTargets; } - - // update VM to export the package - addExportsToAllUnnamed0(this, pn); } } diff --git a/src/java.base/share/classes/java/lang/Short.java b/src/java.base/share/classes/java/lang/Short.java index 57e88442b2783..f0ae8b28e4594 100644 --- a/src/java.base/share/classes/java/lang/Short.java +++ b/src/java.base/share/classes/java/lang/Short.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -352,7 +352,7 @@ public static Short decode(String nm) throws NumberFormatException { * {@link #valueOf(short)} is generally a better choice, as it is * likely to yield significantly better space and time performance. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Short(short value) { this.value = value; } @@ -375,7 +375,7 @@ public Short(short value) { * {@code short} primitive, or use {@link #valueOf(String)} * to convert a string to a {@code Short} object. */ - @Deprecated(since="9", forRemoval = true) + @Deprecated(since="9") public Short(String s) throws NumberFormatException { this.value = parseShort(s, 10); } diff --git a/src/java.base/share/classes/java/lang/StableValue.java b/src/java.base/share/classes/java/lang/StableValue.java new file mode 100644 index 0000000000000..b12d6f5e92128 --- /dev/null +++ b/src/java.base/share/classes/java/lang/StableValue.java @@ -0,0 +1,757 @@ +/* + * 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 java.lang; + +import jdk.internal.access.SharedSecrets; +import jdk.internal.javac.PreviewFeature; +import jdk.internal.lang.stable.StableEnumFunction; +import jdk.internal.lang.stable.StableFunction; +import jdk.internal.lang.stable.StableIntFunction; +import jdk.internal.lang.stable.StableSupplier; +import jdk.internal.lang.stable.StableUtil; +import jdk.internal.lang.stable.StableValueImpl; + +import java.io.Serializable; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.RandomAccess; +import java.util.Set; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Supplier; + +/** + * A stable value is a holder of contents that can be set at most once. + *

+ * A {@code StableValue} is typically created using the factory method + * {@linkplain StableValue#of() {@code StableValue.of()}}. When created this way, + * the stable value is unset, which means it holds no contents. + * Its contents, of type {@code T}, can be set by calling + * {@linkplain #trySet(Object) trySet()}, {@linkplain #setOrThrow(Object) setOrThrow()}, + * or {@linkplain #orElseSet(Supplier) orElseSet()}. Once set, the contents + * can never change and can be retrieved by calling {@linkplain #orElseThrow() orElseThrow()} + * , {@linkplain #orElse(Object) orElse()}, or {@linkplain #orElseSet(Supplier) orElseSet()}. + *

+ * Consider the following example where a stable value field "{@code logger}" is a + * shallowly immutable holder of contents of type {@code Logger} and that is initially + * created as unset, which means it holds no contents. Later in the example, the + * state of the "{@code logger}" field is checked and if it is still unset, + * the contents is set: + * + * {@snippet lang = java: + * public class Component { + * + * // Creates a new unset stable value with no contents + * // @link substring="of" target="#of" : + * private final StableValue logger = StableValue.of(); + * + * private Logger getLogger() { + * if (!logger.isSet()) { + * logger.trySet(Logger.create(Component.class)); + * } + * return logger.orElseThrow(); + * } + * + * public void process() { + * getLogger().info("Process started"); + * // ... + * } + * } + *} + *

+ * If {@code getLogger()} is called from several threads, several instances of + * {@code Logger} might be created. However, the contents can only be set at most once + * meaning the first writer wins. + *

+ * In order to guarantee that, even under races, only one instance of {@code Logger} is + * ever created, the {@linkplain #orElseSet(Supplier) orElseSet()} method can be used + * instead, where the contents are lazily computed, and atomically set, via a + * {@linkplain Supplier supplier}. In the example below, the supplier is provided in the + * form of a lambda expression: + * + * {@snippet lang = java: + * public class Component { + * + * // Creates a new unset stable value with no contents + * // @link substring="of" target="#of" : + * private final StableValue logger = StableValue.of(); + * + * private Logger getLogger() { + * return logger.orElseSet( () -> Logger.create(Component.class) ); + * } + * + * public void process() { + * getLogger().info("Process started"); + * // ... + * } + * } + *} + *

+ * The {@code getLogger()} method calls {@code logger.orElseSet()} on the stable value to + * retrieve its contents. If the stable value is unset, then {@code orElseSet()} + * evaluates the given supplier, and sets the contents to the result; the result is then + * returned to the client. In other words, {@code orElseSet()} guarantees that a + * stable value's contents is set before it returns. + *

+ * Furthermore, {@code orElseSet()} guarantees that out of one or more suppliers provided, + * only at most one is ever evaluated, and that one is only ever evaluated once, + * even when {@code logger.orElseSet()} is invoked concurrently. This property is crucial + * as evaluation of the supplier may have side effects, for example, the call above to + * {@code Logger.create()} may result in storage resources being prepared. + * + *

Stable Functions

+ * Stable values provide the foundation for higher-level functional abstractions. A + * stable supplier is a supplier that computes a value and then caches it into + * a backing stable value storage for subsequent use. A stable supplier is created via the + * {@linkplain StableValue#supplier(Supplier) StableValue.supplier()} factory, by + * providing an underlying {@linkplain Supplier} which is invoked when the stable supplier + * is first accessed: + * + * {@snippet lang = java: + * public class Component { + * + * private final Supplier logger = + * // @link substring="supplier" target="#supplier(Supplier)" : + * StableValue.supplier( () -> Logger.getLogger(Component.class) ); + * + * public void process() { + * logger.get().info("Process started"); + * // ... + * } + * } + *} + * A stable supplier encapsulates access to its backing stable value storage. This means + * that code inside {@code Component} can obtain the logger object directly from the + * stable supplier, without having to go through an accessor method like {@code getLogger()}. + *

+ * A stable int function is a function that takes an {@code int} parameter and + * uses it to compute a result that is then cached by the backing stable value storage + * for that parameter value. A stable {@link IntFunction} is created via the + * {@linkplain StableValue#intFunction(int, IntFunction) StableValue.intFunction()} + * factory. Upon creation, the input range (i.e. {@code [0, size)}) is specified together + * with an underlying {@linkplain IntFunction} which is invoked at most once per input + * value. In effect, the stable int function will act like a cache for the underlying + * {@linkplain IntFunction}: + * + * {@snippet lang = java: + * final class PowerOf2Util { + * + * private PowerOf2Util() {} + * + * private static final int SIZE = 6; + * private static final IntFunction UNDERLYING_POWER_OF_TWO = + * v -> 1 << v; + * + * private static final IntFunction POWER_OF_TWO = + * // @link substring="intFunction" target="#intFunction(int,IntFunction)" : + * StableValue.intFunction(SIZE, UNDERLYING_POWER_OF_TWO); + * + * public static int powerOfTwo(int a) { + * return POWER_OF_TWO.apply(a); + * } + * } + * + * int result = PowerOf2Util.powerOfTwo(4); // May eventually constant fold to 16 at runtime + * + *} + * The {@code PowerOf2Util.powerOfTwo()} function is a partial function that only + * allows a subset {@code [0, 5]} of the underlying function's {@code UNDERLYING_POWER_OF_TWO} + * input range. + * + *

+ * A stable function is a function that takes a parameter (of type {@code T}) and + * uses it to compute a result (of type {@code R}) that is then cached by the backing + * stable value storage for that parameter value. A stable function is created via the + * {@linkplain StableValue#function(Set, Function) StableValue.function()} factory. + * Upon creation, the input {@linkplain Set} is specified together with an underlying + * {@linkplain Function} which is invoked at most once per input value. In effect, the + * stable function will act like a cache for the underlying {@linkplain Function}: + * + * {@snippet lang = java: + * class Log2Util { + * + * private Log2Util() {} + * + * private static final Set KEYS = + * Set.of(1, 2, 4, 8, 16, 32); + * private static final UnaryOperator UNDERLYING_LOG2 = + * i -> 31 - Integer.numberOfLeadingZeros(i); + * + * private static final Function LOG2 = + * // @link substring="function" target="#function(Set,Function)" : + * StableValue.function(KEYS, UNDERLYING_LOG2); + * + * public static int log2(int a) { + * return LOG2.apply(a); + * } + * + * } + * + * int result = Log2Util.log2(16); // May eventually constant fold to 4 at runtime + *} + * + * The {@code Log2Util.log2()} function is a partial function that only allows + * a subset {@code {1, 2, 4, 8, 16, 32}} of the underlying function's + * {@code UNDERLYING_LOG2} input range. + * + *

Stable Collections

+ * Stable values can also be used as backing storage for + * {@linkplain Collection##unmodifiable unmodifiable collections}. A stable list + * is an unmodifiable list, backed by an array of stable values. The stable list elements + * are computed when they are first accessed, using a provided {@linkplain IntFunction}: + * + * {@snippet lang = java: + * final class PowerOf2Util { + * + * private PowerOf2Util() {} + * + * private static final int SIZE = 6; + * private static final IntFunction UNDERLYING_POWER_OF_TWO = + * v -> 1 << v; + * + * private static final List POWER_OF_TWO = + * // @link substring="list" target="#list(int,IntFunction)" : + * StableValue.list(SIZE, UNDERLYING_POWER_OF_TWO); + * + * public static int powerOfTwo(int a) { + * return POWER_OF_TWO.get(a); + * } + * } + * + * int result = PowerOf2Util.powerOfTwo(4); // May eventually constant fold to 16 at runtime + * + * } + *

+ * Similarly, a stable map is an unmodifiable map whose keys are known at + * construction. The stable map values are computed when they are first accessed, + * using a provided {@linkplain Function}: + * + * {@snippet lang = java: + * class Log2Util { + * + * private Log2Util() {} + * + * private static final Set KEYS = + * Set.of(1, 2, 4, 8, 16, 32); + * private static final UnaryOperator UNDERLYING_LOG2 = + * i -> 31 - Integer.numberOfLeadingZeros(i); + * + * private static final Map LOG2 = + * // @link substring="map" target="#map(Set,Function)" : + * StableValue.map(CACHED_KEYS, UNDERLYING_LOG2); + * + * public static int log2(int a) { + * return LOG2.get(a); + * } + * + * } + * + * int result = Log2Util.log2(16); // May eventually constant fold to 4 at runtime + * + *} + * + *

Composing stable values

+ * A stable value can depend on other stable values, forming a dependency graph + * that can be lazily computed but where access to individual elements can still be + * performant. In the following example, a single {@code Foo} and a {@code Bar} + * instance (that is dependent on the {@code Foo} instance) are lazily created, both of + * which are held by stable values: + * {@snippet lang = java: + * public final class DependencyUtil { + * + * private DependencyUtil() {} + * + * public static class Foo { + * // ... + * } + * + * public static class Bar { + * public Bar(Foo foo) { + * // ... + * } + * } + * + * private static final Supplier FOO = StableValue.supplier(Foo::new); + * private static final Supplier BAR = StableValue.supplier(() -> new Bar(FOO.get())); + * + * public static Foo foo() { + * return FOO.get(); + * } + * + * public static Bar bar() { + * return BAR.get(); + * } + * + * } + *} + * Calling {@code bar()} will create the {@code Bar} singleton if it is not already + * created. Upon such a creation, the dependent {@code Foo} will first be created if + * the {@code Foo} does not already exist. + *

+ * Another example, which has a more complex dependency graph, is to compute the + * Fibonacci sequence lazily: + * {@snippet lang = java: + * public final class Fibonacci { + * + * private Fibonacci() {} + * + * private static final int MAX_SIZE_INT = 46; + * + * private static final IntFunction FIB = + * StableValue.intFunction(MAX_SIZE_INT, Fibonacci::fib); + * + * public static int fib(int n) { + * return n < 2 + * ? n + * : FIB.apply(n - 1) + FIB.apply(n - 2); + * } + * + * } + *} + * Both {@code FIB} and {@code Fibonacci::fib} recurse into each other. Because the + * stable int function {@code FIB} caches intermediate results, the initial + * computational complexity is reduced from exponential to linear compared to a + * traditional non-caching recursive fibonacci method. Once computed, the VM is free to + * constant-fold expressions like {@code Fibonacci.fib(5)}. + *

+ * The fibonacci example above is a directed acyclic graph (i.e., + * it has no circular dependencies and is therefore a dependency tree): + *{@snippet lang=text : + * + * ___________fib(5)____________ + * / \ + * ____fib(4)____ ____fib(3)____ + * / \ / \ + * fib(3) fib(2) fib(2) fib(1) + * / \ / \ / \ + * fib(2) fib(1) fib(1) fib(0) fib(1) fib(0) + *} + * + * If there are circular dependencies in a dependency graph, a stable value will + * eventually throw an {@linkplain IllegalStateException} upon referencing elements in + * a circularity. + * + *

Thread Safety

+ * The contents of a stable value is guaranteed to be set at most once. If competing + * threads are racing to set a stable value, only one update succeeds, while the other + * updates are blocked until the stable value is set, whereafter the other updates + * observes the stable value is set and leave the stable value unchanged. + *

+ * The at-most-once write operation on a stable value that succeeds + * (e.g. {@linkplain #trySet(Object) trySet()}) + * {@linkplain java.util.concurrent##MemoryVisibility happens-before} + * any successful read operation (e.g. {@linkplain #orElseThrow()}). + * A successful write operation can be either: + *

    + *
  • a {@link #trySet(Object)} that returns {@code true},
  • + *
  • a {@link #setOrThrow(Object)} that does not throw, or
  • + *
  • an {@link #orElseSet(Supplier)} that successfully runs the supplier
  • + *
+ * A successful read operation can be either: + *
    + *
  • a {@link #orElseThrow()} that does not throw,
  • + *
  • a {@link #orElse(Object) orElse(other)} that does not return the {@code other} value
  • + *
  • an {@link #orElseSet(Supplier)} that does not {@code throw}, or
  • + *
  • an {@link #isSet()} that returns {@code true}
  • + *
+ *

+ * The method {@link #orElseSet(Supplier)} guarantees that the provided + * {@linkplain Supplier} is invoked successfully at most once, even under race. + * Invocations of {@link #setOrThrow(Object)} form a total order of zero or more + * exceptional invocations followed by zero (if the contents were already set) or one + * successful invocation. Since stable functions and stable collections are built on top + * of the same principles as {@linkplain StableValue#orElseSet(Supplier) orElseSet()} they + * too are thread safe and guarantee at-most-once-per-input invocation. + * + *

Performance

+ * As the contents of a stable value can never change after it has been set, a JVM + * implementation may, for a set stable value, elide all future reads of that + * stable value, and instead directly use any contents that it has previously observed. + * This is true if the reference to the stable value is a constant (e.g. in cases where + * the stable value itself is stored in a {@code static final} field). Stable functions + * and collections are built on top of StableValue. As such, they might also be eligible + * for the same JVM optimizations as for StableValue. + * + * @implSpec Implementing classes of {@code StableValue} are free to synchronize on + * {@code this} and consequently, it should be avoided to + * (directly or indirectly) synchronize on a {@code StableValue}. Hence, + * synchronizing on {@code this} may lead to deadlock. + *

+ * Except for a {@code StableValue}'s contents itself, + * an {@linkplain #orElse(Object) orElse(other)} parameter, and + * an {@linkplain #equals(Object) equals(obj)} parameter; all + * method parameters must be non-null or a {@link NullPointerException} + * will be thrown. + * + * @implNote A {@code StableValue} is mainly intended to be a non-public field in + * a class and is usually neither exposed directly via accessors nor passed as + * a method parameter. + *

+ * Stable functions and collections make reasonable efforts to provide + * {@link Object#toString()} operations that do not trigger evaluation + * of the internal stable values when called. + * Stable collections have {@link Object#equals(Object)} operations that try + * to minimize evaluation of the internal stable values when called. + *

+ * As objects can be set via stable values but never removed, this can be a + * source of unintended memory leaks. A stable value's contents are + * {@linkplain java.lang.ref##reachability strongly reachable}. + * Be advised that reachable stable values will hold their set contents until + * the stable value itself is collected. + *

+ * A {@code StableValue} that has a type parameter {@code T} that is an array + * type (of arbitrary rank) will only allow the JVM to treat the + * array reference as a stable value but not its components. + * Instead, a {@linkplain #list(int, IntFunction) a stable list} of arbitrary + * depth can be used, which provides stable components. More generally, a + * stable value can hold other stable values of arbitrary depth and still + * provide transitive constantness. + *

+ * Stable values, functions, and collections are not {@link Serializable}. + * + * @param type of the contents + * + * @since 25 + */ +@PreviewFeature(feature = PreviewFeature.Feature.STABLE_VALUES) +public sealed interface StableValue + permits StableValueImpl { + + // Principal methods + + /** + * Tries to set the contents of this StableValue to the provided {@code contents}. + * The contents of this StableValue can only be set once, implying this method only + * returns {@code true} once. + *

+ * When this method returns, the contents of this StableValue is always set. + * + * @return {@code true} if the contents of this StableValue was set to the + * provided {@code contents}, {@code false} otherwise + * @param contents to set + * @throws IllegalStateException if a supplier invoked by {@link #orElseSet(Supplier)} + * recursively attempts to set this stable value by calling this method + * directly or indirectly. + */ + boolean trySet(T contents); + + /** + * {@return the contents if set, otherwise, returns the provided {@code other} value} + * + * @param other to return if the contents is not set + */ + T orElse(T other); + + /** + * {@return the contents if set, otherwise, throws {@code NoSuchElementException}} + * + * @throws NoSuchElementException if no contents is set + */ + T orElseThrow(); + + /** + * {@return {@code true} if the contents is set, {@code false} otherwise} + */ + boolean isSet(); + + /** + * {@return the contents; if unset, first attempts to compute and set the + * contents using the provided {@code supplier}} + *

+ * The provided {@code supplier} is guaranteed to be invoked at most once if it + * completes without throwing an exception. If this method is invoked several times + * with different suppliers, only one of them will be invoked provided it completes + * without throwing an exception. + *

+ * If the supplier throws an (unchecked) exception, the exception is rethrown and no + * contents is set. The most common usage is to construct a new object serving + * as a lazily computed value or memoized result, as in: + * + * {@snippet lang=java: + * Value v = stable.orElseSet(Value::new); + * } + *

+ * When this method returns successfully, the contents is always set. + *

+ * The provided {@code supplier} will only be invoked once even if invoked from + * several threads unless the {@code supplier} throws an exception. + * + * @param supplier to be used for computing the contents, if not previously set + * @throws IllegalStateException if the provided {@code supplier} recursively + * attempts to set this stable value. + */ + T orElseSet(Supplier supplier); + + // Convenience methods + + /** + * Sets the contents of this StableValue to the provided {@code contents}, or, if + * already set, throws {@code IllegalStateException}. + *

+ * When this method returns (or throws an exception), the contents is always set. + * + * @param contents to set + * @throws IllegalStateException if the contents was already set + */ + void setOrThrow(T contents); + + // Object methods + + /** + * {@return {@code true} if {@code this == obj}, {@code false} otherwise} + * + * @param obj to check for equality + */ + boolean equals(Object obj); + + /** + * {@return the {@linkplain System#identityHashCode(Object) identity hash code} of + * {@code this} object} + */ + int hashCode(); + + // Factories + + /** + * {@return a new unset stable value} + *

+ * An unset stable value has no contents. + * + * @param type of the contents + */ + static StableValue of() { + return StableValueImpl.of(); + } + + /** + * {@return a new pre-set stable value with the provided {@code contents}} + * + * @param contents to set + * @param type of the contents + */ + static StableValue of(T contents) { + final StableValue stableValue = StableValue.of(); + stableValue.trySet(contents); + return stableValue; + } + + /** + * {@return a new stable supplier} + *

+ * The returned {@linkplain Supplier supplier} is a caching supplier that records + * the value of the provided {@code underlying} supplier upon being first accessed via + * the returned supplier's {@linkplain Supplier#get() get()} method. + *

+ * The provided {@code underlying} supplier is guaranteed to be successfully invoked + * at most once even in a multi-threaded environment. Competing threads invoking the + * returned supplier's {@linkplain Supplier#get() get()} method when a value is + * already under computation will block until a value is computed or an exception is + * thrown by the computing thread. The computing threads will then observe the newly + * computed value (if any) and will then never execute. + *

+ * If the provided {@code underlying} supplier throws an exception, it is rethrown + * to the initial caller and no contents is recorded. + *

+ * If the provided {@code underlying} supplier recursively calls the returned + * supplier, an {@linkplain IllegalStateException} will be thrown. + * + * @param underlying supplier used to compute a cached value + * @param the type of results supplied by the returned supplier + */ + static Supplier supplier(Supplier underlying) { + Objects.requireNonNull(underlying); + return StableSupplier.of(underlying); + } + + /** + * {@return a new stable {@linkplain IntFunction}} + *

+ * The returned function is a caching function that, for each allowed {@code int} + * input, records the values of the provided {@code underlying} + * function upon being first accessed via the returned function's + * {@linkplain IntFunction#apply(int) apply()} method. If the returned function is + * invoked with an input that is not in the range {@code [0, size)}, an + * {@link IllegalArgumentException} will be thrown. + *

+ * The provided {@code underlying} function is guaranteed to be successfully invoked + * at most once per allowed input, even in a multi-threaded environment. Competing + * threads invoking the returned function's + * {@linkplain IntFunction#apply(int) apply()} method when a value is already under + * computation will block until a value is computed or an exception is thrown by + * the computing thread. + *

+ * If invoking the provided {@code underlying} function throws an exception, it is + * rethrown to the initial caller and no contents is recorded. + *

+ * If the provided {@code underlying} function recursively calls the returned + * function for the same input, an {@linkplain IllegalStateException} will + * be thrown. + * + * @param size the size of the allowed inputs in the continuous + * interval {@code [0, size)} + * @param underlying IntFunction used to compute cached values + * @param the type of results delivered by the returned IntFunction + * @throws IllegalArgumentException if the provided {@code size} is negative. + */ + static IntFunction intFunction(int size, + IntFunction underlying) { + StableUtil.assertSizeNonNegative(size); + Objects.requireNonNull(underlying); + return StableIntFunction.of(size, underlying); + } + + /** + * {@return a new stable {@linkplain Function}} + *

+ * The returned function is a caching function that, for each allowed + * input in the given set of {@code inputs}, records the values of the provided + * {@code underlying} function upon being first accessed via the returned function's + * {@linkplain Function#apply(Object) apply()} method. If the returned function is + * invoked with an input that is not in {@code inputs}, an {@link IllegalArgumentException} + * will be thrown. + *

+ * The provided {@code underlying} function is guaranteed to be successfully invoked + * at most once per allowed input, even in a multi-threaded environment. Competing + * threads invoking the returned function's {@linkplain Function#apply(Object) apply()} + * method when a value is already under computation will block until a value is + * computed or an exception is thrown by the computing thread. + *

+ * If invoking the provided {@code underlying} function throws an exception, it is + * rethrown to the initial caller and no contents is recorded. + *

+ * If the provided {@code underlying} function recursively calls the returned + * function for the same input, an {@linkplain IllegalStateException} will + * be thrown. + * + * @param inputs the set of (non-null) allowed input values + * @param underlying {@code Function} used to compute cached values + * @param the type of the input to the returned Function + * @param the type of results delivered by the returned Function + * @throws NullPointerException if the provided set of {@code inputs} contains a + * {@code null} element. + */ + static Function function(Set inputs, + Function underlying) { + Objects.requireNonNull(inputs); + // Checking that the Set of inputs does not contain a `null` value is made in the + // implementing classes. + Objects.requireNonNull(underlying); + return inputs instanceof EnumSet && !inputs.isEmpty() + ? StableEnumFunction.of(inputs, underlying) + : StableFunction.of(inputs, underlying); + } + + /** + * {@return a new stable list with the provided {@code size}} + *

+ * The returned list is an {@linkplain Collection##unmodifiable unmodifiable} list + * with the provided {@code size}. The list's elements are computed via the + * provided {@code mapper} when they are first accessed + * (e.g. via {@linkplain List#get(int) List::get}). + *

+ * The provided {@code mapper} function is guaranteed to be successfully invoked + * at most once per list index, even in a multi-threaded environment. Competing + * threads accessing an element already under computation will block until an element + * is computed or an exception is thrown by the computing thread. + *

+ * If invoking the provided {@code mapper} function throws an exception, it + * is rethrown to the initial caller and no value for the element is recorded. + *

+ * Any direct {@link List#subList(int, int) subList} or {@link List#reversed()} views + * of the returned list are also stable. + *

+ * The returned list and its {@link List#subList(int, int) subList} or + * {@link List#reversed()} views implement the {@link RandomAccess} interface. + *

+ * The returned list is unmodifiable and does not implement the + * {@linkplain Collection##optional-operation optional operations} in the + * {@linkplain List} interface. + *

+ * If the provided {@code mapper} recursively calls the returned list for the + * same index, an {@linkplain IllegalStateException} will be thrown. + * + * @param size the size of the returned list + * @param mapper to invoke whenever an element is first accessed + * (may return {@code null}) + * @param the type of elements in the returned list + * @throws IllegalArgumentException if the provided {@code size} is negative. + */ + static List list(int size, + IntFunction mapper) { + StableUtil.assertSizeNonNegative(size); + Objects.requireNonNull(mapper); + return SharedSecrets.getJavaUtilCollectionAccess().stableList(size, mapper); + } + + /** + * {@return a new stable map with the provided {@code keys}} + *

+ * The returned map is an {@linkplain Collection##unmodifiable unmodifiable} map whose + * keys are known at construction. The map's values are computed via the provided + * {@code mapper} when they are first accessed + * (e.g. via {@linkplain Map#get(Object) Map::get}). + *

+ * The provided {@code mapper} function is guaranteed to be successfully invoked + * at most once per key, even in a multi-threaded environment. Competing + * threads accessing a value already under computation will block until an element + * is computed or an exception is thrown by the computing thread. + *

+ * If invoking the provided {@code mapper} function throws an exception, it + * is rethrown to the initial caller and no value associated with the provided key + * is recorded. + *

+ * Any direct {@link Map#values()} or {@link Map#entrySet()} views + * of the returned map are also stable. + *

+ * The returned map is unmodifiable and does not implement the + * {@linkplain Collection##optional-operations optional operations} in the + * {@linkplain Map} interface. + *

+ * If the provided {@code mapper} recursively calls the returned map for + * the same key, an {@linkplain IllegalStateException} will be thrown. + * + * @param keys the (non-null) keys in the returned map + * @param mapper to invoke whenever an associated value is first accessed + * (may return {@code null}) + * @param the type of keys maintained by the returned map + * @param the type of mapped values in the returned map + * @throws NullPointerException if the provided set of {@code inputs} contains a + * {@code null} element. + */ + static Map map(Set keys, + Function mapper) { + Objects.requireNonNull(keys); + // Checking that the Set of keys does not contain a `null` value is made in the + // implementing class. + Objects.requireNonNull(mapper); + return SharedSecrets.getJavaUtilCollectionAccess().stableMap(keys, mapper); + } + +} diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 033d32611b0f3..0632785d89931 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -173,11 +173,14 @@ public final class String private final byte coder; /** Cache the hash code for the string */ + @Stable private int hash; // Default to 0 /** * Cache if the hash has been calculated as actually being zero, enabling - * us to avoid recalculating this. + * us to avoid recalculating this. This field is _not_ annotated @Stable as + * the `hashCode()` method reads the field `hash` first anyhow and if `hash` + * is the default zero value, is not trusted. */ private boolean hashIsZero; // Default to false; diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 99226ac1012ba..a07c022040f7b 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,6 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; import static java.lang.String.UTF16; -import static java.lang.String.LATIN1; final class StringUTF16 { diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 903f6e34e2ee1..a60958f6f8223 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -116,15 +116,23 @@ private System() { /** * The "standard" input stream. This stream is already - * open and ready to supply input data. Typically this stream + * open and ready to supply input data. This stream * corresponds to keyboard input or another input source specified by - * the host environment or user. In case this stream is wrapped - * in a {@link java.io.InputStreamReader}, {@link Console#charset()} - * should be used for the charset, or consider using - * {@link Console#reader()}. + * the host environment or user. Applications should use the encoding + * specified by the {@link ##stdin.encoding stdin.encoding} property + * to convert input bytes to character data. * - * @see Console#charset() - * @see Console#reader() + * @apiNote + * The typical approach to read character data is to wrap {@code System.in} + * within an {@link java.io.InputStreamReader InputStreamReader} or other object + * that handles character encoding. After this is done, subsequent reading should + * use only the wrapper object; operating directly on {@code System.in} results + * in unspecified behavior. + *

+ * For handling interactive input, consider using {@link Console}. + * + * @see Console + * @see ##stdin.encoding stdin.encoding */ public static final InputStream in = null; @@ -575,17 +583,22 @@ public static native void arraycopy(Object src, int srcPos, * {@systemProperty user.dir} * User's current working directory * {@systemProperty native.encoding} - * Character encoding name derived from the host environment and/or - * the user's settings. Setting this system property has no effect. + * Character encoding name derived from the host environment and + * the user's settings. Setting this system property on the command line + * has no effect. + * {@systemProperty stdin.encoding} + * Character encoding name for {@link System#in System.in}. + * The Java runtime can be started with the system property set to {@code UTF-8}. + * Starting it with the property set to another value results in unspecified behavior. * {@systemProperty stdout.encoding} * Character encoding name for {@link System#out System.out} and * {@link System#console() System.console()}. - * The Java runtime can be started with the system property set to {@code UTF-8}, - * starting it with the property set to another value leads to undefined behavior. + * The Java runtime can be started with the system property set to {@code UTF-8}. + * Starting it with the property set to another value results in unspecified behavior. * {@systemProperty stderr.encoding} * Character encoding name for {@link System#err System.err}. - * The Java runtime can be started with the system property set to {@code UTF-8}, - * starting it with the property set to another value leads to undefined behavior. + * The Java runtime can be started with the system property set to {@code UTF-8}. + * Starting it with the property set to another value results in unspecified behavior. * * *

@@ -639,7 +652,7 @@ public static native void arraycopy(Object src, int srcPos, * the value {@code COMPAT} then the value is replaced with the * value of the {@code native.encoding} property during startup. * Setting the property to a value other than {@code UTF-8} or - * {@code COMPAT} leads to unspecified behavior. + * {@code COMPAT} results in unspecified behavior. * * * @@ -2004,6 +2017,9 @@ public byte[] getRawExecutableTypeAnnotations(Executable executable) { E[] getEnumConstantsShared(Class klass) { return klass.getEnumConstantsShared(); } + public int classFileVersion(Class clazz) { + return clazz.getClassFileVersion(); + } public void blockedOn(Interruptible b) { Thread.currentThread().blockedOn(b); } @@ -2059,9 +2075,6 @@ public void addOpens(Module m, String pn, Module other) { public void addOpensToAllUnnamed(Module m, String pn) { m.implAddOpensToAllUnnamed(pn); } - public void addOpensToAllUnnamed(Module m, Set concealedPackages, Set exportedPackages) { - m.implAddOpensToAllUnnamed(concealedPackages, exportedPackages); - } public void addUses(Module m, Class service) { m.implAddUses(service); } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ClassEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ClassEntry.java index 5017838d68eba..108c47c80897e 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ClassEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ClassEntry.java @@ -104,8 +104,22 @@ default ConstantDesc constantValue() { * returned descriptor is never {@linkplain ClassDesc#isPrimitive() * primitive}. * + * @apiNote + * If only symbol equivalence is desired, {@link #matches(ClassDesc) + * matches} should be used. It requires reduced parsing and can + * improve {@code class} file reading performance. + * * @see ConstantPoolBuilder#classEntry(ClassDesc) * ConstantPoolBuilder::classEntry(ClassDesc) */ ClassDesc asSymbol(); + + /** + * {@return whether this entry describes the given reference type} Returns + * {@code false} if {@code desc} is primitive. + * + * @param desc the reference type + * @since 25 + */ + boolean matches(ClassDesc desc); } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/MethodTypeEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/MethodTypeEntry.java index 5da3650267825..2f56ce2e6abb0 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/MethodTypeEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/MethodTypeEntry.java @@ -70,6 +70,19 @@ default ConstantDesc constantValue() { /** * {@return a symbolic descriptor for the {@linkplain #descriptor() method * type}} + * + * @apiNote + * If only symbol equivalence is desired, {@link #matches(MethodTypeDesc) + * matches} should be used. It requires reduced parsing and can + * improve {@code class} file reading performance. */ MethodTypeDesc asSymbol(); + + /** + * {@return whether this entry describes the given method type} + * + * @param desc the method type descriptor + * @since 25 + */ + boolean matches(MethodTypeDesc desc); } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ModuleEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ModuleEntry.java index fd920aa12316f..169927868b65e 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ModuleEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ModuleEntry.java @@ -55,6 +55,19 @@ public sealed interface ModuleEntry extends PoolEntry /** * {@return a symbolic descriptor for the {@linkplain #name() module name}} + * + * @apiNote + * If only symbol equivalence is desired, {@link #matches(ModuleDesc) + * matches} should be used. It requires reduced parsing and can + * improve {@code class} file reading performance. */ ModuleDesc asSymbol(); + + /** + * {@return whether this entry describes the given module} + * + * @param desc the module descriptor + * @since 25 + */ + boolean matches(ModuleDesc desc); } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/PackageEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/PackageEntry.java index ec56d0a4870cc..19f4560b5e6b8 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/PackageEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/PackageEntry.java @@ -58,6 +58,19 @@ public sealed interface PackageEntry extends PoolEntry /** * {@return a symbolic descriptor for the {@linkplain #name() package name}} + * + * @apiNote + * If only symbol equivalence is desired, {@link #matches(PackageDesc) + * matches} should be used. It requires reduced parsing and can + * improve {@code class} file reading performance. */ PackageDesc asSymbol(); + + /** + * {@return whether this entry describes the given package} + * + * @param desc the package descriptor + * @since 25 + */ + boolean matches(PackageDesc desc); } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/StringEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/StringEntry.java index 8a0bbb4b0150d..b49e74df40474 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/StringEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/StringEntry.java @@ -56,7 +56,22 @@ public sealed interface StringEntry /** * {@return the string value for this entry} * + * @apiNote + * A {@code Utf8Entry} can be used directly as a {@link CharSequence} if + * {@code String} functionalities are not strictly desired. If only string + * equivalence is desired, {@link #equalsString(String) equalsString} should + * be used. Reduction of string processing can significantly improve {@code + * class} file reading performance. + * * @see ConstantPoolBuilder#stringEntry(String) */ String stringValue(); + + /** + * {@return whether this entry describes the same string as the provided string} + * + * @param value the string to compare to + * @since 25 + */ + boolean equalsString(String value); } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java index 1d885051b2b1a..81a4e973d3f83 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/Utf8Entry.java @@ -84,4 +84,22 @@ public sealed interface Utf8Entry * @param s the string to compare to */ boolean equalsString(String s); + + /** + * {@return whether this entry describes the descriptor string of this + * field type} + * + * @param desc the field type + * @since 25 + */ + boolean isFieldType(ClassDesc desc); + + /** + * {@return whether this entry describes the descriptor string of this + * method type} + * + * @param desc the method type + * @since 25 + */ + boolean isMethodType(MethodTypeDesc desc); } diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index 6e7aae9f72b0c..8f1cab22b86e5 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -859,10 +859,11 @@ static Option firstVariadicArg(int index) { * @see #captureStateLayout() */ static Option captureCallState(String... capturedState) { - Set set = Stream.of(Objects.requireNonNull(capturedState)) + int set = Stream.of(Objects.requireNonNull(capturedState)) .map(Objects::requireNonNull) .map(CapturableState::forName) - .collect(Collectors.toSet()); + .mapToInt(state -> 1 << state.ordinal()) + .sum(); return new LinkerOptions.CaptureCallState(set); } diff --git a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java index 0a943d253af3b..ad6387e8c9e56 100644 --- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java @@ -214,7 +214,6 @@ private static LambdaForm preparedLambdaForm(MemberName m, boolean adaptToSpecia which = LF_INVSPECIAL_IFC; } LambdaForm lform = preparedLambdaForm(mtype, which); - maybeCompile(lform, m); assert(lform.methodType().dropParameterTypes(0, 1) .equals(m.getInvocationType().basicType())) : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType()); @@ -320,12 +319,6 @@ static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) { return null; } - private static void maybeCompile(LambdaForm lform, MemberName m) { - if (lform.vmentry == null && VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class)) - // Help along bootstrapping... - lform.compileToBytecode(); - } - /** Static wrapper for DirectMethodHandle.internalMemberName. */ @ForceInline /*non-public*/ @@ -621,10 +614,9 @@ Object checkCast(Object obj) { // Enumerate the different field kinds using Wrapper, // with an extra case added for checked references. static final int - FT_LAST_WRAPPER = Wrapper.COUNT-1, FT_UNCHECKED_REF = Wrapper.OBJECT.ordinal(), - FT_CHECKED_REF = FT_LAST_WRAPPER+1, - FT_LIMIT = FT_LAST_WRAPPER+2; + FT_CHECKED_REF = Wrapper.VOID.ordinal(), + FT_LIMIT = Wrapper.COUNT; private static int afIndex(byte formOp, boolean isVolatile, int ftypeKind) { return ((formOp * FT_LIMIT * 2) + (isVolatile ? FT_LIMIT : 0) @@ -667,7 +659,6 @@ private static LambdaForm preparedFieldLambdaForm(MemberName m) { formOp += (AF_GETSTATIC_INIT - AF_GETSTATIC); } LambdaForm lform = preparedFieldLambdaForm(formOp, isVolatile, ftype); - maybeCompile(lform, m); assert(lform.methodType().dropParameterTypes(0, 1) .equals(m.getInvocationType().basicType())) : Arrays.asList(m, m.getInvocationType().basicType(), lform, lform.methodType()); @@ -683,63 +674,69 @@ private static LambdaForm preparedFieldLambdaForm(byte formOp, boolean isVolatil return lform; } - private static final Wrapper[] ALL_WRAPPERS = Wrapper.values(); - - private static Kind getFieldKind(boolean isGetter, boolean isVolatile, Wrapper wrapper) { - if (isGetter) { - if (isVolatile) { - switch (wrapper) { - case BOOLEAN: return GET_BOOLEAN_VOLATILE; - case BYTE: return GET_BYTE_VOLATILE; - case SHORT: return GET_SHORT_VOLATILE; - case CHAR: return GET_CHAR_VOLATILE; - case INT: return GET_INT_VOLATILE; - case LONG: return GET_LONG_VOLATILE; - case FLOAT: return GET_FLOAT_VOLATILE; - case DOUBLE: return GET_DOUBLE_VOLATILE; - case OBJECT: return GET_REFERENCE_VOLATILE; - } + private static final @Stable Wrapper[] ALL_WRAPPERS = Wrapper.values(); + + // Names in kind may overload but differ from their basic type + private static Kind getFieldKind(boolean isVolatile, boolean needsInit, boolean needsCast, Wrapper wrapper) { + if (isVolatile) { + if (needsInit) { + return switch (wrapper) { + case BYTE -> VOLATILE_FIELD_ACCESS_INIT_B; + case CHAR -> VOLATILE_FIELD_ACCESS_INIT_C; + case SHORT -> VOLATILE_FIELD_ACCESS_INIT_S; + case BOOLEAN -> VOLATILE_FIELD_ACCESS_INIT_Z; + default -> needsCast ? VOLATILE_FIELD_ACCESS_INIT_CAST : VOLATILE_FIELD_ACCESS_INIT; + }; } else { - switch (wrapper) { - case BOOLEAN: return GET_BOOLEAN; - case BYTE: return GET_BYTE; - case SHORT: return GET_SHORT; - case CHAR: return GET_CHAR; - case INT: return GET_INT; - case LONG: return GET_LONG; - case FLOAT: return GET_FLOAT; - case DOUBLE: return GET_DOUBLE; - case OBJECT: return GET_REFERENCE; - } + return switch (wrapper) { + case BYTE -> VOLATILE_FIELD_ACCESS_B; + case CHAR -> VOLATILE_FIELD_ACCESS_C; + case SHORT -> VOLATILE_FIELD_ACCESS_S; + case BOOLEAN -> VOLATILE_FIELD_ACCESS_Z; + default -> needsCast ? VOLATILE_FIELD_ACCESS_CAST : VOLATILE_FIELD_ACCESS; + }; } } else { - if (isVolatile) { - switch (wrapper) { - case BOOLEAN: return PUT_BOOLEAN_VOLATILE; - case BYTE: return PUT_BYTE_VOLATILE; - case SHORT: return PUT_SHORT_VOLATILE; - case CHAR: return PUT_CHAR_VOLATILE; - case INT: return PUT_INT_VOLATILE; - case LONG: return PUT_LONG_VOLATILE; - case FLOAT: return PUT_FLOAT_VOLATILE; - case DOUBLE: return PUT_DOUBLE_VOLATILE; - case OBJECT: return PUT_REFERENCE_VOLATILE; - } + if (needsInit) { + return switch (wrapper) { + case BYTE -> FIELD_ACCESS_INIT_B; + case CHAR -> FIELD_ACCESS_INIT_C; + case SHORT -> FIELD_ACCESS_INIT_S; + case BOOLEAN -> FIELD_ACCESS_INIT_Z; + default -> needsCast ? FIELD_ACCESS_INIT_CAST : FIELD_ACCESS_INIT; + }; } else { - switch (wrapper) { - case BOOLEAN: return PUT_BOOLEAN; - case BYTE: return PUT_BYTE; - case SHORT: return PUT_SHORT; - case CHAR: return PUT_CHAR; - case INT: return PUT_INT; - case LONG: return PUT_LONG; - case FLOAT: return PUT_FLOAT; - case DOUBLE: return PUT_DOUBLE; - case OBJECT: return PUT_REFERENCE; - } + return switch (wrapper) { + case BYTE -> FIELD_ACCESS_B; + case CHAR -> FIELD_ACCESS_C; + case SHORT -> FIELD_ACCESS_S; + case BOOLEAN -> FIELD_ACCESS_Z; + default -> needsCast ? FIELD_ACCESS_CAST : FIELD_ACCESS; + }; } } - throw new AssertionError("Invalid arguments"); + } + + private static String unsafeMethodName(boolean isGetter, boolean isVolatile, Wrapper wrapper) { + var name = switch (wrapper) { + case BOOLEAN -> "Boolean"; + case BYTE -> "Byte"; + case CHAR -> "Char"; + case SHORT -> "Short"; + case INT -> "Int"; + case FLOAT -> "Float"; + case LONG -> "Long"; + case DOUBLE -> "Double"; + case OBJECT -> "Reference"; + case VOID -> throw new InternalError(); + }; + var sb = new StringBuilder(3 + name.length() + (isVolatile ? 8 : 0)) + .append(isGetter ? "get" : "put") + .append(name); + if (isVolatile) { + sb.append("Volatile"); + } + return sb.toString(); } static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) { @@ -752,14 +749,16 @@ static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, i assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind); // getObject, putIntVolatile, etc. - Kind kind = getFieldKind(isGetter, isVolatile, fw); + String unsafeMethodName = unsafeMethodName(isGetter, isVolatile, fw); + // isGetter and isStatic is reflected in field type; basic type clash for subwords + Kind kind = getFieldKind(isVolatile, needsInit, needsCast, fw); MethodType linkerType; if (isGetter) linkerType = MethodType.methodType(ft, Object.class, long.class); else linkerType = MethodType.methodType(void.class, Object.class, long.class, ft); - MemberName linker = new MemberName(Unsafe.class, kind.methodName, linkerType, REF_invokeVirtual); + MemberName linker = new MemberName(Unsafe.class, unsafeMethodName, linkerType, REF_invokeVirtual); try { linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, LM_TRUSTED, NoSuchMethodException.class); @@ -817,18 +816,12 @@ static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, i names[POST_CAST] = new Name(getFunction(NF_checkCast), names[DMH_THIS], names[LINKER_CALL]); for (Name n : names) assert(n != null); - LambdaForm form; - if (needsCast || needsInit) { - // can't use the pre-generated form when casting and/or initializing - form = LambdaForm.create(ARG_LIMIT, names, RESULT); - } else { - form = LambdaForm.create(ARG_LIMIT, names, RESULT, kind); - } + LambdaForm form = LambdaForm.create(ARG_LIMIT, names, RESULT, kind); if (LambdaForm.debugNames()) { // add some detail to the lambdaForm debugname, // significant only for debugging - StringBuilder nameBuilder = new StringBuilder(kind.methodName); + StringBuilder nameBuilder = new StringBuilder(unsafeMethodName); if (isStatic) { nameBuilder.append("Static"); } else { @@ -842,6 +835,9 @@ static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, i } LambdaForm.associateWithDebugName(form, nameBuilder.toString()); } + + // NF_UNSAFE uses field form, avoid circular dependency in interpreter + form.compileToBytecode(); return form; } diff --git a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java index e52438544aef3..561f912239865 100644 --- a/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java +++ b/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java @@ -430,24 +430,21 @@ static byte[] generateDirectMethodHandleHolderClassBytes(String className, names.add(form.kind.defaultLambdaName); } for (Wrapper wrapper : Wrapper.values()) { - if (wrapper == Wrapper.VOID) { - continue; - } + int ftype = wrapper == Wrapper.VOID ? DirectMethodHandle.FT_CHECKED_REF : DirectMethodHandle.ftypeKind(wrapper.primitiveType()); for (byte b = DirectMethodHandle.AF_GETFIELD; b < DirectMethodHandle.AF_LIMIT; b++) { - int ftype = DirectMethodHandle.ftypeKind(wrapper.primitiveType()); LambdaForm form = DirectMethodHandle .makePreparedFieldLambdaForm(b, /*isVolatile*/false, ftype); - if (form.kind != LambdaForm.Kind.GENERIC) { - forms.add(form); - names.add(form.kind.defaultLambdaName); - } + if (form.kind == GENERIC) + throw new InternalError(b + " non-volatile " + ftype); + forms.add(form); + names.add(form.kind.defaultLambdaName); // volatile form = DirectMethodHandle .makePreparedFieldLambdaForm(b, /*isVolatile*/true, ftype); - if (form.kind != LambdaForm.Kind.GENERIC) { - forms.add(form); - names.add(form.kind.defaultLambdaName); - } + if (form.kind == GENERIC) + throw new InternalError(b + " volatile " + ftype); + forms.add(form); + names.add(form.kind.defaultLambdaName); } } return generateCodeBytesForLFs(className, diff --git a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index f5998c46aa17f..b3d2ff2c8806f 100644 --- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -468,24 +468,30 @@ private static MemberName lookupPregenerated(LambdaForm form, MethodType invoker case LINK_TO_TARGET_METHOD: // fall-through case GENERIC_INVOKER: // fall-through case GENERIC_LINKER: return resolveFrom(name, invokerType, Invokers.Holder.class); - case GET_REFERENCE: // fall-through - case GET_BOOLEAN: // fall-through - case GET_BYTE: // fall-through - case GET_CHAR: // fall-through - case GET_SHORT: // fall-through - case GET_INT: // fall-through - case GET_LONG: // fall-through - case GET_FLOAT: // fall-through - case GET_DOUBLE: // fall-through - case PUT_REFERENCE: // fall-through - case PUT_BOOLEAN: // fall-through - case PUT_BYTE: // fall-through - case PUT_CHAR: // fall-through - case PUT_SHORT: // fall-through - case PUT_INT: // fall-through - case PUT_LONG: // fall-through - case PUT_FLOAT: // fall-through - case PUT_DOUBLE: // fall-through + case FIELD_ACCESS: // fall-through + case FIELD_ACCESS_INIT: // fall-through + case VOLATILE_FIELD_ACCESS: // fall-through + case VOLATILE_FIELD_ACCESS_INIT:// fall-through + case FIELD_ACCESS_B: // fall-through + case FIELD_ACCESS_INIT_B: // fall-through + case VOLATILE_FIELD_ACCESS_B: // fall-through + case VOLATILE_FIELD_ACCESS_INIT_B:// fall-through + case FIELD_ACCESS_C: // fall-through + case FIELD_ACCESS_INIT_C: // fall-through + case VOLATILE_FIELD_ACCESS_C: // fall-through + case VOLATILE_FIELD_ACCESS_INIT_C:// fall-through + case FIELD_ACCESS_S: // fall-through + case FIELD_ACCESS_INIT_S: // fall-through + case VOLATILE_FIELD_ACCESS_S: // fall-through + case VOLATILE_FIELD_ACCESS_INIT_S:// fall-through + case FIELD_ACCESS_Z: // fall-through + case FIELD_ACCESS_INIT_Z: // fall-through + case VOLATILE_FIELD_ACCESS_Z: // fall-through + case VOLATILE_FIELD_ACCESS_INIT_Z:// fall-through + case FIELD_ACCESS_CAST: // fall-through + case FIELD_ACCESS_INIT_CAST: // fall-through + case VOLATILE_FIELD_ACCESS_CAST: // fall-through + case VOLATILE_FIELD_ACCESS_INIT_CAST:// fall-through case DIRECT_NEW_INVOKE_SPECIAL: // fall-through case DIRECT_INVOKE_INTERFACE: // fall-through case DIRECT_INVOKE_SPECIAL: // fall-through diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java index 7ba66a473fea2..c1a07d8d0defb 100644 --- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -263,42 +263,30 @@ enum Kind { DIRECT_NEW_INVOKE_SPECIAL("DMH.newInvokeSpecial", "newInvokeSpecial"), DIRECT_INVOKE_INTERFACE("DMH.invokeInterface", "invokeInterface"), DIRECT_INVOKE_STATIC_INIT("DMH.invokeStaticInit", "invokeStaticInit"), - GET_REFERENCE("getReference"), - PUT_REFERENCE("putReference"), - GET_REFERENCE_VOLATILE("getReferenceVolatile"), - PUT_REFERENCE_VOLATILE("putReferenceVolatile"), - GET_INT("getInt"), - PUT_INT("putInt"), - GET_INT_VOLATILE("getIntVolatile"), - PUT_INT_VOLATILE("putIntVolatile"), - GET_BOOLEAN("getBoolean"), - PUT_BOOLEAN("putBoolean"), - GET_BOOLEAN_VOLATILE("getBooleanVolatile"), - PUT_BOOLEAN_VOLATILE("putBooleanVolatile"), - GET_BYTE("getByte"), - PUT_BYTE("putByte"), - GET_BYTE_VOLATILE("getByteVolatile"), - PUT_BYTE_VOLATILE("putByteVolatile"), - GET_CHAR("getChar"), - PUT_CHAR("putChar"), - GET_CHAR_VOLATILE("getCharVolatile"), - PUT_CHAR_VOLATILE("putCharVolatile"), - GET_SHORT("getShort"), - PUT_SHORT("putShort"), - GET_SHORT_VOLATILE("getShortVolatile"), - PUT_SHORT_VOLATILE("putShortVolatile"), - GET_LONG("getLong"), - PUT_LONG("putLong"), - GET_LONG_VOLATILE("getLongVolatile"), - PUT_LONG_VOLATILE("putLongVolatile"), - GET_FLOAT("getFloat"), - PUT_FLOAT("putFloat"), - GET_FLOAT_VOLATILE("getFloatVolatile"), - PUT_FLOAT_VOLATILE("putFloatVolatile"), - GET_DOUBLE("getDouble"), - PUT_DOUBLE("putDouble"), - GET_DOUBLE_VOLATILE("getDoubleVolatile"), - PUT_DOUBLE_VOLATILE("putDoubleVolatile"), + FIELD_ACCESS("fieldAccess"), + FIELD_ACCESS_INIT("fieldAccessInit"), + VOLATILE_FIELD_ACCESS("volatileFieldAccess"), + VOLATILE_FIELD_ACCESS_INIT("volatileFieldAccessInit"), + FIELD_ACCESS_B("fieldAccessB"), + FIELD_ACCESS_INIT_B("fieldAccessInitB"), + VOLATILE_FIELD_ACCESS_B("volatileFieldAccessB"), + VOLATILE_FIELD_ACCESS_INIT_B("volatileFieldAccessInitB"), + FIELD_ACCESS_C("fieldAccessC"), + FIELD_ACCESS_INIT_C("fieldAccessInitC"), + VOLATILE_FIELD_ACCESS_C("volatileFieldAccessC"), + VOLATILE_FIELD_ACCESS_INIT_C("volatileFieldAccessInitC"), + FIELD_ACCESS_S("fieldAccessS"), + FIELD_ACCESS_INIT_S("fieldAccessInitS"), + VOLATILE_FIELD_ACCESS_S("volatileFieldAccessS"), + VOLATILE_FIELD_ACCESS_INIT_S("volatileFieldAccessInitS"), + FIELD_ACCESS_Z("fieldAccessZ"), + FIELD_ACCESS_INIT_Z("fieldAccessInitZ"), + VOLATILE_FIELD_ACCESS_Z("volatileFieldAccessZ"), + VOLATILE_FIELD_ACCESS_INIT_Z("volatileFieldAccessInitZ"), + FIELD_ACCESS_CAST("fieldAccessCast"), + FIELD_ACCESS_INIT_CAST("fieldAccessInitCast"), + VOLATILE_FIELD_ACCESS_CAST("volatileFieldAccessCast"), + VOLATILE_FIELD_ACCESS_INIT_CAST("volatileFieldAccessInitCast"), TRY_FINALLY("tryFinally"), TABLE_SWITCH("tableSwitch"), COLLECTOR("collector"), diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java index 6a25bf9c0a83a..471de5aa48fd4 100644 --- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java @@ -38,7 +38,6 @@ import static java.lang.invoke.MethodHandleImpl.Intrinsic; import static java.lang.invoke.MethodHandleImpl.NF_loop; import static java.lang.invoke.MethodHandleImpl.makeIntrinsic; -import static java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE; /** Transforms on LFs. * A lambda-form editor can derive new LFs from its base LF. @@ -90,17 +89,12 @@ static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) { * Tightly coupled with the TransformKey class, which is used to lookup existing * Transforms. */ - private static final class Transform { - final Object cache; + private static final class Transform extends SoftReference { final long packedBytes; final byte[] fullBytes; private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) { - if (USE_SOFT_CACHE) { - cache = new SoftReference(result); - } else { - cache = result; - } + super(result); this.packedBytes = packedBytes; this.fullBytes = fullBytes; } @@ -141,15 +135,6 @@ public String toString() { } return buf.toString(); } - - @SuppressWarnings("unchecked") - public LambdaForm get() { - if (cache instanceof LambdaForm lf) { - return lf; - } else { - return ((SoftReference)cache).get(); - } - } } /** diff --git a/src/java.base/share/classes/java/lang/invoke/MemberName.java b/src/java.base/share/classes/java/lang/invoke/MemberName.java index 35fd26331b1a4..918d1b1079182 100644 --- a/src/java.base/share/classes/java/lang/invoke/MemberName.java +++ b/src/java.base/share/classes/java/lang/invoke/MemberName.java @@ -711,7 +711,7 @@ public MemberName getDefinition() { } @Override - @SuppressWarnings("removal") + @SuppressWarnings("deprecation") public int hashCode() { // Avoid autoboxing getReferenceKind(), since this is used early and will force // early initialization of Byte$ByteCache diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java index 9df7d25258d8e..0db7a6a8ddb98 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -665,22 +665,4 @@ static boolean canBeCalledVirtual(MemberName symbolicRef, Class definingClass return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef symbolicRefClass.isInterface()); // Mdef implements Msym } - - //--- AOTCache support - - /** - * In normal execution, this is set to true, so that LambdaFormEditor and MethodTypeForm will - * use soft references to allow class unloading. - * - * When dumping the AOTCache, this is set to false so that no cached heap objects will - * contain soft references (which are not yet supported by AOTCache - see JDK-8341587). AOTCache - * only stores LambdaFormEditors and MethodTypeForms for classes in the boot/platform/app loaders. - * Such classes will never be unloaded, so it's OK to use hard references. - */ - static final boolean USE_SOFT_CACHE; - - static { - USE_SOFT_CACHE = Boolean.parseBoolean( - System.getProperty("java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE", "true")); - } } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index 51846e682b6f1..70592351827ef 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -35,12 +35,10 @@ import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; @@ -55,6 +53,7 @@ import jdk.internal.loader.ClassLoaders; import jdk.internal.module.Modules; import jdk.internal.util.ClassFileDumper; +import jdk.internal.util.ReferencedKeySet; import static java.lang.constant.ConstantDescs.*; import static java.lang.invoke.MethodHandleStatics.*; @@ -200,7 +199,7 @@ private record MethodInfo(MethodTypeDesc desc, List thrown, String fi private static final ClassFileDumper DUMPER = ClassFileDumper.getInstance( "jdk.invoke.MethodHandleProxies.dumpClassFiles", "DUMP_MH_PROXY_CLASSFILES"); - private static final Set> WRAPPER_TYPES = Collections.newSetFromMap(new WeakHashMap<>()); + private static final Set> WRAPPER_TYPES = ReferencedKeySet.create(false, ReferencedKeySet.concurrentHashMapSupplier()); private static final ClassValue>> PROXIES = new ClassValue<>() { @Override protected WeakReferenceHolder> computeValue(Class intfc) { diff --git a/src/java.base/share/classes/java/lang/invoke/MethodType.java b/src/java.base/share/classes/java/lang/invoke/MethodType.java index 5e1c0f8581c9a..3d15ce6871086 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodType.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,8 +31,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.function.Supplier; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -42,7 +40,6 @@ import jdk.internal.util.ReferencedKeySet; import jdk.internal.util.ReferenceKey; -import jdk.internal.misc.CDS; import jdk.internal.vm.annotation.Stable; import sun.invoke.util.BytecodeDescriptor; import sun.invoke.util.VerifyType; @@ -394,17 +391,6 @@ private static MethodType makeImpl(Class rtype, Class[] ptypes, boolean tr ptypes = NO_PTYPES; trusted = true; } MethodType primordialMT = new MethodType(rtype, ptypes); - if (archivedMethodTypes != null) { - // If this JVM process reads from archivedMethodTypes, it never - // modifies the table. So there's no need for synchronization. - // See copyInternTable() below. - assert CDS.isUsingArchive(); - MethodType mt = archivedMethodTypes.get(primordialMT); - if (mt != null) { - return mt; - } - } - MethodType mt = internTable.get(primordialMT); if (mt != null) return mt; @@ -425,7 +411,6 @@ private static MethodType makeImpl(Class rtype, Class[] ptypes, boolean tr } private static final @Stable MethodType[] objectOnlyTypes = new MethodType[20]; - private static @Stable HashMap archivedMethodTypes; /** * Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array. @@ -1397,29 +1382,9 @@ private Object readResolve() { return mt; } - static HashMap copyInternTable() { - HashMap copy = new HashMap<>(); - - for (Iterator i = internTable.iterator(); i.hasNext(); ) { - MethodType t = i.next(); - copy.put(t, t); - } - - return copy; - } - // This is called from C code, at the very end of Java code execution // during the AOT cache assembly phase. - static void createArchivedObjects() { - // After the archivedMethodTypes field is assigned, this table - // is never modified. So we don't need synchronization when reading from - // it (which happens only in a future JVM process, never in the current process). - // - // @implNote CDS.isDumpingStaticArchive() is mutually exclusive with - // CDS.isUsingArchive(); at most one of them can return true for any given JVM - // process. - assert CDS.isDumpingStaticArchive(); - archivedMethodTypes = copyInternTable(); - internTable.clear(); + private static void assemblySetup() { + internTable.prepareForAOTCache(); } } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java b/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java index 8bbc4dd5f727a..d5272337585cc 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java @@ -30,7 +30,6 @@ import java.lang.ref.SoftReference; import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException; -import static java.lang.invoke.MethodHandleNatives.USE_SOFT_CACHE; /** * Shared information for a group of method types, which differ @@ -52,7 +51,7 @@ final class MethodTypeForm { final MethodType basicType; // the canonical erasure, with primitives simplified // Cached adapter information: - private final Object[] methodHandles; + private final SoftReference[] methodHandles; // Indexes into methodHandles: static final int @@ -62,7 +61,7 @@ final class MethodTypeForm { MH_LIMIT = 3; // Cached lambda form information, for basic types only: - private final Object[] lambdaForms; + private final SoftReference[] lambdaForms; private SoftReference interpretEntry; @@ -111,16 +110,9 @@ public MethodType basicType() { return basicType; } - @SuppressWarnings("unchecked") public MethodHandle cachedMethodHandle(int which) { - Object entry = methodHandles[which]; - if (entry == null) { - return null; - } else if (entry instanceof MethodHandle mh) { - return mh; - } else { - return ((SoftReference)entry).get(); - } + SoftReference entry = methodHandles[which]; + return (entry != null) ? entry.get() : null; } public synchronized MethodHandle setCachedMethodHandle(int which, MethodHandle mh) { @@ -129,24 +121,13 @@ public synchronized MethodHandle setCachedMethodHandle(int which, MethodHandle m if (prev != null) { return prev; } - if (USE_SOFT_CACHE) { - methodHandles[which] = new SoftReference<>(mh); - } else { - methodHandles[which] = mh; - } + methodHandles[which] = new SoftReference<>(mh); return mh; } - @SuppressWarnings("unchecked") public LambdaForm cachedLambdaForm(int which) { - Object entry = lambdaForms[which]; - if (entry == null) { - return null; - } else if (entry instanceof LambdaForm lf) { - return lf; - } else { - return ((SoftReference)entry).get(); - } + SoftReference entry = lambdaForms[which]; + return (entry != null) ? entry.get() : null; } public synchronized LambdaForm setCachedLambdaForm(int which, LambdaForm form) { @@ -155,11 +136,7 @@ public synchronized LambdaForm setCachedLambdaForm(int which, LambdaForm form) { if (prev != null) { return prev; } - if (USE_SOFT_CACHE) { - lambdaForms[which] = new SoftReference<>(form); - } else { - lambdaForms[which] = form; - } + lambdaForms[which] = new SoftReference<>(form); return form; } @@ -181,6 +158,7 @@ public synchronized MemberName setCachedInterpretEntry(MemberName mn) { * This MTF will stand for that type and all un-erased variations. * Eagerly compute some basic properties of the type, common to all variations. */ + @SuppressWarnings({"rawtypes", "unchecked"}) protected MethodTypeForm(MethodType erasedType) { this.erasedType = erasedType; @@ -221,8 +199,8 @@ protected MethodTypeForm(MethodType erasedType) { this.primitiveCount = primitiveCount; this.parameterSlotCount = (short)pslotCount; - this.lambdaForms = new Object[LF_LIMIT]; - this.methodHandles = new Object[MH_LIMIT]; + this.lambdaForms = new SoftReference[LF_LIMIT]; + this.methodHandles = new SoftReference[MH_LIMIT]; } else { this.basicType = MethodType.methodType(basicReturnType, basicPtypes, true); // fill in rest of data from the basic type: 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 13ba76e5dd2f2..e109b974adc55 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.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 @@ -76,10 +76,10 @@ public abstract sealed class Reference * indicate end of list. * * Dequeued: Added to the associated queue and then removed. - * queue = ReferenceQueue.NULL; next = this. + * queue = ReferenceQueue.NULL_QUEUE; next = this. * * Unregistered: Not associated with a queue when created. - * queue = ReferenceQueue.NULL. + * queue = ReferenceQueue.NULL_QUEUE. * * The collector only needs to examine the referent field and the * discovered field to determine whether a (non-FinalReference) Reference @@ -161,8 +161,8 @@ public abstract sealed class Reference * * When registered: the queue with which this reference is registered. * enqueued: ReferenceQueue.ENQUEUE - * dequeued: ReferenceQueue.NULL - * unregistered: ReferenceQueue.NULL + * dequeued: ReferenceQueue.NULL_QUEUE + * unregistered: ReferenceQueue.NULL_QUEUE */ volatile ReferenceQueue queue; @@ -232,7 +232,7 @@ public void run() { */ private void enqueueFromPending() { var q = queue; - if (q != ReferenceQueue.NULL) q.enqueue(this); + if (q != ReferenceQueue.NULL_QUEUE) q.enqueue(this); } private static final Object processPendingLock = new Object(); @@ -306,7 +306,12 @@ static void startReferenceHandlerThread(ThreadGroup tg) { handler.start(); } + // Called from JVM when loading an AOT cache static { + runtimeSetup(); + } + + private static void runtimeSetup() { // provide access in SharedSecrets SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { @Override @@ -540,7 +545,7 @@ protected Object clone() throws CloneNotSupportedException { Reference(T referent, ReferenceQueue queue) { this.referent = referent; - this.queue = (queue == null) ? ReferenceQueue.NULL : queue; + this.queue = (queue == null) ? ReferenceQueue.NULL_QUEUE : queue; } /** diff --git a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java index 07110a19fa132..4dcaf796e2500 100644 --- a/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java +++ b/src/java.base/share/classes/java/lang/ref/ReferenceQueue.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 @@ -55,7 +55,7 @@ boolean enqueue(Reference r) { } } - static final ReferenceQueue NULL = new Null(); + static final ReferenceQueue NULL_QUEUE = new Null(); static final ReferenceQueue ENQUEUED = new Null(); private volatile Reference head; @@ -74,7 +74,7 @@ private boolean enqueue0(Reference r) { // must hold lock // Check that since getting the lock this reference hasn't already been // enqueued (and even then removed) ReferenceQueue queue = r.queue; - if ((queue == NULL) || (queue == ENQUEUED)) { + if ((queue == NULL_QUEUE) || (queue == ENQUEUED)) { return false; } assert queue == this; @@ -96,7 +96,7 @@ private boolean enqueue0(Reference r) { // must hold lock private Reference poll0() { // must hold lock Reference r = head; if (r != null) { - r.queue = NULL; + r.queue = NULL_QUEUE; // Update r.queue *before* removing from list, to avoid // race with concurrent enqueued checks and fast-path // poll(). Volatiles ensure ordering. @@ -248,7 +248,7 @@ void forEach(Consumer> action) { // still enqueued -> we reached end of chain r = null; } else { - // already dequeued: r.queue == NULL; -> + // already dequeued: r.queue == NULL_QUEUE; -> // restart from head when overtaken by queue poller(s) r = head; } diff --git a/src/java.base/share/classes/java/lang/reflect/AccessFlag.java b/src/java.base/share/classes/java/lang/reflect/AccessFlag.java index bc6b20be9a825..4314c8c410dd4 100644 --- a/src/java.base/share/classes/java/lang/reflect/AccessFlag.java +++ b/src/java.base/share/classes/java/lang/reflect/AccessFlag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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,12 +25,31 @@ package java.lang.reflect; -import java.util.Collections; -import java.util.Objects; +import java.lang.classfile.ClassModel; +import java.lang.classfile.FieldModel; +import java.lang.classfile.MethodModel; +import java.lang.classfile.attribute.InnerClassInfo; +import java.lang.classfile.attribute.MethodParameterInfo; +import java.lang.classfile.attribute.ModuleAttribute; +import java.lang.classfile.attribute.ModuleExportInfo; +import java.lang.classfile.attribute.ModuleOpenInfo; +import java.lang.classfile.attribute.ModuleRequireInfo; +import java.lang.module.ModuleDescriptor; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; -import java.util.function.Function; -import static java.util.Map.entry; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import jdk.internal.vm.annotation.Stable; + +import static java.lang.classfile.ClassFile.*; +import static java.lang.reflect.ClassFileFormatVersion.*; /** * Represents a JVM access or module-related flag on a runtime member, @@ -93,26 +112,14 @@ */ @SuppressWarnings("doclint:reference") // cross-module link public enum AccessFlag { - // Note to maintainers: anonymous class instances are used rather - // than lambdas to initialize the functions used for the - // cffvToLocations field to avoid using lambdas too early in JDK - // initialization. - /** * The access flag {@code ACC_PUBLIC}, corresponding to the source * modifier {@link Modifier#PUBLIC public}, with a mask value of * {@value "0x%04x" Modifier#PUBLIC}. */ PUBLIC(Modifier.PUBLIC, true, - Location.SET_PUBLIC_1, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv == ClassFileFormatVersion.RELEASE_0) ? - Location.SET_CLASS_FIELD_METHOD: - Location.SET_PUBLIC_1; - } - }), + Location.SET_CLASS_FIELD_METHOD_INNER_CLASS, + List.of(Map.entry(RELEASE_0, Location.SET_CLASS_FIELD_METHOD))), /** * The access flag {@code ACC_PRIVATE}, corresponding to the @@ -120,14 +127,7 @@ public Set apply(ClassFileFormatVersion cffv) { * value of {@value "0x%04x" Modifier#PRIVATE}. */ PRIVATE(Modifier.PRIVATE, true, Location.SET_FIELD_METHOD_INNER_CLASS, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv == ClassFileFormatVersion.RELEASE_0) ? - Location.SET_FIELD_METHOD: - Location.SET_FIELD_METHOD_INNER_CLASS; - } - }), + List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))), /** * The access flag {@code ACC_PROTECTED}, corresponding to the @@ -135,14 +135,7 @@ public Set apply(ClassFileFormatVersion cffv) { * value of {@value "0x%04x" Modifier#PROTECTED}. */ PROTECTED(Modifier.PROTECTED, true, Location.SET_FIELD_METHOD_INNER_CLASS, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv == ClassFileFormatVersion.RELEASE_0) ? - Location.SET_FIELD_METHOD: - Location.SET_FIELD_METHOD_INNER_CLASS; - } - }), + List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))), /** * The access flag {@code ACC_STATIC}, corresponding to the source @@ -150,13 +143,7 @@ public Set apply(ClassFileFormatVersion cffv) { * {@value "0x%04x" Modifier#STATIC}. */ STATIC(Modifier.STATIC, true, Location.SET_FIELD_METHOD_INNER_CLASS, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv == ClassFileFormatVersion.RELEASE_0) ? - Location.SET_FIELD_METHOD: - Location.SET_FIELD_METHOD_INNER_CLASS;} - }), + List.of(Map.entry(RELEASE_0, Location.SET_FIELD_METHOD))), /** * The access flag {@code ACC_FINAL}, corresponding to the source @@ -165,18 +152,8 @@ public Set apply(ClassFileFormatVersion cffv) { */ FINAL(Modifier.FINAL, true, Location.SET_FINAL_8, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - if (cffv.compareTo(ClassFileFormatVersion.RELEASE_8) >= 0) { - return Location.SET_FINAL_8; - } else { - return (cffv == ClassFileFormatVersion.RELEASE_0) ? - Location.SET_CLASS_FIELD_METHOD : - Location.SET_CLASS_FIELD_METHOD_INNER_CLASS; - } - } - }), + List.of(Map.entry(RELEASE_7, Location.SET_CLASS_FIELD_METHOD_INNER_CLASS), + Map.entry(RELEASE_0, Location.SET_CLASS_FIELD_METHOD))), /** * The access flag {@code ACC_SUPER} with a mask value of {@code @@ -186,21 +163,15 @@ public Set apply(ClassFileFormatVersion cffv) { * In Java SE 8 and above, the JVM treats the {@code ACC_SUPER} * flag as set in every class file (JVMS {@jvms 4.1}). */ - SUPER(0x0000_0020, false, Location.SET_CLASS, null), + SUPER(0x0000_0020, false, Location.SET_CLASS, List.of()), /** * The module flag {@code ACC_OPEN} with a mask value of {@code * 0x0020}. * @see java.lang.module.ModuleDescriptor#isOpen */ - OPEN(0x0000_0020, false, Location.SET_MODULE, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? - Location.SET_MODULE: - Location.EMPTY_SET;} - }), + OPEN(0x0000_0020, false, Location.SET_MODULE, + List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))), /** * The module requires flag {@code ACC_TRANSITIVE} with a mask @@ -208,20 +179,14 @@ public Set apply(ClassFileFormatVersion cffv) { * @see java.lang.module.ModuleDescriptor.Requires.Modifier#TRANSITIVE */ TRANSITIVE(0x0000_0020, false, Location.SET_MODULE_REQUIRES, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? - Location.SET_MODULE_REQUIRES: - Location.EMPTY_SET;} - }), + List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))), /** * The access flag {@code ACC_SYNCHRONIZED}, corresponding to the * source modifier {@link Modifier#SYNCHRONIZED synchronized}, with * a mask value of {@value "0x%04x" Modifier#SYNCHRONIZED}. */ - SYNCHRONIZED(Modifier.SYNCHRONIZED, true, Location.SET_METHOD, null), + SYNCHRONIZED(Modifier.SYNCHRONIZED, true, Location.SET_METHOD, List.of()), /** * The module requires flag {@code ACC_STATIC_PHASE} with a mask @@ -229,20 +194,14 @@ public Set apply(ClassFileFormatVersion cffv) { * @see java.lang.module.ModuleDescriptor.Requires.Modifier#STATIC */ STATIC_PHASE(0x0000_0040, false, Location.SET_MODULE_REQUIRES, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? - Location.SET_MODULE_REQUIRES: - Location.EMPTY_SET;} - }), - - /** + List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))), + + /** * The access flag {@code ACC_VOLATILE}, corresponding to the * source modifier {@link Modifier#VOLATILE volatile}, with a mask * value of {@value "0x%04x" Modifier#VOLATILE}. */ - VOLATILE(Modifier.VOLATILE, true, Location.SET_FIELD, null), + VOLATILE(Modifier.VOLATILE, true, Location.SET_FIELD, List.of()), /** * The access flag {@code ACC_BRIDGE} with a mask value of @@ -250,41 +209,29 @@ public Set apply(ClassFileFormatVersion cffv) { * @see Method#isBridge() */ BRIDGE(Modifier.BRIDGE, false, Location.SET_METHOD, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) >= 0 ) ? - Location.SET_METHOD: - Location.EMPTY_SET;} - }), + List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_TRANSIENT}, corresponding to the * source modifier {@link Modifier#TRANSIENT transient}, with a * mask value of {@value "0x%04x" Modifier#TRANSIENT}. */ - TRANSIENT(Modifier.TRANSIENT, true, Location.SET_FIELD, null), + TRANSIENT(Modifier.TRANSIENT, true, Location.SET_FIELD, List.of()), /** * The access flag {@code ACC_VARARGS} with a mask value of - {@value "0x%04x" Modifier#VARARGS}. + * {@value "0x%04x" Modifier#VARARGS}. * @see Executable#isVarArgs() */ VARARGS(Modifier.VARARGS, false, Location.SET_METHOD, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) >= 0 ) ? - Location.SET_METHOD: - Location.EMPTY_SET;} - }), + List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_NATIVE}, corresponding to the source * modifier {@link Modifier#NATIVE native}, with a mask value of * {@value "0x%04x" Modifier#NATIVE}. */ - NATIVE(Modifier.NATIVE, true, Location.SET_METHOD, null), + NATIVE(Modifier.NATIVE, true, Location.SET_METHOD, List.of()), /** * The access flag {@code ACC_INTERFACE} with a mask value of @@ -292,13 +239,7 @@ public Set apply(ClassFileFormatVersion cffv) { * @see Class#isInterface() */ INTERFACE(Modifier.INTERFACE, false, Location.SET_CLASS_INNER_CLASS, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_0) == 0 ) ? - Location.SET_CLASS: - Location.SET_CLASS_INNER_CLASS;} - }), + List.of(Map.entry(RELEASE_0, Location.SET_CLASS))), /** * The access flag {@code ACC_ABSTRACT}, corresponding to the @@ -307,13 +248,7 @@ public Set apply(ClassFileFormatVersion cffv) { */ ABSTRACT(Modifier.ABSTRACT, true, Location.SET_CLASS_METHOD_INNER_CLASS, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_0) == 0 ) ? - Location.SET_CLASS_METHOD: - Location.SET_CLASS_METHOD_INNER_CLASS;} - }), + List.of(Map.entry(RELEASE_0, Location.SET_CLASS_METHOD))), /** * The access flag {@code ACC_STRICT}, corresponding to the source @@ -326,14 +261,8 @@ public Set apply(ClassFileFormatVersion cffv) { * corresponding to Java SE 1.2 through 16. */ STRICT(Modifier.STRICT, true, Location.EMPTY_SET, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_2) >= 0 && - cffv.compareTo(ClassFileFormatVersion.RELEASE_16) <= 0) ? - Location.SET_METHOD: - Location.EMPTY_SET;} - }), + List.of(Map.entry(RELEASE_16, Location.SET_METHOD), + Map.entry(RELEASE_1, Location.EMPTY_SET))), /** * The access flag {@code ACC_SYNTHETIC} with a mask value of @@ -343,21 +272,9 @@ public Set apply(ClassFileFormatVersion cffv) { * @see java.lang.module.ModuleDescriptor.Modifier#SYNTHETIC */ SYNTHETIC(Modifier.SYNTHETIC, false, Location.SET_SYNTHETIC_9, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - if (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) - return Location.SET_SYNTHETIC_9; - else { - return - switch(cffv) { - case RELEASE_7 -> Location.SET_SYNTHETIC_7; - case RELEASE_8 -> Location.SET_SYNTHETIC_8; - default -> Location.EMPTY_SET; - }; - } - } - }), + List.of(Map.entry(RELEASE_8, Location.SET_SYNTHETIC_8), + Map.entry(RELEASE_7, Location.SET_SYNTHETIC_5), + Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_ANNOTATION} with a mask value of @@ -365,13 +282,7 @@ public Set apply(ClassFileFormatVersion cffv) { * @see Class#isAnnotation() */ ANNOTATION(Modifier.ANNOTATION, false, Location.SET_CLASS_INNER_CLASS, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) >= 0 ) ? - Location.SET_CLASS_INNER_CLASS: - Location.EMPTY_SET;} - }), + List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_ENUM} with a mask value of @@ -379,44 +290,22 @@ public Set apply(ClassFileFormatVersion cffv) { * @see Class#isEnum() */ ENUM(Modifier.ENUM, false, Location.SET_CLASS_FIELD_INNER_CLASS, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_5) >= 0 ) ? - Location.SET_CLASS_FIELD_INNER_CLASS: - Location.EMPTY_SET;} - }), + List.of(Map.entry(RELEASE_4, Location.EMPTY_SET))), /** * The access flag {@code ACC_MANDATED} with a mask value of * {@value "0x%04x" Modifier#MANDATED}. */ MANDATED(Modifier.MANDATED, false, Location.SET_MANDATED_9, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - if (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) { - return Location.SET_MANDATED_9; - } else { - return (cffv == ClassFileFormatVersion.RELEASE_8) ? - Location.SET_METHOD_PARAM: - Location.EMPTY_SET; - } - } - }), + List.of(Map.entry(RELEASE_8, Location.SET_METHOD_PARAM), + Map.entry(RELEASE_7, Location.EMPTY_SET))), /** * The access flag {@code ACC_MODULE} with a mask value of {@code * 0x8000}. */ MODULE(0x0000_8000, false, Location.SET_CLASS, - new Function>() { - @Override - public Set apply(ClassFileFormatVersion cffv) { - return (cffv.compareTo(ClassFileFormatVersion.RELEASE_9) >= 0 ) ? - Location.SET_CLASS: - Location.EMPTY_SET;} - }) + List.of(Map.entry(RELEASE_8, Location.EMPTY_SET))), ; // May want to override toString for a different enum constant -> @@ -425,31 +314,31 @@ public Set apply(ClassFileFormatVersion cffv) { private final int mask; private final boolean sourceModifier; - // Intentionally using Set rather than EnumSet since EnumSet is - // mutable. + // immutable private final Set locations; - // Lambda to implement locations(ClassFileFormatVersion cffv) - private final Function> cffvToLocations; + // historical locations up to a given version; immutable + private final List>> historicalLocations; private AccessFlag(int mask, boolean sourceModifier, Set locations, - Function> cffvToLocations) { + List>> historicalLocations) { this.mask = mask; this.sourceModifier = sourceModifier; this.locations = locations; - this.cffvToLocations = cffvToLocations; + this.historicalLocations = Location.ensureHistoryOrdered(historicalLocations); } /** - * {@return the corresponding integer mask for the access flag} + * {@return the corresponding mask for the access flag} The mask has + * exactly one bit set and is in the range of {@code char}. */ public int mask() { return mask; } /** - * {@return whether or not the flag has a directly corresponding + * {@return whether or not this flag has a directly corresponding * modifier in the Java programming language} */ public boolean sourceModifier() { @@ -457,116 +346,217 @@ public boolean sourceModifier() { } /** - * {@return kinds of constructs the flag can be applied to in the - * latest class file format version} + * {@return locations this flag can be applied to in the current class file + * format version} + *

+ * This method returns an empty set if this flag is not defined in + * the current class file format version. */ public Set locations() { return locations; } /** - * {@return kinds of constructs the flag can be applied to in the - * given class file format version} + * {@return locations this flag can be applied to in the given class file + * format version} + *

+ * This method returns an empty set if this flag is not defined in + * the given {@code cffv}. + * * @param cffv the class file format version to use * @throws NullPointerException if the parameter is {@code null} */ public Set locations(ClassFileFormatVersion cffv) { - Objects.requireNonNull(cffv); - if (cffvToLocations == null) { - return locations; - } else { - return cffvToLocations.apply(cffv); - } + return Location.findInHistory(locations, historicalLocations, cffv); } /** * {@return an unmodifiable set of access flags for the given mask value - * appropriate for the location in question} + * appropriate for the location in the current class file format version} * * @param mask bit mask of access flags * @param location context to interpret mask value * @throws IllegalArgumentException if the mask contains bit - * positions not support for the location in question + * positions not defined for the location in the current class file format + * @throws NullPointerException if {@code location} is {@code null} */ public static Set maskToAccessFlags(int mask, Location location) { - Set result = java.util.EnumSet.noneOf(AccessFlag.class); - for (var accessFlag : LocationToFlags.locationToFlags.get(location)) { - int accessMask = accessFlag.mask(); - if ((mask & accessMask) != 0) { - result.add(accessFlag); - mask = mask & ~accessMask; - } - } - if (mask != 0) { + var definition = findDefinition(location); // null checks location + int unmatchedMask = mask & (~location.flagsMask()); + if (unmatchedMask != 0) { throw new IllegalArgumentException("Unmatched bit position 0x" + - Integer.toHexString(mask) + - " for location " + location); + Integer.toHexString(unmatchedMask) + + " for location " + location); } - return Collections.unmodifiableSet(result); + return new AccessFlagSet(definition, mask); } /** - * A location within a class file where flags can be applied. + * {@return an unmodifiable set of access flags for the given mask value + * appropriate for the location in the given class file format version} * - * Note that since these locations represent class file structures - * rather than language structures many language structures, such + * @param mask bit mask of access flags + * @param location context to interpret mask value + * @param cffv the class file format to interpret mask value + * @throws IllegalArgumentException if the mask contains bit + * positions not defined for the location in the given class file format + * @throws NullPointerException if {@code location} or {@code cffv} is {@code null} + * @since 25 + */ + public static Set maskToAccessFlags(int mask, Location location, ClassFileFormatVersion cffv) { + var definition = findDefinition(location); // null checks location + int unmatchedMask = mask & (~location.flagsMask(cffv)); // null checks cffv + if (unmatchedMask != 0) { + throw new IllegalArgumentException("Unmatched bit position 0x" + + Integer.toHexString(unmatchedMask) + + " for location " + location + + " for class file format " + cffv); + } + return new AccessFlagSet(definition, mask); + } + + /** + * A location within a {@code class} file where flags can be applied. + *

+ * Note that since these locations represent {@code class} file structures + * rather than language structures, many language structures, such * as constructors and interfaces, are not present. * @since 20 */ public enum Location { /** * Class location. - * @jvms 4.1 The ClassFile Structure + * + * @see Class#accessFlags() + * @see ClassModel#flags() + * @see Modifier#classModifiers() + * @see Modifier#interfaceModifiers() + * @jvms 4.1 The {@code ClassFile} Structure */ - CLASS, + CLASS(ACC_PUBLIC | ACC_FINAL | ACC_SUPER | + ACC_INTERFACE | ACC_ABSTRACT | + ACC_SYNTHETIC | ACC_ANNOTATION | + ACC_ENUM | ACC_MODULE, + List.of(Map.entry(RELEASE_8, // no module + ACC_PUBLIC | ACC_FINAL | ACC_SUPER | + ACC_INTERFACE | ACC_ABSTRACT | + ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM), + Map.entry(RELEASE_4, // no synthetic, annotation, enum + ACC_PUBLIC | ACC_FINAL | ACC_SUPER | + ACC_INTERFACE | ACC_ABSTRACT))), /** * Field location. + * + * @see Field#accessFlags() + * @see FieldModel#flags() + * @see Modifier#fieldModifiers() * @jvms 4.5 Fields */ - FIELD, + FIELD(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | + ACC_STATIC | ACC_FINAL | ACC_VOLATILE | + ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM, + List.of(Map.entry(RELEASE_4, // no synthetic, enum + ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | + ACC_STATIC | ACC_FINAL | ACC_VOLATILE | + ACC_TRANSIENT))), /** * Method location. + * + * @see Executable#accessFlags() + * @see MethodModel#flags() + * @see Modifier#methodModifiers() + * @see Modifier#constructorModifiers() * @jvms 4.6 Methods */ - METHOD, + METHOD(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | + ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | + ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | + ACC_ABSTRACT | ACC_SYNTHETIC, + List.of(Map.entry(RELEASE_16, // had strict + ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | + ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | + ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | + ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC), + Map.entry(RELEASE_4, // no bridge, varargs, synthetic + ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | + ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | + ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT), + Map.entry(RELEASE_1, // no strict + ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | + ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | + ACC_NATIVE | ACC_ABSTRACT))), /** * Inner class location. - * @jvms 4.7.6 The InnerClasses Attribute + * + * @see Class#accessFlags() + * @see InnerClassInfo#flags() + * @see Modifier#classModifiers() + * @see Modifier#interfaceModifiers() + * @jvms 4.7.6 The {@code InnerClasses} Attribute */ - INNER_CLASS, + INNER_CLASS(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | + ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT | + ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM, + List.of(Map.entry(RELEASE_4, // no synthetic, annotation, enum + ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | + ACC_STATIC | ACC_FINAL | ACC_INTERFACE | + ACC_ABSTRACT), + Map.entry(RELEASE_0, 0))), // did not exist /** * Method parameter location. - * @jvms 4.7.24 The MethodParameters Attribute + * + * @see Parameter#accessFlags() + * @see MethodParameterInfo#flags() + * @see Modifier#parameterModifiers() + * @jvms 4.7.24 The {@code MethodParameters} Attribute */ - METHOD_PARAMETER, + METHOD_PARAMETER(ACC_FINAL | ACC_SYNTHETIC | ACC_MANDATED, + List.of(Map.entry(RELEASE_7, 0))), // did not exist /** - * Module location - * @jvms 4.7.25 The Module Attribute + * Module location. + * + * @see ModuleDescriptor#accessFlags() + * @see ModuleAttribute#moduleFlags() + * @jvms 4.7.25 The {@code Module} Attribute */ - MODULE, + MODULE(ACC_OPEN | ACC_SYNTHETIC | ACC_MANDATED, + List.of(Map.entry(RELEASE_8, 0))), // did not exist /** - * Module requires location - * @jvms 4.7.25 The Module Attribute + * Module requires location. + * + * @see ModuleDescriptor.Requires#accessFlags() + * @see ModuleRequireInfo#requiresFlags() + * @jvms 4.7.25 The {@code Module} Attribute */ - MODULE_REQUIRES, + MODULE_REQUIRES(ACC_TRANSITIVE | ACC_STATIC_PHASE | ACC_SYNTHETIC | ACC_MANDATED, + List.of(Map.entry(RELEASE_8, 0))), // did not exist /** - * Module exports location - * @jvms 4.7.25 The Module Attribute + * Module exports location. + * + * @see ModuleDescriptor.Exports#accessFlags() + * @see ModuleExportInfo#exportsFlags() + * @jvms 4.7.25 The {@code Module} Attribute */ - MODULE_EXPORTS, + MODULE_EXPORTS(ACC_SYNTHETIC | ACC_MANDATED, + List.of(Map.entry(RELEASE_8, 0))), // did not exist /** - * Module opens location - * @jvms 4.7.25 The Module Attribute + * Module opens location. + * + * @see ModuleDescriptor.Opens#accessFlags() + * @see ModuleOpenInfo#opensFlags() + * @jvms 4.7.25 The {@code Module} Attribute */ - MODULE_OPENS; + MODULE_OPENS(ACC_SYNTHETIC | ACC_MANDATED, + List.of(Map.entry(RELEASE_8, 0))), // did not exist + ; // Repeated sets of locations used by AccessFlag constants private static final Set EMPTY_SET = Set.of(); @@ -593,20 +583,18 @@ public enum Location { Set.of(CLASS, INNER_CLASS); private static final Set SET_MODULE_REQUIRES = Set.of(MODULE_REQUIRES); - private static final Set SET_PUBLIC_1 = - Set.of(CLASS, FIELD, METHOD, INNER_CLASS); private static final Set SET_FINAL_8 = Set.of(CLASS, FIELD, METHOD, INNER_CLASS, /* added in 1.1 */ METHOD_PARAMETER); /* added in 8 */ - private static final Set SET_SYNTHETIC_7 = + private static final Set SET_SYNTHETIC_5 = Set.of(CLASS, FIELD, METHOD, INNER_CLASS); private static final Set SET_SYNTHETIC_8 = Set.of(CLASS, FIELD, METHOD, INNER_CLASS, METHOD_PARAMETER); private static final Set SET_SYNTHETIC_9 = - // Added as an access flag in 7 + // Added as an access flag in 5.0 Set.of(CLASS, FIELD, METHOD, INNER_CLASS, METHOD_PARAMETER, // Added in 8 @@ -618,37 +606,227 @@ public enum Location { // Starting in 9 MODULE, MODULE_REQUIRES, MODULE_EXPORTS, MODULE_OPENS); + + private final int flagsMask; + private final List> historicalFlagsMasks; + + Location(int flagsMask, + List> historicalFlagsMasks) { + this.flagsMask = flagsMask; + this.historicalFlagsMasks = ensureHistoryOrdered(historicalFlagsMasks); + } + + // Ensures the historical versions are from newest to oldest and do not include the latest + // These 2 utilities reside in Location because Location must be initialized before AccessFlag + private static List> ensureHistoryOrdered( + List> history) { + ClassFileFormatVersion lastVersion = ClassFileFormatVersion.latest(); + for (var e : history) { + var historyVersion = e.getKey(); + if (lastVersion.compareTo(historyVersion) <= 0) { + throw new IllegalArgumentException("Versions out of order"); + } + lastVersion = historyVersion; + } + return history; + } + + private static T findInHistory(T candidate, List> history, + ClassFileFormatVersion cffv) { + Objects.requireNonNull(cffv); + for (var e : history) { + if (e.getKey().compareTo(cffv) < 0) { + // last version found was valid + return candidate; + } + candidate = e.getValue(); + } + return candidate; + } + + /** + * {@return the union of masks of all access flags defined for + * this location in the current class file format version} + *

+ * This method returns {@code 0} if this location does not exist in + * the current class file format version. + * + * @since 25 + */ + public int flagsMask() { + return flagsMask; + } + + /** + * {@return the union of masks of all access flags defined for + * this location in the given class file format version} + *

+ * This method returns {@code 0} if this location does not exist in + * the given {@code cffv}. + * + * @param cffv the class file format version + * @throws NullPointerException if {@code cffv} is {@code null} + * @since 25 + */ + public int flagsMask(ClassFileFormatVersion cffv) { + return findInHistory(flagsMask, historicalFlagsMasks, cffv); + } + + /** + * {@return the set of access flags defined for this location in the + * current class file format version} The set is immutable. + *

+ * This method returns an empty set if this location does not exist + * in the current class file format version. + * + * @since 25 + */ + public Set flags() { + return new AccessFlagSet(findDefinition(this), flagsMask()); + } + + /** + * {@return the set of access flags defined for this location in the + * given class file format version} The set is immutable. + *

+ * This method returns an empty set if this location does not exist + * in the given {@code cffv}. + * + * @param cffv the class file format version + * @throws NullPointerException if {@code cffv} is {@code null} + * @since 25 + */ + public Set flags(ClassFileFormatVersion cffv) { + // implicit null check cffv + return new AccessFlagSet(findDefinition(this), flagsMask(cffv)); + } } - private static class LocationToFlags { - private static Map> locationToFlags = - Map.ofEntries(entry(Location.CLASS, - Set.of(PUBLIC, FINAL, SUPER, - INTERFACE, ABSTRACT, - SYNTHETIC, ANNOTATION, - ENUM, AccessFlag.MODULE)), - entry(Location.FIELD, - Set.of(PUBLIC, PRIVATE, PROTECTED, - STATIC, FINAL, VOLATILE, - TRANSIENT, SYNTHETIC, ENUM)), - entry(Location.METHOD, - Set.of(PUBLIC, PRIVATE, PROTECTED, - STATIC, FINAL, SYNCHRONIZED, - BRIDGE, VARARGS, NATIVE, - ABSTRACT, STRICT, SYNTHETIC)), - entry(Location.INNER_CLASS, - Set.of(PUBLIC, PRIVATE, PROTECTED, - STATIC, FINAL, INTERFACE, ABSTRACT, - SYNTHETIC, ANNOTATION, ENUM)), - entry(Location.METHOD_PARAMETER, - Set.of(FINAL, SYNTHETIC, MANDATED)), - entry(Location.MODULE, - Set.of(OPEN, SYNTHETIC, MANDATED)), - entry(Location.MODULE_REQUIRES, - Set.of(TRANSITIVE, STATIC_PHASE, SYNTHETIC, MANDATED)), - entry(Location.MODULE_EXPORTS, - Set.of(SYNTHETIC, MANDATED)), - entry(Location.MODULE_OPENS, - Set.of(SYNTHETIC, MANDATED))); + private static AccessFlag[] createDefinition(AccessFlag... known) { + var ret = new AccessFlag[Character.SIZE]; + for (var flag : known) { + var mask = flag.mask; + int pos = Integer.numberOfTrailingZeros(mask); + assert ret[pos] == null : ret[pos] + " " + flag; + ret[pos] = flag; + } + return ret; + } + + // Will take extra args in the future for valhalla switch + private static AccessFlag[] findDefinition(Location location) { + return switch (location) { + case CLASS -> CLASS_FLAGS; + case FIELD -> FIELD_FLAGS; + case METHOD -> METHOD_FLAGS; + case INNER_CLASS -> INNER_CLASS_FLAGS; + case METHOD_PARAMETER -> METHOD_PARAMETER_FLAGS; + case MODULE -> MODULE_FLAGS; + case MODULE_REQUIRES -> MODULE_REQUIRES_FLAGS; + case MODULE_EXPORTS -> MODULE_EXPORTS_FLAGS; + case MODULE_OPENS -> MODULE_OPENS_FLAGS; + }; + } + + private static final @Stable AccessFlag[] // Can use stable array and lazy init in the future + CLASS_FLAGS = createDefinition(PUBLIC, FINAL, SUPER, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM, MODULE), + FIELD_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, VOLATILE, TRANSIENT, SYNTHETIC, ENUM), + METHOD_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, SYNCHRONIZED, BRIDGE, VARARGS, NATIVE, ABSTRACT, STRICT, SYNTHETIC), + INNER_CLASS_FLAGS = createDefinition(PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT, SYNTHETIC, ANNOTATION, ENUM), + METHOD_PARAMETER_FLAGS = createDefinition(FINAL, SYNTHETIC, MANDATED), + MODULE_FLAGS = createDefinition(OPEN, SYNTHETIC, MANDATED), + MODULE_REQUIRES_FLAGS = createDefinition(TRANSITIVE, STATIC_PHASE, SYNTHETIC, MANDATED), + MODULE_EXPORTS_FLAGS = createDefinition(SYNTHETIC, MANDATED), + MODULE_OPENS_FLAGS = createDefinition(SYNTHETIC, MANDATED); + + private static int undefinedMask(AccessFlag[] definition, int mask) { + assert definition.length == Character.SIZE; + int definedMask = 0; + for (int i = 0; i < Character.SIZE; i++) { + if (definition[i] != null) { + definedMask |= 1 << i; + } + } + return mask & ~definedMask; + } + + private static final class AccessFlagSet extends AbstractSet { + private final @Stable AccessFlag[] definition; + private final int mask; + + // all mutating methods throw UnsupportedOperationException + @Override public boolean add(AccessFlag e) { throw uoe(); } + @Override public boolean addAll(Collection c) { throw uoe(); } + @Override public void clear() { throw uoe(); } + @Override public boolean remove(Object o) { throw uoe(); } + @Override public boolean removeAll(Collection c) { throw uoe(); } + @Override public boolean removeIf(Predicate filter) { throw uoe(); } + @Override public boolean retainAll(Collection c) { throw uoe(); } + private static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); } + + private AccessFlagSet(AccessFlag[] definition, int mask) { + assert undefinedMask(definition, mask) == 0 : mask; + this.definition = definition; + this.mask = mask; + } + + @Override + public Iterator iterator() { + return new AccessFlagIterator(definition, mask); + } + + @Override + public void forEach(Consumer action) { + Objects.requireNonNull(action); // in case of empty + for (int i = 0; i < Character.SIZE; i++) { + if ((mask & (1 << i)) != 0) { + action.accept(definition[i]); + } + } + } + + private static final class AccessFlagIterator implements Iterator { + private final @Stable AccessFlag[] definition; + private int remainingMask; + + private AccessFlagIterator(AccessFlag[] definition, int remainingMask) { + this.definition = definition; + this.remainingMask = remainingMask; + } + + @Override + public boolean hasNext() { + return remainingMask != 0; + } + + @Override + public AccessFlag next() { + int flagBit = Integer.lowestOneBit(remainingMask); + if (flagBit == 0) { + throw new NoSuchElementException(); + } + remainingMask &= ~flagBit; + return definition[Integer.numberOfTrailingZeros(flagBit)]; + } + } + + @Override + public int size() { + return Integer.bitCount(mask); + } + + @Override + public boolean contains(Object o) { + if (Objects.requireNonNull(o) instanceof AccessFlag flag) { + int bit = flag.mask; + return (bit & mask) != 0 && definition[Integer.numberOfTrailingZeros(bit)] == flag; + } + return false; + } + + @Override + public boolean isEmpty() { + return mask == 0; + } } } diff --git a/src/java.base/share/classes/java/lang/reflect/Executable.java b/src/java.base/share/classes/java/lang/reflect/Executable.java index b2978d73c8b00..2f6f6cca89f04 100644 --- a/src/java.base/share/classes/java/lang/reflect/Executable.java +++ b/src/java.base/share/classes/java/lang/reflect/Executable.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 @@ -221,8 +221,9 @@ String sharedToGenericString(int modifierMask, boolean isDefault) { */ @Override public Set accessFlags() { - return AccessFlag.maskToAccessFlags(getModifiers(), - AccessFlag.Location.METHOD); + return reflectionFactory.parseAccessFlags(getModifiers(), + AccessFlag.Location.METHOD, + getDeclaringClass()); } /** diff --git a/src/java.base/share/classes/java/lang/reflect/Field.java b/src/java.base/share/classes/java/lang/reflect/Field.java index bffa211fe1215..e26d8b03ff8aa 100644 --- a/src/java.base/share/classes/java/lang/reflect/Field.java +++ b/src/java.base/share/classes/java/lang/reflect/Field.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -218,7 +218,7 @@ public int getModifiers() { */ @Override public Set accessFlags() { - return AccessFlag.maskToAccessFlags(getModifiers(), AccessFlag.Location.FIELD); + return reflectionFactory.parseAccessFlags(getModifiers(), AccessFlag.Location.FIELD, getDeclaringClass()); } /** diff --git a/src/java.base/share/classes/java/lang/reflect/Modifier.java b/src/java.base/share/classes/java/lang/reflect/Modifier.java index 1b7bc3bf65b41..624f32b4e04e3 100644 --- a/src/java.base/share/classes/java/lang/reflect/Modifier.java +++ b/src/java.base/share/classes/java/lang/reflect/Modifier.java @@ -448,6 +448,8 @@ static boolean isMandated(int mod) { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a class. * + * @see AccessFlag.Location#CLASS + * @see AccessFlag.Location#INNER_CLASS * @jls 8.1.1 Class Modifiers * @since 1.7 */ @@ -461,6 +463,8 @@ public static int classModifiers() { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to an interface. * + * @see AccessFlag.Location#CLASS + * @see AccessFlag.Location#INNER_CLASS * @jls 9.1.1 Interface Modifiers * @since 1.7 */ @@ -474,6 +478,7 @@ public static int interfaceModifiers() { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a constructor. * + * @see AccessFlag.Location#METHOD * @jls 8.8.3 Constructor Modifiers * @since 1.7 */ @@ -487,6 +492,7 @@ public static int constructorModifiers() { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a method. * + * @see AccessFlag.Location#METHOD * @jls 8.4.3 Method Modifiers * @since 1.7 */ @@ -500,6 +506,7 @@ public static int methodModifiers() { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a field. * + * @see AccessFlag.Location#FIELD * @jls 8.3.1 Field Modifiers * @since 1.7 */ @@ -513,6 +520,7 @@ public static int fieldModifiers() { * @return an {@code int} value OR-ing together the source language * modifiers that can be applied to a parameter. * + * @see AccessFlag.Location#METHOD_PARAMETER * @jls 8.4.1 Formal Parameters * @since 1.8 */ diff --git a/src/java.base/share/classes/java/lang/reflect/Parameter.java b/src/java.base/share/classes/java/lang/reflect/Parameter.java index eac020012949a..b8a57a9790b73 100644 --- a/src/java.base/share/classes/java/lang/reflect/Parameter.java +++ b/src/java.base/share/classes/java/lang/reflect/Parameter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -172,8 +172,8 @@ public int getModifiers() { * @since 20 */ public Set accessFlags() { - return AccessFlag.maskToAccessFlags(getModifiers(), - AccessFlag.Location.METHOD_PARAMETER); + return AccessibleObject.reflectionFactory.parseAccessFlags(getModifiers(), + AccessFlag.Location.METHOD_PARAMETER, getDeclaringExecutable().getDeclaringClass()); } /** diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index 2c31b1d060669..f724d7355b005 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -27,6 +27,8 @@ import java.lang.Enum.EnumDesc; import java.lang.classfile.CodeBuilder; +import java.lang.classfile.attribute.StackMapFrameInfo; +import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; import java.lang.constant.MethodTypeDesc; @@ -48,6 +50,7 @@ import java.lang.classfile.Label; import java.lang.classfile.instruction.SwitchCase; +import jdk.internal.classfile.impl.DirectCodeBuilder; import jdk.internal.constant.ClassOrInterfaceDescImpl; import jdk.internal.constant.ConstantUtils; import jdk.internal.constant.MethodTypeDescImpl; @@ -103,6 +106,13 @@ private SwitchBootstraps() {} private static final MethodType MT_TYPE_SWITCH = MethodType.methodType(int.class, Object.class, int.class); + private static final List TYPE_SWITCH_LOCALS = List.of( + StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_Object), StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER + ); + private static final List TYPE_SWITCH_EXTRA_LOCALS = List.of( + StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_Object), StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER, + StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_BiPredicate), StackMapFrameInfo.ObjectVerificationTypeInfo.of(CD_List) + ); private static class StaticHolders { private static final MethodHandle MAPPED_ENUM_SWITCH; @@ -482,8 +492,11 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto int ENUM_CACHE = 2; int EXTRA_CLASS_LABELS = 3; + var locals = enumDescs == null && extraClassLabels == null ? TYPE_SWITCH_LOCALS : TYPE_SWITCH_EXTRA_LOCALS; + return cb -> { // Objects.checkIndex(RESTART_IDX, labelConstants + 1) + var stackMapFrames = new ArrayList(labelConstants.length * 2); cb.iload(RESTART_IDX) .loadConstant(labelConstants.length + 1) .invokestatic(CD_Objects, "checkIndex", CHECK_INDEX_DESCRIPTOR) @@ -494,9 +507,12 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto .iconst_m1() .ireturn() .labelBinding(nonNullLabel); + stackMapFrames.add(StackMapFrameInfo.of(nonNullLabel, locals, List.of())); if (labelConstants.length == 0) { cb.loadConstant(0) - .ireturn(); + .ireturn() + .with(StackMapTableAttribute.of(stackMapFrames)); + DirectCodeBuilder.withMaxs(cb, 2, locals.size()); // checkIndex uses 2 return; } cb.iload(RESTART_IDX); @@ -509,6 +525,7 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto for (int idx = labelConstants.length - 1; idx >= 0; idx--) { Object currentLabel = labelConstants[idx]; Label target = cb.newLabel(); + stackMapFrames.add(StackMapFrameInfo.of(target, locals, List.of())); Label next; if (lastLabel == null) { next = dflt; @@ -541,7 +558,7 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto } else if (!unconditionalExactnessCheck(Wrapper.asPrimitiveType(selectorType), classLabel)) { // Integer i = ... or int i = ... // o instanceof float - Label notNumber = cb.newLabel(); + Label notNumber = cb.newLabel(); // this label may end up unbound cb.aload(SELECTOR_OBJ) .instanceOf(CD_Number); if (selectorType == long.class || selectorType == float.class || selectorType == double.class || @@ -570,8 +587,9 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto "intValue", MethodTypeDesc.of(CD_int)) .goto_(compare) - .labelBinding(notNumber) - .aload(SELECTOR_OBJ) + .labelBinding(notNumber); + stackMapFrames.add(StackMapFrameInfo.of(notNumber, locals, List.of())); + cb.aload(SELECTOR_OBJ) .instanceOf(CD_Character) .ifeq(next) .aload(SELECTOR_OBJ) @@ -580,6 +598,7 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto "charValue", MethodTypeDesc.of(CD_char)) .labelBinding(compare); + stackMapFrames.add(StackMapFrameInfo.of(compare, locals, List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER))); } TypePairs typePair = TypePairs.of(Wrapper.asPrimitiveType(selectorType), classLabel); @@ -648,8 +667,9 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto "intValue", MethodTypeDesc.of(CD_int)) .goto_(compare) - .labelBinding(notNumber) - .aload(SELECTOR_OBJ) + .labelBinding(notNumber); + stackMapFrames.add(StackMapFrameInfo.of(notNumber, locals, List.of())); + cb.aload(SELECTOR_OBJ) .instanceOf(CD_Character) .ifeq(next) .aload(SELECTOR_OBJ) @@ -657,9 +677,9 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto .invokevirtual(CD_Character, "charValue", MethodTypeDesc.of(CD_char)) - .labelBinding(compare) - - .loadConstant(integerLabel) + .labelBinding(compare); + stackMapFrames.add(StackMapFrameInfo.of(compare, locals, List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER))); + cb.loadConstant(integerLabel) .if_icmpne(next); } else if ((caseLabel instanceof Long || caseLabel instanceof Float || @@ -688,9 +708,12 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto cb.loadConstant(idx) .ireturn(); } + stackMapFrames.add(StackMapFrameInfo.of(dflt, locals, List.of())); cb.labelBinding(dflt) .loadConstant(labelConstants.length) - .ireturn(); + .ireturn() + .with(StackMapTableAttribute.of(stackMapFrames)); + DirectCodeBuilder.withMaxs(cb, 3, locals.size()); // enum labels use 3 stack, others use 2 }; } @@ -702,7 +725,7 @@ private static MethodHandle generateTypeSwitch(MethodHandles.Lookup caller, Clas List> enumDescs = addExtraInfo ? new ArrayList<>() : null; List> extraClassLabels = addExtraInfo ? new ArrayList<>() : null; - byte[] classBytes = ClassFile.of().build(ConstantUtils.binaryNameToDesc(typeSwitchClassName(caller.lookupClass())), + byte[] classBytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS).build(ConstantUtils.binaryNameToDesc(typeSwitchClassName(caller.lookupClass())), clb -> { clb.withFlags(AccessFlag.FINAL, AccessFlag.SUPER, AccessFlag.SYNTHETIC) .withMethodBody("typeSwitch", diff --git a/src/java.base/share/classes/java/math/BitSieve.java b/src/java.base/share/classes/java/math/BitSieve.java index 8d0d370f9be44..4403d79a22ff8 100644 --- a/src/java.base/share/classes/java/math/BitSieve.java +++ b/src/java.base/share/classes/java/math/BitSieve.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2007, 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 @@ -44,22 +44,22 @@ * @author Michael McCloskey * @since 1.3 */ -class BitSieve { +final class BitSieve { /** * Stores the bits in this bitSieve. */ - private long bits[]; + private final long[] bits; /** * Length is how many bits this sieve holds. */ - private int length; + private final int length; /** * A small sieve used to filter out multiples of small primes in a search * sieve. */ - private static BitSieve smallSieve = new BitSieve(); + private static final BitSieve smallSieve = new BitSieve(); /** * Construct a "small sieve" with a base of 0. This constructor is diff --git a/src/java.base/share/classes/java/security/doc-files/debug-system-property.html b/src/java.base/share/classes/java/security/doc-files/debug-system-property.html new file mode 100644 index 0000000000000..4ea34252b3cac --- /dev/null +++ b/src/java.base/share/classes/java/security/doc-files/debug-system-property.html @@ -0,0 +1,193 @@ + + + + + + The java.security.debug system property + + + +

{@systemProperty java.security.debug}

+

To monitor security access, you can set the java.security.debug + system property, which determines what trace messages are printed during + execution. The value of the property is one or more options separated by a + comma. +

+ +

Printing Thread and Timestamp Information

+

+ You can append the following strings to any option specified in the + java.security.debug system property to print additional + information: +

    +
  • +thread: Print thread and caller information
  • +
  • +timestamp: Print timestamp information
  • +
+

+ For example, to add thread, caller, and timestamp information to all + debugging output, set the java.security.debug system property + on the command line as follows: +

java -Djava.security.debug=all+thread+timestamp MyApp
+ +

The following table lists the java.security.debug options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Security Debug Options
OptionDescription
allTurn on all the debugging options
certpathTurns on debugging for the PKIX CertPathValidator and + CertPathBuilder implementations. The following sub-options + can be used with the certpath option: +
    +
  • ocsp: Dump OCSP protocol exchanges
  • +
  • verbose: A hexadecimal dump of the OCSP + request and response bytes is displayed.
  • +
+
configfileJAAS (Java Authentication and Authorization Service) configuration file loading
configparserJAAS configuration file parsing
gssloginconfigJava GSS (Generic Security Services) login configuration file debugging
jarJAR file verification
jcaJCA engine class debugging
KeyStoreKeyStore debugging
logincontextLoginContext results
pcscJava Smart Card I/O and SunPCSC provider debugging
pkcs11PKCS11 session manager debugging
pkcs11keystorePKCS11 KeyStore debugging
pkcs12PKCS12 KeyStore debugging
propertiesjava.security configuration file debugging
providerSecurity provider debugging. The following sub-option can be used + with the provider option: engine=(engines) + : The output is displayed only for a specified list of one or more JCA + engines, separated by a comma. The supported values for (engines) are: +
    +
  • Cipher
  • +
  • KDF
  • +
  • KeyAgreement
  • +
  • KeyGenerator
  • +
  • KeyPairGenerator
  • +
  • KeyStore
  • +
  • Mac
  • +
  • MessageDigest
  • +
  • SecureRandom
  • +
  • Signature
  • +
+
securerandomSecureRandom debugging
sunpkcs11SunPKCS11 provider debugging
tsTimestamping debugging
x509X.509 certificate debugging. The following sub-option can be used + with the X.509 option. +
    +
  • ava: Embed non-printable/non-escaped + characters in AVA components as hex strings
  • +
+
+ + diff --git a/src/java.base/share/classes/java/security/package-info.java b/src/java.base/share/classes/java/security/package-info.java index b887d5c17a3f6..fafdca738015b 100644 --- a/src/java.base/share/classes/java/security/package-info.java +++ b/src/java.base/share/classes/java/security/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,6 +71,12 @@ * * * + *

Additional Specification

+ * + * * @spec security/standard-names.html Java Security Standard Algorithm Names * @since 1.1 */ diff --git a/src/java.base/share/classes/java/time/Clock.java b/src/java.base/share/classes/java/time/Clock.java index 731a117fcea18..7fab5d27fb366 100644 --- a/src/java.base/share/classes/java/time/Clock.java +++ b/src/java.base/share/classes/java/time/Clock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, 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 @@ -61,8 +61,6 @@ */ package java.time; -import java.io.IOException; -import java.io.ObjectInputStream; import java.io.ObjectStreamException; import static java.time.LocalTime.NANOS_PER_MINUTE; @@ -780,7 +778,7 @@ public boolean equals(Object obj) { } @Override public int hashCode() { - return baseClock.hashCode() ^ ((int) (tickNanos ^ (tickNanos >>> 32))); + return baseClock.hashCode() ^ Long.hashCode(tickNanos); } @Override public String toString() { diff --git a/src/java.base/share/classes/java/time/Duration.java b/src/java.base/share/classes/java/time/Duration.java index d640121f32ddb..88d49fa9e4523 100644 --- a/src/java.base/share/classes/java/time/Duration.java +++ b/src/java.base/share/classes/java/time/Duration.java @@ -1460,7 +1460,7 @@ public boolean equals(Object other) { */ @Override public int hashCode() { - return ((int) (seconds ^ (seconds >>> 32))) + (51 * nanos); + return Long.hashCode(seconds) + (51 * nanos); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/Instant.java b/src/java.base/share/classes/java/time/Instant.java index 82ff18c142112..1c7a41d7f0ebe 100644 --- a/src/java.base/share/classes/java/time/Instant.java +++ b/src/java.base/share/classes/java/time/Instant.java @@ -1363,7 +1363,7 @@ public boolean equals(Object other) { */ @Override public int hashCode() { - return ((int) (seconds ^ (seconds >>> 32))) + 51 * nanos; + return Long.hashCode(seconds) + 51 * nanos; } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/LocalDate.java b/src/java.base/share/classes/java/time/LocalDate.java index 1347853030172..016bdab539426 100644 --- a/src/java.base/share/classes/java/time/LocalDate.java +++ b/src/java.base/share/classes/java/time/LocalDate.java @@ -2133,10 +2133,7 @@ public boolean equals(Object obj) { */ @Override public int hashCode() { - int yearValue = year; - int monthValue = month; - int dayValue = day; - return (yearValue & 0xFFFFF800) ^ ((yearValue << 11) + (monthValue << 6) + (dayValue)); + return (year & 0xFFFFF800) ^ ((year << 11) + (month << 6) + day); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/chrono/ChronoLocalDateImpl.java b/src/java.base/share/classes/java/time/chrono/ChronoLocalDateImpl.java index a79ecebe0c268..ca226b70d24be 100644 --- a/src/java.base/share/classes/java/time/chrono/ChronoLocalDateImpl.java +++ b/src/java.base/share/classes/java/time/chrono/ChronoLocalDateImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019, 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 @@ -421,7 +421,7 @@ public boolean equals(Object obj) { @Override public int hashCode() { long epDay = toEpochDay(); - return getChronology().hashCode() ^ ((int) (epDay ^ (epDay >>> 32))); + return getChronology().hashCode() ^ Long.hashCode(epDay); } @Override diff --git a/src/java.base/share/classes/java/util/Collection.java b/src/java.base/share/classes/java/util/Collection.java index 0253dbc7e1aa7..43e8db55d7f4c 100644 --- a/src/java.base/share/classes/java/util/Collection.java +++ b/src/java.base/share/classes/java/util/Collection.java @@ -58,7 +58,7 @@ * constructors) but all of the general-purpose {@code Collection} * implementations in the Java platform libraries comply. * - *

Certain methods are specified to be + *

Certain methods are specified to be * optional. If a collection implementation doesn't implement a * particular operation, it should define the corresponding method to throw * {@code UnsupportedOperationException}. Such methods are marked "optional diff --git a/src/java.base/share/classes/java/util/ImmutableCollections.java b/src/java.base/share/classes/java/util/ImmutableCollections.java index 205a6be6f89a3..9becf1671761e 100644 --- a/src/java.base/share/classes/java/util/ImmutableCollections.java +++ b/src/java.base/share/classes/java/util/ImmutableCollections.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,11 +36,19 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.IntFunction; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.function.UnaryOperator; + import jdk.internal.access.JavaUtilCollectionAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.lang.stable.StableUtil; +import jdk.internal.lang.stable.StableValueImpl; import jdk.internal.misc.CDS; +import jdk.internal.util.ArraysSupport; +import jdk.internal.util.NullableKeyValueHolder; +import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; /** @@ -128,6 +136,12 @@ public List listFromTrustedArray(Object[] array) { public List listFromTrustedArrayNullsAllowed(Object[] array) { return ImmutableCollections.listFromTrustedArrayNullsAllowed(array); } + public List stableList(int size, IntFunction mapper) { + return ImmutableCollections.stableList(size, mapper); + } + public Map stableMap(Set keys, Function mapper) { + return new StableMap<>(keys, mapper); + } }); } } @@ -250,6 +264,11 @@ static List listFromTrustedArrayNullsAllowed(Object... input) { } } + static List stableList(int size, IntFunction mapper) { + // A lazy list is not Serializable so, we cannot return `List.of()` if size == 0 + return new StableList<>(size, mapper); + } + // ---------- List Implementations ---------- @jdk.internal.ValueBased @@ -448,7 +467,7 @@ static final class SubList extends AbstractImmutableList private final int size; private SubList(AbstractImmutableList root, int offset, int size) { - assert root instanceof List12 || root instanceof ListN; + assert root instanceof List12 || root instanceof ListN || root instanceof StableList; this.root = root; this.offset = offset; this.size = size; @@ -499,7 +518,8 @@ private void rangeCheck(int index) { } private boolean allowNulls() { - return root instanceof ListN && ((ListN)root).allowNulls; + return root instanceof ListN listN && listN.allowNulls + || root instanceof StableList; } @Override @@ -551,6 +571,15 @@ public T[] toArray(T[] a) { } return array; } + + @Override + public String toString() { + if (root instanceof StableList stableList) { + return StableUtil.renderElements(root, "StableList", stableList.delegates, offset, size); + } else { + return super.toString(); + } + } } @jdk.internal.ValueBased @@ -768,6 +797,116 @@ public int lastIndexOf(Object o) { } } + @jdk.internal.ValueBased + static final class StableList extends AbstractImmutableList { + + @Stable + private final IntFunction mapper; + @Stable + final StableValueImpl[] delegates; + + StableList(int size, IntFunction mapper) { + this.mapper = mapper; + this.delegates = StableUtil.array(size); + } + + @Override public boolean isEmpty() { return delegates.length == 0;} + @Override public int size() { return delegates.length; } + @Override public Object[] toArray() { return copyInto(new Object[size()]); } + + @ForceInline + @Override + public E get(int i) { + final StableValueImpl delegate; + try { + delegate = delegates[i]; + } catch (ArrayIndexOutOfBoundsException aioobe) { + throw new IndexOutOfBoundsException(i); + } + return delegate.orElseSet(new Supplier() { + @Override public E get() { return mapper.apply(i); }}); + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + final int size = delegates.length; + if (a.length < size) { + // Make a new array of a's runtime type, but my contents: + T[] n = (T[])Array.newInstance(a.getClass().getComponentType(), size); + return copyInto(n); + } + copyInto(a); + if (a.length > size) { + a[size] = null; // null-terminate + } + return a; + } + + @Override + public int indexOf(Object o) { + final int size = size(); + for (int i = 0; i < size; i++) { + if (Objects.equals(o, get(i))) { + return i; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object o) { + for (int i = size() - 1; i >= 0; i--) { + if (Objects.equals(o, get(i))) { + return i; + } + } + return -1; + } + + @SuppressWarnings("unchecked") + private T[] copyInto(Object[] a) { + final int len = delegates.length; + for (int i = 0; i < len; i++) { + a[i] = get(i); + } + return (T[]) a; + } + + @Override + public List reversed() { + return new StableReverseOrderListView<>(this); + } + + @Override + public String toString() { + return StableUtil.renderElements(this, "StableList", delegates); + } + + private static final class StableReverseOrderListView extends ReverseOrderListView.Rand { + + private StableReverseOrderListView(List base) { + super(base, false); + } + + // This method does not evaluate the elements + @Override + public String toString() { + final StableValueImpl[] delegates = ((StableList)base).delegates; + final StableValueImpl[] reversed = ArraysSupport.reverse( + Arrays.copyOf(delegates, delegates.length)); + return StableUtil.renderElements(base, "Collection", reversed); + } + + @Override + public List reversed() { + return base; + } + + } + + } + // ---------- Set Implementations ---------- @jdk.internal.ValueBased @@ -1112,7 +1251,7 @@ public T[] toArray(T[] a) { // ---------- Map Implementations ---------- // Not a jdk.internal.ValueBased class; disqualified by fields in superclass AbstractMap - abstract static class AbstractImmutableMap extends AbstractMap implements Serializable { + abstract static class AbstractImmutableMap extends AbstractMap { @Override public void clear() { throw uoe(); } @Override public V compute(K key, BiFunction rf) { throw uoe(); } @Override public V computeIfAbsent(K key, Function mf) { throw uoe(); } @@ -1143,7 +1282,7 @@ public V getOrDefault(Object key, V defaultValue) { } // Not a jdk.internal.ValueBased class; disqualified by fields in superclass AbstractMap - static final class Map1 extends AbstractImmutableMap { + static final class Map1 extends AbstractImmutableMap implements Serializable { @Stable private final K k0; @Stable @@ -1215,7 +1354,7 @@ public void forEach(BiConsumer action) { * @param the value type */ // Not a jdk.internal.ValueBased class; disqualified by fields in superclass AbstractMap - static final class MapN extends AbstractImmutableMap { + static final class MapN extends AbstractImmutableMap implements Serializable { @Stable final Object[] table; // pairs of key, value @@ -1405,6 +1544,130 @@ private Object writeReplace() { return new CollSer(CollSer.IMM_MAP, array); } } + + static final class StableMap + extends AbstractImmutableMap { + + @Stable + private final Function mapper; + @Stable + private final Map> delegate; + + StableMap(Set keys, Function mapper) { + this.mapper = mapper; + this.delegate = StableUtil.map(keys); + } + + @Override public boolean containsKey(Object o) { return delegate.containsKey(o); } + @Override public int size() { return delegate.size(); } + @Override public Set> entrySet() { return new StableMapEntrySet(); } + + @ForceInline + @Override + public V get(Object key) { + return getOrDefault(key, null); + } + + @ForceInline + @Override + public V getOrDefault(Object key, V defaultValue) { + final StableValueImpl stable = delegate.get(key); + if (stable == null) { + return defaultValue; + } + @SuppressWarnings("unchecked") + final K k = (K) key; + return stable.orElseSet(new Supplier() { + @Override public V get() { return mapper.apply(k); }}); + } + + @jdk.internal.ValueBased + final class StableMapEntrySet extends AbstractImmutableSet> { + + @Stable + private final Set>> delegateEntrySet; + + StableMapEntrySet() { + this.delegateEntrySet = delegate.entrySet(); + } + + @Override public Iterator> iterator() { return new LazyMapIterator(); } + @Override public int size() { return delegateEntrySet.size(); } + @Override public int hashCode() { return StableMap.this.hashCode(); } + + @Override + public String toString() { + return StableUtil.renderMappings(this, "StableSet", delegateEntrySet, false); + } + + @jdk.internal.ValueBased + final class LazyMapIterator implements Iterator> { + + @Stable + private final Iterator>> delegateIterator; + + LazyMapIterator() { + this.delegateIterator = delegateEntrySet.iterator(); + } + + @Override public boolean hasNext() { return delegateIterator.hasNext(); } + + @Override + public Entry next() { + final Map.Entry> inner = delegateIterator.next(); + final K k = inner.getKey(); + return new NullableKeyValueHolder<>(k, inner.getValue().orElseSet(new Supplier() { + @Override public V get() { return mapper.apply(k); }})); + } + + @Override + public void forEachRemaining(Consumer> action) { + final Consumer>> innerAction = + new Consumer<>() { + @Override + public void accept(Entry> inner) { + final K k = inner.getKey(); + action.accept(new NullableKeyValueHolder<>(k, inner.getValue().orElseSet(new Supplier() { + @Override public V get() { return mapper.apply(k); }}))); + } + }; + delegateIterator.forEachRemaining(innerAction); + } + } + } + + @Override + public Collection values() { + return new StableMapValues(); + } + + final class StableMapValues extends AbstractImmutableCollection { + @Override public Iterator iterator() { return new ValueIterator(); } + @Override public int size() { return StableMap.this.size(); } + @Override public boolean isEmpty() { return StableMap.this.isEmpty();} + @Override public boolean contains(Object v) { return StableMap.this.containsValue(v); } + + private static final IntFunction[]> GENERATOR = new IntFunction[]>() { + @Override + public StableValueImpl[] apply(int len) { + return new StableValueImpl[len]; + } + }; + + @Override + public String toString() { + final StableValueImpl[] values = delegate.values().toArray(GENERATOR); + return StableUtil.renderElements(StableMap.this, "StableMap", values); + } + } + + @Override + public String toString() { + return StableUtil.renderMappings(this, "StableMap", delegate.entrySet(), true); + } + + } + } // ---------- Serialization Proxy ---------- diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 28468165fab59..14f3fe13918b4 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -3497,8 +3497,7 @@ public int hashCode() { if (h == 0) { h = 17; h = 37*h + range.hashCode(); - long bitsWeight = Double.doubleToLongBits(weight); - h = 37*h + (int)(bitsWeight ^ (bitsWeight >>> 32)); + h = 37*h + Double.hashCode(weight); if (h != 0) { hash = h; } diff --git a/src/java.base/share/classes/java/util/Optional.java b/src/java.base/share/classes/java/util/Optional.java index 21128fa980d09..e19dde6383ea2 100644 --- a/src/java.base/share/classes/java/util/Optional.java +++ b/src/java.base/share/classes/java/util/Optional.java @@ -394,7 +394,7 @@ public T orElseThrow() { * @return the value, if present * @throws X if no value is present * @throws NullPointerException if no value is present and the exception - * supplying function is {@code null} + * supplying function is {@code null} or produces a {@code null} result */ public T orElseThrow(Supplier exceptionSupplier) throws X { if (value != null) { diff --git a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java index 649eaa563c7c1..0b0db4e8120ff 100644 --- a/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java +++ b/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java @@ -35,7 +35,9 @@ package java.util.concurrent; -import java.lang.reflect.Field; +import jdk.internal.misc.Unsafe; + +import java.lang.invoke.VarHandle; import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; @@ -176,6 +178,8 @@ public ConcurrentSkipListSet clone() { ConcurrentSkipListSet clone = (ConcurrentSkipListSet) super.clone(); clone.setMap(new ConcurrentSkipListMap(m)); + // Needed to ensure safe publication of setMap() + VarHandle.releaseFence(); return clone; } catch (CloneNotSupportedException e) { throw new InternalError(); @@ -527,12 +531,11 @@ public Spliterator spliterator() { /** Initializes map field; for use in clone. */ private void setMap(ConcurrentNavigableMap map) { - try { - Field mapField = ConcurrentSkipListSet.class.getDeclaredField("m"); - mapField.setAccessible(true); - mapField.set(this, map); - } catch (IllegalAccessException | NoSuchFieldException e) { - throw new Error(e); - } + final Unsafe U = Unsafe.getUnsafe(); + U.putReference( + this, + U.objectFieldOffset(ConcurrentSkipListSet.class, "m"), + map + ); } } diff --git a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java index 2d3bdb429e948..9024fca802413 100644 --- a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java +++ b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java @@ -35,7 +35,6 @@ package java.util.concurrent; import java.lang.invoke.VarHandle; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -57,6 +56,7 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; import jdk.internal.access.SharedSecrets; +import jdk.internal.misc.Unsafe; import jdk.internal.util.ArraysSupport; /** @@ -2095,12 +2095,11 @@ public List reversed() { /** Initializes the lock; for use when deserializing or cloning. */ private void resetLock() { - try { - Field lockField = CopyOnWriteArrayList.class.getDeclaredField("lock"); - lockField.setAccessible(true); - lockField.set(this, new Object()); - } catch (IllegalAccessException | NoSuchFieldException e) { - throw new Error(e); - } + final Unsafe U = Unsafe.getUnsafe(); + U.putReference( + this, + U.objectFieldOffset(CopyOnWriteArrayList.class, "lock"), + new Object() + ); } } diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java index ff4e9fa735552..67315296a8d18 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -35,10 +35,11 @@ package java.util.concurrent.atomic; +import jdk.internal.misc.Unsafe; + import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.lang.reflect.Array; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.function.BinaryOperator; import java.util.function.UnaryOperator; @@ -330,14 +331,13 @@ private void readObject(java.io.ObjectInputStream s) throw new java.io.InvalidObjectException("Not array type"); if (a.getClass() != Object[].class) a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class); - try { - Field arrayField = AtomicReferenceArray.class.getDeclaredField("array"); - arrayField.setAccessible(true); - arrayField.set(this, a); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new Error(e); - } + final Unsafe U = Unsafe.getUnsafe(); + U.putReference( + this, + U.objectFieldOffset(AtomicReferenceArray.class, "array"), + a + ); } // jdk9 @@ -523,5 +523,4 @@ public final boolean weakCompareAndSetAcquire(int i, E expectedValue, E newValue public final boolean weakCompareAndSetRelease(int i, E expectedValue, E newValue) { return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue); } - } diff --git a/src/java.base/share/classes/javax/crypto/spec/RC2ParameterSpec.java b/src/java.base/share/classes/javax/crypto/spec/RC2ParameterSpec.java index e37e3872ff269..0e664510324b6 100644 --- a/src/java.base/share/classes/javax/crypto/spec/RC2ParameterSpec.java +++ b/src/java.base/share/classes/javax/crypto/spec/RC2ParameterSpec.java @@ -98,7 +98,12 @@ public RC2ParameterSpec(int effectiveKeyBits, byte[] iv) { */ public RC2ParameterSpec(int effectiveKeyBits, byte[] iv, int offset) { this.effectiveKeyBits = effectiveKeyBits; - if (iv == null) throw new IllegalArgumentException("IV missing"); + if (iv == null) { + throw new IllegalArgumentException("IV missing"); + } + if (offset < 0) { + throw new ArrayIndexOutOfBoundsException("offset is negative"); + } int blockSize = 8; if (iv.length - offset < blockSize) { throw new IllegalArgumentException("IV too short"); diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 8edeb731cee42..20b6cd23682ab 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -119,6 +119,12 @@ public interface JavaLangAccess { */ > E[] getEnumConstantsShared(Class klass); + /** + * Returns the big-endian packed minor-major version of the class file + * of this class. + */ + int classFileVersion(Class clazz); + /** * Set current thread's blocker field. */ @@ -230,11 +236,6 @@ public interface JavaLangAccess { */ void addOpensToAllUnnamed(Module m, String pkg); - /** - * Updates module m to open all packages in the given sets. - */ - void addOpensToAllUnnamed(Module m, Set concealedPkgs, Set exportedPkgs); - /** * Updates module m to use a service. */ diff --git a/src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java index f88d57521ac5a..cac8785b158dd 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaUtilCollectionAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,8 +26,14 @@ package jdk.internal.access; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.IntFunction; public interface JavaUtilCollectionAccess { List listFromTrustedArray(Object[] array); List listFromTrustedArrayNullsAllowed(Object[] array); + List stableList(int size, IntFunction mapper); + Map stableMap(Set keys, Function mapper); } diff --git a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java index 3ec9d6d4bae8c..bc955a76abb8e 100644 --- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java +++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java @@ -69,7 +69,6 @@ public class SharedSecrets { private static JavaLangReflectAccess javaLangReflectAccess; private static JavaIOAccess javaIOAccess; private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; - private static JavaIOFilePermissionAccess javaIOFilePermissionAccess; private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess; private static JavaObjectInputStreamReadString javaObjectInputStreamReadString; private static JavaObjectInputStreamAccess javaObjectInputStreamAccess; @@ -287,20 +286,6 @@ public static void setJavaIOFileDescriptorAccess(JavaIOFileDescriptorAccess jiof javaIOFileDescriptorAccess = jiofda; } - @SuppressWarnings("removal") - public static JavaIOFilePermissionAccess getJavaIOFilePermissionAccess() { - var access = javaIOFilePermissionAccess; - if (access == null) { - ensureClassInitialized(FilePermission.class); - access = javaIOFilePermissionAccess; - } - return access; - } - - public static void setJavaIOFilePermissionAccess(JavaIOFilePermissionAccess jiofpa) { - javaIOFilePermissionAccess = jiofpa; - } - public static JavaIOFileDescriptorAccess getJavaIOFileDescriptorAccess() { var access = javaIOFileDescriptorAccess; if (access == null) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 3e50773d59ec2..bf558ddef166c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,8 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.constant.ClassOrInterfaceDescImpl; +import jdk.internal.constant.PrimitiveClassDescImpl; import jdk.internal.util.ArraysSupport; import jdk.internal.vm.annotation.Stable; @@ -73,11 +75,6 @@ static int hashClassFromDescriptor(int descriptorHash) { return hash1(PoolEntry.TAG_CLASS, descriptorHash); } - static boolean isArrayDescriptor(Utf8EntryImpl cs) { - // Do not throw out-of-bounds for empty strings - return !cs.isEmpty() && cs.charAt(0) == '['; - } - @SuppressWarnings("unchecked") public static T maybeClone(ConstantPoolBuilder cp, T entry) { if (cp.canWriteDirect(entry.constantPool())) @@ -438,6 +435,79 @@ public MethodTypeDesc methodTypeSymbol() { typeSym = ret; return ret; } + + @Override + public boolean isFieldType(ClassDesc desc) { + var sym = typeSym; + if (sym != null) { + return sym instanceof ClassDesc cd && cd.equals(desc); + } + + // In parsing, Utf8Entry is not even inflated by this point + // We can operate on the raw byte arrays, as all ascii are compatible + var ret = state == State.RAW + ? rawEqualsSym(desc) + : equalsString(desc.descriptorString()); + if (ret) + this.typeSym = desc; + return ret; + } + + private boolean rawEqualsSym(ClassDesc desc) { + int len = rawLen; + if (len < 1) { + return false; + } + int c = rawBytes[offset]; + if (len == 1) { + return desc instanceof PrimitiveClassDescImpl pd && pd.wrapper().basicTypeChar() == c; + } else if (c == 'L') { + return desc.isClassOrInterface() && equalsString(desc.descriptorString()); + } else if (c == '[') { + return desc.isArray() && equalsString(desc.descriptorString()); + } else { + return false; + } + } + + boolean mayBeArrayDescriptor() { + if (state == State.RAW) { + return rawLen > 0 && rawBytes[offset] == '['; + } else { + return charLen > 0 && charAt(0) == '['; + } + } + + @Override + public boolean isMethodType(MethodTypeDesc desc) { + var sym = typeSym; + if (sym != null) { + return sym instanceof MethodTypeDesc mtd && mtd.equals(desc); + } + + // In parsing, Utf8Entry is not even inflated by this point + // We can operate on the raw byte arrays, as all ascii are compatible + var ret = state == State.RAW + ? rawEqualsSym(desc) + : equalsString(desc.descriptorString()); + if (ret) + this.typeSym = desc; + return ret; + } + + private boolean rawEqualsSym(MethodTypeDesc desc) { + if (rawLen < 3) { + return false; + } + var bytes = rawBytes; + int index = offset; + int c = bytes[index] | (bytes[index + 1] << Byte.SIZE); + if ((desc.parameterCount() == 0) != (c == ('(' | (')' << Byte.SIZE)))) { + // heuristic - avoid inflation for no-arg status mismatch + return false; + } + return (c & 0xFF) == '(' && equalsString(desc.descriptorString()); + } } abstract static sealed class AbstractRefEntry extends AbstractPoolEntry { @@ -538,7 +608,7 @@ public ClassDesc asSymbol() { return sym; } - if (isArrayDescriptor(ref1)) { + if (ref1.mayBeArrayDescriptor()) { sym = ref1.fieldTypeSymbol(); // array, symbol already available } else { sym = ClassDesc.ofInternalName(asInternalName()); // class or interface @@ -546,6 +616,28 @@ public ClassDesc asSymbol() { return this.sym = sym; } + @Override + public boolean matches(ClassDesc desc) { + var sym = this.sym; + if (sym != null) { + return sym.equals(desc); + } + + var ret = rawEqualsSymbol(desc); + if (ret) + this.sym = desc; + return ret; + } + + private boolean rawEqualsSymbol(ClassDesc desc) { + if (ref1.mayBeArrayDescriptor()) { + return desc.isArray() && ref1.isFieldType(desc); + } else { + return desc instanceof ClassOrInterfaceDescImpl coid + && ref1.equalsString(coid.internalName()); + } + } + @Override public boolean equals(Object o) { if (o == this) return true; @@ -571,7 +663,7 @@ public int hashCode() { if (hash != 0) return hash; - return this.hash = hashClassFromUtf8(isArrayDescriptor(ref1), ref1); + return this.hash = hashClassFromUtf8(ref1.mayBeArrayDescriptor(), ref1); } } @@ -596,6 +688,11 @@ public PackageDesc asSymbol() { return PackageDesc.ofInternalName(asInternalName()); } + @Override + public boolean matches(PackageDesc desc) { + return ref1.equalsString(desc.internalName()); + } + @Override public boolean equals(Object o) { if (o == this) return true; @@ -627,6 +724,11 @@ public ModuleDesc asSymbol() { return ModuleDesc.of(asInternalName()); } + @Override + public boolean matches(ModuleDesc desc) { + return ref1.equalsString(desc.name()); + } + @Override public boolean equals(Object o) { if (o == this) return true; @@ -983,6 +1085,11 @@ public MethodTypeDesc asSymbol() { return ref1.methodTypeSymbol(); } + @Override + public boolean matches(MethodTypeDesc desc) { + return ref1.isMethodType(desc); + } + @Override public boolean equals(Object o) { if (o == this) return true; @@ -1016,6 +1123,11 @@ public String stringValue() { return ref1.toString(); } + @Override + public boolean equalsString(String value) { + return ref1.equalsString(value); + } + @Override public ConstantDesc constantValue() { return stringValue(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java index 62d812ce8d41f..56682214566d4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BlockCodeBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -72,11 +72,10 @@ public boolean isEmpty() { } private int topLocal(CodeBuilder parent) { - return switch (parent) { - case BlockCodeBuilderImpl b -> b.topLocal; - case ChainedCodeBuilder b -> b.terminal.curTopLocal(); - case TerminalCodeBuilder b -> b.curTopLocal(); - }; + if (parent instanceof BlockCodeBuilderImpl bcb) { + return bcb.topLocal; + } + return findTerminal(parent).curTopLocal(); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java index 48d5ceb72d641..b4c8ad5870535 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedClassBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,10 +39,8 @@ public final class ChainedClassBuilder public ChainedClassBuilder(ClassBuilder downstream, Consumer consumer) { this.consumer = consumer; - this.terminal = switch (downstream) { - case ChainedClassBuilder cb -> cb.terminal; - case DirectClassBuilder db -> db; - }; + this.terminal = downstream instanceof ChainedClassBuilder ccb ? + ccb.terminal : (DirectClassBuilder) downstream; } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java index 13ca52f18de2f..9eac25d567c8a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedFieldBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,10 +38,8 @@ public final class ChainedFieldBuilder implements FieldBuilder { public ChainedFieldBuilder(FieldBuilder downstream, Consumer consumer) { this.consumer = consumer; - this.terminal = switch (downstream) { - case ChainedFieldBuilder cb -> cb.terminal; - case TerminalFieldBuilder tb -> tb; - }; + this.terminal = downstream instanceof ChainedFieldBuilder cfb ? + cfb.terminal : (TerminalFieldBuilder) downstream; } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java index 7bd0fc2ca1b23..e96351b88567e 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ChainedMethodBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,10 +41,8 @@ public final class ChainedMethodBuilder implements MethodBuilder { public ChainedMethodBuilder(MethodBuilder downstream, Consumer consumer) { this.consumer = consumer; - this.terminal = switch (downstream) { - case ChainedMethodBuilder cb -> cb.terminal; - case TerminalMethodBuilder tb -> tb; - }; + this.terminal = downstream instanceof ChainedMethodBuilder cmb ? + cmb.terminal : (TerminalMethodBuilder) downstream; } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index 286d29029ad24..c8c6d2546502b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -264,14 +264,16 @@ private void inflateJumpTargets() { //fallback to jump targets inflation without StackMapTableAttribute for (int pos=codeStart; pos br.target(); - case DiscontinuedInstruction.JsrInstruction jsr -> jsr.target(); - case LookupSwitchInstruction ls -> { + switch (i.opcode().kind()) { + case BRANCH -> ((BranchInstruction) i).target(); + case DISCONTINUED_JSR -> ((DiscontinuedInstruction.JsrInstruction) i).target(); + case LOOKUP_SWITCH -> { + var ls = (LookupSwitchInstruction) i; ls.defaultTarget(); ls.cases(); } - case TableSwitchInstruction ts -> { + case TABLE_SWITCH -> { + var ts = (TableSwitchInstruction) i; ts.defaultTarget(); ts.cases(); } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index 6dc6251163989..1e12969f204c7 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -42,7 +42,7 @@ public final class DirectClassBuilder implements ClassBuilder { /** The value of default class access flags */ - static final int DEFAULT_CLASS_FLAGS = ClassFile.ACC_PUBLIC; + static final int DEFAULT_CLASS_FLAGS = ClassFile.ACC_PUBLIC | ClassFile.ACC_SUPER; static final Util.Writable[] EMPTY_WRITABLE_ARRAY = {}; static final ClassEntry[] EMPTY_CLASS_ENTRY_ARRAY = {}; final ClassEntry thisClassEntry; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index 2741d1918caeb..bc563482f79d2 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -48,10 +48,8 @@ public final class DirectCodeBuilder extends AbstractDirectBuilder implements TerminalCodeBuilder { private static final CharacterRange[] EMPTY_CHARACTER_RANGE = {}; - private static final DeferredLabel[] EMPTY_LABEL_ARRAY = {}; private static final LocalVariable[] EMPTY_LOCAL_VARIABLE_ARRAY = {}; private static final LocalVariableType[] EMPTY_LOCAL_VARIABLE_TYPE_ARRAY = {}; - private static final AbstractPseudoInstruction.ExceptionCatchImpl[] EMPTY_HANDLER_ARRAY = {}; private static final DeferredLabel[] EMPTY_DEFERRED_LABEL_ARRAY = {}; final List handlers = new ArrayList<>(); @@ -74,6 +72,9 @@ public final class DirectCodeBuilder private DeferredLabel[] deferredLabels = EMPTY_DEFERRED_LABEL_ARRAY; private int deferredLabelsCount = 0; + private int maxStackHint = -1; + private int maxLocalsHint = -1; + /* Locals management lazily computed maxLocal = -1 first time: derive count from methodType descriptor (for new methods) & ACC_STATIC, @@ -173,6 +174,12 @@ public MethodInfo methodInfo() { return methodInfo; } + public static void withMaxs(CodeBuilder cob, int stacks, int locals) { + var dcb = (DirectCodeBuilder) cob; + dcb.maxStackHint = stacks; + dcb.maxLocalsHint = locals; + } + private UnboundAttribute content = null; private void writeExceptionHandlers(BufWriterImpl buf) { @@ -319,6 +326,8 @@ private void writeCounters(boolean codeMatch, BufWriterImpl buf) { if (codeMatch) { var originalAttribute = (CodeImpl) original; buf.writeU2U2(originalAttribute.maxStack(), originalAttribute.maxLocals()); + } else if (maxLocalsHint >= 0 && maxStackHint >= 0) { + buf.writeU2U2(maxStackHint, maxLocalsHint); } else { StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); buf.writeU2U2(cntr.maxStack(), cntr.maxLocals()); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/NonterminalCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/NonterminalCodeBuilder.java index f40da3a2beac6..d7eaffb325072 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/NonterminalCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/NonterminalCodeBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,10 +35,12 @@ public abstract sealed class NonterminalCodeBuilder implements CodeBuilder public NonterminalCodeBuilder(CodeBuilder parent) { this.parent = parent; - this.terminal = switch (parent) { - case NonterminalCodeBuilder cb -> cb.terminal; - case TerminalCodeBuilder cb -> cb; - }; + this.terminal = findTerminal(parent); + } + + static TerminalCodeBuilder findTerminal(CodeBuilder cob) { + return cob instanceof NonterminalCodeBuilder ncb ? + ncb.terminal : (TerminalCodeBuilder) cob; } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index 5ba81fc292750..2ea6fa943a7a0 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -497,7 +497,7 @@ AbstractPoolEntry.Utf8EntryImpl maybeCloneUtf8Entry(Utf8Entry entry) { @Override public AbstractPoolEntry.ClassEntryImpl classEntry(Utf8Entry nameEntry) { var ne = maybeCloneUtf8Entry(nameEntry); - return classEntry(ne, AbstractPoolEntry.isArrayDescriptor(ne)); + return classEntry(ne, ne.mayBeArrayDescriptor()); } AbstractPoolEntry.ClassEntryImpl classEntry(AbstractPoolEntry.Utf8EntryImpl ne, boolean isArray) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/BufferStack.java b/src/java.base/share/classes/jdk/internal/foreign/BufferStack.java new file mode 100644 index 0000000000000..dbf21601c53ac --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/BufferStack.java @@ -0,0 +1,214 @@ +/* + * 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 jdk.internal.foreign; + +import jdk.internal.misc.CarrierThreadLocal; +import jdk.internal.vm.annotation.ForceInline; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; +import java.lang.ref.Reference; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Consumer; + +/** + * A buffer stack that allows efficient reuse of memory segments. This is useful in cases + * where temporary memory is needed. + *

+ * Use the factories {@code BufferStack.of(...)} to create new instances of this class. + *

+ * Note: The reused segments are neither zeroed out before nor after re-use. + */ +public final class BufferStack { + + private final long byteSize; + private final long byteAlignment; + private final CarrierThreadLocal tl; + + private BufferStack(long byteSize, long byteAlignment) { + this.byteSize = byteSize; + this.byteAlignment = byteAlignment; + this.tl = new CarrierThreadLocal<>() { + @Override + protected BufferStack.PerThread initialValue() { + return BufferStack.PerThread.of(byteSize, byteAlignment); + } + }; + } + + /** + * {@return a new Arena that tries to provide {@code byteSize} and {@code byteAlignment} + * allocations by recycling the BufferStack's internal memory} + * + * @param byteSize to be reserved from this BufferStack's internal memory + * @param byteAlignment to be used for reservation + */ + @ForceInline + public Arena pushFrame(long byteSize, long byteAlignment) { + return tl.get().pushFrame(byteSize, byteAlignment); + } + + /** + * {@return a new Arena that tries to provide {@code byteSize} + * allocations by recycling the BufferStack's internal memory} + * + * @param byteSize to be reserved from this BufferStack's internal memory + */ + @ForceInline + public Arena pushFrame(long byteSize) { + return pushFrame(byteSize, 1); + } + + /** + * {@return a new Arena that tries to provide {@code layout} + * allocations by recycling the BufferStack's internal memory} + * + * @param layout for which to reserve internal memory + */ + @ForceInline + public Arena pushFrame(MemoryLayout layout) { + return pushFrame(layout.byteSize(), layout.byteAlignment()); + } + + @Override + public String toString() { + return "BufferStack[byteSize=" + byteSize + ", byteAlignment=" + byteAlignment + "]"; + } + + private record PerThread(ReentrantLock lock, + Arena arena, + SlicingAllocator stack, + CleanupAction cleanupAction) { + + @ForceInline + public Arena pushFrame(long size, long byteAlignment) { + boolean needsLock = Thread.currentThread().isVirtual() && !lock.isHeldByCurrentThread(); + if (needsLock && !lock.tryLock()) { + // Rare: another virtual thread on the same carrier competed for acquisition. + return Arena.ofConfined(); + } + if (!stack.canAllocate(size, byteAlignment)) { + if (needsLock) lock.unlock(); + return Arena.ofConfined(); + } + return new Frame(needsLock, size, byteAlignment); + } + + static PerThread of(long byteSize, long byteAlignment) { + final Arena arena = Arena.ofAuto(); + return new PerThread(new ReentrantLock(), + arena, + new SlicingAllocator(arena.allocate(byteSize, byteAlignment)), + new CleanupAction(arena)); + } + + private record CleanupAction(Arena arena) implements Consumer { + @Override + public void accept(MemorySegment memorySegment) { + Reference.reachabilityFence(arena); + } + } + + private final class Frame implements Arena { + + private final boolean locked; + private final long parentOffset; + private final long topOfStack; + private final Arena confinedArena; + private final SegmentAllocator frame; + + @SuppressWarnings("restricted") + @ForceInline + public Frame(boolean locked, long byteSize, long byteAlignment) { + this.locked = locked; + this.parentOffset = stack.currentOffset(); + final MemorySegment frameSegment = stack.allocate(byteSize, byteAlignment); + this.topOfStack = stack.currentOffset(); + this.confinedArena = Arena.ofConfined(); + // The cleanup action will keep the original automatic `arena` (from which + // the reusable segment is first allocated) alive even if this Frame + // becomes unreachable but there are reachable segments still alive. + this.frame = new SlicingAllocator(frameSegment.reinterpret(confinedArena, cleanupAction)); + } + + @ForceInline + private void assertOrder() { + if (topOfStack != stack.currentOffset()) + throw new IllegalStateException("Out of order access: frame not top-of-stack"); + } + + @ForceInline + @Override + @SuppressWarnings("restricted") + public MemorySegment allocate(long byteSize, long byteAlignment) { + // Make sure we are on the right thread and not closed + MemorySessionImpl.toMemorySession(confinedArena).checkValidState(); + return frame.allocate(byteSize, byteAlignment); + } + + @ForceInline + @Override + public MemorySegment.Scope scope() { + return confinedArena.scope(); + } + + @ForceInline + @Override + public void close() { + assertOrder(); + // the Arena::close method is called "early" as it checks thread + // confinement and crucially before any mutation of the internal + // state takes place. + confinedArena.close(); + stack.resetTo(parentOffset); + if (locked) { + lock.unlock(); + } + } + } + } + + public static BufferStack of(long byteSize, long byteAlignment) { + if (byteSize < 0) { + throw new IllegalArgumentException("Negative byteSize: " + byteSize); + } + if (byteAlignment < 0) { + throw new IllegalArgumentException("Negative byteAlignment: " + byteAlignment); + } + return new BufferStack(byteSize, byteAlignment); + } + + public static BufferStack of(long byteSize) { + return new BufferStack(byteSize, 1); + } + + public static BufferStack of(MemoryLayout layout) { + // Implicit null check + return of(layout.byteSize(), layout.byteAlignment()); + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java b/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java index db7d476053e54..6b1a071c2af07 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SlicingAllocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -38,6 +38,22 @@ public SlicingAllocator(MemorySegment segment) { this.segment = segment; } + public long currentOffset() { + return sp; + } + + public void resetTo(long offset) { + if (offset < 0 || offset > sp) + throw new IllegalArgumentException(String.format("offset %d should be in [0, %d] ", offset, sp)); + this.sp = offset; + } + + public boolean canAllocate(long byteSize, long byteAlignment) { + long min = segment.address(); + long start = Utils.alignUp(min + sp, byteAlignment) - min; + return start + byteSize <= segment.byteSize(); + } + MemorySegment trySlice(long byteSize, long byteAlignment) { long min = segment.address(); long start = Utils.alignUp(min + sp, byteAlignment) - min; diff --git a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java index bf5371d43a067..640c7580d159a 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java @@ -25,6 +25,7 @@ package jdk.internal.foreign; +import jdk.internal.loader.NativeLibraries; import jdk.internal.loader.NativeLibrary; import jdk.internal.loader.RawNativeLibraries; import jdk.internal.util.OperatingSystem; @@ -66,7 +67,7 @@ private static SymbolLookup makeSystemLookup() { if (OperatingSystem.isWindows()) { return makeWindowsLookup(); } else { - return libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); + return sysLookup(); } } catch (Throwable ex) { // This can happen in the event of a library loading failure - e.g. if one of the libraries the @@ -84,13 +85,12 @@ private static SymbolLookup makeWindowsLookup() { boolean useUCRT = Files.exists(ucrtbase); Path stdLib = useUCRT ? ucrtbase : msvcrt; - SymbolLookup lookup = libLookup(libs -> libs.load(stdLib)); + SymbolLookup lookup = stdLibLookup(libs -> libs.load(stdLib)); if (useUCRT) { // use a fallback lookup to look up inline functions from fallback lib - SymbolLookup fallbackLibLookup = - libLookup(libs -> libs.load(jdkLibraryPath("syslookup"))); + SymbolLookup fallbackLibLookup = sysLookup(); @SuppressWarnings("restricted") MemorySegment funcs = fallbackLibLookup.findOrThrow("funcs") @@ -110,8 +110,7 @@ private static SymbolLookup makeWindowsLookup() { return lookup; } - private static SymbolLookup libLookup(Function loader) { - NativeLibrary lib = loader.apply(RawNativeLibraries.newInstance(MethodHandles.lookup())); + private static SymbolLookup lookup(NativeLibrary lib) { return name -> { Objects.requireNonNull(name); if (Utils.containsNullChars(name)) return Optional.empty(); @@ -126,16 +125,17 @@ private static SymbolLookup libLookup(Function loader) { + NativeLibrary lib = loader.apply(RawNativeLibraries.newInstance(MethodHandles.lookup())); + return lookup(lib); } + @SuppressWarnings("restricted") + private static SymbolLookup sysLookup() { + NativeLibraries libs = NativeLibraries.newInstance(null); + NativeLibrary lib = libs.loadLibrary(SymbolLookup.class, "syslookup"); + return lookup(lib); + } public static SystemLookup getInstance() { return INSTANCE; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index 19b235963a37f..401d3e9a9c58d 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -71,7 +71,20 @@ public interface UpcallStubFactory { MemorySegment makeStub(MethodHandle target, Arena arena); } - private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {} + private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) { + // Overrides for boot performance + @Override + public boolean equals(Object obj) { + return obj instanceof LinkRequest other && + other.descriptor.equals(descriptor) && + other.options.equals(options); + } + + @Override + public int hashCode() { + return descriptor.hashCode() * 1237 + options.hashCode(); + } + } private final SoftReferenceCache DOWNCALL_CACHE = new SoftReferenceCache<>(); private final SoftReferenceCache UPCALL_CACHE = new SoftReferenceCache<>(); private final Set CANONICAL_LAYOUTS_CACHE = new HashSet<>(canonicalLayouts().values()); @@ -308,22 +321,30 @@ private static MemoryLayout stripNames(MemoryLayout ml) { case StructLayout sl -> MemoryLayout.structLayout(stripNames(sl.memberLayouts())); case UnionLayout ul -> MemoryLayout.unionLayout(stripNames(ul.memberLayouts())); case SequenceLayout sl -> MemoryLayout.sequenceLayout(sl.elementCount(), stripNames(sl.elementLayout())); - case AddressLayout al -> al.targetLayout() - .map(tl -> al.withoutName().withTargetLayout(stripNames(tl))) // restricted - .orElseGet(al::withoutName); + case AddressLayout al -> { + var stripped = al.withoutName(); + var target = al.targetLayout(); + if (target.isPresent()) + stripped = stripped.withTargetLayout(stripNames(target.get())); + yield stripped; + } default -> ml.withoutName(); // ValueLayout and PaddingLayout }; } private static MemoryLayout[] stripNames(List layouts) { - return layouts.stream() - .map(AbstractLinker::stripNames) - .toArray(MemoryLayout[]::new); + var ret = new MemoryLayout[layouts.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = stripNames(layouts.get(i)); + } + return ret; } private static FunctionDescriptor stripNames(FunctionDescriptor function) { - return function.returnLayout() - .map(rl -> FunctionDescriptor.of(stripNames(rl), stripNames(function.argumentLayouts()))) - .orElseGet(() -> FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts()))); + var retLayout = function.returnLayout(); + if (retLayout.isEmpty()) { + return FunctionDescriptor.ofVoid(stripNames(function.argumentLayouts())); + } + return FunctionDescriptor.of(stripNames(retLayout.get()), stripNames(function.argumentLayouts())); } } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java b/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java index 7fb62c56bf12e..d95e713541f59 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/CapturableState.java @@ -30,6 +30,7 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.StructLayout; import java.lang.foreign.ValueLayout; +import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,6 +43,11 @@ public enum CapturableState { public static final StructLayout LAYOUT = MemoryLayout.structLayout( supportedStates().map(CapturableState::layout).toArray(MemoryLayout[]::new)); + public static final List BY_ORDINAL = List.of(values()); + + static { + assert (BY_ORDINAL.size() < Integer.SIZE); // Update LinkerOptions.CaptureCallState + } private final String stateName; private final ValueLayout layout; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java index 5ca410f40e2bb..4ada4b2341783 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java @@ -26,6 +26,7 @@ import java.lang.foreign.FunctionDescriptor; import java.lang.foreign.Linker; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -137,18 +138,57 @@ public void validateForDowncall(FunctionDescriptor descriptor) { throw new IllegalArgumentException("Index '" + index + "' not in bounds for descriptor: " + descriptor); } } + + @Override + public int hashCode() { + return index; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof FirstVariadicArg that && index == that.index; + } } - public record CaptureCallState(Set saved) implements LinkerOptionImpl { + public record CaptureCallState(int compact) implements LinkerOptionImpl { @Override public void validateForDowncall(FunctionDescriptor descriptor) { // done during construction } + + public Set saved() { + var set = EnumSet.noneOf(CapturableState.class); + int mask = compact; + int i = 0; + while (mask != 0) { + if ((mask & 1) == 1) { + set.add(CapturableState.BY_ORDINAL.get(i)); + } + mask >>= 1; + i++; + } + return set; + } + + + @Override + public boolean equals(Object obj) { + return obj instanceof CaptureCallState that && compact == that.compact; + } + + @Override + public int hashCode() { + return compact; + } } - public record Critical(boolean allowHeapAccess) implements LinkerOptionImpl { - public static Critical ALLOW_HEAP = new Critical(true); - public static Critical DONT_ALLOW_HEAP = new Critical(false); + public enum Critical implements LinkerOptionImpl { + ALLOW_HEAP, + DONT_ALLOW_HEAP; + + public boolean allowHeapAccess() { + return ordinal() == 0; // this == ALLOW_HEAP + } @Override public void validateForDowncall(FunctionDescriptor descriptor) { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java index abcc602791900..3940f996e0d78 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/NativeEntryPoint.java @@ -48,7 +48,29 @@ public class NativeEntryPoint { private record CacheKey(MethodType methodType, ABIDescriptor abi, List argMoves, List retMoves, boolean needsReturnBuffer, int capturedStateMask, - boolean needsTransition) {} + boolean needsTransition) { + + @Override + public boolean equals(Object o) { + if (!(o instanceof CacheKey other)) return false; + + return methodType == other.methodType && abi == other.abi && capturedStateMask == other.capturedStateMask + && needsTransition == other.needsTransition && needsReturnBuffer == other.needsReturnBuffer + && argMoves.equals(other.argMoves) && retMoves.equals(other.retMoves); + } + + @Override + public int hashCode() { + int result = System.identityHashCode(methodType); + result = 31 * result + abi.hashCode(); + result = 31 * result + argMoves.hashCode(); + result = 31 * result + retMoves.hashCode(); + result = 31 * result + Boolean.hashCode(needsReturnBuffer); + result = 31 * result + capturedStateMask; + result = 31 * result + Boolean.hashCode(needsTransition); + return result; + } + } private NativeEntryPoint(MethodType methodType, long downcallStubAddress) { this.methodType = methodType; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java index 125730560a2e6..37200598d5b07 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java @@ -24,9 +24,9 @@ */ package jdk.internal.foreign.abi; -import jdk.internal.access.JavaLangAccess; import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.foreign.BufferStack; import jdk.internal.foreign.CABI; import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory; import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; @@ -390,26 +390,12 @@ static long pickChunkOffset(long chunkOffset, long byteWidth, int chunkWidth) { : chunkOffset; } - public static Arena newBoundedArena(long size) { - return new Arena() { - final Arena arena = Arena.ofConfined(); - final SegmentAllocator slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size)); - - @Override - public Scope scope() { - return arena.scope(); - } + private static final int LINKER_STACK_SIZE = Integer.getInteger("jdk.internal.foreign.LINKER_STACK_SIZE", 256); + private static final BufferStack LINKER_STACK = BufferStack.of(LINKER_STACK_SIZE, 1); - @Override - public void close() { - arena.close(); - } - - @Override - public MemorySegment allocate(long byteSize, long byteAlignment) { - return slicingAllocator.allocate(byteSize, byteAlignment); - } - }; + @ForceInline + public static Arena newBoundedArena(long size) { + return LINKER_STACK.pushFrame(size, 8); } public static Arena newEmptyArena() { diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/VMStorage.java b/src/java.base/share/classes/jdk/internal/foreign/abi/VMStorage.java index 1237650e026bc..0be1675ccd81f 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/VMStorage.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/VMStorage.java @@ -24,6 +24,8 @@ */ package jdk.internal.foreign.abi; +import java.util.Objects; + /** * * @param type the type of storage. e.g. stack, or which register type (GP, FP, vector) @@ -32,7 +34,7 @@ * @param indexOrOffset the index is either a register number within a type, or * a stack offset in bytes if type = stack. * (a particular platform might add a bias to this in generate code) - * @param debugName the debug name + * @param debugName the debug name, mostly derived from type */ public record VMStorage(byte type, short segmentMaskOrSize, @@ -43,4 +45,14 @@ public VMStorage(byte type, short segmentMaskOrSize, int indexOrOffset) { this(type, segmentMaskOrSize, indexOrOffset, "Stack@" + indexOrOffset); } + @Override + public int hashCode() { + return Objects.hash(type, segmentMaskOrSize, indexOrOffset); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof VMStorage that && + type == that.type && segmentMaskOrSize == that.segmentMaskOrSize && indexOrOffset == that.indexOrOffset; + } } diff --git a/src/java.base/share/classes/jdk/internal/io/JdkConsole.java b/src/java.base/share/classes/jdk/internal/io/JdkConsole.java index 08bd840de37f2..2168a1c4febf1 100644 --- a/src/java.base/share/classes/jdk/internal/io/JdkConsole.java +++ b/src/java.base/share/classes/jdk/internal/io/JdkConsole.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,8 +40,6 @@ public interface JdkConsole { Reader reader(); JdkConsole println(Object obj); JdkConsole print(Object obj); - String readln(String prompt); - String readln(); JdkConsole format(Locale locale, String format, Object ... args); String readLine(Locale locale, String format, Object ... args); String readLine(); diff --git a/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java b/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java index 3c0afd2005cb1..008ed68f56f35 100644 --- a/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java +++ b/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,40 +71,6 @@ public JdkConsole print(Object obj) { return this; } - @Override - public String readln(String prompt) { - String line = null; - synchronized (writeLock) { - synchronized(readLock) { - pw.print(prompt); - pw.flush(); // automatic flushing does not cover print - try { - char[] ca = readline(false); - if (ca != null) - line = new String(ca); - } catch (IOException x) { - throw new IOError(x); - } - } - } - return line; - } - - @Override - public String readln() { - String line = null; - synchronized(readLock) { - try { - char[] ca = readline(false); - if (ca != null) - line = new String(ca); - } catch (IOException x) { - throw new IOError(x); - } - } - return line; - } - @Override public JdkConsole format(Locale locale, String format, Object ... args) { formatter.format(locale, format, args).flush(); diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index deb786b42bd03..7461815e4c502 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -68,18 +68,18 @@ public enum Feature { // keeping the constant of a feature that has been integrated or dropped, serves the purpose of muting such warnings. //--- - @JEP(number=495, title="Simple Source Files and Instance Main Methods", status="Fourth Preview") - IMPLICIT_CLASSES, + IMPLICIT_CLASSES, //to be removed when boot JDK is 25 @JEP(number=487, title="Scoped Values", status="Fourth Preview") SCOPED_VALUES, @JEP(number=499, title="Structured Concurrency", status="Fourth Preview") STRUCTURED_CONCURRENCY, CLASSFILE_API, STREAM_GATHERERS, - @JEP(number=494, title="Module Import Declarations", status="Second Preview") - MODULE_IMPORTS, + MODULE_IMPORTS, //remove when the boot JDK is JDK 25 @JEP(number=478, title="Key Derivation Function API", status="Preview") KEY_DERIVATION, + @JEP(number = 502, title = "Stable Values", status = "Preview") + STABLE_VALUES, LANGUAGE_MODEL, /** * A key for testing. diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableEnumFunction.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableEnumFunction.java new file mode 100644 index 0000000000000..88be2cea1b6bc --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/lang/stable/StableEnumFunction.java @@ -0,0 +1,117 @@ +/* + * 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 jdk.internal.lang.stable; + +import jdk.internal.util.ImmutableBitSetPredicate; +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.IntPredicate; +import java.util.function.Supplier; + +/** + * Optimized implementation of a stable Function with enums as keys. + * + * @implNote This implementation can be used early in the boot sequence as it does not + * rely on reflection, MethodHandles, Streams etc. + * + * @param firstOrdinal the lowest ordinal used + * @param delegates a delegate array of inputs to StableValue mappings + * @param original the original Function + * @param the type of the input to the function + * @param the type of the result of the function + */ +public record StableEnumFunction, R>(Class enumType, + int firstOrdinal, + IntPredicate member, + @Stable StableValueImpl[] delegates, + Function original) implements Function { + @ForceInline + @Override + public R apply(E value) { + if (!member.test(value.ordinal())) { // Implicit null-check of value + throw new IllegalArgumentException("Input not allowed: " + value); + } + final int index = value.ordinal() - firstOrdinal; + final StableValueImpl delegate; + // Since we did the member.test above, we know the index is in bounds + delegate = delegates[index]; + return delegate.orElseSet(new Supplier() { + @Override public R get() { return original.apply(value); }}); + + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } + + @Override + public String toString() { + final E[] enumElements = enumType.getEnumConstants(); + final Collection>> entries = new ArrayList<>(enumElements.length); + int ordinal = firstOrdinal; + for (int i = 0; i < delegates.length; i++, ordinal++) { + if (member.test(ordinal)) { + entries.add(new AbstractMap.SimpleImmutableEntry<>(enumElements[ordinal], delegates[i])); + } + } + return StableUtil.renderMappings(this, "StableFunction", entries, true); + } + + @SuppressWarnings("unchecked") + public static , R> Function of(Set inputs, + Function original) { + // The input set is not empty + final Class enumType = (Class)inputs.iterator().next().getClass(); + final BitSet bitSet = new BitSet(enumType.getEnumConstants().length); + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (T t : inputs) { + final int ordinal = ((E) t).ordinal(); + min = Math.min(min, ordinal); + max = Math.max(max, ordinal); + bitSet.set(ordinal); + } + final int size = max - min + 1; + final IntPredicate member = ImmutableBitSetPredicate.of(bitSet); + return (Function) new StableEnumFunction(enumType, min, member, StableUtil.array(size), (Function) original); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableFunction.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableFunction.java new file mode 100644 index 0000000000000..1b10593e5e883 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/lang/stable/StableFunction.java @@ -0,0 +1,83 @@ +/* + * 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 jdk.internal.lang.stable; + +import jdk.internal.vm.annotation.ForceInline; + +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; + +// Note: It would be possible to just use `LazyMap::get` with some additional logic +// instead of this class but explicitly providing a class like this provides better +// debug capability, exception handling, and may provide better performance. +/** + * Implementation of a stable Function. + * + * @implNote This implementation can be used early in the boot sequence as it does not + * rely on reflection, MethodHandles, Streams etc. + * + * @param values a delegate map of inputs to StableValue mappings + * @param original the original Function + * @param the type of the input to the function + * @param the type of the result of the function + */ +public record StableFunction(Map> values, + Function original) implements Function { + + @ForceInline + @Override + public R apply(T value) { + final StableValueImpl stable = values.get(value); + if (stable == null) { + throw new IllegalArgumentException("Input not allowed: " + value); + } + return stable.orElseSet(new Supplier() { + @Override public R get() { return original.apply(value); }}); + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } + + @Override + public String toString() { + return StableUtil.renderMappings(this, "StableFunction", values.entrySet(), true); + } + + public static StableFunction of(Set inputs, + Function original) { + return new StableFunction<>(StableUtil.map(inputs), original); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableIntFunction.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableIntFunction.java new file mode 100644 index 0000000000000..8bb046a762d94 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/lang/stable/StableIntFunction.java @@ -0,0 +1,83 @@ +/* + * 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 jdk.internal.lang.stable; + +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; + +import java.util.function.IntFunction; +import java.util.function.Supplier; + +// Note: It would be possible to just use `LazyList::get` instead of this +// class but explicitly providing a class like this provides better +// debug capability, exception handling, and may provide better performance. +/** + * Implementation of a stable IntFunction. + *

+ * For performance reasons (~10%), we are not delegating to a StableList but are using + * the more primitive functions in StableValueUtil that are shared with StableList/StableValueImpl. + * + * @implNote This implementation can be used early in the boot sequence as it does not + * rely on reflection, MethodHandles, Streams etc. + * + * @param the return type + */ +public record StableIntFunction(@Stable StableValueImpl[] delegates, + IntFunction original) implements IntFunction { + + @ForceInline + @Override + public R apply(int index) { + final StableValueImpl delegate; + try { + delegate = delegates[index]; + } catch (ArrayIndexOutOfBoundsException ioob) { + throw new IllegalArgumentException("Input not allowed: " + index, ioob); + } + return delegate.orElseSet(new Supplier() { + @Override public R get() { return original.apply(index); }}); + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } + + @Override + public String toString() { + return StableUtil.renderElements(this, "StableIntFunction", delegates); + } + + public static StableIntFunction of(int size, IntFunction original) { + return new StableIntFunction<>(StableUtil.array(size), original); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableSupplier.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableSupplier.java new file mode 100644 index 0000000000000..bdb40648db6f0 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/lang/stable/StableSupplier.java @@ -0,0 +1,69 @@ +/* + * 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 jdk.internal.lang.stable; + +import jdk.internal.vm.annotation.ForceInline; + +import java.util.function.Supplier; + +/** + * Implementation of a stable supplier. + *

+ * @implNote This implementation can be used early in the boot sequence as it does not + * rely on reflection, MethodHandles, Streams etc. + * + * @param the return type + */ +public record StableSupplier(StableValueImpl delegate, + Supplier original) implements Supplier { + + @ForceInline + @Override + public T get() { + return delegate.orElseSet(original); + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } + + @Override + public String toString() { + final Object t = delegate.wrappedContentAcquire(); + return t == this ? "(this StableSupplier)" : StableValueImpl.renderWrapped(t); + } + + public static StableSupplier of(Supplier original) { + return new StableSupplier<>(StableValueImpl.of(), original); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableUtil.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableUtil.java new file mode 100644 index 0000000000000..f6f33f9b1e86b --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/lang/stable/StableUtil.java @@ -0,0 +1,105 @@ +/* + * 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 jdk.internal.lang.stable; + +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.StringJoiner; + +public final class StableUtil { + + private StableUtil() {} + + public static String renderElements(Object self, + String selfName, + StableValueImpl[] delegates) { + return renderElements(self, selfName, delegates, 0, delegates.length); + } + + public static String renderElements(Object self, + String selfName, + StableValueImpl[] delegates, + int offset, + int length) { + final StringJoiner sj = new StringJoiner(", ", "[", "]"); + for (int i = 0; i < length; i++) { + final Object value = delegates[i + offset].wrappedContentAcquire(); + if (value == self) { + sj.add("(this " + selfName + ")"); + } else { + sj.add(StableValueImpl.renderWrapped(value)); + } + } + return sj.toString(); + } + + public static String renderMappings(Object self, + String selfName, + Iterable>> delegates, + boolean curly) { + final StringJoiner sj = new StringJoiner(", ", curly ? "{" : "[", curly ? "}" : "]"); + for (var e : delegates) { + final Object value = e.getValue().wrappedContentAcquire(); + final String valueString; + if (value == self) { + valueString = ("(this ") + selfName + ")"; + } else { + valueString = StableValueImpl.renderWrapped(value); + } + sj.add(e.getKey() + "=" + valueString); + } + return sj.toString(); + } + + public static StableValueImpl[] array(int size) { + assertSizeNonNegative(size); + @SuppressWarnings("unchecked") + final var stableValues = (StableValueImpl[]) new StableValueImpl[size]; + for (int i = 0; i < size; i++) { + stableValues[i] = StableValueImpl.of(); + } + return stableValues; + } + + public static Map> map(Set keys) { + Objects.requireNonNull(keys); + @SuppressWarnings("unchecked") + final var entries = (Map.Entry>[]) new Map.Entry[keys.size()]; + int i = 0; + for (K key : keys) { + entries[i++] = Map.entry(key, StableValueImpl.of()); + } + return Map.ofEntries(entries); + } + + public static void assertSizeNonNegative(int size) { + if (size < 0) { + throw new IllegalArgumentException("size can not be negative: " + size); + } + } + +} diff --git a/src/java.base/share/classes/jdk/internal/lang/stable/StableValueImpl.java b/src/java.base/share/classes/jdk/internal/lang/stable/StableValueImpl.java new file mode 100644 index 0000000000000..88c80eb6b395f --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/lang/stable/StableValueImpl.java @@ -0,0 +1,218 @@ +/* + * 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 jdk.internal.lang.stable; + +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.DontInline; +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; + +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * The implementation of StableValue. + * + * @implNote This implementation can be used early in the boot sequence as it does not + * rely on reflection, MethodHandles, Streams etc. + * + * @param type of the contents + */ +public final class StableValueImpl implements StableValue { + + static final String UNSET_LABEL = ".unset"; + + // Unsafe allows StableValue to be used early in the boot sequence + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + // Unsafe offsets for direct field access + + private static final long CONTENT_OFFSET = + UNSAFE.objectFieldOffset(StableValueImpl.class, "contents"); + // Used to indicate a holder value is `null` (see field `value` below) + // A wrapper method `nullSentinel()` is used for generic type conversion. + private static final Object NULL_SENTINEL = new Object(); + + // Generally, fields annotated with `@Stable` are accessed by the JVM using special + // memory semantics rules (see `parse.hpp` and `parse(1|2|3).cpp`). + // + // This field is used directly and reflectively via Unsafe using explicit memory semantics. + // + // | Value | Meaning | + // | -------------- | ------------ | + // | null | Unset | + // | NULL_SENTINEL | Set(null) | + // | other | Set(other) | + // + @Stable + private Object contents; + + // Only allow creation via the factory `StableValueImpl::newInstance` + private StableValueImpl() {} + + @ForceInline + @Override + public boolean trySet(T contents) { + if (wrappedContentAcquire() != null) { + return false; + } + // Prevent reentry via an orElseSet(supplier) + preventReentry(); + // Mutual exclusion is required here as `orElseSet` might also + // attempt to modify the `wrappedValue` + synchronized (this) { + return wrapAndSet(contents); + } + } + + @ForceInline + @Override + public void setOrThrow(T contents) { + if (!trySet(contents)) { + // Neither the set contents nor the provided contents is revealed in the + // exception message as it might be sensitive. + throw new IllegalStateException("The contents is already set"); + } + } + + @ForceInline + @Override + public T orElseThrow() { + final Object t = wrappedContentAcquire(); + if (t == null) { + throw new NoSuchElementException("No contents set"); + } + return unwrap(t); + } + + @ForceInline + @Override + public T orElse(T other) { + final Object t = wrappedContentAcquire(); + return (t == null) ? other : unwrap(t); + } + + @ForceInline + @Override + public boolean isSet() { + return wrappedContentAcquire() != null; + } + + @ForceInline + @Override + public T orElseSet(Supplier supplier) { + Objects.requireNonNull(supplier); + final Object t = wrappedContentAcquire(); + return (t == null) ? orElseSetSlowPath(supplier) : unwrap(t); + } + + @DontInline + private T orElseSetSlowPath(Supplier supplier) { + preventReentry(); + synchronized (this) { + final Object t = contents; // Plain semantics suffice here + if (t == null) { + final T newValue = supplier.get(); + // The mutex is not reentrant so we know newValue should be returned + wrapAndSet(newValue); + return newValue; + } + return unwrap(t); + } + } + + // The methods equals() and hashCode() should be based on identity (defaults from Object) + + @Override + public String toString() { + final Object t = wrappedContentAcquire(); + return t == this + ? "(this StableValue)" + : renderWrapped(t); + } + + // Internal methods shared with other internal classes + + @ForceInline + public Object wrappedContentAcquire() { + return UNSAFE.getReferenceAcquire(this, CONTENT_OFFSET); + } + + static String renderWrapped(Object t) { + return (t == null) ? UNSET_LABEL : Objects.toString(unwrap(t)); + } + + // Private methods + + // This method is not annotated with @ForceInline as it is always called + // in a slow path. + private void preventReentry() { + if (Thread.holdsLock(this)) { + throw new IllegalStateException("Recursive initialization of a stable value is illegal"); + } + } + + /** + * Wraps the provided {@code newValue} and tries to set the contents. + *

+ * This method ensures the {@link Stable} field is written to at most once. + * + * @param newValue to wrap and set + * @return if the contents was set + */ + @ForceInline + private boolean wrapAndSet(Object newValue) { + assert Thread.holdsLock(this); + // We know we hold the monitor here so plain semantic is enough + if (contents == null) { + UNSAFE.putReferenceRelease(this, CONTENT_OFFSET, wrap(newValue)); + return true; + } + return false; + } + + + // Wraps `null` values into a sentinel value + @ForceInline + private static Object wrap(Object t) { + return (t == null) ? NULL_SENTINEL : t; + } + + // Unwraps null sentinel values into `null` + @SuppressWarnings("unchecked") + @ForceInline + private static T unwrap(Object t) { + return t != NULL_SENTINEL ? (T) t : null; + } + + // Factory + + public static StableValueImpl of() { + return new StableValueImpl<>(); + } + +} diff --git a/src/java.base/share/classes/jdk/internal/misc/CDS.java b/src/java.base/share/classes/jdk/internal/misc/CDS.java index f070738e0c767..94696551645c9 100644 --- a/src/java.base/share/classes/jdk/internal/misc/CDS.java +++ b/src/java.base/share/classes/jdk/internal/misc/CDS.java @@ -82,9 +82,36 @@ public static boolean isDumpingStaticArchive() { return (configStatus & IS_DUMPING_STATIC_ARCHIVE) != 0; } + public static boolean isSingleThreadVM() { + return isDumpingStaticArchive(); + } + private static native int getCDSConfigStatus(); private static native void logLambdaFormInvoker(String line); + + // Used only when dumping static archive to keep weak references alive to + // ensure that Soft/Weak Reference objects can be reliably archived. + private static ArrayList keepAliveList; + + public static void keepAlive(Object s) { + assert isSingleThreadVM(); // no need for synchronization + assert isDumpingStaticArchive(); + if (keepAliveList == null) { + keepAliveList = new ArrayList<>(); + } + keepAliveList.add(s); + } + + // This is called by native JVM code at the very end of Java execution before + // dumping the static archive. + // It collects the objects from keepAliveList so that they can be easily processed + // by the native JVM code to check that any Reference objects that need special + // clean up must have been registed with keepAlive() + private static Object[] getKeepAliveObjects() { + return keepAliveList.toArray(); + } + /** * Initialize archived static fields in the given Class using archived * values from CDS dump time. Also initialize the classes of objects in diff --git a/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java b/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java index 970cebe74ed34..af98e364a77c5 100644 --- a/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java +++ b/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -82,10 +82,14 @@ private MethodFinder() { * @jls 12.1.4 Invoke a main method */ public static Method findMainMethod(Class cls) { - boolean isPreview = PreviewFeatures.isEnabled(); - Method mainMethod = JLA.findMethod(cls, !isPreview, "main", String[].class); + Method mainMethod = JLA.findMethod(cls, true, "main", String[].class); - if (isPreview && mainMethod == null) { + if (mainMethod == null) { + //if not public method, try to lookup a non-public one + mainMethod = JLA.findMethod(cls, false, "main", String[].class); + } + + if (mainMethod == null) { mainMethod = JLA.findMethod(cls, false, "main"); } @@ -97,8 +101,7 @@ public static Method findMainMethod(Class cls) { if (Modifier.isAbstract(mods) || mainMethod.getReturnType() != void.class || - (isPreview && Modifier.isPrivate(mods)) || - (!isPreview && !Modifier.isStatic(mods))) { + Modifier.isPrivate(mods)) { return null; } diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java index 0a9044178a443..39c52f2551476 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java @@ -142,8 +142,7 @@ private static boolean canUseArchivedBootLayer() { return getProperty("jdk.module.upgrade.path") == null && getProperty("jdk.module.patch.0") == null && // --patch-module getProperty("jdk.module.limitmods") == null && // --limit-modules - getProperty("jdk.module.addreads.0") == null && // --add-reads - getProperty("jdk.module.addopens.0") == null; // --add-opens + getProperty("jdk.module.addreads.0") == null; // --add-reads } /** @@ -452,7 +451,7 @@ private static ModuleLayer boot2() { // --add-reads, --add-exports/--add-opens addExtraReads(bootLayer); - boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer); + addExtraExportsAndOpens(bootLayer); // add enable native access addEnableNativeAccess(bootLayer); @@ -722,15 +721,13 @@ private static void addExtraReads(ModuleLayer bootLayer) { * Process the --add-exports and --add-opens options to export/open * additional packages specified on the command-line. */ - private static boolean addExtraExportsAndOpens(ModuleLayer bootLayer) { - boolean extraExportsOrOpens = false; + private static void addExtraExportsAndOpens(ModuleLayer bootLayer) { // --add-exports String prefix = "jdk.module.addexports."; Map> extraExports = decode(prefix); if (!extraExports.isEmpty()) { addExtraExportsOrOpens(bootLayer, extraExports, false); - extraExportsOrOpens = true; } @@ -739,10 +736,8 @@ private static boolean addExtraExportsAndOpens(ModuleLayer bootLayer) { Map> extraOpens = decode(prefix); if (!extraOpens.isEmpty()) { addExtraExportsOrOpens(bootLayer, extraOpens, true); - extraExportsOrOpens = true; } - return extraExportsOrOpens; } private static void addExtraExportsOrOpens(ModuleLayer bootLayer, diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java b/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java index ff3ffada22a75..ed97937e39a59 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java @@ -408,24 +408,10 @@ private Builder readModuleAttribute(DataInput in, ConstantPool cpool, int major, throw invalidModuleDescriptor("The requires entry for java.base" + " has ACC_SYNTHETIC set"); } - // requires transitive java.base is illegal unless: - // - the major version is 53 (JDK 9), or: - // - the classfile is a preview classfile, or: - // - the module is deemed to be participating in preview - // (i.e. the module is a java.* module) - // requires static java.base is illegal unless: - // - the major version is 53 (JDK 9), or: - if (major >= 54 - && ((mods.contains(Requires.Modifier.TRANSITIVE) - && !isPreview - && !"java.se".equals(mn)) - || mods.contains(Requires.Modifier.STATIC))) { - String flagName; - if (mods.contains(Requires.Modifier.STATIC)) { - flagName = "ACC_STATIC_PHASE"; - } else { - flagName = "ACC_TRANSITIVE"; - } + // requires static java.base is illegal unless + // the major version is 53 (JDK 9) + if (major >= 54 && mods.contains(Requires.Modifier.STATIC)) { + String flagName = "ACC_STATIC_PHASE"; throw invalidModuleDescriptor("The requires entry for java.base" + " has " + flagName + " set"); } diff --git a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java index 20b390855c908..19be5e537982f 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java +++ b/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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,15 +32,10 @@ import java.io.ObjectStreamField; import java.io.OptionalDataException; import java.io.Serializable; +import java.lang.classfile.ClassFile; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; +import java.lang.reflect.*; import java.util.Set; import jdk.internal.access.JavaLangReflectAccess; @@ -518,6 +513,31 @@ public final ObjectStreamField[] serialPersistentFields(Class cl) { } } + public final Set parseAccessFlags(int mask, AccessFlag.Location location, Class classFile) { + var cffv = classFileFormatVersion(classFile); + return cffv == null ? + AccessFlag.maskToAccessFlags(mask, location) : + AccessFlag.maskToAccessFlags(mask, location, cffv); + } + + private final ClassFileFormatVersion classFileFormatVersion(Class cl) { + int raw = SharedSecrets.getJavaLangAccess().classFileVersion(cl); + + int major = raw & 0xFFFF; + int minor = raw >>> Character.SIZE; + + assert VM.isSupportedClassFileVersion(major, minor) : major + "." + minor; + + if (major >= ClassFile.JAVA_12_VERSION) { + if (minor == 0) + return ClassFileFormatVersion.fromMajor(raw); + return null; // preview or old preview, fallback to default handling + } else if (major == ClassFile.JAVA_1_VERSION) { + return minor < 3 ? ClassFileFormatVersion.RELEASE_0 : ClassFileFormatVersion.RELEASE_1; + } + return ClassFileFormatVersion.fromMajor(major); + } + //-------------------------------------------------------------------------- // // Internals only below this point diff --git a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java index 766aca20b2167..150b9d752bbd9 100644 --- a/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java +++ b/src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,7 @@ import java.util.stream.Stream; import jdk.internal.access.SharedSecrets; +import jdk.internal.misc.CDS; /** * This class provides management of {@link Map maps} where it is desirable to @@ -336,6 +337,40 @@ public void removeStaleReferences() { } } + @SuppressWarnings("unchecked") + public void prepareForAOTCache() { + // We are running the AOT assembly phase. The JVM has a single Java thread, so + // we don't have any concurrent threads that may modify the map while we are + // iterating its keys. + // + // Also, the java.lang.ref.Reference$ReferenceHandler thread is not running, + // so even if the GC has put some of the keys on the pending ReferencePendingList, + // none of the keys would have been added to the stale queue yet. + assert CDS.isSingleThreadVM(); + + for (ReferenceKey key : map.keySet()) { + Object referent = key.get(); + if (referent == null) { + // We don't need this key anymore. Add to stale queue + ((Reference)key).enqueue(); + } else { + // Make sure the referent cannot be collected. Otherwise, when + // the referent is collected, the GC may push the key onto + // Universe::reference_pending_list() at an unpredictable time, + // making it difficult to correctly serialize the key's + // state into the CDS archive. + // + // See aotReferenceObjSupport.cpp for more info. + CDS.keepAlive(referent); + } + Reference.reachabilityFence(referent); + } + + // Remove all keys enqueued above + removeStaleReferences(); + } + + /** * Puts an entry where the key and the value are the same. Used for * interning values in a set. diff --git a/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java b/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java index 355af10aa7c31..960b63b12d37d 100644 --- a/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java +++ b/src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -193,4 +193,8 @@ public T intern(T e) { public T intern(T e, UnaryOperator interner) { return ReferencedKeyMap.intern(map, e, interner); } + + public void prepareForAOTCache() { + map.prepareForAOTCache(); + } } diff --git a/src/java.base/share/classes/jdk/internal/util/SystemProps.java b/src/java.base/share/classes/jdk/internal/util/SystemProps.java index 26110a1dab3c9..5760f04831e0e 100644 --- a/src/java.base/share/classes/jdk/internal/util/SystemProps.java +++ b/src/java.base/share/classes/jdk/internal/util/SystemProps.java @@ -88,9 +88,12 @@ public static Map initProperties() { put(props, "file.encoding", nativeEncoding); } - // "stdout/err.encoding", prepared for System.out/err. For compatibility - // purposes, substitute them with "sun.*" if they don't exist. If "sun.*" aren't - // available either, fall back to "native.encoding". + // Encoding properties for stdin, stdout, and stderr. For stdout and stderr, + // check "sun.stdout.encoding" and "sun.stderr.encoding" properties for backward + // compatibility reasons before falling back to the "native.encoding" property. + putIfAbsent(props, "stdin.encoding", + raw.propDefault(Raw._stdin_encoding_NDX)); + putIfAbsent(props, "stdin.encoding", nativeEncoding); putIfAbsent(props, "stdout.encoding", props.getOrDefault("sun.stdout.encoding", raw.propDefault(Raw._stdout_encoding_NDX))); putIfAbsent(props, "stdout.encoding", nativeEncoding); @@ -241,7 +244,8 @@ public static class Raw { @Native private static final int _socksProxyHost_NDX = 1 + _socksNonProxyHosts_NDX; @Native private static final int _socksProxyPort_NDX = 1 + _socksProxyHost_NDX; @Native private static final int _stderr_encoding_NDX = 1 + _socksProxyPort_NDX; - @Native private static final int _stdout_encoding_NDX = 1 + _stderr_encoding_NDX; + @Native private static final int _stdin_encoding_NDX = 1 + _stderr_encoding_NDX; + @Native private static final int _stdout_encoding_NDX = 1 + _stdin_encoding_NDX; @Native private static final int _sun_arch_abi_NDX = 1 + _stdout_encoding_NDX; @Native private static final int _sun_arch_data_model_NDX = 1 + _sun_arch_abi_NDX; @Native private static final int _sun_cpu_endian_NDX = 1 + _sun_arch_data_model_NDX; diff --git a/src/java.base/share/classes/jdk/internal/access/JavaIOFilePermissionAccess.java b/src/java.base/share/classes/jdk/internal/vm/vector/Utils.java similarity index 57% rename from src/java.base/share/classes/jdk/internal/access/JavaIOFilePermissionAccess.java rename to src/java.base/share/classes/jdk/internal/vm/vector/Utils.java index ed9f88685d682..de0bb83d39d99 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaIOFilePermissionAccess.java +++ b/src/java.base/share/classes/jdk/internal/vm/vector/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,29 +22,28 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.internal.access; +package jdk.internal.vm.vector; -import java.io.FilePermission; +import jdk.internal.reflect.CallerSensitive; +import jdk.internal.reflect.Reflection; -public interface JavaIOFilePermissionAccess { +/** + * Miscellaneous utility methods. + */ +public class Utils { + public static final boolean DEBUG = Boolean.getBoolean("jdk.incubator.vector.DEBUG"); - /** - * Returns a new FilePermission plus an alternative path. - * - * @param input the input - * @return the new FilePermission plus the alt path (as npath2) - * or the input itself if no alt path is available. - */ - @SuppressWarnings("removal") - FilePermission newPermPlusAltPath(FilePermission input); + public static boolean isNonCapturingLambda(Object o) { + return o.getClass().getDeclaredFields().length == 0; + } - /** - * Returns a new FilePermission using an alternative path. - * - * @param input the input - * @return the new FilePermission using the alt path (as npath) - * or null if no alt path is available - */ - @SuppressWarnings("removal") - FilePermission newPermUsingAltPath(FilePermission input); + @CallerSensitive + public static void debug(String format, Object... args) { + if (DEBUG) { + Class caller = Reflection.getCallerClass(); + System.out.printf("DEBUG: %s: ", caller.getSimpleName()); + System.out.printf(format, args); + System.out.println(); + } + } } diff --git a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java index cbf30da228934..4a8ad79b50c3d 100644 --- a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java +++ b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java @@ -30,6 +30,8 @@ import java.util.function.*; +import static jdk.internal.vm.vector.Utils.isNonCapturingLambda; + public class VectorSupport { static { registerNatives(); @@ -114,6 +116,9 @@ public class VectorSupport { public static final int VECTOR_OP_EXPM1 = 117; public static final int VECTOR_OP_HYPOT = 118; + public static final int VECTOR_OP_MATHLIB_FIRST = VECTOR_OP_TAN; + public static final int VECTOR_OP_MATHLIB_LAST = VECTOR_OP_HYPOT; + public static final int VECTOR_OP_SADD = 119; public static final int VECTOR_OP_SSUB = 120; public static final int VECTOR_OP_SUADD = 121; @@ -323,6 +328,23 @@ V unaryOp(int oprId, /* ============================================================================ */ +// public interface LibraryUnaryOperation, +// M extends VectorMask> { +// V apply(MemorySegment entry, V v, M m); +// } + + @IntrinsicCandidate + public static + , E> + V libraryUnaryOp(long addr, Class vClass, Class eClass, int length, String debugName, + V v, + UnaryOperation defaultImpl) { + assert isNonCapturingLambda(defaultImpl) : defaultImpl; + return defaultImpl.apply(v, null); + } + + /* ============================================================================ */ + public interface BinaryOperation> { VM apply(VM v1, VM v2, M m); @@ -341,6 +363,24 @@ VM binaryOp(int oprId, assert isNonCapturingLambda(defaultImpl) : defaultImpl; return defaultImpl.apply(v1, v2, m); } + + /* ============================================================================ */ + +// public interface LibraryBinaryOperation> { +// V apply(MemorySegment entry, V v1, V v2, M m); +// } + + @IntrinsicCandidate + public static + + V libraryBinaryOp(long addr, Class vClass, Class eClass, int length, String debugName, + V v1, V v2, + BinaryOperation defaultImpl) { + assert isNonCapturingLambda(defaultImpl) : defaultImpl; + return defaultImpl.apply(v1, v2, null); + } + /* ============================================================================ */ public interface SelectFromTwoVector> { @@ -718,13 +758,19 @@ long maskReductionCoerced(int oper, /* ============================================================================ */ + // Returns a string containing a list of CPU features VM detected. + public static native String getCPUFeatures(); + + /* ============================================================================ */ + // query the JVM's supported vector sizes and types public static native int getMaxLaneCount(Class etype); /* ============================================================================ */ - public static boolean isNonCapturingLambda(Object o) { - return o.getClass().getDeclaredFields().length == 0; + @SuppressWarnings({"restricted"}) + public static void loadNativeLibrary(String name) { + System.loadLibrary(name); } /* ============================================================================ */ diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 22f40d9cead1d..6775dda269a38 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -272,6 +272,7 @@ java.security.jgss, java.smartcardio, jdk.charsets, + jdk.incubator.vector, jdk.internal.vm.ci, jdk.jlink, jdk.jpackage, diff --git a/src/java.base/share/classes/sun/launcher/LauncherHelper.java b/src/java.base/share/classes/sun/launcher/LauncherHelper.java index 8fbf76851cc66..e31aca243b31f 100644 --- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java +++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java @@ -73,7 +73,6 @@ import java.util.stream.Stream; import jdk.internal.misc.MethodFinder; -import jdk.internal.misc.PreviewFeatures; import jdk.internal.misc.VM; import jdk.internal.module.ModuleBootstrap; import jdk.internal.module.Modules; @@ -945,17 +944,8 @@ private static void validateMainMethod(Class mainClass) { int mods = mainMethod.getModifiers(); isStaticMain = Modifier.isStatic(mods); - boolean isPublic = Modifier.isPublic(mods); noArgMain = mainMethod.getParameterCount() == 0; - if (!PreviewFeatures.isEnabled()) { - if (!isStaticMain || !isPublic || noArgMain) { - abort(null, "java.launcher.cls.error2", mainClass.getName(), - JAVAFX_APPLICATION_CLASS_NAME); - } - return; - } - if (!isStaticMain) { String className = mainMethod.getDeclaringClass().getName(); if (mainClass.isMemberClass() && !Modifier.isStatic(mainClass.getModifiers())) { diff --git a/src/java.base/share/classes/sun/security/provider/SHA3.java b/src/java.base/share/classes/sun/security/provider/SHA3.java index 5f974bc6ea658..a096cac5f5041 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA3.java +++ b/src/java.base/share/classes/sun/security/provider/SHA3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -476,9 +476,28 @@ public byte[] squeeze(int numBytes) { public void reset() { engineReset(); + // engineReset (final in DigestBase) skips implReset if there's + // no update. This works for MessageDigest, since digest() always + // resets. But for XOF, squeeze() may be called without update, + // and still modifies state. So we always call implReset here + // to ensure correct behavior. + implReset(); } } + public static final class SHAKE128Hash extends SHA3 { + public SHAKE128Hash() { + super("SHAKE128-256", 32, (byte) 0x1F, 32); + } + } + + public static final class SHAKE256Hash extends SHA3 { + public SHAKE256Hash() { + super("SHAKE256-512", 64, (byte) 0x1F, 64); + } + } + + /* * The SHAKE128 extendable output function. */ diff --git a/src/java.base/share/classes/sun/security/provider/SunEntries.java b/src/java.base/share/classes/sun/security/provider/SunEntries.java index 9f5e3447aeb56..bfc702ec6a266 100644 --- a/src/java.base/share/classes/sun/security/provider/SunEntries.java +++ b/src/java.base/share/classes/sun/security/provider/SunEntries.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -264,6 +264,10 @@ public final class SunEntries { "sun.security.provider.SHA3$SHA384", attrs); addWithAlias(p, "MessageDigest", "SHA3-512", "sun.security.provider.SHA3$SHA512", attrs); + addWithAlias(p, "MessageDigest", "SHAKE128-256", + "sun.security.provider.SHA3$SHAKE128Hash", attrs); + addWithAlias(p, "MessageDigest", "SHAKE256-512", + "sun.security.provider.SHA3$SHAKE256Hash", attrs); /* * Certificates diff --git a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java index 9736bfaba0dc1..ec5dee26cfcd2 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -142,8 +142,10 @@ private static boolean hasOption(String option) { if (property.contains("all")) { return true; } else { - int offset = property.indexOf("ssl"); - if (offset != -1 && property.indexOf("sslctx", offset) != -1) { + // remove first occurrence of "sslctx" since + // it interferes with search for "ssl" + String modified = property.replaceFirst("sslctx", ""); + if (modified.contains("ssl")) { // don't enable data and plaintext options by default if (!(option.equals("data") || option.equals("packet") diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java index 2e7d99ee293a9..566415e23c7e4 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java @@ -27,7 +27,6 @@ import sun.security.provider.X509Factory; import java.io.IOException; -import java.math.BigInteger; import java.net.InetAddress; import java.nio.ByteBuffer; import java.security.Principal; @@ -279,6 +278,8 @@ final class SSLSessionImpl extends ExtendedSSLSession { * < 1 byte > Number of requestedServerNames entries * < 1 byte > ServerName length * < length in bytes > ServerName + * < 4 bytes > maximumPacketSize + * < 4 bytes > negotiatedMaxFragSize * < 4 bytes > creationTime * < 2 byte > status response length * < 2 byte > status response entry length @@ -304,8 +305,6 @@ final class SSLSessionImpl extends ExtendedSSLSession { * < length in bytes> PSK identity * Anonymous * < 1 byte > - * < 4 bytes > maximumPacketSize - * < 4 bytes > negotiatedMaxFragSize */ SSLSessionImpl(HandshakeContext hc, ByteBuffer buf) throws IOException { @@ -1300,12 +1299,12 @@ public String[] getValueNames() { /** * Use large packet sizes now or follow RFC 2246 packet sizes (2^14) * until changed. - * + *

* In the TLS specification (section 6.2.1, RFC2246), it is not * recommended that the plaintext has more than 2^14 bytes. * However, some TLS implementations violate the specification. * This is a workaround for interoperability with these stacks. - * + *

* Application could accept large fragments up to 2^15 bytes by * setting the system property jsse.SSLEngine.acceptLargeFragments * to "true". @@ -1318,7 +1317,7 @@ public String[] getValueNames() { * Expand the buffer size of both SSL/TLS network packet and * application data. */ - protected void expandBufferSizes() { + void expandBufferSizes() { sessionLock.lock(); try { acceptLargeFragments = true; diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java index 80029e73afb18..a95b31583bbec 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java @@ -25,6 +25,7 @@ package sun.security.ssl; +import static sun.security.ssl.SignatureScheme.CERTIFICATE_SCOPE; import static sun.security.ssl.SignatureScheme.HANDSHAKE_SCOPE; import java.io.IOException; @@ -33,6 +34,7 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; +import javax.net.ssl.SSLException; import javax.net.ssl.SSLProtocolException; import sun.security.ssl.SSLExtension.ExtensionConsumer; import sun.security.ssl.SSLExtension.SSLExtensionSpec; @@ -270,30 +272,8 @@ public void consume(ConnectionContext context, return; } - // update the context - List sss = - SignatureScheme.getSupportedAlgorithms( - shc.sslConfig, - shc.algorithmConstraints, shc.negotiatedProtocol, - spec.signatureSchemes, - HANDSHAKE_SCOPE); - - if (sss == null || sss.isEmpty()) { - throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, - "No supported signature algorithm"); - } - shc.peerRequestedSignatureSchemes = sss; - - // If no "signature_algorithms_cert" extension is present, then - // the "signature_algorithms" extension also applies to - // signatures appearing in certificates. - SignatureSchemesSpec certSpec = - (SignatureSchemesSpec)shc.handshakeExtensions.get( - SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT); - if (certSpec == null) { - shc.peerRequestedCertSignSchemes = sss; - shc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss); - } + updateHandshakeContext(shc, spec.signatureSchemes, + SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT); if (!shc.isResumption && shc.negotiatedProtocol.useTLS13PlusSpec()) { @@ -497,30 +477,8 @@ public void consume(ConnectionContext context, return; } - // update the context - List sss = - SignatureScheme.getSupportedAlgorithms( - chc.sslConfig, - chc.algorithmConstraints, chc.negotiatedProtocol, - spec.signatureSchemes, - HANDSHAKE_SCOPE); - - if (sss == null || sss.isEmpty()) { - throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, - "No supported signature algorithm"); - } - chc.peerRequestedSignatureSchemes = sss; - - // If no "signature_algorithms_cert" extension is present, then - // the "signature_algorithms" extension also applies to - // signatures appearing in certificates. - SignatureSchemesSpec certSpec = - (SignatureSchemesSpec)chc.handshakeExtensions.get( - SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT); - if (certSpec == null) { - chc.peerRequestedCertSignSchemes = sss; - chc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss); - } + updateHandshakeContext(chc, spec.signatureSchemes, + SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT); } } @@ -543,4 +501,49 @@ public void absent(ConnectionContext context, "received CertificateRequest handshake message"); } } + + // Updates given HandshakeContext with peer signature schemes. + private static void updateHandshakeContext(HandshakeContext hc, + int[] signatureSchemes, SSLExtension signatureAlgorithmsCertExt) + throws SSLException { + List handshakeSS = + SignatureScheme.getSupportedAlgorithms( + hc.sslConfig, + hc.algorithmConstraints, + hc.negotiatedProtocol, + signatureSchemes, + HANDSHAKE_SCOPE); + + if (handshakeSS.isEmpty()) { + throw hc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No supported signature algorithm"); + } + + hc.peerRequestedSignatureSchemes = handshakeSS; + + // If no "signature_algorithms_cert" extension is present, then + // the "signature_algorithms" extension also applies to + // signatures appearing in certificates. + SignatureSchemesSpec certSpec = + (SignatureSchemesSpec) hc.handshakeExtensions.get( + signatureAlgorithmsCertExt); + + if (certSpec == null) { + List certSS = + SignatureScheme.getSupportedAlgorithms( + hc.sslConfig, + hc.algorithmConstraints, + hc.negotiatedProtocol, + signatureSchemes, + CERTIFICATE_SCOPE); + + if (certSS.isEmpty()) { + throw hc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No supported signature algorithm"); + } + + hc.peerRequestedCertSignSchemes = certSS; + hc.handshakeSession.setPeerSupportedSignatureAlgorithms(certSS); + } + } } diff --git a/src/java.base/share/classes/sun/security/util/FilePermCompat.java b/src/java.base/share/classes/sun/security/util/FilePermCompat.java deleted file mode 100644 index 651c84a462bef..0000000000000 --- a/src/java.base/share/classes/sun/security/util/FilePermCompat.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2016, 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.util; - -import java.io.FilePermission; -import java.security.Permission; -import jdk.internal.access.SharedSecrets; - -/** - * Take care of FilePermission compatibility after JDK-8164705. - */ -public class FilePermCompat { - /** - * New behavior? Keep compatibility? Both default true. - */ - public static final boolean nb; - public static final boolean compat; - - static { - String flag = SecurityProperties.getOverridableProperty( - "jdk.io.permissionsUseCanonicalPath"); - if (flag == null) { - flag = "false"; - } - switch (flag) { - case "true": - nb = false; - compat = false; - break; - case "false": - nb = true; - compat = true; - break; - default: - throw new RuntimeException( - "Invalid jdk.io.permissionsUseCanonicalPath: " + flag); - } - } - - @SuppressWarnings("removal") - public static Permission newPermPlusAltPath(Permission input) { - if (compat && input instanceof FilePermission) { - return SharedSecrets.getJavaIOFilePermissionAccess() - .newPermPlusAltPath((FilePermission) input); - } - return input; - } - - @SuppressWarnings("removal") - public static Permission newPermUsingAltPath(Permission input) { - if (input instanceof FilePermission) { - return SharedSecrets.getJavaIOFilePermissionAccess() - .newPermUsingAltPath((FilePermission) input); - } - return null; - } -} diff --git a/src/java.base/share/classes/sun/security/util/KnownOIDs.java b/src/java.base/share/classes/sun/security/util/KnownOIDs.java index 223dddb7f61a0..9435a7a9d39b1 100644 --- a/src/java.base/share/classes/sun/security/util/KnownOIDs.java +++ b/src/java.base/share/classes/sun/security/util/KnownOIDs.java @@ -152,8 +152,8 @@ public enum KnownOIDs { SHA3_256("2.16.840.1.101.3.4.2.8", "SHA3-256"), SHA3_384("2.16.840.1.101.3.4.2.9", "SHA3-384"), SHA3_512("2.16.840.1.101.3.4.2.10", "SHA3-512"), - SHAKE128("2.16.840.1.101.3.4.2.11"), - SHAKE256("2.16.840.1.101.3.4.2.12"), + SHAKE128_256("2.16.840.1.101.3.4.2.11", "SHAKE128-256", "SHAKE128"), + SHAKE256_512("2.16.840.1.101.3.4.2.12", "SHAKE256-512", "SHAKE256"), HmacSHA3_224("2.16.840.1.101.3.4.2.13", "HmacSHA3-224"), HmacSHA3_256("2.16.840.1.101.3.4.2.14", "HmacSHA3-256"), HmacSHA3_384("2.16.840.1.101.3.4.2.15", "HmacSHA3-384"), diff --git a/src/java.base/share/classes/sun/security/util/PBEUtil.java b/src/java.base/share/classes/sun/security/util/PBEUtil.java index 12e71418bf4da..5b7d6d5ce8321 100644 --- a/src/java.base/share/classes/sun/security/util/PBEUtil.java +++ b/src/java.base/share/classes/sun/security/util/PBEUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Red Hat, Inc. + * Copyright (c) 2023, 2025, Red Hat, Inc. * 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,9 @@ package sun.security.util; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -195,10 +198,7 @@ public PBEKeySpec getPBEKeySpec(int blkSize, int keyLength, int opmode, } initialize(blkSize, opmode, iCountInit, saltInit, ivSpecInit, random); - passwdChars = new char[passwdBytes.length]; - for (int i = 0; i < passwdChars.length; i++) { - passwdChars[i] = (char) (passwdBytes[i] & 0x7f); - } + passwdChars = decodePassword(passwdBytes); return new PBEKeySpec(passwdChars, salt, iCount, keyLength); } finally { // password char[] was cloned in PBEKeySpec constructor, @@ -254,7 +254,7 @@ private static int check(int iCount) public static PBEKeySpec getPBAKeySpec(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { - char[] passwdChars; + char[] passwdChars = null; byte[] salt = null; int iCount = 0; if (key instanceof javax.crypto.interfaces.PBEKey pbeKey) { @@ -267,10 +267,7 @@ public static PBEKeySpec getPBAKeySpec(Key key, (passwdBytes = key.getEncoded()) == null) { throw new InvalidKeyException("Missing password"); } - passwdChars = new char[passwdBytes.length]; - for (int i = 0; i < passwdChars.length; i++) { - passwdChars[i] = (char) (passwdBytes[i] & 0x7f); - } + passwdChars = decodePassword(passwdBytes); Arrays.fill(passwdBytes, (byte)0x00); } else { throw new InvalidKeyException("SecretKey of PBE type required"); @@ -286,11 +283,7 @@ public static PBEKeySpec getPBAKeySpec(Key key, "PBEParameterSpec required for salt " + "and iteration count"); } - } else if (!(params instanceof PBEParameterSpec)) { - throw new InvalidAlgorithmParameterException( - "PBEParameterSpec type required"); - } else { - PBEParameterSpec pbeParams = (PBEParameterSpec) params; + } else if (params instanceof PBEParameterSpec pbeParams) { // make sure the parameter values are consistent if (salt != null) { if (!Arrays.equals(salt, pbeParams.getSalt())) { @@ -310,7 +303,11 @@ public static PBEKeySpec getPBAKeySpec(Key key, } else { iCount = pbeParams.getIterationCount(); } + } else { + throw new InvalidAlgorithmParameterException( + "PBEParameterSpec type required"); } + // For security purpose, we need to enforce a minimum length // for salt; just require the minimum salt length to be 8-byte // which is what PKCS#5 recommends and openssl does. @@ -324,32 +321,34 @@ public static PBEKeySpec getPBAKeySpec(Key key, } return new PBEKeySpec(passwdChars, salt, iCount); } finally { - Arrays.fill(passwdChars, '\0'); + if (passwdChars != null) { + Arrays.fill(passwdChars, '\0'); + } } } /* - * Check that the key implements the PBEKey interface. If params is an - * instance of PBEParameterSpec, validate consistency with the key's - * derivation data. Used by P11Mac and P11PBECipher (SunPKCS11). + * Converts the password char[] to the UTF-8 encoded byte[]. Used by PBEKey + * and PBKDF2KeyImpl (SunJCE). */ - public static void checkKeyAndParams(Key key, - AlgorithmParameterSpec params, String algorithm) - throws InvalidKeyException, InvalidAlgorithmParameterException { - if (key instanceof javax.crypto.interfaces.PBEKey pbeKey) { - if (params instanceof PBEParameterSpec pbeParams) { - if (pbeParams.getIterationCount() != - pbeKey.getIterationCount() || - !Arrays.equals(pbeParams.getSalt(), pbeKey.getSalt())) { - throw new InvalidAlgorithmParameterException( - "Salt or iteration count parameters are " + - "not consistent with PBE key"); - } - } - } else { - throw new InvalidKeyException( - "Cannot use a " + algorithm + " service with a key that " + - "does not implement javax.crypto.interfaces.PBEKey"); - } + public static byte[] encodePassword(char[] passwd) { + ByteBuffer bb = StandardCharsets.UTF_8.encode(CharBuffer.wrap(passwd)); + int len = bb.limit(); + byte[] passwdBytes = new byte[len]; + bb.get(passwdBytes, 0, len); + bb.clear().put(new byte[len]); + + return passwdBytes; + } + + // converts the UTF-8 encoded byte[] to the password char[] + private static char[] decodePassword(byte[] passwdBytes) { + CharBuffer cb = StandardCharsets.UTF_8.decode( + ByteBuffer.wrap(passwdBytes)); + int len = cb.limit(); + char[] passwd = new char[len]; + cb.get(passwd); + cb.clear().put(new char[len]); + return passwd; } } diff --git a/src/java.base/share/classes/sun/security/util/SignatureUtil.java b/src/java.base/share/classes/sun/security/util/SignatureUtil.java index c1ffd248f2a60..05cad48c04265 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureUtil.java +++ b/src/java.base/share/classes/sun/security/util/SignatureUtil.java @@ -198,7 +198,7 @@ public static class EdDSADigestAlgHolder { static { try { sha512 = new AlgorithmId(ObjectIdentifier.of(KnownOIDs.SHA_512)); - shake256 = new AlgorithmId(ObjectIdentifier.of(KnownOIDs.SHAKE256)); + shake256 = new AlgorithmId(ObjectIdentifier.of(KnownOIDs.SHAKE256_512)); shake256$512 = new AlgorithmId( ObjectIdentifier.of(KnownOIDs.SHAKE256_LEN), new DerValue((byte) 2, new byte[]{2, 0})); // int 512 diff --git a/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java index 591a7a26c6893..984f5a0ab47f9 100644 --- a/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java +++ b/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java @@ -43,26 +43,14 @@ final class CamerfirmaTLSPolicy { private static final Debug debug = Debug.getInstance("certpath"); - // SHA-256 certificate fingerprints of distrusted roots - private static final Set FINGERPRINTS = Set.of( - // cacerts alias: camerfirmachamberscommerceca - // DN: CN=Chambers of Commerce Root, - // OU=http://www.chambersign.org, - // O=AC Camerfirma SA CIF A82743287, C=EU - "0C258A12A5674AEF25F28BA7DCFAECEEA348E541E6F5CC4EE63B71B361606AC3", - // cacerts alias: camerfirmachambersca - // DN: CN=Chambers of Commerce Root - 2008, - // O=AC Camerfirma S.A., SERIALNUMBER=A82743287, - // L=Madrid (see current address at www.camerfirma.com/address), - // C=EU - "063E4AFAC491DFD332F3089B8542E94617D893D7FE944E10A7937EE29D9693C0", - // cacerts alias: camerfirmachambersignca - // DN: CN=Global Chambersign Root - 2008, - // O=AC Camerfirma S.A., SERIALNUMBER=A82743287, - // L=Madrid (see current address at www.camerfirma.com/address), - // C=EU - "136335439334A7698016A0D324DE72284E079D7B5220BB8FBD747816EEBEBACA" - ); + // SHA-256 certificate fingerprint of distrusted root for TLS + // cacerts alias: camerfirmachambersca + // DN: CN=Chambers of Commerce Root - 2008, + // O=AC Camerfirma S.A., SERIALNUMBER=A82743287, + // L=Madrid (see current address at www.camerfirma.com/address), + // C=EU + private static final String FINGERPRINT = + "063E4AFAC491DFD332F3089B8542E94617D893D7FE944E10A7937EE29D9693C0"; // Any TLS Server certificate that is anchored by one of the Camerfirma // roots above and is issued after this date will be distrusted. @@ -85,7 +73,7 @@ static void checkDistrust(X509Certificate[] chain) throw new ValidatorException("Cannot generate fingerprint for " + "trust anchor of TLS server certificate"); } - if (FINGERPRINTS.contains(fp)) { + if (FINGERPRINT.equalsIgnoreCase(fp)) { Date notBefore = chain[0].getNotBefore(); LocalDate ldNotBefore = LocalDate.ofInstant(notBefore.toInstant(), ZoneOffset.UTC); diff --git a/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java b/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java index 2763ac30ca73d..dced9151d5b80 100644 --- a/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java +++ b/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -929,9 +929,7 @@ protected final Object[][] getContents() { {"Japan", JST}, {"Kwajalein", MHT}, {"Libya", EET}, - {"MET", new String[] {"Middle Europe Time", "MET", - "Middle Europe Summer Time", "MEST", - "Middle Europe Time", "MET"}}, + {"MET", CET}, {"Mexico/BajaNorte", PST}, {"Mexico/BajaSur", MST}, {"Mexico/General", CST}, diff --git a/src/java.base/share/data/cacerts/camerfirmachamberscommerceca b/src/java.base/share/data/cacerts/camerfirmachamberscommerceca deleted file mode 100644 index b92255f770cdf..0000000000000 --- a/src/java.base/share/data/cacerts/camerfirmachamberscommerceca +++ /dev/null @@ -1,35 +0,0 @@ -Owner: CN=Chambers of Commerce Root, OU=http://www.chambersign.org, O=AC Camerfirma SA CIF A82743287, C=EU -Issuer: CN=Chambers of Commerce Root, OU=http://www.chambersign.org, O=AC Camerfirma SA CIF A82743287, C=EU -Serial number: 0 -Valid from: Tue Sep 30 16:13:43 GMT 2003 until: Wed Sep 30 16:13:44 GMT 2037 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn -MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL -ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg -b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa -MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB -ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw -IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B -AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb -unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d -BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq -7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 -0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX -roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG -A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j -aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p -26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA -BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud -EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN -BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz -aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB -AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd -p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi -1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc -XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 -eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu -tGWaIZDgqtCYvDi1czyL+Nw= ------END CERTIFICATE----- diff --git a/src/java.base/share/data/cacerts/camerfirmachambersignca b/src/java.base/share/data/cacerts/camerfirmachambersignca deleted file mode 100644 index 935eea9c2121e..0000000000000 --- a/src/java.base/share/data/cacerts/camerfirmachambersignca +++ /dev/null @@ -1,48 +0,0 @@ -Owner: CN=Global Chambersign Root - 2008, O=AC Camerfirma S.A., SERIALNUMBER=A82743287, L=Madrid (see current address at www.camerfirma.com/address), C=EU -Issuer: CN=Global Chambersign Root - 2008, O=AC Camerfirma S.A., SERIALNUMBER=A82743287, L=Madrid (see current address at www.camerfirma.com/address), C=EU -Serial number: c9cdd3e9d57d23ce -Valid from: Fri Aug 01 12:31:40 GMT 2008 until: Sat Jul 31 12:31:40 GMT 2038 -Signature algorithm name: SHA1withRSA -Subject Public Key Algorithm: 4096-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD -VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 -IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 -MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD -aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx -MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy -cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG -A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl -BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI -hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed -KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 -G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 -zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 -ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG -HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 -Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V -yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e -beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r -6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh -wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog -zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW -BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr -ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp -ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk -cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt -YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC -CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow -KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI -hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ -UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz -X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x -fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz -a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd -Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd -SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O -AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso -M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge -v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z -09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B ------END CERTIFICATE----- diff --git a/src/java.base/share/native/libjava/System.c b/src/java.base/share/native/libjava/System.c index b1feaf3dff7a2..725c1e302272c 100644 --- a/src/java.base/share/native/libjava/System.c +++ b/src/java.base/share/native/libjava/System.c @@ -154,6 +154,7 @@ Java_jdk_internal_util_SystemProps_00024Raw_platformProperties(JNIEnv *env, jcla PUTPROP(propArray, _sun_jnu_encoding_NDX, sprops->sun_jnu_encoding); /* encodings for standard streams, may be NULL */ + PUTPROP(propArray, _stdin_encoding_NDX, sprops->stdin_encoding); PUTPROP(propArray, _stdout_encoding_NDX, sprops->stdout_encoding); PUTPROP(propArray, _stderr_encoding_NDX, sprops->stderr_encoding); diff --git a/src/java.base/share/native/libjava/java_props.h b/src/java.base/share/native/libjava/java_props.h index 95fb6d9e7f91a..774a7435e5478 100644 --- a/src/java.base/share/native/libjava/java_props.h +++ b/src/java.base/share/native/libjava/java_props.h @@ -65,6 +65,7 @@ typedef struct { char *display_variant; char *encoding; /* always set non-NULL by platform code */ char *sun_jnu_encoding; /* always set non-NULL by platform code */ + char *stdin_encoding; char *stdout_encoding; char *stderr_encoding; diff --git a/src/java.base/share/native/libsyslookup/syslookup.c b/src/java.base/share/native/libsyslookup/syslookup.c index b1f543bfdb763..fa7ddca41566e 100644 --- a/src/java.base/share/native/libsyslookup/syslookup.c +++ b/src/java.base/share/native/libsyslookup/syslookup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -27,6 +27,10 @@ // Adding at least one #include removes unwanted warnings on some platforms. #include +#include "jni_util.h" + +DEF_STATIC_JNI_OnLoad + // Simple dummy function so this library appears as a normal library to tooling. char* syslookup() { return "syslookup"; diff --git a/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java b/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java index a466331de9339..2f633ad711d98 100644 --- a/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java +++ b/src/java.base/unix/classes/sun/net/dns/ResolverConfigurationImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ public final class ResolverConfigurationImpl extends ResolverConfiguration { // Lock helds whilst loading configuration or checking - private static Object lock = new Object(); + private static final Object lock = new Object(); // Time of last refresh. private static long lastRefresh = -1; diff --git a/src/java.base/unix/native/libjava/java_props_md.c b/src/java.base/unix/native/libjava/java_props_md.c index 6db307088b019..1d5435d5229c5 100644 --- a/src/java.base/unix/native/libjava/java_props_md.c +++ b/src/java.base/unix/native/libjava/java_props_md.c @@ -464,6 +464,9 @@ GetJavaProperties(JNIEnv *env) sprops.sun_jnu_encoding = sprops.encoding; #endif + if (isatty(STDIN_FILENO) == 1) { + sprops.stdin_encoding = sprops.encoding; + } if (isatty(STDOUT_FILENO) == 1) { sprops.stdout_encoding = sprops.encoding; } diff --git a/src/java.base/windows/native/libjava/java_props_md.c b/src/java.base/windows/native/libjava/java_props_md.c index 275dd3795c500..9495faf81e554 100644 --- a/src/java.base/windows/native/libjava/java_props_md.c +++ b/src/java.base/windows/native/libjava/java_props_md.c @@ -634,7 +634,7 @@ GetJavaProperties(JNIEnv* env) LCID userDefaultUILCID = MAKELCID(userDefaultUILang, SORTIDFROMLCID(userDefaultLCID)); { - HANDLE hStdOutErr; + HANDLE hStdHandle; // Windows UI Language selection list only cares "language" // information of the UI Language. For example, the list @@ -677,14 +677,19 @@ GetJavaProperties(JNIEnv* env) sprops.sun_jnu_encoding = "MS950_HKSCS"; } - hStdOutErr = GetStdHandle(STD_OUTPUT_HANDLE); - if (hStdOutErr != INVALID_HANDLE_VALUE && - GetFileType(hStdOutErr) == FILE_TYPE_CHAR) { + hStdHandle = GetStdHandle(STD_INPUT_HANDLE); + if (hStdHandle != INVALID_HANDLE_VALUE && + GetFileType(hStdHandle) == FILE_TYPE_CHAR) { + sprops.stdin_encoding = getConsoleEncoding(FALSE); + } + hStdHandle = GetStdHandle(STD_OUTPUT_HANDLE); + if (hStdHandle != INVALID_HANDLE_VALUE && + GetFileType(hStdHandle) == FILE_TYPE_CHAR) { sprops.stdout_encoding = getConsoleEncoding(TRUE); } - hStdOutErr = GetStdHandle(STD_ERROR_HANDLE); - if (hStdOutErr != INVALID_HANDLE_VALUE && - GetFileType(hStdOutErr) == FILE_TYPE_CHAR) { + hStdHandle = GetStdHandle(STD_ERROR_HANDLE); + if (hStdHandle != INVALID_HANDLE_VALUE && + GetFileType(hStdHandle) == FILE_TYPE_CHAR) { if (sprops.stdout_encoding != NULL) sprops.stderr_encoding = sprops.stdout_encoding; else diff --git a/src/java.base/windows/native/libsyslookup/syslookup.c b/src/java.base/windows/native/libsyslookup/syslookup.c index c7ddce46a79a5..3216632c53d26 100644 --- a/src/java.base/windows/native/libsyslookup/syslookup.c +++ b/src/java.base/windows/native/libsyslookup/syslookup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,10 @@ #include #include +#include "jni_util.h" + +DEF_STATIC_JNI_OnLoad + // Forces generation of inline code on Windows __declspec(dllexport) void* funcs[] = { // stdio.h diff --git a/src/java.desktop/macosx/classes/apple/laf/JRSUIConstants.java b/src/java.desktop/macosx/classes/apple/laf/JRSUIConstants.java index 1e98cb19ba4c2..9da55002b7269 100644 --- a/src/java.desktop/macosx/classes/apple/laf/JRSUIConstants.java +++ b/src/java.desktop/macosx/classes/apple/laf/JRSUIConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, 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 @@ -194,6 +194,14 @@ public static class State extends Property { @Native private static final byte _pressed = 4; public static final State PRESSED = new State(_pressed); @Native private static final byte _pulsed = 5; + /** + * This identifies the default button in a window/dialog. + * The name PULSED has become misleading over time. + * The default button used to continually pulse up until + * Mac OS 10.9, but now there is no pulsing animation. + * We still need this State constant to render default + * buttons correctly, though. + */ public static final State PULSED = new State(_pulsed); @Native private static final byte _rollover = 6; public static final State ROLLOVER = new State(_rollover); diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaButtonBorder.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaButtonBorder.java index 1d11a0a8d96f4..60ceb4893a836 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaButtonBorder.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaButtonBorder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, 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 @@ -202,12 +202,6 @@ public Dynamic(final Dynamic other) { super(other); } - protected State getButtonState(final AbstractButton b, final ButtonModel model) { - final State state = super.getButtonState(b, model); - painter.state.set(state == State.PULSED ? Animating.YES : Animating.NO); - return state; - } - public Insets getContentInsets(final AbstractButton b, final int width, final int height) { final Size size = AquaUtilControlSize.getUserSizeFrom(b); final Widget style = getStyleForSize(b, size, width, height); diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaButtonUI.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaButtonUI.java index 063923ff80754..17818d091217e 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaButtonUI.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaButtonUI.java @@ -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 @@ -58,8 +58,6 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; -import javax.swing.event.AncestorEvent; -import javax.swing.event.AncestorListener; import javax.swing.plaf.ButtonUI; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.RootPaneUI; @@ -225,8 +223,6 @@ protected void installListeners(final AbstractButton b) { // put the listener in the button's client properties so that // we can get at it later b.putClientProperty(this, listener); - - b.addAncestorListener(listener); } installHierListener(b); AquaUtilControlSize.addSizePropertyListener(b); @@ -252,11 +248,7 @@ protected void uninstallKeyboardActions(final AbstractButton b) { protected void uninstallListeners(final AbstractButton b) { super.uninstallListeners(b); - final AquaButtonListener listener = (AquaButtonListener)b.getClientProperty(this); b.putClientProperty(this, null); - if (listener != null) { - b.removeAncestorListener(listener); - } uninstallHierListener(b); AquaUtilControlSize.removeSizePropertyListener(b); } @@ -591,7 +583,7 @@ public void hierarchyChanged(final HierarchyEvent e) { } } - class AquaButtonListener extends BasicButtonListener implements AncestorListener { + class AquaButtonListener extends BasicButtonListener { protected final AbstractButton b; public AquaButtonListener(final AbstractButton b) { @@ -658,27 +650,5 @@ public void propertyChange(final PropertyChangeEvent e) { } } } - - public void ancestorMoved(final AncestorEvent e) {} - - public void ancestorAdded(final AncestorEvent e) { - updateDefaultButton(); - } - - public void ancestorRemoved(final AncestorEvent e) { - updateDefaultButton(); - } - - protected void updateDefaultButton() { - if (!(b instanceof JButton)) return; - if (!((JButton)b).isDefaultButton()) return; - - final JRootPane rootPane = b.getRootPane(); - if (rootPane == null) return; - - final RootPaneUI ui = rootPane.getUI(); - if (!(ui instanceof AquaRootPaneUI)) return; - ((AquaRootPaneUI)ui).updateDefaultButton(rootPane); - } } } diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaRootPaneUI.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaRootPaneUI.java index 2b4e1bb0867f8..6bd472b760523 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaRootPaneUI.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaRootPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, 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 @@ -40,17 +40,10 @@ /** * From AquaRootPaneUI.java * - * The JRootPane manages the default button. There can be only one active rootpane, - * and one default button, so we need only one timer - * * AquaRootPaneUI is a singleton object */ public class AquaRootPaneUI extends BasicRootPaneUI implements AncestorListener, WindowListener, ContainerListener { private static final RecyclableSingleton sRootPaneUI = new RecyclableSingletonFromDefaultConstructor(AquaRootPaneUI.class); - - static final int kDefaultButtonPaintDelayBetweenFrames = 50; - JButton fCurrentDefaultButton = null; - Timer fTimer = null; static final boolean sUseScreenMenuBar = AquaMenuBarUI.getScreenMenuBarProperty(); public static ComponentUI createUI(final JComponent c) { @@ -61,10 +54,6 @@ public void installUI(final JComponent c) { super.installUI(c); c.addAncestorListener(this); - if (c.isShowing() && c.isEnabled()) { - updateDefaultButton((JRootPane)c); - } - // for REGR: Realtime LAF updates no longer work // // because the JFrame parent has a LAF background set (why without a UI element I don't know!) @@ -92,7 +81,6 @@ public void installUI(final JComponent c) { } public void uninstallUI(final JComponent c) { - stopTimer(); c.removeAncestorListener(this); if (sUseScreenMenuBar) { @@ -161,73 +149,6 @@ public void componentRemoved(final ContainerEvent e) { } } - /** - * Invoked when a property changes on the root pane. If the event - * indicates the {@code defaultButton} has changed, this will - * update the animation. - * If the enabled state changed, it will start or stop the animation - */ - public void propertyChange(final PropertyChangeEvent e) { - super.propertyChange(e); - - final String prop = e.getPropertyName(); - if ("defaultButton".equals(prop) || "temporaryDefaultButton".equals(prop)) { - // Change the animating button if this root is showing and enabled - // otherwise do nothing - someone else may be active - final JRootPane root = (JRootPane)e.getSource(); - - if (root.isShowing() && root.isEnabled()) { - updateDefaultButton(root); - } - } else if ("enabled".equals(prop) || AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(prop)) { - final JRootPane root = (JRootPane)e.getSource(); - if (root.isShowing()) { - if (((Boolean)e.getNewValue()).booleanValue()) { - updateDefaultButton((JRootPane)e.getSource()); - } else { - stopTimer(); - } - } - } - } - - synchronized void stopTimer() { - if (fTimer != null) { - fTimer.stop(); - fTimer = null; - } - } - - synchronized void updateDefaultButton(final JRootPane root) { - final JButton button = root.getDefaultButton(); - //System.err.println("in updateDefaultButton button = " + button); - fCurrentDefaultButton = button; - stopTimer(); - if (button != null) { - fTimer = new Timer(kDefaultButtonPaintDelayBetweenFrames, new DefaultButtonPainter(root)); - fTimer.start(); - } - } - - class DefaultButtonPainter implements ActionListener { - JRootPane root; - - public DefaultButtonPainter(final JRootPane root) { - this.root = root; - } - - public void actionPerformed(final ActionEvent e) { - final JButton defaultButton = root.getDefaultButton(); - if ((defaultButton != null) && defaultButton.isShowing()) { - if (defaultButton.isEnabled()) { - defaultButton.repaint(); - } - } else { - stopTimer(); - } - } - } - /** * This is sort of like viewDidMoveToWindow:. When the root pane is put into a window * this method gets called for the notification. @@ -249,18 +170,6 @@ public void ancestorAdded(final AncestorEvent event) { owningWindow.removeWindowListener(this); owningWindow.addWindowListener(this); } - - // The root pane has been added to the hierarchy. If it's enabled update the default - // button to start the throbbing. Since the UI is a singleton make sure the root pane - // we are checking has a default button before calling update otherwise we will stop - // throbbing the current default button. - final JComponent comp = event.getComponent(); - if (comp instanceof JRootPane) { - final JRootPane rp = (JRootPane)comp; - if (rp.isEnabled() && rp.getDefaultButton() != null) { - updateDefaultButton((JRootPane)comp); - } - } } /** diff --git a/src/java.desktop/macosx/classes/sun/awt/CGraphicsConfig.java b/src/java.desktop/macosx/classes/sun/awt/CGraphicsConfig.java index 3c09758e87f3b..0ca8510481704 100644 --- a/src/java.desktop/macosx/classes/sun/awt/CGraphicsConfig.java +++ b/src/java.desktop/macosx/classes/sun/awt/CGraphicsConfig.java @@ -41,7 +41,8 @@ public abstract class CGraphicsConfig extends GraphicsConfiguration private final CGraphicsDevice device; private ColorModel colorModel; - private final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache(); + private final SurfaceManager.ProxyCache surfaceDataProxyCache = + new SurfaceManager.ProxyCache(); protected CGraphicsConfig(CGraphicsDevice device) { this.device = device; diff --git a/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java b/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java index fe4431e309128..e11dabc7a10db 100644 --- a/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java +++ b/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java @@ -37,9 +37,7 @@ import java.util.ListIterator; import java.util.Map; -import sun.java2d.MacosxSurfaceManagerFactory; import sun.java2d.SunGraphicsEnvironment; -import sun.java2d.SurfaceManagerFactory; /** * This is an implementation of a GraphicsEnvironment object for the default @@ -70,8 +68,6 @@ public static void init() { } static { // Load libraries and initialize the Toolkit. Toolkit.getDefaultToolkit(); - // Install the correct surface manager factory. - SurfaceManagerFactory.setInstance(new MacosxSurfaceManagerFactory()); } /** diff --git a/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java b/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java deleted file mode 100644 index 93fac3fb22a49..0000000000000 --- a/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. - * 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.java2d; - -import sun.awt.image.SunVolatileImage; -import sun.awt.image.VolatileSurfaceManager; -import sun.awt.CGraphicsDevice; -import sun.java2d.metal.MTLVolatileSurfaceManager; -import sun.java2d.opengl.CGLVolatileSurfaceManager; - -/** - * This is a factory class with static methods for creating a - * platform-specific instance of a particular SurfaceManager. Each platform - * (Windows, Unix, etc.) has its own specialized SurfaceManagerFactory. - */ -public class MacosxSurfaceManagerFactory extends SurfaceManagerFactory { - - /** - * Creates a new instance of a VolatileSurfaceManager given any - * arbitrary SunVolatileImage. An optional context Object can be supplied - * as a way for the caller to pass pipeline-specific context data to - * the VolatileSurfaceManager (such as a backbuffer handle, for example). - * - * For Mac OS X, this method returns either an CGL/MTL-specific - * VolatileSurfaceManager based on the GraphicsConfiguration - * under which the SunVolatileImage was created. - */ - public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg, - Object context) - { - return CGraphicsDevice.usingMetalPipeline() ? new MTLVolatileSurfaceManager(vImg, context) : - new CGLVolatileSurfaceManager(vImg, context); - } -} diff --git a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java index 82adac72c03b5..e4afec42482ab 100644 --- a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java +++ b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java @@ -29,6 +29,8 @@ import sun.awt.CGraphicsDevice; import sun.awt.image.OffScreenImage; import sun.awt.image.SunVolatileImage; +import sun.awt.image.SurfaceManager; +import sun.awt.image.VolatileSurfaceManager; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; import sun.java2d.Surface; @@ -67,7 +69,7 @@ import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_BIOP_SHADER; public final class MTLGraphicsConfig extends CGraphicsConfig - implements AccelGraphicsConfig + implements AccelGraphicsConfig, SurfaceManager.Factory { private static ImageCapabilities imageCaps = new MTLImageCaps(); @@ -372,4 +374,10 @@ public int getMaxTextureHeight() { return Math.max(maxTextureSize / getDevice().getScaleFactor(), getBounds().height); } + + @Override + public VolatileSurfaceManager createVolatileManager(SunVolatileImage image, + Object context) { + return new MTLVolatileSurfaceManager(image, context); + } } diff --git a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java index ee9b0aec3a009..6022d516bf90f 100644 --- a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java +++ b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java @@ -46,6 +46,8 @@ import sun.awt.CGraphicsDevice; import sun.awt.image.OffScreenImage; import sun.awt.image.SunVolatileImage; +import sun.awt.image.SurfaceManager; +import sun.awt.image.VolatileSurfaceManager; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; import sun.java2d.Surface; @@ -386,4 +388,10 @@ public int getMaxTextureHeight() { return Math.max(maxTextureSize / getDevice().getScaleFactor(), getBounds().height); } + + @Override + public VolatileSurfaceManager createVolatileManager(SunVolatileImage image, + Object context) { + return new CGLVolatileSurfaceManager(image, context); + } } diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java index cb7333c79d62c..1ca94eb3f51c9 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 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 @@ -36,6 +36,7 @@ import javax.print.*; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.standard.Chromaticity; import javax.print.attribute.standard.Copies; import javax.print.attribute.standard.Destination; import javax.print.attribute.standard.Media; @@ -73,6 +74,8 @@ public final class CPrinterJob extends RasterPrinterJob { private Throwable printerAbortExcpn; + private boolean monochrome = false; + // This is the NSPrintInfo for this PrinterJob. Protect multi thread // access to it. It is used by the pageDialog, jobDialog, and printLoop. // This way the state of these items is shared across these calls. @@ -212,6 +215,11 @@ protected void setAttributes(PrintRequestAttributeSet attributes) throws Printer setPageRange(-1, -1); } } + + PrintService service = getPrintService(); + Chromaticity chromaticity = (Chromaticity)attributes.get(Chromaticity.class); + monochrome = chromaticity == Chromaticity.MONOCHROME && service != null && + service.isAttributeCategorySupported(Chromaticity.class); } private void setPageRangeAttribute(int from, int to, boolean isRangeSet) { @@ -788,6 +796,9 @@ private void printToPathGraphics( final PeekGraphics graphics, // Always an a Graphics2D pathGraphics = new CPrinterGraphics(delegate, printerJob); // Just stores delegate into an ivar Rectangle2D pageFormatArea = getPageFormatArea(page); initPrinterGraphics(pathGraphics, pageFormatArea); + if (monochrome) { + pathGraphics = new GrayscaleProxyGraphics2D(pathGraphics, printerJob); + } painter.print(pathGraphics, FlipPageFormat.getOriginal(page), pageIndex); delegate.dispose(); delegate = null; diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index ddd6c8ec35454..8be635a6c495b 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java @@ -735,12 +735,8 @@ public CSS() { valueConvertor.put(CSS.Attribute.BACKGROUND_ATTACHMENT, valueMapper); Object generic = new CssValue(); - int n = CSS.Attribute.allAttributes.length; - for (int i = 0; i < n; i++) { - CSS.Attribute key = CSS.Attribute.allAttributes[i]; - if (valueConvertor.get(key) == null) { - valueConvertor.put(key, generic); - } + for (CSS.Attribute key : CSS.Attribute.allAttributes) { + valueConvertor.putIfAbsent(key, generic); } } diff --git a/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java b/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java index 55dfc8db7662a..4bcd5f6b44313 100644 --- a/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java +++ b/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java @@ -37,7 +37,6 @@ import java.awt.image.ImageObserver; import java.awt.image.VolatileImage; import sun.java2d.SunGraphics2D; -import sun.java2d.SurfaceManagerFactory; import sun.java2d.DestSurfaceProvider; import sun.java2d.Surface; import sun.java2d.pipe.Region; @@ -158,29 +157,19 @@ public int getForcedAccelSurfaceType() { return forcedAccelSurfaceType; } - protected VolatileSurfaceManager createSurfaceManager(Object context, - ImageCapabilities caps) - { - /** - * Platform-specific SurfaceManagerFactories will return a - * manager suited to acceleration on each platform. But if - * the user is asking for a VolatileImage from a BufferedImageGC, - * then we need to return the appropriate unaccelerated manager. - * Note: this could change in the future; if some platform would - * like to accelerate BIGC volatile images, then this special-casing - * of the BIGC graphicsConfig should live in platform-specific - * code instead. - * We do the same for a Printer Device, and if user requested an - * unaccelerated VolatileImage by passing the capabilities object. - */ - if (graphicsConfig instanceof BufferedImageGraphicsConfig || - graphicsConfig instanceof sun.print.PrinterGraphicsConfig || - (caps != null && !caps.isAccelerated())) - { + private VolatileSurfaceManager createSurfaceManager( + Object context, ImageCapabilities caps) { + // GraphicsConfig may provide some specific surface manager + // implementation. + // In case it doesn't, or we were specifically requested to use + // an unaccelerated surface, fall back to the buffered image + // surface manager. + if ((caps == null || caps.isAccelerated()) && + graphicsConfig instanceof SurfaceManager.Factory factory) { + return factory.createVolatileManager(this, context); + } else { return new BufImgVolatileSurfaceManager(this, context); } - SurfaceManagerFactory smf = SurfaceManagerFactory.getInstance(); - return smf.createVolatileManager(this, context); } private Color getForeground() { diff --git a/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java b/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java index 9f605eed4e398..9b86d469d984d 100644 --- a/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java +++ b/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java @@ -183,6 +183,23 @@ public boolean isAccelerated() { } } + /** + * An interface for GraphicsConfiguration objects to implement if + * they create their own VolatileSurfaceManager implementations. + */ + public interface Factory { + + /** + * Creates a new instance of a VolatileSurfaceManager given a + * compatible SunVolatileImage. + * An optional context Object can be supplied as a way for the caller + * to pass pipeline-specific context data to the VolatileSurfaceManager + * (such as a backbuffer handle, for example). + */ + VolatileSurfaceManager createVolatileManager(SunVolatileImage image, + Object context); + } + /** * An interface for GraphicsConfiguration objects to implement if * their surfaces accelerate images using SurfaceDataProxy objects. @@ -201,7 +218,8 @@ public interface ProxiedGraphicsConfig { } public static class ProxyCache { - private final Map map = Collections.synchronizedMap(new WeakHashMap<>()); + private final Map map = + Collections.synchronizedMap(new WeakHashMap<>()); /** * Return a cached SurfaceDataProxy object for a given SurfaceManager. @@ -252,7 +270,8 @@ public synchronized void flush() { void flush(boolean deaccelerate) { synchronized (weakCache) { - Iterator> i = weakCache.values().iterator(); + Iterator> i = + weakCache.values().iterator(); while (i.hasNext()) { SurfaceDataProxy sdp = i.next().get(); if (sdp == null || sdp.flush(deaccelerate)) { diff --git a/src/java.desktop/share/classes/sun/java2d/SurfaceManagerFactory.java b/src/java.desktop/share/classes/sun/java2d/SurfaceManagerFactory.java deleted file mode 100644 index 39b6d2e33cf5b..0000000000000 --- a/src/java.desktop/share/classes/sun/java2d/SurfaceManagerFactory.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. - * 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.java2d; - -import sun.awt.image.SunVolatileImage; -import sun.awt.image.VolatileSurfaceManager; - -/** - * This factory creates platform specific VolatileSurfaceManager - * implementations. - * - * There are two platform specific SurfaceManagerFactories in OpenJDK, - * UnixSurfaceManagerFactory and WindowsSurfaceManagerFactory. - * The actually used SurfaceManagerFactory is set by the respective platform - * GraphicsEnvironment implementations in the static initializer. - */ -public abstract class SurfaceManagerFactory { - - /** - * The single shared instance. - */ - private static SurfaceManagerFactory instance; - - /** - * Returns the surface manager factory instance. This returns a factory - * that has been set by {@link #setInstance(SurfaceManagerFactory)}. - * - * @return the surface manager factory - */ - public static synchronized SurfaceManagerFactory getInstance() { - - if (instance == null) { - throw new IllegalStateException("No SurfaceManagerFactory set."); - } - return instance; - } - - /** - * Sets the surface manager factory. This may only be called once, and it - * may not be set back to {@code null} when the factory is already - * instantiated. - * - * @param factory the factory to set - */ - public static synchronized void setInstance(SurfaceManagerFactory factory) { - - if (factory == null) { - // We don't want to allow setting this to null at any time. - throw new IllegalArgumentException("factory must be non-null"); - } - - if (instance != null) { - // We don't want to re-set the instance at any time. - throw new IllegalStateException("The surface manager factory is already initialized"); - } - - instance = factory; - } - - /** - * Creates a new instance of a VolatileSurfaceManager given any - * arbitrary SunVolatileImage. An optional context Object can be supplied - * as a way for the caller to pass pipeline-specific context data to - * the VolatileSurfaceManager (such as a backbuffer handle, for example). - */ - public abstract VolatileSurfaceManager - createVolatileManager(SunVolatileImage image, Object context); -} diff --git a/src/java.desktop/share/classes/sun/java2d/opengl/OGLGraphicsConfig.java b/src/java.desktop/share/classes/sun/java2d/opengl/OGLGraphicsConfig.java index 252644b7e49f4..a090458801943 100644 --- a/src/java.desktop/share/classes/sun/java2d/opengl/OGLGraphicsConfig.java +++ b/src/java.desktop/share/classes/sun/java2d/opengl/OGLGraphicsConfig.java @@ -35,7 +35,7 @@ * methods directly from OGLSurfaceData. */ interface OGLGraphicsConfig extends - AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig + AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig, SurfaceManager.Factory { OGLContext getContext(); long getNativeConfigInfo(); diff --git a/src/java.desktop/share/classes/sun/print/GrayscaleProxyGraphics2D.java b/src/java.desktop/share/classes/sun/print/GrayscaleProxyGraphics2D.java new file mode 100644 index 0000000000000..25f32181b719e --- /dev/null +++ b/src/java.desktop/share/classes/sun/print/GrayscaleProxyGraphics2D.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, BELLSOFT. All rights reserved. + * 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.print; + + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.LinearGradientPaint; +import java.awt.Paint; +import java.awt.RadialGradientPaint; +import java.awt.TexturePaint; +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.print.PrinterJob; + +/** + * Proxy class to print with grayscale. + * Convert Colors, Paints and Images to the grayscale. + * + */ +public class GrayscaleProxyGraphics2D extends ProxyGraphics2D { + + /** + * The new ProxyGraphics2D will forward all graphics + * calls to 'graphics'. + * + * @param graphics + * @param printerJob + */ + public GrayscaleProxyGraphics2D(Graphics2D graphics, PrinterJob printerJob) { + super(graphics, printerJob); + } + + @Override + public void setBackground(Color color) { + Color gcolor = getGrayscaleColor(color); + super.setBackground(gcolor); + } + + @Override + public void setColor(Color c) { + Color gcolor = getGrayscaleColor(c); + super.setColor(gcolor); + } + + @Override + public void setPaint(Paint paint) { + if (paint instanceof Color color) { + super.setPaint(getGrayscaleColor(color)); + } else if (paint instanceof TexturePaint texturePaint) { + super.setPaint(new TexturePaint(getGrayscaleImage(texturePaint.getImage()), texturePaint.getAnchorRect())); + } else if (paint instanceof GradientPaint gradientPaint) { + super.setPaint(new GradientPaint(gradientPaint.getPoint1(), + getGrayscaleColor(gradientPaint.getColor1()), + gradientPaint.getPoint2(), + getGrayscaleColor(gradientPaint.getColor2()), + gradientPaint.isCyclic())); + } else if (paint instanceof LinearGradientPaint linearGradientPaint) { + Color[] colors = new Color[linearGradientPaint.getColors().length]; + Color[] oldColors = linearGradientPaint.getColors(); + for (int i = 0; i < colors.length; i++) { + colors[i] = getGrayscaleColor(oldColors[i]); + } + super.setPaint(new LinearGradientPaint(linearGradientPaint.getStartPoint(), + linearGradientPaint.getEndPoint(), + linearGradientPaint.getFractions(), + colors, + linearGradientPaint.getCycleMethod(), + linearGradientPaint.getColorSpace(), + linearGradientPaint.getTransform() + )); + } else if (paint instanceof RadialGradientPaint radialGradientPaint) { + Color[] colors = new Color[radialGradientPaint.getColors().length]; + Color[] oldColors = radialGradientPaint.getColors(); + for (int i = 0; i < colors.length; i++) { + colors[i] = getGrayscaleColor(oldColors[i]); + } + super.setPaint(new RadialGradientPaint(radialGradientPaint.getCenterPoint(), + radialGradientPaint.getRadius(), + radialGradientPaint.getFocusPoint(), + radialGradientPaint.getFractions(), + colors, + radialGradientPaint.getCycleMethod(), + radialGradientPaint.getColorSpace(), + radialGradientPaint.getTransform())); + } else if (paint == null) { + super.setPaint(paint); + } else { + throw new IllegalArgumentException("Unsupported Paint"); + } + } + + @Override + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + BufferedImage grayImage = new BufferedImage(img.getWidth() + img.getTileWidth(), + img.getHeight() + img.getTileHeight(), BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g2 = grayImage.createGraphics(); + g2.drawRenderedImage(img, new AffineTransform()); + g2.dispose(); + super.drawRenderedImage(getGrayscaleImage(grayImage), xform); + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, + Color bgcolor, ImageObserver observer) { + return super.drawImage(getGrayscaleImage(img), dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); + } + + @Override + public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, + ImageObserver observer) { + return super.drawImage(getGrayscaleImage(img), dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { + return super.drawImage(getGrayscaleImage(img), x, y, width, height, bgcolor, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { + return super.drawImage(getGrayscaleImage(img), x, y, bgcolor, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { + return super.drawImage(getGrayscaleImage(img), x, y, width, height, observer); + } + + @Override + public boolean drawImage(Image img, int x, int y, ImageObserver observer) { + return super.drawImage(getGrayscaleImage(img), x, y, observer); + } + + @Override + public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { + super.drawImage(getGrayscaleImage(img), op, x, y); + } + + @Override + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { + return super.drawImage(getGrayscaleImage(img), xform, obs); + } + + /** + * Returns grayscale variant of the input Color + * @param color color to transform to grayscale + * @return grayscale color + */ + private Color getGrayscaleColor(Color color) { + if (color == null) { + return null; + } + float[] gcolor = color.getComponents(ColorSpace.getInstance(ColorSpace.CS_GRAY), null); + return switch (gcolor.length) { + case 1 -> new Color(gcolor[0], gcolor[0], gcolor[0]); + case 2 -> new Color(gcolor[0], gcolor[0], gcolor[0], gcolor[1]); + default -> throw new IllegalArgumentException("Unknown grayscale color. " + + "Expected 1 or 2 components, received " + gcolor.length + " components."); + }; + } + + /** + * Converts Image to a grayscale + * @param img colored image + * @return grayscale BufferedImage + */ + private BufferedImage getGrayscaleImage(Image img) { + if (img == null) { + return null; + } + BufferedImage grayImage = new BufferedImage(img.getWidth(null), img.getHeight(null), + BufferedImage.TYPE_BYTE_GRAY); + Graphics grayGraphics = grayImage.getGraphics(); + grayGraphics.drawImage(img, 0, 0, null); + grayGraphics.dispose(); + return grayImage; + } + +} diff --git a/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c b/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c index 3654c677493fa..cbdad61f78e83 100644 --- a/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c +++ b/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, 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 @@ -48,7 +48,7 @@ // restore the area overwritten by the graphic with // what was there prior to rendering the graphic. -static const char szNetscape20ext[11] = "NETSCAPE2.0"; +static const char szNetscape20ext[] = "NETSCAPE2.0"; #define NSEXT_LOOP 0x01 // Loop Count field code @@ -181,7 +181,7 @@ SplashDecodeGif(Splash * splash, GifFileType * gif) } case APPLICATION_EXT_FUNC_CODE: { - if (size == sizeof(szNetscape20ext) + if (size == strlen(szNetscape20ext) && memcmp(pExtension, szNetscape20ext, size) == 0) { int iSubCode; diff --git a/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java b/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java index ba06574cdd708..9c7a7d5c376a1 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java +++ b/src/java.desktop/unix/classes/sun/awt/X11GraphicsConfig.java @@ -47,6 +47,7 @@ import sun.awt.image.OffScreenImage; import sun.awt.image.SunVolatileImage; import sun.awt.image.SurfaceManager; +import sun.awt.image.VolatileSurfaceManager; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; import sun.java2d.SurfaceData; @@ -55,6 +56,7 @@ import sun.java2d.loops.SurfaceType; import sun.java2d.pipe.Region; import sun.java2d.x11.X11SurfaceData; +import sun.java2d.x11.X11VolatileSurfaceManager; /** * This is an implementation of a GraphicsConfiguration object for a @@ -64,7 +66,7 @@ * @see GraphicsDevice */ public class X11GraphicsConfig extends GraphicsConfiguration - implements SurfaceManager.ProxiedGraphicsConfig + implements SurfaceManager.ProxiedGraphicsConfig, SurfaceManager.Factory { private final X11GraphicsDevice device; protected int visual; @@ -500,4 +502,10 @@ public boolean isTranslucencyCapable() { } private native boolean isTranslucencyCapable(long x11ConfigData); + + @Override + public VolatileSurfaceManager createVolatileManager(SunVolatileImage image, + Object context) { + return new X11VolatileSurfaceManager(image, context); + } } diff --git a/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java b/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java index 39ecb13c6a4d0..a7ec9ccc9fd63 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java +++ b/src/java.desktop/unix/classes/sun/awt/X11GraphicsDevice.java @@ -62,7 +62,8 @@ public final class X11GraphicsDevice extends GraphicsDevice * therefore methods, which is using this id should be ready to it. */ private volatile int screen; - Map x11ProxyCacheMap = Collections.synchronizedMap(new HashMap<>()); + Map x11ProxyCacheMap = + Collections.synchronizedMap(new HashMap<>()); private static Boolean xrandrExtSupported; private SunDisplayChanger topLevels = new SunDisplayChanger(); @@ -95,7 +96,8 @@ public int getScreen() { } public SurfaceManager.ProxyCache getProxyCacheFor(SurfaceType st) { - return x11ProxyCacheMap.computeIfAbsent(st, unused -> new SurfaceManager.ProxyCache()); + return x11ProxyCacheMap.computeIfAbsent(st, + unused -> new SurfaceManager.ProxyCache()); } /** diff --git a/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java b/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java index 82490e88a99d2..c24e311017e80 100644 --- a/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java +++ b/src/java.desktop/unix/classes/sun/awt/X11GraphicsEnvironment.java @@ -41,8 +41,6 @@ import sun.awt.X11.XToolkit; import sun.java2d.SunGraphicsEnvironment; -import sun.java2d.SurfaceManagerFactory; -import sun.java2d.UnixSurfaceManagerFactory; import sun.java2d.xr.XRSurfaceData; /** @@ -124,10 +122,6 @@ private static void initStatic() { XRSurfaceData.initXRSurfaceData(); } } - - // Install the correct surface manager factory. - SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory()); - } diff --git a/src/java.desktop/unix/classes/sun/java2d/UnixSurfaceManagerFactory.java b/src/java.desktop/unix/classes/sun/java2d/UnixSurfaceManagerFactory.java deleted file mode 100644 index 5ab78ee39eefd..0000000000000 --- a/src/java.desktop/unix/classes/sun/java2d/UnixSurfaceManagerFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * 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.java2d; - -import java.awt.GraphicsConfiguration; - -import sun.awt.image.SunVolatileImage; -import sun.awt.image.VolatileSurfaceManager; -import sun.java2d.opengl.GLXGraphicsConfig; -import sun.java2d.opengl.GLXVolatileSurfaceManager; -import sun.java2d.x11.X11VolatileSurfaceManager; -import sun.java2d.xr.*; - -/** - * The SurfaceManagerFactory that creates VolatileSurfaceManager - * implementations for the Unix volatile images. - */ -public class UnixSurfaceManagerFactory extends SurfaceManagerFactory { - - /** - * Creates a new instance of a VolatileSurfaceManager given any - * arbitrary SunVolatileImage. An optional context Object can be supplied - * as a way for the caller to pass pipeline-specific context data to - * the VolatileSurfaceManager (such as a backbuffer handle, for example). - * - * For Unix platforms, this method returns either an X11- or a GLX- - * specific VolatileSurfaceManager based on the GraphicsConfiguration - * under which the SunVolatileImage was created. - */ - public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg, - Object context) - { - GraphicsConfiguration gc = vImg.getGraphicsConfig(); - - if (gc instanceof GLXGraphicsConfig) { - return new GLXVolatileSurfaceManager(vImg, context); - } else if(gc instanceof XRGraphicsConfig) { - return new XRVolatileSurfaceManager(vImg, context); - }else { - return new X11VolatileSurfaceManager(vImg, context); - } - } - -} diff --git a/src/java.desktop/unix/classes/sun/java2d/opengl/GLXGraphicsConfig.java b/src/java.desktop/unix/classes/sun/java2d/opengl/GLXGraphicsConfig.java index 96b0dc491830c..2ea076f805166 100644 --- a/src/java.desktop/unix/classes/sun/java2d/opengl/GLXGraphicsConfig.java +++ b/src/java.desktop/unix/classes/sun/java2d/opengl/GLXGraphicsConfig.java @@ -49,6 +49,7 @@ import sun.awt.image.OffScreenImage; import sun.awt.image.SunVolatileImage; import sun.awt.image.SurfaceManager; +import sun.awt.image.VolatileSurfaceManager; import sun.java2d.SunGraphics2D; import sun.java2d.Surface; import sun.java2d.SurfaceData; @@ -72,7 +73,8 @@ public final class GLXGraphicsConfig private long pConfigInfo; private ContextCapabilities oglCaps; private final OGLContext context; - private final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache(); + private final SurfaceManager.ProxyCache surfaceDataProxyCache = + new SurfaceManager.ProxyCache(); private static native long getGLXConfigInfo(int screennum, int visualnum); private static native int getOGLCapabilities(long configInfo); @@ -413,4 +415,10 @@ public ImageCapabilities getImageCapabilities() { public ContextCapabilities getContextCapabilities() { return oglCaps; } + + @Override + public VolatileSurfaceManager createVolatileManager(SunVolatileImage image, + Object context) { + return new GLXVolatileSurfaceManager(image, context); + } } diff --git a/src/java.desktop/unix/classes/sun/java2d/xr/XRGraphicsConfig.java b/src/java.desktop/unix/classes/sun/java2d/xr/XRGraphicsConfig.java index fb2c8ad480108..43f17e11ae122 100644 --- a/src/java.desktop/unix/classes/sun/java2d/xr/XRGraphicsConfig.java +++ b/src/java.desktop/unix/classes/sun/java2d/xr/XRGraphicsConfig.java @@ -30,11 +30,14 @@ import sun.awt.X11GraphicsDevice; import sun.awt.X11GraphicsEnvironment; import sun.awt.image.SurfaceManager; +import sun.awt.image.SunVolatileImage; +import sun.awt.image.VolatileSurfaceManager; import sun.java2d.SurfaceData; public class XRGraphicsConfig extends X11GraphicsConfig implements SurfaceManager.ProxiedGraphicsConfig { - private final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache(); + private final SurfaceManager.ProxyCache surfaceDataProxyCache = + new SurfaceManager.ProxyCache(); private XRGraphicsConfig(X11GraphicsDevice device, int visualnum, int depth, int colormap, boolean doubleBuffer) { @@ -59,4 +62,10 @@ public static XRGraphicsConfig getConfig(X11GraphicsDevice device, public SurfaceManager.ProxyCache getSurfaceDataProxyCache() { return surfaceDataProxyCache; } + + @Override + public VolatileSurfaceManager createVolatileManager(SunVolatileImage image, + Object context) { + return new XRVolatileSurfaceManager(image, context); + } } diff --git a/src/java.desktop/unix/classes/sun/print/IPPPrintService.java b/src/java.desktop/unix/classes/sun/print/IPPPrintService.java index 53089401e33ee..96fcdef3a529f 100644 --- a/src/java.desktop/unix/classes/sun/print/IPPPrintService.java +++ b/src/java.desktop/unix/classes/sun/print/IPPPrintService.java @@ -572,8 +572,15 @@ public DocPrintJob createPrintJob() { flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) || !isIPPSupportedImages(flavor.getMimeType())) { - Chromaticity[]arr = new Chromaticity[1]; - arr[0] = Chromaticity.COLOR; + Chromaticity[] arr; + if (PrintServiceLookupProvider.isMac()) { + arr = new Chromaticity[2]; + arr[0] = Chromaticity.COLOR; + arr[1] = Chromaticity.MONOCHROME; + } else { + arr = new Chromaticity[1]; + arr[0] = Chromaticity.COLOR; + } return (arr); } else { return null; @@ -1400,7 +1407,7 @@ public boolean isAttributeValueSupported(Attribute attr, flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE) || flavor.equals(DocFlavor.SERVICE_FORMATTED.PRINTABLE) || !isIPPSupportedImages(flavor.getMimeType())) { - return attr == Chromaticity.COLOR; + return PrintServiceLookupProvider.isMac() || attr == Chromaticity.COLOR; } else { return false; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java index a71b3b8367f5c..a91e1a97ee1ea 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java @@ -62,7 +62,7 @@ * * @author Igor Kushnirskiy */ -class AnimationController implements ActionListener, PropertyChangeListener { +final class AnimationController implements ActionListener, PropertyChangeListener { private static final boolean VISTA_ANIMATION_DISABLED = Boolean.getBoolean("swing.disablevistaanimation"); @@ -253,6 +253,7 @@ static void paintSkin(JComponent component, Skin skin, } } + @Override public synchronized void propertyChange(PropertyChangeEvent e) { if ("lookAndFeel" == e.getPropertyName() && ! (e.getNewValue() instanceof WindowsLookAndFeel) ) { @@ -260,6 +261,7 @@ public synchronized void propertyChange(PropertyChangeEvent e) { } } + @Override public synchronized void actionPerformed(ActionEvent e) { java.util.List componentsToRemove = null; java.util.List partsToRemove = null; @@ -319,7 +321,7 @@ private synchronized void dispose() { } } - private static class AnimationState { + private static final class AnimationState { private final State startState; //animation duration in nanoseconds @@ -407,7 +409,7 @@ boolean isDone() { } } - private static class PartUIClientPropertyKey + private static final class PartUIClientPropertyKey implements UIClientPropertyKey { private static final Map map = @@ -426,6 +428,7 @@ static synchronized PartUIClientPropertyKey getKey(Part part) { private PartUIClientPropertyKey(Part part) { this.part = part; } + @Override public String toString() { return part.toString(); } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/TMSchema.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/TMSchema.java index 90c7f79704310..93628d8576600 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/TMSchema.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/TMSchema.java @@ -221,6 +221,7 @@ public String getControlName(Component component) { return str + control.toString(); } + @Override public String toString() { return control.toString()+"."+name(); } @@ -531,6 +532,7 @@ public int getValue() { return value; } + @Override public String toString() { return name()+"["+type.getName()+"] = "+value; } @@ -559,6 +561,7 @@ private TypeEnum(Prop prop, String enumName, int value) { private final String enumName; private final int value; + @Override public String toString() { return prop+"="+enumName+"="+value; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java index 1e10ccce1bf5a..31a490bebccc7 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java @@ -43,7 +43,7 @@ * @author Rich Schiavi */ -public class WindowsBorders { +public final class WindowsBorders { /** * Returns a border instance for a Windows Progress Bar @@ -115,7 +115,7 @@ public static Border getInternalFrameBorder() { } @SuppressWarnings("serial") // Superclass is not serializable across versions - public static class ProgressBarBorder extends AbstractBorder implements UIResource { + public static final class ProgressBarBorder extends AbstractBorder implements UIResource { protected Color shadow; protected Color highlight; @@ -124,6 +124,7 @@ public ProgressBarBorder(Color shadow, Color highlight) { this.shadow = shadow; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { g.setColor(shadow); @@ -134,6 +135,7 @@ public void paintBorder(Component c, Graphics g, int x, int y, g.drawLine(width-1,y, width-1,height-1); // draw right } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(1,1,1,1); return insets; @@ -146,7 +148,7 @@ public Insets getBorderInsets(Component c, Insets insets) { * @since 1.4 */ @SuppressWarnings("serial") // Superclass is not serializable across versions - public static class ToolBarBorder extends AbstractBorder implements UIResource, SwingConstants { + public static final class ToolBarBorder extends AbstractBorder implements UIResource, SwingConstants { protected Color shadow; protected Color highlight; @@ -155,6 +157,7 @@ public ToolBarBorder(Color shadow, Color highlight) { this.shadow = shadow; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (!(c instanceof JToolBar)) { @@ -224,6 +227,7 @@ public void paintBorder(Component c, Graphics g, int x, int y, g.translate(-x, -y); } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(1,1,1,1); if (!(c instanceof JToolBar)) { @@ -259,6 +263,7 @@ public DashedBorder(Color color, int thickness) { super(color, thickness); } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { Color oldColor = g.getColor(); int i; @@ -276,7 +281,7 @@ public void paintBorder(Component c, Graphics g, int x, int y, int width, int he * of the component's background color. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - static class ComplementDashedBorder extends LineBorder implements UIResource { + static final class ComplementDashedBorder extends LineBorder implements UIResource { private Color origColor; private Color paintColor; @@ -284,6 +289,7 @@ public ComplementDashedBorder() { super(null); } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { Color color = c.getBackground(); @@ -302,7 +308,7 @@ public void paintBorder(Component c, Graphics g, int x, int y, int width, int he * @since 1.4 */ @SuppressWarnings("serial") // Superclass is not serializable across versions - public static class InternalFrameLineBorder extends LineBorder implements + public static final class InternalFrameLineBorder extends LineBorder implements UIResource { protected Color activeColor; protected Color inactiveColor; @@ -315,6 +321,7 @@ public InternalFrameLineBorder(Color activeBorderColor, inactiveColor = inactiveBorderColor; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java index b1f7f3902e2cd..33dea2b3b082d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java @@ -58,7 +58,7 @@ * * @author Jeff Dinkins */ -public class WindowsButtonUI extends BasicButtonUI +public final class WindowsButtonUI extends BasicButtonUI { protected int dashedRectGapX; protected int dashedRectGapY; @@ -89,6 +89,7 @@ public static ComponentUI createUI(JComponent c) { // ******************************** // Defaults // ******************************** + @Override protected void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -108,6 +109,7 @@ protected void installDefaults(AbstractButton b) { } } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -124,10 +126,12 @@ protected Color getFocusColor() { /** * Overridden method to render the text without the mnemonic */ + @Override protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) { WindowsGraphicsUtils.paintText(g, b, textRect, text, getTextShiftOffset()); } + @Override protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, Rectangle textRect, Rectangle iconRect){ // focus painted same color as text on Basic?? @@ -138,6 +142,7 @@ protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, Rect width - dashedRectGapWidth, height - dashedRectGapHeight); } + @Override protected void paintButtonPressed(Graphics g, AbstractButton b){ setTextShiftOffset(); } @@ -145,6 +150,7 @@ protected void paintButtonPressed(Graphics g, AbstractButton b){ // ******************************** // Layout Methods // ******************************** + @Override public Dimension getPreferredSize(JComponent c) { Dimension d = super.getPreferredSize(c); @@ -167,6 +173,7 @@ public Dimension getPreferredSize(JComponent c) { */ private Rectangle viewRect = new Rectangle(); + @Override public void paint(Graphics g, JComponent c) { if (XPStyle.getXP() != null) { WindowsButtonUI.paintXPButtonBackground(g, c); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java index c7007bec3db8e..6b85a88e50caf 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java @@ -41,11 +41,12 @@ /** * Windows check box menu item. */ -public class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { +public final class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { + @Override public JMenuItem getMenuItem() { return menuItem; } @@ -54,6 +55,7 @@ public State getState(JMenuItem menuItem) { return WindowsMenuItemUI.getState(this, menuItem); } + @Override public Part getPart(JMenuItem menuItem) { return WindowsMenuItemUI.getPart(this, menuItem); } @@ -80,6 +82,7 @@ protected void paintBackground(Graphics g, JMenuItem menuItem, * @param text String to render * @since 1.4 */ + @Override protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) { if (WindowsMenuItemUI.isVistaPainting()) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java index f94b58c56107d..3ead1228b0e6f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java @@ -37,7 +37,7 @@ * * @author Jeff Dinkins */ -public class WindowsCheckBoxUI extends WindowsRadioButtonUI +public final class WindowsCheckBoxUI extends WindowsRadioButtonUI { // NOTE: WindowsCheckBoxUI inherits from WindowsRadioButtonUI instead // of BasicCheckBoxUI because we want to pick up all the @@ -64,6 +64,7 @@ public static ComponentUI createUI(JComponent c) { } + @Override public String getPropertyPrefix() { return propertyPrefix; } @@ -71,6 +72,7 @@ public String getPropertyPrefix() { // ******************************** // Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -79,6 +81,7 @@ public void installDefaults(AbstractButton b) { } } + @Override public void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java index 6a2c9eeae3181..59eace01a4c8f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java @@ -31,7 +31,8 @@ * @since 1.5 */ @SuppressWarnings("serial") // Superclass is not serializable across versions -public class WindowsClassicLookAndFeel extends WindowsLookAndFeel { +public final class WindowsClassicLookAndFeel extends WindowsLookAndFeel { + @Override public String getName() { return "Windows Classic"; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java index 1a601f40332b9..f37ce17d87632 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java @@ -75,7 +75,7 @@ * @author Tom Santos * @author Igor Kushnirskiy */ -public class WindowsComboBoxUI extends BasicComboBoxUI { +public final class WindowsComboBoxUI extends BasicComboBoxUI { private static final MouseListener rolloverListener = new MouseAdapter() { @@ -162,6 +162,7 @@ public static ComponentUI createUI(JComponent c) { return new WindowsComboBoxUI(); } + @Override public void installUI( JComponent c ) { super.installUI( c ); isRollover = false; @@ -176,6 +177,7 @@ public void installUI( JComponent c ) { } } + @Override public void uninstallUI(JComponent c ) { comboBox.removeMouseListener(rolloverListener); if(arrowButton != null) { @@ -215,6 +217,7 @@ protected void uninstallListeners() { * {@inheritDoc} * @since 1.6 */ + @Override protected void configureEditor() { super.configureEditor(); if (XPStyle.getXP() != null) { @@ -226,6 +229,7 @@ protected void configureEditor() { * {@inheritDoc} * @since 1.6 */ + @Override protected void unconfigureEditor() { super.unconfigureEditor(); editor.removeMouseListener(rolloverListener); @@ -235,6 +239,7 @@ protected void unconfigureEditor() { * {@inheritDoc} * @since 1.6 */ + @Override public void paint(Graphics g, JComponent c) { if (XPStyle.getXP() != null) { paintXPComboBoxBackground(g, c); @@ -283,6 +288,7 @@ private void paintXPComboBoxBackground(Graphics g, JComponent c) { * @throws NullPointerException if any of the arguments are null. * @since 1.5 */ + @Override public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus) { XPStyle xp = XPStyle.getXP(); @@ -347,6 +353,7 @@ public void paintCurrentValue(Graphics g, Rectangle bounds, * {@inheritDoc} * @since 1.6 */ + @Override public void paintCurrentValueBackground(Graphics g, Rectangle bounds, boolean hasFocus) { if (XPStyle.getXP() == null) { @@ -354,6 +361,7 @@ public void paintCurrentValueBackground(Graphics g, Rectangle bounds, } } + @Override public Dimension getMinimumSize( JComponent c ) { Dimension d = super.getMinimumSize(c); if (XPStyle.getXP() != null) { @@ -380,6 +388,7 @@ public Dimension getMinimumSize( JComponent c ) { * * @return an instance of a layout manager */ + @Override protected LayoutManager createLayoutManager() { return new BasicComboBoxUI.ComboBoxLayoutManager() { public void layoutContainer(Container parent) { @@ -407,10 +416,12 @@ public void layoutContainer(Container parent) { }; } + @Override protected void installKeyboardActions() { super.installKeyboardActions(); } + @Override protected ComboPopup createPopup() { return new WinComboPopUp(comboBox); } @@ -423,6 +434,7 @@ protected ComboPopup createPopup() { * @return a ComboBoxEditor used for the combo box * @see javax.swing.JComboBox#setEditor */ + @Override protected ComboBoxEditor createEditor() { return new WindowsComboBoxEditor(); } @@ -447,6 +459,7 @@ protected ListCellRenderer createRenderer() { * * @return a button which represents the popup control */ + @Override protected JButton createArrowButton() { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -457,7 +470,7 @@ protected JButton createArrowButton() { } @SuppressWarnings("serial") // Superclass is not serializable across versions - private class XPComboBoxButton extends XPStyle.GlyphButton { + private final class XPComboBoxButton extends XPStyle.GlyphButton { private State prevState = null; public XPComboBoxButton(XPStyle xp) { @@ -504,6 +517,7 @@ protected State getState() { return rv; } + @Override public Dimension getPreferredSize() { return new Dimension(17, 21); } @@ -518,7 +532,7 @@ WindowsComboBoxUI getWindowsComboBoxUI() { } @SuppressWarnings("serial") // Same-version serialization only - protected class WinComboPopUp extends BasicComboPopup { + protected final class WinComboPopUp extends BasicComboPopup { private Skin listBoxBorder = null; private XPStyle xp; @@ -531,16 +545,18 @@ public WinComboPopUp(JComboBox combo) { } } + @Override protected KeyListener createKeyListener() { return new InvocationKeyHandler(); } - protected class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler { + protected final class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler { protected InvocationKeyHandler() { WinComboPopUp.this.super(); } } + @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (this.listBoxBorder != null) { @@ -554,13 +570,14 @@ protected void paintComponent(Graphics g) { /** * Subclassed to highlight selected item in an editable combo box. */ - public static class WindowsComboBoxEditor + public static final class WindowsComboBoxEditor extends BasicComboBoxEditor.UIResource { /** * {@inheritDoc} * @since 1.6 */ + @Override protected JTextField createEditorComponent() { JTextField editor = super.createEditorComponent(); Border border = (Border)UIManager.get("ComboBox.editorBorder"); @@ -572,6 +589,7 @@ protected JTextField createEditorComponent() { return editor; } + @Override public void setItem(Object item) { super.setItem(item); Object focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); @@ -586,14 +604,14 @@ public void setItem(Object item) { * and to show border for focused cells. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - private static class WindowsComboBoxRenderer + private static final class WindowsComboBoxRenderer extends BasicComboBoxRenderer.UIResource { private static final Object BORDER_KEY = new StringUIClientPropertyKey("BORDER_KEY"); private static final Border NULL_BORDER = new EmptyBorder(0, 0, 0, 0); // Create own version of DashedBorder with more space on left side - private static class WindowsComboBoxDashedBorder extends DashedBorder { + private static final class WindowsComboBoxDashedBorder extends DashedBorder { public WindowsComboBoxDashedBorder(Color color, int thickness) { super(color, thickness); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java index 564ae5b9f712e..8da4bd7921b8e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java @@ -36,18 +36,20 @@ /** * Windows icon for a minimized window on the desktop. */ -public class WindowsDesktopIconUI extends BasicDesktopIconUI { +public final class WindowsDesktopIconUI extends BasicDesktopIconUI { private int width; public static ComponentUI createUI(JComponent c) { return new WindowsDesktopIconUI(); } + @Override public void installDefaults() { super.installDefaults(); width = UIManager.getInt("DesktopIcon.width"); } + @Override public void installUI(JComponent c) { super.installUI(c); @@ -55,6 +57,7 @@ public void installUI(JComponent c) { } // Uninstall the listeners added by the WindowsInternalFrameTitlePane + @Override public void uninstallUI(JComponent c) { WindowsInternalFrameTitlePane thePane = (WindowsInternalFrameTitlePane)iconPane; @@ -62,6 +65,7 @@ public void uninstallUI(JComponent c) { thePane.uninstallListeners(); } + @Override protected void installComponents() { iconPane = new WindowsInternalFrameTitlePane(frame); desktopIcon.setLayout(new BorderLayout()); @@ -72,6 +76,7 @@ protected void installComponents() { } } + @Override public Dimension getPreferredSize(JComponent c) { // Windows desktop icons can not be resized. Therefore, we should // always return the minimum size of the desktop icon. See @@ -83,6 +88,7 @@ public Dimension getPreferredSize(JComponent c) { * Windows desktop icons are restricted to a width of 160 pixels by * default. This value is retrieved by the DesktopIcon.width property. */ + @Override public Dimension getMinimumSize(JComponent c) { Dimension dim = super.getMinimumSize(c); dim.width = width; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java index 82708f571e5dc..355f70b46071d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java @@ -52,7 +52,7 @@ * @author Thomas Ball */ @SuppressWarnings("serial") // JDK-implementation class -public class WindowsDesktopManager extends DefaultDesktopManager +public final class WindowsDesktopManager extends DefaultDesktopManager implements java.io.Serializable, javax.swing.plaf.UIResource { /* The frame which is currently selected/activated. @@ -60,6 +60,7 @@ public class WindowsDesktopManager extends DefaultDesktopManager */ private WeakReference currentFrameRef; + @Override public void activateFrame(JInternalFrame f) { JInternalFrame currentFrame = currentFrameRef != null ? currentFrameRef.get() : null; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java index 3ceea2d31dca4..49ab809dddd19 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java @@ -34,12 +34,13 @@ * * @author David Kloba */ -public class WindowsDesktopPaneUI extends BasicDesktopPaneUI +public final class WindowsDesktopPaneUI extends BasicDesktopPaneUI { public static ComponentUI createUI(JComponent c) { return new WindowsDesktopPaneUI(); } + @Override protected void installDesktopManager() { desktopManager = desktop.getDesktopManager(); if(desktopManager == null) { @@ -48,10 +49,12 @@ protected void installDesktopManager() { } } + @Override protected void installDefaults() { super.installDefaults(); } + @Override @SuppressWarnings("deprecation") protected void installKeyboardActions() { super.installKeyboardActions(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java index 2f5c45633d4c5..abccb6b9a4816 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java @@ -33,7 +33,7 @@ /** * Windows rendition of the component. */ -public class WindowsEditorPaneUI extends BasicEditorPaneUI +public final class WindowsEditorPaneUI extends BasicEditorPaneUI { /** @@ -54,6 +54,7 @@ public static ComponentUI createUI(JComponent c) { * * @return the caret object */ + @Override protected Caret createCaret() { return new WindowsTextUI.WindowsCaret(); } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java index 7065e3db19ba6..37d45bbf90220 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java @@ -101,7 +101,7 @@ * * @author Jeff Dinkins */ -public class WindowsFileChooserUI extends BasicFileChooserUI { +public final class WindowsFileChooserUI extends BasicFileChooserUI { // The following are private because the implementation of the // Windows FileChooser L&F is not complete yet. @@ -194,61 +194,75 @@ public WindowsFileChooserUI(JFileChooser filechooser) { super(filechooser); } + @Override public void installUI(JComponent c) { super.installUI(c); } + @Override public void uninstallComponents(JFileChooser fc) { fc.removeAll(); } - private class WindowsFileChooserUIAccessor implements FilePane.FileChooserUIAccessor { + private final class WindowsFileChooserUIAccessor implements FilePane.FileChooserUIAccessor { + @Override public JFileChooser getFileChooser() { return WindowsFileChooserUI.this.getFileChooser(); } + @Override public BasicDirectoryModel getModel() { return WindowsFileChooserUI.this.getModel(); } + @Override public JPanel createList() { return WindowsFileChooserUI.this.createList(getFileChooser()); } + @Override public JPanel createDetailsView() { return WindowsFileChooserUI.this.createDetailsView(getFileChooser()); } + @Override public boolean isDirectorySelected() { return WindowsFileChooserUI.this.isDirectorySelected(); } + @Override public File getDirectory() { return WindowsFileChooserUI.this.getDirectory(); } + @Override public Action getChangeToParentDirectoryAction() { return WindowsFileChooserUI.this.getChangeToParentDirectoryAction(); } + @Override public Action getApproveSelectionAction() { return WindowsFileChooserUI.this.getApproveSelectionAction(); } + @Override public Action getNewFolderAction() { return WindowsFileChooserUI.this.getNewFolderAction(); } + @Override public MouseListener createDoubleClickListener(JList list) { return WindowsFileChooserUI.this.createDoubleClickListener(getFileChooser(), list); } + @Override public ListSelectionListener createListSelectionListener() { return WindowsFileChooserUI.this.createListSelectionListener(getFileChooser()); } } + @Override public void installComponents(JFileChooser fc) { filePane = new FilePane(new WindowsFileChooserUIAccessor()); fc.addPropertyChangeListener(filePane); @@ -584,6 +598,7 @@ protected JPanel getBottomPanel() { return bottomPanel; } + @Override protected void installStrings(JFileChooser fc) { super.installStrings(fc); @@ -615,6 +630,7 @@ private Integer getMnemonic(String key, Locale l) { return SwingUtilities2.getUIDefaultsInt(key, l); } + @Override protected void installListeners(JFileChooser fc) { super.installListeners(fc); ActionMap actionMap = getActionMap(); @@ -645,10 +661,12 @@ protected JPanel createDetailsView(JFileChooser fc) { * @param fc a JFileChooser * @return a ListSelectionListener */ + @Override public ListSelectionListener createListSelectionListener(JFileChooser fc) { return super.createListSelectionListener(fc); } + @Override public void uninstallUI(JComponent c) { // Remove listeners c.removePropertyChangeListener(filterComboBoxModel); @@ -859,6 +877,7 @@ private void doControlButtonsChanged(PropertyChangeEvent e) { * Listen for filechooser property changes, such as * the selected file changing, or the type of the dialog changing. */ + @Override public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { return new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { @@ -913,14 +932,17 @@ protected void addControlButtons() { getBottomPanel().add(getButtonPanel()); } + @Override public void ensureFileIsVisible(JFileChooser fc, File f) { filePane.ensureFileIsVisible(fc, f); } + @Override public void rescanCurrentDirectory(JFileChooser fc) { filePane.rescanCurrentDirectory(); } + @Override public String getFileName() { if(filenameTextField != null) { return filenameTextField.getText(); @@ -929,6 +951,7 @@ public String getFileName() { } } + @Override public void setFileName(String filename) { if(filenameTextField != null) { filenameTextField.setText(filename); @@ -942,6 +965,7 @@ public void setFileName(String filename) { * @param directorySelected if a directory is currently selected. * @since 1.4 */ + @Override protected void setDirectorySelected(boolean directorySelected) { super.setDirectorySelected(directorySelected); JFileChooser chooser = getFileChooser(); @@ -956,11 +980,13 @@ protected void setDirectorySelected(boolean directorySelected) { } } + @Override public String getDirectoryName() { // PENDING(jeff) - get the name from the directory combobox return null; } + @Override public void setDirectoryName(String dirname) { // PENDING(jeff) - set the name in the directory combobox } @@ -1032,8 +1058,9 @@ public void focusLost(FocusEvent e) { // Renderer for DirectoryComboBox // @SuppressWarnings("serial") // Superclass is not serializable across versions - class DirectoryComboBoxRenderer extends DefaultListCellRenderer { + final class DirectoryComboBoxRenderer extends DefaultListCellRenderer { IndentIcon ii = new IndentIcon(); + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -1056,11 +1083,12 @@ public Component getListCellRendererComponent(JList list, Object value, } static final int space = 10; - static class IndentIcon implements Icon { + static final class IndentIcon implements Icon { Icon icon = null; int depth = 0; + @Override public void paintIcon(Component c, Graphics g, int x, int y) { if (c.getComponentOrientation().isLeftToRight()) { icon.paintIcon(c, g, x+depth*space, y); @@ -1069,10 +1097,12 @@ public void paintIcon(Component c, Graphics g, int x, int y) { } } + @Override public int getIconWidth() { return icon.getIconWidth() + depth*space; } + @Override public int getIconHeight() { return icon.getIconHeight(); } @@ -1090,7 +1120,7 @@ protected DirectoryComboBoxModel createDirectoryComboBoxModel(JFileChooser fc) { * Data model for a type-face selection combo-box. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { + protected final class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { Vector directories = new Vector(); int[] depths = null; File selectedDirectory = null; @@ -1187,19 +1217,23 @@ public int getDepth(int i) { return (depths != null && i >= 0 && i < depths.length) ? depths[i] : 0; } + @Override public void setSelectedItem(Object selectedDirectory) { this.selectedDirectory = (File)selectedDirectory; fireContentsChanged(this, -1, -1); } + @Override public Object getSelectedItem() { return selectedDirectory; } + @Override public int getSize() { return directories.size(); } + @Override public File getElementAt(int index) { return directories.elementAt(index); } @@ -1216,7 +1250,8 @@ protected FilterComboBoxRenderer createFilterComboBoxRenderer() { * Render different type sizes and styles. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - public class FilterComboBoxRenderer extends DefaultListCellRenderer { + public final class FilterComboBoxRenderer extends DefaultListCellRenderer { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -1242,7 +1277,7 @@ protected FilterComboBoxModel createFilterComboBoxModel() { * Data model for a type-face selection combo-box. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, + protected final class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener { protected FileFilter[] filters; protected FilterComboBoxModel() { @@ -1250,6 +1285,7 @@ protected FilterComboBoxModel() { filters = getFileChooser().getChoosableFileFilters(); } + @Override public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if(prop == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) { @@ -1260,6 +1296,7 @@ public void propertyChange(PropertyChangeEvent e) { } } + @Override public void setSelectedItem(Object filter) { if(filter != null) { getFileChooser().setFileFilter((FileFilter) filter); @@ -1267,6 +1304,7 @@ public void setSelectedItem(Object filter) { } } + @Override public Object getSelectedItem() { // Ensure that the current filter is in the list. // NOTE: we shouldn't have to do this, since JFileChooser adds @@ -1288,6 +1326,7 @@ public Object getSelectedItem() { return getFileChooser().getFileFilter(); } + @Override public int getSize() { if(filters != null) { return filters.length; @@ -1296,6 +1335,7 @@ public int getSize() { } } + @Override public FileFilter getElementAt(int index) { if(index > getSize() - 1) { // This shouldn't happen. Try to recover gracefully. @@ -1320,21 +1360,24 @@ public void valueChanged(ListSelectionEvent e) { /** * Acts when DirectoryComboBox has changed the selected item. */ - protected class DirectoryComboBoxAction implements ActionListener { + protected final class DirectoryComboBoxAction implements ActionListener { + @Override public void actionPerformed(ActionEvent e) { File f = (File)directoryComboBox.getSelectedItem(); getFileChooser().setCurrentDirectory(f); } } + @Override protected JButton getApproveButton(JFileChooser fc) { return approveButton; } + @Override public FileView getFileView(JFileChooser fc) { return fileView; } @@ -1342,9 +1385,10 @@ public FileView getFileView(JFileChooser fc) { // *********************** // * FileView operations * // *********************** - protected class WindowsFileView extends BasicFileView { + protected final class WindowsFileView extends BasicFileView { /* FileView type descriptions */ + @Override public Icon getIcon(File f) { Icon icon = getCachedIcon(f); if (icon != null) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsGraphicsUtils.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsGraphicsUtils.java index cb4b5caa86de9..b5859b08ea8ad 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsGraphicsUtils.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsGraphicsUtils.java @@ -58,7 +58,7 @@ * @author Mark Davidson * @since 1.4 */ -public class WindowsGraphicsUtils { +public final class WindowsGraphicsUtils { /** * Renders a text String in Windows without the mnemonic. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java index 072ff606b5b78..cf2fd119423ad 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java @@ -63,7 +63,7 @@ * @author Rich Schiavi */ @SuppressWarnings("serial") // Same-version serialization only -public class WindowsIconFactory implements Serializable +public final class WindowsIconFactory implements Serializable { private static Icon frame_closeIcon; private static Icon frame_iconifyIcon; @@ -173,13 +173,14 @@ public static Icon createFrameResizeIcon() { @SuppressWarnings("serial") // Same-version serialization only - private static class FrameButtonIcon implements Icon, Serializable { + private static final class FrameButtonIcon implements Icon, Serializable { private final Part part; private FrameButtonIcon(Part part) { this.part = part; } + @Override public void paintIcon(Component c, Graphics g, int x0, int y0) { int width = getIconWidth(); int height = getIconHeight(); @@ -281,6 +282,7 @@ public void paintIcon(Component c, Graphics g, int x0, int y0) { } } + @Override public int getIconWidth() { int width; if (XPStyle.getXP() != null) { @@ -293,6 +295,7 @@ public int getIconWidth() { return width; } + @Override public int getIconHeight() { int height = UIManager.getInt("InternalFrame.titleButtonHeight")-4; return height; @@ -302,7 +305,8 @@ public int getIconHeight() { @SuppressWarnings("serial") // Same-version serialization only - private static class ResizeIcon implements Icon, Serializable { + private static final class ResizeIcon implements Icon, Serializable { + @Override public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(UIManager.getColor("InternalFrame.resizeIconHighlight")); g.drawLine(0, 11, 11, 0); @@ -317,14 +321,17 @@ public void paintIcon(Component c, Graphics g, int x, int y) { g.drawLine(9, 11, 11, 9); g.drawLine(10, 11, 11, 10); } + @Override public int getIconWidth() { return 13; } + @Override public int getIconHeight() { return 13; } } @SuppressWarnings("serial") // Same-version serialization only - private static class CheckBoxIcon implements Icon, Serializable + private static final class CheckBoxIcon implements Icon, Serializable { static final int csize = 13; + @Override public void paintIcon(Component c, Graphics g, int x, int y) { JCheckBox cb = (JCheckBox) c; ButtonModel model = cb.getModel(); @@ -425,6 +432,7 @@ public void paintIcon(Component c, Graphics g, int x, int y) { } } + @Override public int getIconWidth() { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -434,6 +442,7 @@ public int getIconWidth() { } } + @Override public int getIconHeight() { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -445,8 +454,9 @@ public int getIconHeight() { } @SuppressWarnings("serial") // Same-version serialization only - private static class RadioButtonIcon implements Icon, UIResource, Serializable + private static final class RadioButtonIcon implements Icon, UIResource, Serializable { + @Override public void paintIcon(Component c, Graphics g, int x, int y) { AbstractButton b = (AbstractButton) c; ButtonModel model = b.getModel(); @@ -579,6 +589,7 @@ public void paintIcon(Component c, Graphics g, int x, int y) { } } + @Override public int getIconWidth() { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -588,6 +599,7 @@ public int getIconWidth() { } } + @Override public int getIconHeight() { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -600,8 +612,9 @@ public int getIconHeight() { @SuppressWarnings("serial") // Same-version serialization only - private static class CheckBoxMenuItemIcon implements Icon, UIResource, Serializable + private static final class CheckBoxMenuItemIcon implements Icon, UIResource, Serializable { + @Override public void paintIcon(Component c, Graphics g, int x, int y) { AbstractButton b = (AbstractButton) c; ButtonModel model = b.getModel(); @@ -619,15 +632,18 @@ public void paintIcon(Component c, Graphics g, int x, int y) { g.drawLine(x+3, y+6, x+4, y+6); } } + @Override public int getIconWidth() { return 9; } + @Override public int getIconHeight() { return 9; } } // End class CheckBoxMenuItemIcon @SuppressWarnings("serial") // Same-version serialization only - private static class RadioButtonMenuItemIcon implements Icon, UIResource, Serializable + private static final class RadioButtonMenuItemIcon implements Icon, UIResource, Serializable { + @Override public void paintIcon(Component c, Graphics g, int x, int y) { AbstractButton b = (AbstractButton) c; ButtonModel model = b.getModel(); @@ -636,14 +652,17 @@ public void paintIcon(Component c, Graphics g, int x, int y) { 4, 4); } } + @Override public int getIconWidth() { return 12; } + @Override public int getIconHeight() { return 12; } } // End class RadioButtonMenuItemIcon @SuppressWarnings("serial") // Same-version serialization only - private static class MenuItemCheckIcon implements Icon, UIResource, Serializable{ + private static final class MenuItemCheckIcon implements Icon, UIResource, Serializable{ + @Override public void paintIcon(Component c, Graphics g, int x, int y) { /* For debugging: Color oldColor = g.getColor(); @@ -652,13 +671,16 @@ public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(oldColor); */ } + @Override public int getIconWidth() { return 9; } + @Override public int getIconHeight() { return 9; } } // End class MenuItemCheckIcon @SuppressWarnings("serial") // Same-version serialization only - private static class MenuItemArrowIcon implements Icon, UIResource, Serializable { + private static final class MenuItemArrowIcon implements Icon, UIResource, Serializable { + @Override public void paintIcon(Component c, Graphics g, int x, int y) { /* For debugging: Color oldColor = g.getColor(); @@ -667,13 +689,16 @@ public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(oldColor); */ } + @Override public int getIconWidth() { return 4; } + @Override public int getIconHeight() { return 8; } } // End class MenuItemArrowIcon @SuppressWarnings("serial") // Same-version serialization only - private static class MenuArrowIcon implements Icon, UIResource, Serializable { + private static final class MenuArrowIcon implements Icon, UIResource, Serializable { + @Override public void paintIcon(Component c, Graphics g, int x, int y) { XPStyle xp = XPStyle.getXP(); if (WindowsMenuItemUI.isVistaPainting(xp)) { @@ -708,6 +733,7 @@ public void paintIcon(Component c, Graphics g, int x, int y) { g.translate(-x,-y); } } + @Override public int getIconWidth() { XPStyle xp = XPStyle.getXP(); if (WindowsMenuItemUI.isVistaPainting(xp)) { @@ -717,6 +743,7 @@ public int getIconWidth() { return 4; } } + @Override public int getIconHeight() { XPStyle xp = XPStyle.getXP(); if (WindowsMenuItemUI.isVistaPainting(xp)) { @@ -728,14 +755,16 @@ public int getIconHeight() { } } // End class MenuArrowIcon - static class VistaMenuItemCheckIconFactory + static final class VistaMenuItemCheckIconFactory implements MenuItemCheckIconFactory { private static final int OFFSET = 3; + @Override public Icon getIcon(JMenuItem component) { return new VistaMenuItemCheckIcon(component); } + @Override public boolean isCompatible(Object icon, String prefix) { return icon instanceof VistaMenuItemCheckIcon && ((VistaMenuItemCheckIcon) icon).type == getType(prefix); @@ -788,7 +817,7 @@ private static Class getType(String type) { * Note: to be used on Vista only. */ @SuppressWarnings("serial") // Same-version serialization only - private static class VistaMenuItemCheckIcon + private static final class VistaMenuItemCheckIcon implements Icon, UIResource, Serializable { private final JMenuItem menuItem; @@ -803,6 +832,7 @@ private static class VistaMenuItemCheckIcon this.menuItem = null; } + @Override public int getIconHeight() { Icon lafIcon = getLaFIcon(); if (lafIcon != null) { @@ -825,6 +855,7 @@ public int getIconHeight() { return height; } + @Override public int getIconWidth() { Icon lafIcon = getLaFIcon(); if (lafIcon != null) { @@ -840,6 +871,7 @@ public int getIconWidth() { return width; } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { Icon lafIcon = getLaFIcon(); if (lafIcon != null) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java index 083563b4464df..ba4bde12122d9 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java @@ -83,6 +83,7 @@ public WindowsInternalFrameTitlePane(JInternalFrame f) { super(f); } + @Override protected void addSubComponents() { add(systemLabel); add(iconButton); @@ -90,6 +91,7 @@ protected void addSubComponents() { add(closeButton); } + @Override protected void installDefaults() { super.installDefaults(); @@ -117,11 +119,13 @@ protected void installDefaults() { UIManager.getColor("InternalFrame.inactiveTitleGradient"); } + @Override protected void uninstallListeners() { // Get around protected method in superclass super.uninstallListeners(); } + @Override protected void createButtons() { super.createButtons(); if (XPStyle.getXP() != null) { @@ -131,6 +135,7 @@ protected void createButtons() { } } + @Override protected void setButtonIcons() { super.setButtonIcons(); @@ -142,6 +147,7 @@ protected void setButtonIcons() { } + @Override public void paintComponent(Graphics g) { XPStyle xp = XPStyle.getXP(); @@ -224,10 +230,12 @@ public void paintComponent(Graphics g) { } } + @Override public Dimension getPreferredSize() { return getMinimumSize(); } + @Override public Dimension getMinimumSize() { Dimension d = new Dimension(super.getMinimumSize()); d.height = titlePaneHeight + 2; @@ -245,6 +253,7 @@ public Dimension getMinimumSize() { return d; } + @Override protected void paintTitleBackground(Graphics g) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -285,6 +294,7 @@ protected void paintTitleBackground(Graphics g) { } } + @Override protected void assembleSystemMenu() { systemPopupMenu = new JPopupMenu(); addSystemMenuItems(systemPopupMenu); @@ -372,6 +382,7 @@ private static int getButtonMnemonic(String button) { } } + @Override protected void showSystemMenu(){ showSystemPopupMenu(systemLabel); } @@ -397,15 +408,17 @@ private void showSystemPopupMenu(Component invoker){ } } + @Override protected PropertyChangeListener createPropertyChangeListener() { return new WindowsPropertyChangeHandler(); } + @Override protected LayoutManager createLayout() { return new WindowsTitlePaneLayout(); } - public class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout { + public final class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout { private Insets captionMargin = null; private Insets contentMargin = null; private XPStyle xp = XPStyle.getXP(); @@ -439,6 +452,7 @@ private int layoutButton(JComponent button, Part part, return x; } + @Override public void layoutContainer(Container c) { boolean leftToRight = WindowsGraphicsUtils.isLeftToRight(frame); int x, y; @@ -492,7 +506,8 @@ public void layoutContainer(Container c) { } } // end WindowsTitlePaneLayout - public class WindowsPropertyChangeHandler extends PropertyChangeHandler { + public final class WindowsPropertyChangeHandler extends PropertyChangeHandler { + @Override public void propertyChange(PropertyChangeEvent evt) { String prop = evt.getPropertyName(); @@ -515,7 +530,7 @@ public void propertyChange(PropertyChangeEvent evt) { *

* Note: We assume here that icons are square. */ - public static class ScalableIconUIResource implements Icon, UIResource { + public static final class ScalableIconUIResource implements Icon, UIResource { // We can use an arbitrary size here because we scale to it in paintIcon() private static final int SIZE = 16; @@ -562,6 +577,7 @@ protected Icon getBestIcon(int size) { } } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2d = (Graphics2D)g.create(); // Calculate how big our drawing area is in pixels @@ -580,10 +596,12 @@ public void paintIcon(Component c, Graphics g, int x, int y) { g2d.dispose(); } + @Override public int getIconWidth() { return SIZE; } + @Override public int getIconHeight() { return SIZE; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java index 19169f6e4241e..5c331533af947 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java @@ -45,10 +45,11 @@ /** * Windows rendition of the component. */ -public class WindowsInternalFrameUI extends BasicInternalFrameUI +public final class WindowsInternalFrameUI extends BasicInternalFrameUI { XPStyle xp = XPStyle.getXP(); + @Override public void installDefaults() { super.installDefaults(); @@ -59,6 +60,7 @@ public void installDefaults() { } } + @Override public void installUI(JComponent c) { super.installUI(c); @@ -66,6 +68,7 @@ public void installUI(JComponent c) { xp == null? Boolean.TRUE : Boolean.FALSE); } + @Override public void uninstallDefaults() { frame.setBorder(null); super.uninstallDefaults(); @@ -79,17 +82,19 @@ public WindowsInternalFrameUI(JInternalFrame w){ super(w); } + @Override protected DesktopManager createDesktopManager(){ return new WindowsDesktopManager(); } + @Override protected JComponent createNorthPane(JInternalFrame w) { titlePane = new WindowsInternalFrameTitlePane(w); return titlePane; } @SuppressWarnings("serial") // Superclass is not serializable across versions - private class XPBorder extends AbstractBorder { + private final class XPBorder extends AbstractBorder { private Skin leftSkin = xp.getSkin(frame, Part.WP_FRAMELEFT); private Skin rightSkin = xp.getSkin(frame, Part.WP_FRAMERIGHT); private Skin bottomSkin = xp.getSkin(frame, Part.WP_FRAMEBOTTOM); @@ -100,6 +105,7 @@ private class XPBorder extends AbstractBorder { * @param width the width of the painted border * @param height the height of the painted border */ + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { State state = ((JInternalFrame)c).isSelected() ? State.ACTIVE : State.INACTIVE; int topBorderHeight = (titlePane != null) ? titlePane.getSize().height : 0; @@ -118,6 +124,7 @@ public void paintBorder(Component c, Graphics g, int x, int y, int width, int he } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.top = 4; insets.left = leftSkin.getWidth(); @@ -127,6 +134,7 @@ public Insets getBorderInsets(Component c, Insets insets) { return insets; } + @Override public boolean isBorderOpaque() { return true; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java index 6266772dcf4a2..d10f3f47c3c33 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java @@ -41,7 +41,7 @@ /** * Windows rendition of the component. */ -public class WindowsLabelUI extends BasicLabelUI { +public final class WindowsLabelUI extends BasicLabelUI { private static final Object WINDOWS_LABEL_UI_KEY = new Object(); @@ -59,6 +59,7 @@ public static ComponentUI createUI(JComponent c) { return windowsLabelUI; } + @Override protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY) { int mnemonicIndex = l.getDisplayedMnemonicIndex(); @@ -72,6 +73,7 @@ protected void paintEnabledText(JLabel l, Graphics g, String s, textX, textY); } + @Override protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY) { int mnemonicIndex = l.getDisplayedMnemonicIndex(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java index 72ad51535ff2e..d1ee714f362e5 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java @@ -145,26 +145,32 @@ public class WindowsLookAndFeel extends BasicLookAndFeel */ private int baseUnitY; + @Override public String getName() { return "Windows"; } + @Override public String getDescription() { return "The Microsoft Windows Look and Feel"; } + @Override public String getID() { return "Windows"; } + @Override public boolean isNativeLookAndFeel() { return OSInfo.getOSType() == OSInfo.OSType.WINDOWS; } + @Override public boolean isSupportedLookAndFeel() { return isNativeLookAndFeel(); } + @Override public void initialize() { super.initialize(); @@ -206,6 +212,7 @@ public void initialize() { * * @see BasicLookAndFeel#getDefaults */ + @Override protected void initClassDefaults(UIDefaults table) { super.initClassDefaults(table); @@ -260,6 +267,7 @@ protected void initClassDefaults(UIDefaults table) * values, otherwise we create color objects whose values match * the defaults Windows95 colors. */ + @Override protected void initSystemColorDefaults(UIDefaults table) { String[] defaultSystemColors = { @@ -310,6 +318,7 @@ private void initResourceBundle(UIDefaults table) { // XXX - there are probably a lot of redundant values that could be removed. // ie. Take a look at RadioButtonBorder, etc... + @Override protected void initComponentDefaults(UIDefaults table) { super.initComponentDefaults( table ); @@ -1893,6 +1902,7 @@ public Object createValue(UIDefaults table) { return lazyDefaults; } + @Override public void uninitialize() { super.uninitialize(); @@ -1944,6 +1954,7 @@ public static boolean isClassicWindows() { * * @see javax.swing.LookAndFeel#provideErrorFeedback */ + @Override public void provideErrorFeedback(Component component) { super.provideErrorFeedback(component); } @@ -1951,6 +1962,7 @@ public void provideErrorFeedback(Component component) { /** * {@inheritDoc} */ + @Override public LayoutStyle getLayoutStyle() { LayoutStyle style = this.style; if (style == null) { @@ -1981,6 +1993,7 @@ public LayoutStyle getLayoutStyle() { * @see #playSound(Action) * @since 1.4 */ + @Override protected Action createAudioAction(Object key) { if (key != null) { String audioKey = (String)key; @@ -2018,7 +2031,7 @@ static void repaintRootPane(Component c) { * @since 1.4 */ @SuppressWarnings("serial") // Superclass is not serializable across versions - private static class AudioAction extends AbstractAction { + private static final class AudioAction extends AbstractAction { private Runnable audioRunnable; private String audioResource; /** @@ -2029,6 +2042,7 @@ public AudioAction(String name, String resource) { super(name); audioResource = resource; } + @Override public void actionPerformed(ActionEvent e) { if (audioRunnable == null) { audioRunnable = (Runnable)Toolkit.getDefaultToolkit().getDesktopProperty(audioResource); @@ -2045,7 +2059,7 @@ public void actionPerformed(ActionEvent e) { * Gets an Icon from the native libraries if available, * otherwise gets it from an image resource file. */ - private static class LazyWindowsIcon implements UIDefaults.LazyValue { + private static final class LazyWindowsIcon implements UIDefaults.LazyValue { private String nativeImage; private String resource; @@ -2054,6 +2068,7 @@ private static class LazyWindowsIcon implements UIDefaults.LazyValue { this.resource = resource; } + @Override public Object createValue(UIDefaults table) { if (nativeImage != null) { Image image = (Image)ShellFolder.get(nativeImage); @@ -2072,7 +2087,7 @@ public Object createValue(UIDefaults table) { * Gets an Icon from the native libraries if available. * A desktop property is used to trigger reloading the icon when needed. */ - private static class ActiveWindowsIcon implements UIDefaults.ActiveValue { + private static final class ActiveWindowsIcon implements UIDefaults.ActiveValue { private Icon icon; private String nativeImageName; private String fallbackName; @@ -2117,7 +2132,7 @@ public Object createValue(UIDefaults table) { /** * Icon backed-up by XP Skin. */ - private static class SkinIcon implements Icon, UIResource { + private static final class SkinIcon implements Icon, UIResource { private final Part part; private final State state; SkinIcon(Part part, State state) { @@ -2130,6 +2145,7 @@ private static class SkinIcon implements Icon, UIResource { * may use the Component argument to get properties useful for * painting, e.g. the foreground or background color. */ + @Override public void paintIcon(Component c, Graphics g, int x, int y) { XPStyle xp = XPStyle.getXP(); assert xp != null; @@ -2144,6 +2160,7 @@ public void paintIcon(Component c, Graphics g, int x, int y) { * * @return an int specifying the fixed width of the icon. */ + @Override public int getIconWidth() { int width = 0; XPStyle xp = XPStyle.getXP(); @@ -2160,6 +2177,7 @@ public int getIconWidth() { * * @return an int specifying the fixed height of the icon. */ + @Override public int getIconHeight() { int height = 0; XPStyle xp = XPStyle.getXP(); @@ -2176,11 +2194,12 @@ public int getIconHeight() { * WindowsDesktopProperty for fonts. If a font with the name 'MS Sans Serif' * is returned, it is mapped to 'Microsoft Sans Serif'. */ - private static class WindowsFontProperty extends WindowsDesktopProperty { + private static final class WindowsFontProperty extends WindowsDesktopProperty { WindowsFontProperty(String key, Object backup) { super(key, backup); } + @Override public void invalidate(LookAndFeel laf) { if ("win.defaultGUI.font.height".equals(getKey())) { ((WindowsLookAndFeel)laf).style = null; @@ -2188,6 +2207,7 @@ public void invalidate(LookAndFeel laf) { super.invalidate(laf); } + @Override protected Object configureValue(Object value) { if (value instanceof Font) { Font font = (Font)value; @@ -2236,7 +2256,7 @@ protected Object configureValue(Object value) { * WindowsDesktopProperty for fonts that only gets sizes from the desktop, * font name and style are passed into the constructor */ - private static class WindowsFontSizeProperty extends + private static final class WindowsFontSizeProperty extends WindowsDesktopProperty { private String fontName; private int fontSize; @@ -2250,6 +2270,7 @@ private static class WindowsFontSizeProperty extends this.fontStyle = fontStyle; } + @Override protected Object configureValue(Object value) { if (value == null) { value = new FontUIResource(fontName, fontStyle, fontSize); @@ -2278,6 +2299,7 @@ private static class XPValue implements UIDefaults.ActiveValue { this.classicValue = classicValue; } + @Override public Object createValue(UIDefaults table) { Object value = null; if (XPStyle.getXP() != null) { @@ -2313,7 +2335,7 @@ private Object recursiveCreateValue(Object value, UIDefaults table) { } } - private static class XPBorderValue extends XPValue { + private static final class XPBorderValue extends XPValue { private final Border extraMargin; XPBorderValue(Part xpValue, Object classicValue) { @@ -2325,6 +2347,7 @@ private static class XPBorderValue extends XPValue { this.extraMargin = extraMargin; } + @Override public Object getXPValue(UIDefaults table) { XPStyle xp = XPStyle.getXP(); Border xpBorder = xp != null ? xp.getBorder(null, (Part)xpValue) : null; @@ -2337,18 +2360,19 @@ public Object getXPValue(UIDefaults table) { } } - private static class XPColorValue extends XPValue { + private static final class XPColorValue extends XPValue { XPColorValue(Part part, State state, Prop prop, Object classicValue) { super(new XPColorValueKey(part, state, prop), classicValue); } + @Override public Object getXPValue(UIDefaults table) { XPColorValueKey key = (XPColorValueKey)xpValue; XPStyle xp = XPStyle.getXP(); return xp != null ? xp.getColor(key.skin, key.prop, null) : null; } - private static class XPColorValueKey { + private static final class XPColorValueKey { Skin skin; Prop prop; @@ -2359,7 +2383,7 @@ private static class XPColorValueKey { } } - private class XPDLUValue extends XPValue { + private final class XPDLUValue extends XPValue { private int direction; XPDLUValue(int xpdlu, int classicdlu, int direction) { @@ -2367,11 +2391,13 @@ private class XPDLUValue extends XPValue { this.direction = direction; } + @Override public Object getXPValue(UIDefaults table) { int px = dluToPixels(((Integer)xpValue).intValue(), direction); return Integer.valueOf(px); } + @Override public Object getClassicValue(UIDefaults table) { int px = dluToPixels(((Integer)classicValue).intValue(), direction); return Integer.valueOf(px); @@ -2387,6 +2413,7 @@ private static class TriggerDesktopProperty extends WindowsDesktopProperty { getValueFromDesktop(); } + @Override protected void updateUI() { super.updateUI(); @@ -2395,11 +2422,12 @@ protected void updateUI() { } } - private static class FontDesktopProperty extends TriggerDesktopProperty { + private static final class FontDesktopProperty extends TriggerDesktopProperty { FontDesktopProperty(String key) { super(key); } + @Override protected void updateUI() { UIDefaults defaults = UIManager.getLookAndFeelDefaults(); SwingUtilities2.putAATextInfo(true, defaults); @@ -2410,7 +2438,7 @@ protected void updateUI() { // Windows LayoutStyle. From: // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch14e.asp @SuppressWarnings("fallthrough") - private class WindowsLayoutStyle extends DefaultLayoutStyle { + private final class WindowsLayoutStyle extends DefaultLayoutStyle { @Override public int getPreferredGap(JComponent component1, JComponent component2, ComponentPlacement type, int position, @@ -2504,6 +2532,7 @@ private void calculateBaseUnits() { * * @since 1.6 */ + @Override public Icon getDisabledIcon(JComponent component, Icon icon) { // if the component has a HI_RES_DISABLED_ICON_CLIENT_KEY // client property set to Boolean.TRUE, then use the new @@ -2525,10 +2554,11 @@ public Icon getDisabledIcon(JComponent component, Icon icon) { return super.getDisabledIcon(component, icon); } - private static class RGBGrayFilter extends RGBImageFilter { + private static final class RGBGrayFilter extends RGBImageFilter { public RGBGrayFilter() { canFilterIndexColorModel = true; } + @Override public int filterRGB(int x, int y, int rgb) { // find the average of red, green, and blue float avg = (((rgb >> 16) & 0xff) / 255f + @@ -2547,7 +2577,7 @@ public int filterRGB(int x, int y, int rgb) { } } - private static class FocusColorProperty extends WindowsDesktopProperty { + private static final class FocusColorProperty extends WindowsDesktopProperty { public FocusColorProperty () { // Fallback value is never used because of the configureValue method doesn't return null super("win.3d.backgroundColor", Color.BLACK); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java index 8c6cbdff5412e..f663d8d1e908d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java @@ -57,7 +57,7 @@ /** * Windows rendition of the component. */ -public class WindowsMenuBarUI extends BasicMenuBarUI +public final class WindowsMenuBarUI extends BasicMenuBarUI { /* to be accessed on the EDT only */ private WindowListener windowListener = null; @@ -125,6 +125,7 @@ public void hierarchyChanged(HierarchyEvent e) { super.installListeners(); } + @Override protected void installKeyboardActions() { super.installKeyboardActions(); ActionMap map = SwingUtilities.getUIActionMap(menuBar); @@ -140,7 +141,7 @@ protected void installKeyboardActions() { * Unlike BasicMenuBarUI.TakeFocus, this Action will not show menu popup. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - private static class TakeFocus extends AbstractAction { + private static final class TakeFocus extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { JMenuBar menuBar = (JMenuBar)e.getSource(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index 2f76f9291332c..2ee1b2d119fb1 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -54,7 +54,7 @@ * * @author Igor Kushnirskiy */ -public class WindowsMenuItemUI extends BasicMenuItemUI { +public final class WindowsMenuItemUI extends BasicMenuItemUI { /** * The instance of {@code PropertyChangeListener}. */ @@ -63,6 +63,7 @@ public class WindowsMenuItemUI extends BasicMenuItemUI { final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { + @Override public JMenuItem getMenuItem() { return menuItem; } @@ -71,6 +72,7 @@ public State getState(JMenuItem menuItem) { return WindowsMenuItemUI.getState(this, menuItem); } + @Override public Part getPart(JMenuItem menuItem) { return WindowsMenuItemUI.getPart(this, menuItem); } @@ -141,6 +143,7 @@ protected void uninstallListeners() { * @param textRect Bounding rectangle to render the text. * @param text String to render */ + @Override protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) { if (WindowsMenuItemUI.isVistaPainting()) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index 4195b4e85cace..5562ce603881b 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -50,13 +50,14 @@ /** * Windows rendition of the component. */ -public class WindowsMenuUI extends BasicMenuUI { +public final class WindowsMenuUI extends BasicMenuUI { protected Integer menuBarHeight; protected boolean hotTrackingOn; final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { + @Override public JMenuItem getMenuItem() { return menuItem; } @@ -106,6 +107,7 @@ public State getState(JMenuItem menu) { return state; } + @Override public Part getPart(JMenuItem menuItem) { return ((JMenu) menuItem).isTopLevelMenu() ? Part.MP_BARITEM : Part.MP_POPUPITEM; @@ -115,6 +117,7 @@ public static ComponentUI createUI(JComponent x) { return new WindowsMenuUI(); } + @Override protected void installDefaults() { super.installDefaults(); if (!WindowsLookAndFeel.isClassicWindows()) { @@ -131,6 +134,7 @@ protected void installDefaults() { * Draws the background of the menu. * @since 1.4 */ + @Override protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) { if (WindowsMenuItemUI.isVistaPainting()) { WindowsMenuItemUI.paintBackground(accessor, g, menuItem, bgColor); @@ -210,6 +214,7 @@ protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) { * @param text String to render * @since 1.4 */ + @Override protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) { if (WindowsMenuItemUI.isVistaPainting()) { @@ -245,6 +250,7 @@ protected void paintText(Graphics g, JMenuItem menuItem, g.setColor(oldColor); } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new WindowsMouseInputHandler(); } @@ -254,7 +260,8 @@ protected MouseInputListener createMouseInputListener(JComponent c) { * true when the mouse enters the menu and false when it exits. * @since 1.4 */ - protected class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler { + protected final class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler { + @Override public void mouseEntered(MouseEvent evt) { super.mouseEntered(evt); @@ -265,6 +272,7 @@ public void mouseEntered(MouseEvent evt) { } } + @Override public void mouseExited(MouseEvent evt) { super.mouseExited(evt); @@ -277,6 +285,7 @@ public void mouseExited(MouseEvent evt) { } } + @Override protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon, Icon arrowIcon, diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java index 2755cc76c96c5..5ced1659adfe2 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java @@ -30,5 +30,5 @@ /** * Windows rendition of the component. */ -public class WindowsOptionPaneUI extends BasicOptionPaneUI { +public final class WindowsOptionPaneUI extends BasicOptionPaneUI { } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java index baf0997ceff30..1f64c18e61f61 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java @@ -33,7 +33,7 @@ /** * Windows rendition of the component. */ -public class WindowsPasswordFieldUI extends BasicPasswordFieldUI { +public final class WindowsPasswordFieldUI extends BasicPasswordFieldUI { /** * Creates a UI for a JPasswordField @@ -54,6 +54,7 @@ public static ComponentUI createUI(JComponent c) { * * @return the caret object */ + @Override protected Caret createCaret() { return new WindowsTextUI.WindowsCaret(); } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java index ed31f00a89ba6..9d67278526a95 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java @@ -42,12 +42,13 @@ * @author Igor Kushnirskiy */ -public class WindowsPopupMenuSeparatorUI extends BasicPopupMenuSeparatorUI { +public final class WindowsPopupMenuSeparatorUI extends BasicPopupMenuSeparatorUI { public static ComponentUI createUI(JComponent c) { return new WindowsPopupMenuSeparatorUI(); } + @Override public void paint(Graphics g, JComponent c) { Dimension s = c.getSize(); XPStyle xp = XPStyle.getXP(); @@ -82,6 +83,7 @@ public void paint(Graphics g, JComponent c) { } } + @Override public Dimension getPreferredSize(JComponent c) { int fontHeight = 0; Font font = c.getFont(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java index 3bf214aff374d..4df236115fb24 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java @@ -57,7 +57,7 @@ * * @author Igor Kushnirskiy */ -public class WindowsPopupMenuUI extends BasicPopupMenuUI { +public final class WindowsPopupMenuUI extends BasicPopupMenuUI { static MnemonicListener mnemonicListener = null; static final Object GUTTER_OFFSET_KEY = @@ -67,6 +67,7 @@ public static ComponentUI createUI(JComponent c) { return new WindowsPopupMenuUI(); } + @Override public void installListeners() { super.installListeners(); if (! UIManager.getBoolean("Button.showMnemonics") && @@ -88,14 +89,16 @@ public void installListeners() { * @return Popup that will show the JPopupMenu * @since 1.4 */ + @Override public Popup getPopup(JPopupMenu popupMenu, int x, int y) { PopupFactory popupFactory = PopupFactory.getSharedInstance(); return popupFactory.getPopup(popupMenu.getInvoker(), popupMenu, x, y); } - static class MnemonicListener implements ChangeListener { + static final class MnemonicListener implements ChangeListener { JRootPane repaintRoot = null; + @Override public void stateChanged(ChangeEvent ev) { MenuSelectionManager msm = (MenuSelectionManager)ev.getSource(); MenuElement[] path = msm.getSelectedPath(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java index f23cb31dff0ff..b58ad07a57b85 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java @@ -45,7 +45,7 @@ * @author Amy Fowler */ @SuppressWarnings("serial") // Superclass is not serializable across versions -class WindowsPopupWindow extends JWindow { +final class WindowsPopupWindow extends JWindow { static final int UNDEFINED_WINDOW_TYPE = 0; static final int TOOLTIP_WINDOW_TYPE = 1; @@ -69,10 +69,12 @@ int getWindowType() { return windowType; } + @Override public void update(Graphics g) { paint(g); } + @Override @SuppressWarnings("deprecation") public void hide() { super.hide(); @@ -85,6 +87,7 @@ public void hide() { removeNotify(); } + @Override @SuppressWarnings("deprecation") public void show() { super.show(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java index 790dc85166b62..9cc7d277ff185 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java @@ -51,7 +51,7 @@ * * @author Michael C. Albers */ -public class WindowsProgressBarUI extends BasicProgressBarUI +public final class WindowsProgressBarUI extends BasicProgressBarUI { private Rectangle previousFullBox; @@ -62,6 +62,7 @@ public static ComponentUI createUI(JComponent x) { } + @Override protected void installDefaults() { super.installDefaults(); @@ -80,6 +81,7 @@ protected void installDefaults() { * @see javax.swing.JComponent#getBaseline(int, int) * @since 1.6 */ + @Override public int getBaseline(JComponent c, int width, int height) { int baseline = super.getBaseline(c, width, height); if (XPStyle.getXP() != null && progressBar.isStringPainted() && @@ -102,6 +104,7 @@ public int getBaseline(JComponent c, int width, int height) { return baseline; } + @Override protected Dimension getPreferredInnerHorizontal() { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -113,6 +116,7 @@ protected Dimension getPreferredInnerHorizontal() { return super.getPreferredInnerHorizontal(); } + @Override protected Dimension getPreferredInnerVertical() { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -124,6 +128,7 @@ protected Dimension getPreferredInnerVertical() { return super.getPreferredInnerVertical(); } + @Override protected void paintDeterminate(Graphics g, JComponent c) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -218,6 +223,7 @@ protected void paintDeterminate(Graphics g, JComponent c) { * {@inheritDoc} * @since 1.6 */ + @Override protected void setAnimationIndex(int newValue) { super.setAnimationIndex(newValue); XPStyle xp = XPStyle.getXP(); @@ -241,6 +247,7 @@ protected void setAnimationIndex(int newValue) { * {@inheritDoc} * @since 1.6 */ + @Override protected int getBoxLength(int availableLength, int otherDimension) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -253,6 +260,7 @@ protected int getBoxLength(int availableLength, int otherDimension) { * {@inheritDoc} * @since 1.6 */ + @Override protected Rectangle getBox(Rectangle r) { Rectangle rect = super.getBox(r); @@ -298,6 +306,7 @@ protected Rectangle getBox(Rectangle r) { } + @Override protected void paintIndeterminate(Graphics g, JComponent c) { XPStyle xp = XPStyle.getXP(); if (xp != null) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index 584a0f1622b1a..f6f3d4f06d11c 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -41,11 +41,12 @@ /** * Windows rendition of the component. */ -public class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { +public final class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { + @Override public JMenuItem getMenuItem() { return menuItem; } @@ -54,6 +55,7 @@ public State getState(JMenuItem menuItem) { return WindowsMenuItemUI.getState(this, menuItem); } + @Override public Part getPart(JMenuItem menuItem) { return WindowsMenuItemUI.getPart(this, menuItem); } @@ -81,6 +83,7 @@ protected void paintBackground(Graphics g, JMenuItem menuItem, * @param text String to render * @since 1.4 */ + @Override protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) { if (WindowsMenuItemUI.isVistaPainting()) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java index dd0a93c95a1f6..8fdc1a315c97a 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java @@ -73,6 +73,7 @@ public static ComponentUI createUI(JComponent c) { // ******************************** // Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!initialized) { @@ -88,6 +89,7 @@ public void installDefaults(AbstractButton b) { } } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); initialized = false; @@ -104,11 +106,13 @@ protected Color getFocusColor() { /** * Overridden method to render the text without the mnemonic */ + @Override protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) { WindowsGraphicsUtils.paintText(g, b, textRect, text, getTextShiftOffset()); } + @Override protected void paintFocus(Graphics g, Rectangle textRect, Dimension d){ g.setColor(getFocusColor()); BasicGraphicsUtils.drawDashedRect(g, textRect.x, textRect.y, textRect.width, textRect.height); @@ -117,6 +121,7 @@ protected void paintFocus(Graphics g, Rectangle textRect, Dimension d){ // ******************************** // Layout Methods // ******************************** + @Override public Dimension getPreferredSize(JComponent c) { Dimension d = super.getPreferredSize(c); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java index f05da909ac1db..19bfad0a1da84 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java @@ -70,7 +70,7 @@ * @author Mark Davidson * @since 1.4 */ -public class WindowsRootPaneUI extends BasicRootPaneUI { +public final class WindowsRootPaneUI extends BasicRootPaneUI { private static final WindowsRootPaneUI windowsRootPaneUI = new WindowsRootPaneUI(); static final AltProcessor altProcessor = new AltProcessor(); @@ -79,7 +79,7 @@ public static ComponentUI createUI(JComponent c) { return windowsRootPaneUI; } - static class AltProcessor implements KeyEventPostProcessor { + static final class AltProcessor implements KeyEventPostProcessor { static boolean altKeyPressed = false; static boolean menuCanceledOnPress = false; static JRootPane root = null; @@ -166,6 +166,7 @@ void altReleased(KeyEvent ev) { } + @Override public boolean postProcessKeyEvent(KeyEvent ev) { if (ev.isConsumed()) { if (ev.getKeyCode() != KeyEvent.VK_ALT) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java index a3dd04362b9cd..c8d62e52834a6 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java @@ -55,7 +55,7 @@ /** * Windows rendition of the component. */ -public class WindowsScrollBarUI extends BasicScrollBarUI { +public final class WindowsScrollBarUI extends BasicScrollBarUI { private Grid thumbGrid; private Grid highlightGrid; private Dimension horizontalThumbSize; @@ -71,6 +71,7 @@ public static ComponentUI createUI(JComponent c) { return new WindowsScrollBarUI(); } + @Override protected void installDefaults() { super.installDefaults(); @@ -100,11 +101,13 @@ protected Dimension getMinimumThumbSize() { : verticalThumbSize; } + @Override public void uninstallUI(JComponent c) { super.uninstallUI(c); thumbGrid = highlightGrid = null; } + @Override protected void configureScrollBarColors() { super.configureScrollBarColors(); Color color = UIManager.getColor("ScrollBar.trackForeground"); @@ -118,6 +121,7 @@ protected void configureScrollBarColors() { } } + @Override protected JButton createDecreaseButton(int orientation) { return new WindowsArrowButton(orientation, UIManager.getColor("ScrollBar.thumb"), @@ -126,6 +130,7 @@ protected JButton createDecreaseButton(int orientation) { UIManager.getColor("ScrollBar.thumbHighlight")); } + @Override protected JButton createIncreaseButton(int orientation) { return new WindowsArrowButton(orientation, UIManager.getColor("ScrollBar.thumb"), @@ -161,6 +166,7 @@ private void repaint() { } } + @Override protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds){ boolean v = (scrollbar.getOrientation() == JScrollBar.VERTICAL); @@ -189,6 +195,7 @@ else if (trackHighlight == INCREASE_HIGHLIGHT) { } } + @Override protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { boolean v = (scrollbar.getOrientation() == JScrollBar.VERTICAL); @@ -231,6 +238,7 @@ protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { } + @Override protected void paintDecreaseHighlight(Graphics g) { if (highlightGrid == null) { super.paintDecreaseHighlight(g); @@ -257,6 +265,7 @@ protected void paintDecreaseHighlight(Graphics g) { } + @Override protected void paintIncreaseHighlight(Graphics g) { if (highlightGrid == null) { super.paintDecreaseHighlight(g); @@ -304,7 +313,7 @@ protected void setThumbRollover(boolean active) { * preferred size is always a square. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - private class WindowsArrowButton extends BasicArrowButton { + private final class WindowsArrowButton extends BasicArrowButton { public WindowsArrowButton(int direction, Color background, Color shadow, Color darkShadow, Color highlight) { @@ -315,6 +324,7 @@ public WindowsArrowButton(int direction) { super(direction); } + @Override public void paint(Graphics g) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -370,6 +380,7 @@ public void paint(Graphics g) { } } + @Override public Dimension getPreferredSize() { int size = 16; if (scrollbar != null) { @@ -398,7 +409,7 @@ public Dimension getPreferredSize() { * a WeakRef so that it can be freed when no longer needed. As the * Grid is rather expensive to draw, it is drawn in a BufferedImage. */ - private static class Grid { + private static final class Grid { private static final int BUFFER_SIZE = 64; private static HashMap> map; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java index 6c89eabc89455..393f7595402a4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java @@ -30,5 +30,5 @@ /** * Windows rendition of the component. */ -public class WindowsScrollPaneUI extends BasicScrollPaneUI +public final class WindowsScrollPaneUI extends BasicScrollPaneUI {} diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java index 65526a40c1b3a..9ce5db3b4ef8f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java @@ -30,4 +30,4 @@ /** * Windows Separator. */ -public class WindowsSeparatorUI extends BasicSeparatorUI { } +public final class WindowsSeparatorUI extends BasicSeparatorUI { } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java index ceaf878cbca5f..88dc91d572b85 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java @@ -44,7 +44,7 @@ /** * Windows rendition of the component. */ -public class WindowsSliderUI extends BasicSliderUI +public final class WindowsSliderUI extends BasicSliderUI { private boolean rollover = false; private boolean pressed = false; @@ -63,32 +63,38 @@ public static ComponentUI createUI(JComponent b) { * the HOT, PRESSED, and FOCUSED states. * @since 1.6 */ + @Override protected TrackListener createTrackListener(JSlider slider) { return new WindowsTrackListener(); } - private class WindowsTrackListener extends TrackListener { + private final class WindowsTrackListener extends TrackListener { + @Override public void mouseMoved(MouseEvent e) { updateRollover(thumbRect.contains(e.getX(), e.getY())); super.mouseMoved(e); } + @Override public void mouseEntered(MouseEvent e) { updateRollover(thumbRect.contains(e.getX(), e.getY())); super.mouseEntered(e); } + @Override public void mouseExited(MouseEvent e) { updateRollover(false); super.mouseExited(e); } + @Override public void mousePressed(MouseEvent e) { updatePressed(thumbRect.contains(e.getX(), e.getY())); super.mousePressed(e); } + @Override public void mouseReleased(MouseEvent e) { updatePressed(false); super.mouseReleased(e); @@ -119,6 +125,7 @@ public void updateRollover(boolean newRollover) { } + @Override public void paintTrack(Graphics g) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -141,6 +148,7 @@ public void paintTrack(Graphics g) { } + @Override protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -149,6 +157,7 @@ protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, i super.paintMinorTickForHorizSlider(g, tickBounds, x); } + @Override protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -157,6 +166,7 @@ protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, i super.paintMajorTickForHorizSlider(g, tickBounds, x); } + @Override protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -165,6 +175,7 @@ protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, in super.paintMinorTickForVertSlider(g, tickBounds, y); } + @Override protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -174,6 +185,7 @@ protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, in } + @Override public void paintThumb(Graphics g) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -199,6 +211,7 @@ public void paintThumb(Graphics g) { } } + @Override protected Dimension getThumbSize() { XPStyle xp = XPStyle.getXP(); if (xp != null) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java index c17328e50cca7..bad66ce3a047f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java @@ -37,7 +37,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; -public class WindowsSpinnerUI extends BasicSpinnerUI { +public final class WindowsSpinnerUI extends BasicSpinnerUI { public static ComponentUI createUI(JComponent c) { return new WindowsSpinnerUI(); } @@ -46,6 +46,7 @@ public static ComponentUI createUI(JComponent c) { * {@inheritDoc} * @since 1.6 */ + @Override public void paint(Graphics g, JComponent c) { if (XPStyle.getXP() != null) { paintXPBackground(g, c); @@ -71,6 +72,7 @@ private void paintXPBackground(Graphics g, JComponent c) { skin.paintSkin(g, 0, 0, c.getWidth(), c.getHeight(), state); } + @Override protected Component createPreviousButton() { if (XPStyle.getXP() != null) { JButton xpButton = new XPStyle.GlyphButton(spinner, Part.SPNP_DOWN); @@ -83,6 +85,7 @@ protected Component createPreviousButton() { return super.createPreviousButton(); } + @Override protected Component createNextButton() { if (XPStyle.getXP() != null) { JButton xpButton = new XPStyle.GlyphButton(spinner, Part.SPNP_UP); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java index 04eeb726f9042..95062ef586ff8 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java @@ -39,7 +39,7 @@ * @author Jeff Dinkins */ @SuppressWarnings("serial") // Superclass is not serializable across versions -public class WindowsSplitPaneDivider extends BasicSplitPaneDivider +public final class WindowsSplitPaneDivider extends BasicSplitPaneDivider { /** @@ -52,6 +52,7 @@ public WindowsSplitPaneDivider(BasicSplitPaneUI ui) { /** * Paints the divider. */ + @Override public void paint(Graphics g) { Color bgColor = (splitPane.hasFocus()) ? UIManager.getColor("SplitPane.shadow") : diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java index 9305e46c36ff9..8c50e39116487 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java @@ -33,7 +33,7 @@ /** * Windows rendition of the component. */ -public class WindowsSplitPaneUI extends BasicSplitPaneUI +public final class WindowsSplitPaneUI extends BasicSplitPaneUI { public WindowsSplitPaneUI() { @@ -50,6 +50,7 @@ public static ComponentUI createUI(JComponent x) { /** * Creates the default divider. */ + @Override public BasicSplitPaneDivider createDefaultDivider() { return new WindowsSplitPaneDivider(this); } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java index 9b4e22102eb1d..86e2ee1fc523c 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java @@ -48,7 +48,7 @@ /** * Windows rendition of the component. */ -public class WindowsTabbedPaneUI extends BasicTabbedPaneUI { +public final class WindowsTabbedPaneUI extends BasicTabbedPaneUI { /** * Keys to use for forward focus traversal when the JComponent is * managing focus. @@ -63,6 +63,7 @@ public class WindowsTabbedPaneUI extends BasicTabbedPaneUI { private boolean contentOpaque = true; + @Override @SuppressWarnings("deprecation") protected void installDefaults() { super.installDefaults(); @@ -82,6 +83,7 @@ protected void installDefaults() { tabPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, managingFocusBackwardTraversalKeys); } + @Override protected void uninstallDefaults() { // sets the focus forward and backward traversal keys to null // to restore the defaults @@ -94,6 +96,7 @@ public static ComponentUI createUI(JComponent c) { return new WindowsTabbedPaneUI(); } + @Override protected void setRolloverTab(int index) { // Rollover is only supported on XP if (XPStyle.getXP() != null) { @@ -119,6 +122,7 @@ protected void setRolloverTab(int index) { } } + @Override protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) { XPStyle xp = XPStyle.getXP(); if (xp != null && (contentOpaque || tabPane.isOpaque())) { @@ -155,6 +159,7 @@ protected void paintContentBorder(Graphics g, int tabPlacement, int selectedInde super.paintContentBorder(g, tabPlacement, selectedIndex); } + @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected ) { if (XPStyle.getXP() == null) { @@ -162,6 +167,7 @@ protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, } } + @Override protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected ) { XPStyle xp = XPStyle.getXP(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java index 507990812cc00..b670bd294b0eb 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java @@ -50,13 +50,14 @@ import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; -public class WindowsTableHeaderUI extends BasicTableHeaderUI { +public final class WindowsTableHeaderUI extends BasicTableHeaderUI { private TableCellRenderer originalHeaderRenderer; public static ComponentUI createUI(JComponent h) { return new WindowsTableHeaderUI(); } + @Override public void installUI(JComponent c) { super.installUI(c); @@ -68,6 +69,7 @@ public void installUI(JComponent c) { } } + @Override public void uninstallUI(JComponent c) { if (header.getDefaultRenderer() instanceof XPDefaultRenderer) { header.setDefaultRenderer(originalHeaderRenderer); @@ -84,7 +86,7 @@ protected void rolloverColumnUpdated(int oldColumn, int newColumn) { } @SuppressWarnings("serial") // JDK-implementation class - private class XPDefaultRenderer extends DefaultTableCellHeaderRenderer { + private final class XPDefaultRenderer extends DefaultTableCellHeaderRenderer { Skin skin; boolean isSelected, hasFocus, hasRollover; int column; @@ -93,6 +95,7 @@ private class XPDefaultRenderer extends DefaultTableCellHeaderRenderer { setHorizontalAlignment(LEADING); } + @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { @@ -181,6 +184,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, return this; } + @Override public void paint(Graphics g) { Dimension size = getSize(); State state = State.NORMAL; @@ -227,7 +231,7 @@ public void paint(Graphics g) { * A border with an Icon at the middle of the top side. * Outer insets can be provided for this border. */ - private static class IconBorder implements Border, UIResource{ + private static final class IconBorder implements Border, UIResource{ private final Icon icon; private final int top; private final int left; @@ -246,12 +250,15 @@ public IconBorder(Icon icon, int top, int left, this.bottom = bottom; this.right = right; } + @Override public Insets getBorderInsets(Component c) { return new Insets(icon.getIconHeight() + top, left, bottom, right); } + @Override public boolean isBorderOpaque() { return false; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { icon.paintIcon(c, g, diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java index 6aca81225ea15..e695d9f670800 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java @@ -33,7 +33,7 @@ /** * Windows rendition of the component. */ -public class WindowsTextAreaUI extends BasicTextAreaUI { +public final class WindowsTextAreaUI extends BasicTextAreaUI { /** * Creates the object to use for a caret. By default an * instance of WindowsCaret is created. This method @@ -42,6 +42,7 @@ public class WindowsTextAreaUI extends BasicTextAreaUI { * * @return the caret object */ + @Override protected Caret createCaret() { return new WindowsTextUI.WindowsCaret(); } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java index be3e6276161c5..508d260895c64 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java @@ -62,7 +62,7 @@ * * @author Timothy Prinzing */ -public class WindowsTextFieldUI extends BasicTextFieldUI +public final class WindowsTextFieldUI extends BasicTextFieldUI { /** * Creates a UI for a JTextField. @@ -82,6 +82,7 @@ public static ComponentUI createUI(JComponent c) { * * @param g the graphics context */ + @Override protected void paintBackground(Graphics g) { super.paintBackground(g); } @@ -91,6 +92,7 @@ protected void paintBackground(Graphics g) { * * @return the caret */ + @Override protected Caret createCaret() { return new WindowsFieldCaret(); } @@ -100,7 +102,7 @@ protected Caret createCaret() { * DefaultCaret. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - static class WindowsFieldCaret extends DefaultCaret implements UIResource { + static final class WindowsFieldCaret extends DefaultCaret implements UIResource { public WindowsFieldCaret() { super(); @@ -112,6 +114,7 @@ public WindowsFieldCaret() { * caret out into the field by about a quarter of * a field length if not visible. */ + @Override protected void adjustVisibility(Rectangle r) { SwingUtilities.invokeLater(new SafeScroller(r)); } @@ -121,16 +124,18 @@ protected void adjustVisibility(Rectangle r) { * * @return the painter */ + @Override protected Highlighter.HighlightPainter getSelectionPainter() { return WindowsTextUI.WindowsPainter; } - private class SafeScroller implements Runnable { + private final class SafeScroller implements Runnable { SafeScroller(Rectangle r) { this.r = r; } + @Override @SuppressWarnings("deprecation") public void run() { JTextField field = (JTextField) getComponent(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java index 372563f74f46e..7858a1195e80d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java @@ -33,7 +33,7 @@ /** * Windows rendition of the component. */ -public class WindowsTextPaneUI extends BasicTextPaneUI +public final class WindowsTextPaneUI extends BasicTextPaneUI { /** * Creates a UI for a JTextPane. @@ -53,6 +53,7 @@ public static ComponentUI createUI(JComponent c) { * * @return the caret object */ + @Override protected Caret createCaret() { return new WindowsTextUI.WindowsCaret(); } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java index b73b38385f22d..89180ffdd5f40 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java @@ -55,6 +55,7 @@ public abstract class WindowsTextUI extends BasicTextUI { * * @return the caret object */ + @Override protected Caret createCaret() { return new WindowsCaret(); } @@ -64,20 +65,21 @@ protected Caret createCaret() { /* public */ @SuppressWarnings("serial") // Superclass is not serializable across versions - static class WindowsCaret extends DefaultCaret + static final class WindowsCaret extends DefaultCaret implements UIResource { /** * Gets the painter for the Highlighter. * * @return the painter */ + @Override protected Highlighter.HighlightPainter getSelectionPainter() { return WindowsTextUI.WindowsPainter; } } /* public */ - static class WindowsHighlightPainter extends + static final class WindowsHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter { WindowsHighlightPainter(Color c) { super(c); @@ -94,6 +96,7 @@ static class WindowsHighlightPainter extends * @param bounds the bounding box for the highlight * @param c the editor */ + @Override @SuppressWarnings("deprecation") public void paint(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) { Rectangle alloc = bounds.getBounds(); @@ -167,6 +170,7 @@ else if (secondIsDot) { * @param view View painting for * @return region drawing occurred in */ + @Override public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) { Color color = getColor(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java index feee0c0aaefec..0f468fdf6b109 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java @@ -45,7 +45,7 @@ * * @author Jeff Dinkins */ -public class WindowsToggleButtonUI extends BasicToggleButtonUI +public final class WindowsToggleButtonUI extends BasicToggleButtonUI { protected int dashedRectGapX; protected int dashedRectGapY; @@ -73,6 +73,7 @@ public static ComponentUI createUI(JComponent b) { // ******************************** // Defaults // ******************************** + @Override protected void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -93,6 +94,7 @@ protected void installDefaults(AbstractButton b) { } } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -112,6 +114,7 @@ protected Color getFocusColor() { private transient Color cachedBackgroundColor = null; private transient Color cachedHighlightColor = null; + @Override protected void paintButtonPressed(Graphics g, AbstractButton b) { if (XPStyle.getXP() == null && b.isContentAreaFilled()) { Color oldColor = g.getColor(); @@ -135,6 +138,7 @@ protected void paintButtonPressed(Graphics g, AbstractButton b) { } } + @Override public void paint(Graphics g, JComponent c) { if (XPStyle.getXP() != null) { WindowsButtonUI.paintXPButtonBackground(g, c); @@ -146,10 +150,12 @@ public void paint(Graphics g, JComponent c) { /** * Overridden method to render the text without the mnemonic */ + @Override protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) { WindowsGraphicsUtils.paintText(g, b, textRect, text, getTextShiftOffset()); } + @Override protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, Rectangle textRect, Rectangle iconRect) { g.setColor(getFocusColor()); @@ -161,6 +167,7 @@ protected void paintFocus(Graphics g, AbstractButton b, // ******************************** // Layout Methods // ******************************** + @Override public Dimension getPreferredSize(JComponent c) { Dimension d = super.getPreferredSize(c); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java index ffd6daa3ad8c6..5350de9ae5c65 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java @@ -40,12 +40,13 @@ * * @author Mark Davidson */ -public class WindowsToolBarSeparatorUI extends BasicToolBarSeparatorUI { +public final class WindowsToolBarSeparatorUI extends BasicToolBarSeparatorUI { public static ComponentUI createUI( JComponent c ) { return new WindowsToolBarSeparatorUI(); } + @Override public Dimension getPreferredSize(JComponent c) { Dimension size = ((JToolBar.Separator)c).getSeparatorSize(); @@ -71,6 +72,7 @@ public Dimension getPreferredSize(JComponent c) { return size; } + @Override public Dimension getMaximumSize(JComponent c) { Dimension pref = getPreferredSize(c); if (((JSeparator)c).getOrientation() == SwingConstants.VERTICAL) { @@ -80,6 +82,7 @@ public Dimension getMaximumSize(JComponent c) { } } + @Override public void paint( Graphics g, JComponent c ) { boolean vertical = ((JSeparator)c).getOrientation() == SwingConstants.VERTICAL; Dimension size = c.getSize(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java index a34ce0cf6a595..68e077fa35033 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java @@ -45,12 +45,13 @@ import static com.sun.java.swing.plaf.windows.TMSchema.Part; -public class WindowsToolBarUI extends BasicToolBarUI { +public final class WindowsToolBarUI extends BasicToolBarUI { public static ComponentUI createUI(JComponent c) { return new WindowsToolBarUI(); } + @Override protected void installDefaults() { if (XPStyle.getXP() != null) { setRolloverBorders(true); @@ -58,6 +59,7 @@ protected void installDefaults() { super.installDefaults(); } + @Override protected Border createRolloverBorder() { if (XPStyle.getXP() != null) { return new EmptyBorder(3, 3, 3, 3); @@ -66,6 +68,7 @@ protected Border createRolloverBorder() { } } + @Override protected Border createNonRolloverBorder() { if (XPStyle.getXP() != null) { return new EmptyBorder(3, 3, 3, 3); @@ -74,6 +77,7 @@ protected Border createNonRolloverBorder() { } } + @Override public void paint(Graphics g, JComponent c) { XPStyle xp = XPStyle.getXP(); if (xp != null) { @@ -88,6 +92,7 @@ public void paint(Graphics g, JComponent c) { * {@inheritDoc} * @since 1.6 */ + @Override protected Border getRolloverBorder(AbstractButton b) { XPStyle xp = XPStyle.getXP(); if (xp != null) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java index 5bef0e075a44a..6ab2352641f32 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java @@ -60,6 +60,7 @@ public static ComponentUI createUI( JComponent c ) * Ensures that the rows identified by beginRow through endRow are * visible. */ + @Override protected void ensureRowsAreVisible(int beginRow, int endRow) { if(tree != null && beginRow >= 0 && endRow < getRowCount(tree)) { Rectangle visRect = tree.getVisibleRect(); @@ -108,6 +109,7 @@ protected void ensureRowsAreVisible(int beginRow, int endRow) { * Returns the default cell renderer that is used to do the * stamping of each node. */ + @Override protected TreeCellRenderer createDefaultCellRenderer() { return new WindowsTreeCellRenderer(); } @@ -127,6 +129,7 @@ Skin getSkin(Component c) { return (xp != null) ? xp.getSkin(c, Part.TVP_GLYPH) : null; } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { Skin skin = getSkin(c); if (skin != null) { @@ -147,11 +150,13 @@ public void paintIcon(Component c, Graphics g, int x, int y) { g.drawLine(x + 2, y + HALF_SIZE, x + (SIZE - 3), y + HALF_SIZE); } + @Override public int getIconWidth() { Skin skin = getSkin(null); return (skin != null) ? skin.getWidth() : SIZE; } + @Override public int getIconHeight() { Skin skin = getSkin(null); return (skin != null) ? skin.getHeight() : SIZE; @@ -162,11 +167,12 @@ public int getIconHeight() { * The plus sign button icon */ @SuppressWarnings("serial") // Superclass is not serializable across versions - public static class CollapsedIcon extends ExpandedIcon { + public static final class CollapsedIcon extends ExpandedIcon { public static Icon createCollapsedIcon() { return new CollapsedIcon(); } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { Skin skin = getSkin(c); if (skin != null) { @@ -179,7 +185,7 @@ public void paintIcon(Component c, Graphics g, int x, int y) { } @SuppressWarnings("serial") // Superclass is not serializable across versions - public class WindowsTreeCellRenderer extends DefaultTreeCellRenderer { + public final class WindowsTreeCellRenderer extends DefaultTreeCellRenderer { /** * Configures the renderer based on the passed in components. @@ -189,6 +195,7 @@ public class WindowsTreeCellRenderer extends DefaultTreeCellRenderer { * The foreground color is set based on the selection and the icon * is set based on on leaf and expanded. */ + @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java index f8f5e3628fb06..89751f6dc94b7 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java @@ -90,7 +90,7 @@ * * @author Leif Samuelsson */ -class XPStyle { +final class XPStyle { // Singleton instance of this class private static XPStyle xp; @@ -331,6 +331,7 @@ private static class XPFillBorder extends LineBorder implements UIResource { super(color, thickness); } + @Override public Insets getBorderInsets(Component c, Insets insets) { Insets margin = null; // @@ -355,7 +356,7 @@ public Insets getBorderInsets(Component c, Insets insets) { } @SuppressWarnings("serial") // Superclass is not serializable across versions - private class XPStatefulFillBorder extends XPFillBorder { + private final class XPStatefulFillBorder extends XPFillBorder { private final Part part; private final Prop prop; XPStatefulFillBorder(Color color, int thickness, Part part, Prop prop) { @@ -364,6 +365,7 @@ private class XPStatefulFillBorder extends XPFillBorder { this.prop = prop; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { State state = State.NORMAL; // special casing for comboboxes. @@ -383,18 +385,20 @@ public void paintBorder(Component c, Graphics g, int x, int y, int width, int he } @SuppressWarnings("serial") // Superclass is not serializable across versions - private class XPImageBorder extends AbstractBorder implements UIResource { + private final class XPImageBorder extends AbstractBorder implements UIResource { Skin skin; XPImageBorder(Component c, Part part) { this.skin = getSkin(c, part); } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { skin.paintSkin(g, x, y, width, height, null); } + @Override public Insets getBorderInsets(Component c, Insets insets) { Insets margin = null; Insets borderInsets = skin.getContentMargin(); @@ -423,11 +427,12 @@ public Insets getBorderInsets(Component c, Insets insets) { } @SuppressWarnings("serial") // Superclass is not serializable across versions - private static class XPEmptyBorder extends EmptyBorder implements UIResource { + private static final class XPEmptyBorder extends EmptyBorder implements UIResource { XPEmptyBorder(Insets m) { super(m.top+2, m.left+2, m.bottom+2, m.right+2); } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets = super.getBorderInsets(c, insets); @@ -494,7 +499,7 @@ long getThemeTransitionDuration(Component c, Part part, State stateFrom, * (component type) and which provides methods for painting backgrounds * and glyphs */ - static class Skin { + static final class Skin { final Component component; final Part part; final State state; @@ -566,14 +571,17 @@ int getHeight() { return getHeight((state != null) ? state : State.NORMAL); } + @Override public String toString() { return string; } + @Override public boolean equals(Object obj) { return (obj instanceof Skin && ((Skin)obj).string.equals(string)); } + @Override public int hashCode() { return string.hashCode(); } @@ -675,16 +683,18 @@ void paintSkin(Graphics g, int dx, int dy, int dw, int dh, State state, } } - private static class SkinPainter extends CachedPainter { + private static final class SkinPainter extends CachedPainter { SkinPainter() { super(30); flush(); } + @Override public void flush() { super.flush(); } + @Override protected void paintToImage(Component c, Image image, Graphics g, int w, int h, Object[] args) { Skin skin = (Skin)args[0]; @@ -717,6 +727,7 @@ protected void paintToImage(Component c, Image image, Graphics g, SunWritableRaster.markDirty(dbi); } + @Override protected Image createImage(Component c, int w, int h, GraphicsConfiguration config, Object[] args) { return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); @@ -737,6 +748,7 @@ public GlyphButton(Component parent, Part part) { setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); } + @Override @SuppressWarnings("deprecation") public boolean isFocusTraversable() { return false; @@ -754,6 +766,7 @@ protected State getState() { return state; } + @Override public void paintComponent(Graphics g) { if (XPStyle.getXP() == null || skin == null) { return; @@ -769,6 +782,7 @@ public void setPart(Component parent, Part part) { repaint(); } + @Override protected void paintBorder(Graphics g) { } diff --git a/src/java.desktop/windows/classes/sun/awt/PlatformGraphicsInfo.java b/src/java.desktop/windows/classes/sun/awt/PlatformGraphicsInfo.java index 642f98b0df238..c888106d77962 100644 --- a/src/java.desktop/windows/classes/sun/awt/PlatformGraphicsInfo.java +++ b/src/java.desktop/windows/classes/sun/awt/PlatformGraphicsInfo.java @@ -30,7 +30,7 @@ import sun.awt.windows.WToolkit; -public class PlatformGraphicsInfo { +public final class PlatformGraphicsInfo { private static final boolean hasDisplays; diff --git a/src/java.desktop/windows/classes/sun/awt/Win32ColorModel24.java b/src/java.desktop/windows/classes/sun/awt/Win32ColorModel24.java index ec86bce8c69e1..fe55cacf8339d 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32ColorModel24.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32ColorModel24.java @@ -40,7 +40,7 @@ * in the reverse order from the base ComponentColorModel to match * the ordering on a Windows 24-bit display. */ -public class Win32ColorModel24 extends ComponentColorModel { +public final class Win32ColorModel24 extends ComponentColorModel { public Win32ColorModel24() { super(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8, 8, 8}, false, false, @@ -53,6 +53,7 @@ public Win32ColorModel24() { * @see WritableRaster * @see SampleModel */ + @Override public WritableRaster createCompatibleWritableRaster (int w, int h) { int[] bOffs = {2, 1, 0}; return Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, @@ -65,6 +66,7 @@ public WritableRaster createCompatibleWritableRaster (int w, int h) { * has a data layout compatible with this ColorModel. * @see SampleModel */ + @Override public SampleModel createCompatibleSampleModel(int w, int h) { int[] bOffs = {2, 1, 0}; return new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, diff --git a/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java b/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java index c2532ff38b381..5084d0bcabe02 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java @@ -70,6 +70,7 @@ public final class Win32FontManager extends SunFontManager { */ private static native String getEUDCFontFile(); + @Override public TrueTypeFont getEUDCFont() { return eudcFont; } @@ -88,6 +89,7 @@ public Win32FontManager() { * Whether registerFontFile expects absolute or relative * font file names. */ + @Override protected boolean useAbsoluteFontFileNames() { return false; } @@ -98,6 +100,7 @@ protected boolean useAbsoluteFontFileNames() { * class reports these back to the GraphicsEnvironment, so these * are the componentFileNames of CompositeFonts. */ + @Override protected void registerFontFile(String fontFileName, String[] nativeNames, int fontRank, boolean defer) { @@ -175,6 +178,7 @@ public FontConfiguration createFontConfiguration(boolean preferLocaleFonts, preferLocaleFonts,preferPropFonts); } + @Override protected void populateFontFileNameMap(HashMap fontToFileMap, HashMap fontToFamilyNameMap, @@ -194,6 +198,7 @@ public FontConfiguration createFontConfiguration(boolean preferLocaleFonts, familyToFontListMap, Locale locale); + @Override protected synchronized native String getFontPath(boolean noType1Fonts); @Override diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java index 6e740de6a9bd4..31fc0847d3b24 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsConfig.java @@ -214,6 +214,7 @@ public AffineTransform getNormalizingTransform() { return new AffineTransform(xscale, 0.0, 0.0, yscale, 0.0, 0.0); } + @Override public String toString() { return (super.toString()+"[dev="+device+",pixfmt="+visual+"]"); } diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java index 973eb916a5cab..fedc7f3898066 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java @@ -89,7 +89,8 @@ public class Win32GraphicsDevice extends GraphicsDevice implements private float scaleX; private float scaleY; - final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache(); + final SurfaceManager.ProxyCache surfaceDataProxyCache = + new SurfaceManager.ProxyCache(); static { @@ -590,7 +591,7 @@ public ColorModel getColorModel() { * The listener restores the default display mode when window is iconified * and sets it back to the one set by the user on de-iconification. */ - private static class Win32FSWindowAdapter extends WindowAdapter { + private static final class Win32FSWindowAdapter extends WindowAdapter { private Win32GraphicsDevice device; private DisplayMode dm; diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java index cb7ab363cdfca..2e11d5510590d 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java @@ -35,8 +35,6 @@ import sun.awt.windows.WToolkit; import sun.java2d.SunGraphicsEnvironment; -import sun.java2d.SurfaceManagerFactory; -import sun.java2d.WindowsSurfaceManagerFactory; import sun.java2d.d3d.D3DGraphicsDevice; import sun.java2d.windows.WindowsFlags; @@ -63,9 +61,6 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment { initDisplay(); - // Install correct surface manager factory. - SurfaceManagerFactory.setInstance(new WindowsSurfaceManagerFactory()); - double sx = -1; double sy = -1; if (isUIScaleEnabled()) { @@ -92,9 +87,11 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment { public Win32GraphicsEnvironment() { } + @Override protected native int getNumScreens(); private native int getDefaultScreen(); + @Override public GraphicsDevice getDefaultScreenDevice() { GraphicsDevice[] screens = getScreenDevices(); if (screens.length == 0) { @@ -207,6 +204,7 @@ public void displayChanged() { * ----END DISPLAY CHANGE SUPPORT---- */ + @Override protected GraphicsDevice makeScreenDevice(int screennum) { GraphicsDevice device = null; if (WindowsFlags.isD3DEnabled()) { @@ -218,6 +216,7 @@ protected GraphicsDevice makeScreenDevice(int screennum) { return device; } + @Override public boolean isDisplayLocal() { return true; } diff --git a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java index c85c2d4ced93a..1777e408ea6a7 100644 --- a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java +++ b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java @@ -212,7 +212,7 @@ static final class KnownLibraries { static final List INSTANCE = getLibraries(); } - static class FolderDisposer implements sun.java2d.DisposerRecord { + static final class FolderDisposer implements sun.java2d.DisposerRecord { /* * This is cached as a concession to getFolderType(), which needs * an absolute PIDL. @@ -226,6 +226,7 @@ static class FolderDisposer implements sun.java2d.DisposerRecord { long relativePIDL; boolean disposed; + @Override public void dispose() { if (disposed) return; invoke(new Callable() { @@ -387,6 +388,7 @@ public void setIsPersonal() { * is a not a normal directory, then returns the first non-removable * drive (normally "C:\"). */ + @Override @Serial protected Object writeReplace() throws java.io.ObjectStreamException { return invoke(new Callable() { @@ -541,6 +543,7 @@ private static boolean pathsEqual(String path1, String path2) { /** * Check to see if two ShellFolder objects are the same */ + @Override public boolean equals(Object o) { if (!(o instanceof Win32ShellFolder2 rhs)) { // Short-circuit circuitous delegation path @@ -588,6 +591,7 @@ public Boolean call() { /** * @return Whether this is a file system shell folder */ + @Override public boolean isFileSystem() { if (cachedIsFileSystem == null) { cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM); @@ -682,10 +686,12 @@ private static boolean isNetworkRoot(String path) { * @return The parent shell folder of this shell folder, null if * there is no parent */ + @Override public File getParentFile() { return parent; } + @Override public boolean isDirectory() { if (isDir == null) { // Folders with SFGAO_BROWSABLE have "shell extension" handlers and are @@ -742,6 +748,7 @@ private native long getEnumObjects(long pIShellFolder, boolean isDesktop, * object. The array will be empty if the folder is empty. Returns * {@code null} if this shellfolder does not denote a directory. */ + @Override public File[] listFiles(final boolean includeHiddenFiles) { try { @@ -851,6 +858,7 @@ public Win32ShellFolder2 call() throws InterruptedException { * symbolic links and junctions. */ + @Override public boolean isLink() { if (cachedIsLink == null) { cachedIsLink = hasAttribute(ATTRIB_LINK) @@ -864,6 +872,7 @@ public boolean isLink() { /** * @return Whether this shell folder is marked as hidden */ + @Override public boolean isHidden() { return hasAttribute(ATTRIB_HIDDEN); } @@ -878,6 +887,7 @@ private static native long getLinkLocation(long parentIShellFolder, * @return The shell folder linked to by this shell folder, or null * if this shell folder is not a link or is a broken or invalid link */ + @Override public ShellFolder getLinkLocation() { return getLinkLocation(true); } @@ -933,6 +943,7 @@ private static native String getDisplayNameOf(long parentIShellFolder, /** * @return The name used to display this shell folder */ + @Override public String getDisplayName() { if (displayName == null) { displayName = @@ -953,6 +964,7 @@ public String call() { /** * @return The type of shell folder as a string */ + @Override public String getFolderType() { if (folderType == null) { final long absolutePIDL = getAbsolutePIDL(); @@ -972,6 +984,7 @@ public String call() { /** * @return The executable type as a string */ + @Override public String getExecutableType() { if (!isFileSystem()) { return null; @@ -1047,6 +1060,7 @@ private static Image makeIcon(long hIcon) { /** * @return The icon image used to display this shell folder */ + @Override public Image getIcon(final boolean getLargeIcon) { Image icon = getLargeIcon ? largeIcon : smallIcon; int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE; @@ -1140,6 +1154,7 @@ public Image call() { /** * @return The icon image of specified size used to display this shell folder */ + @Override public Image getIcon(int width, int height) { int size = Math.max(width, height); return invoke(() -> { @@ -1236,6 +1251,7 @@ static Image getShell32Icon(int iconID, int size) { * * @see java.io.File#getCanonicalFile */ + @Override public File getCanonicalFile() throws IOException { return this; } @@ -1252,6 +1268,7 @@ public boolean isSpecial() { * * @see sun.awt.shell.ShellFolder#compareTo(File) */ + @Override public int compareTo(File file2) { if (!(file2 instanceof Win32ShellFolder2)) { if (isFileSystem() && !isSpecial()) { @@ -1268,6 +1285,7 @@ public int compareTo(File file2) { private static final int LVCFMT_RIGHT = 1; private static final int LVCFMT_CENTER = 2; + @Override public ShellFolderColumnInfo[] getFolderColumns() { ShellFolder library = resolveLibrary(); if (library != null) return library.getFolderColumns(); @@ -1300,6 +1318,7 @@ public ShellFolderColumnInfo[] call() { }); } + @Override public Object getFolderColumnValue(final int column) { if(!isLibrary()) { ShellFolder library = resolveLibrary(); @@ -1342,6 +1361,7 @@ private ShellFolder resolveLibrary() { private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx); + @Override public void sortChildren(final List files) { // To avoid loads of synchronizations with Invoker and improve performance we // synchronize the whole code of the sort method once @@ -1354,7 +1374,7 @@ public Void call() { }); } - private static class ColumnComparator implements Comparator { + private static final class ColumnComparator implements Comparator { private final Win32ShellFolder2 shellFolder; private final int columnIdx; @@ -1365,6 +1385,7 @@ public ColumnComparator(Win32ShellFolder2 shellFolder, int columnIdx) { } // compares 2 objects within this folder by the specified column + @Override public int compare(final File o, final File o1) { Integer result = invoke(new Callable() { public Integer call() { @@ -1405,7 +1426,7 @@ public List call() throws Exception { }); } - static class MultiResolutionIconImage extends AbstractMultiResolutionImage { + static final class MultiResolutionIconImage extends AbstractMultiResolutionImage { final int baseSize; final Map resolutionVariants = new HashMap<>(); diff --git a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java index d8bb2146798c9..075e3d0db50f5 100644 --- a/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java +++ b/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java @@ -75,6 +75,7 @@ final class Win32ShellFolderManager2 extends ShellFolderManager { sun.awt.windows.WToolkit.loadLibraries(); } + @Override public ShellFolder createShellFolder(File file) throws FileNotFoundException { try { return createShellFolder(getDesktop(), file); @@ -264,6 +265,7 @@ static Win32ShellFolder2 getPersonal() { * * @return An Object matching the key string. */ + @Override public Object get(String key) { if (key.equals("fileChooserDefaultFolder")) { File file = getPersonal(); @@ -408,6 +410,7 @@ public Object get(String key) { * Does {@code dir} represent a "computer" such as a node on the network, or * "My Computer" on the desktop. */ + @Override public boolean isComputerNode(final File dir) { if (dir != null && dir == getDrives()) { return true; @@ -418,6 +421,7 @@ public boolean isComputerNode(final File dir) { } } + @Override public boolean isFileSystemRoot(File dir) { //Note: Removable drives don't "exist" but are listed in "My Computer" if (dir != null) { @@ -498,7 +502,7 @@ protected Invoker createInvoker() { return new ComInvoker(); } - private static class ComInvoker extends ThreadPoolExecutor implements ThreadFactory, ShellFolder.Invoker { + private static final class ComInvoker extends ThreadPoolExecutor implements ThreadFactory, ShellFolder.Invoker { private static Thread comThread; private ComInvoker() { @@ -512,6 +516,7 @@ private ComInvoker() { Runtime.getRuntime().addShutdownHook(t); } + @Override public synchronized Thread newThread(final Runnable task) { final Runnable comRun = new Runnable() { public void run() { @@ -539,6 +544,7 @@ leads to memory consumption when listDrives() function is called return comThread; } + @Override public T invoke(Callable task) throws Exception { if (Thread.currentThread() == comThread) { // if it's already called from the COM diff --git a/src/java.desktop/windows/classes/sun/awt/windows/TranslucentWindowPainter.java b/src/java.desktop/windows/classes/sun/awt/windows/TranslucentWindowPainter.java index 6b9d54aaa28af..94a2c4df0a9a5 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/TranslucentWindowPainter.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/TranslucentWindowPainter.java @@ -355,7 +355,7 @@ public void run() { } } - private static class VIOptD3DWindowPainter extends VIOptWindowPainter { + private static final class VIOptD3DWindowPainter extends VIOptWindowPainter { protected VIOptD3DWindowPainter(WWindowPeer peer) { super(peer); @@ -370,7 +370,7 @@ protected boolean updateWindowAccel(long psdops, int w, int h) { } } - private static class VIOptWGLWindowPainter extends VIOptWindowPainter { + private static final class VIOptWGLWindowPainter extends VIOptWindowPainter { protected VIOptWGLWindowPainter(WWindowPeer peer) { super(peer); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java index c07d116abfbcb..8b3218de6674d 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java @@ -789,6 +789,7 @@ public VolatileImage createVolatileImage(int width, int height) { // Object overrides + @Override public String toString() { return getClass().getName() + "[" + target + "]"; } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDataTransferer.java b/src/java.desktop/windows/classes/sun/awt/windows/WDataTransferer.java index 8aca12d268f05..068d577995f83 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDataTransferer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDataTransferer.java @@ -541,7 +541,7 @@ public static EHTMLReadMode getEHTMLReadMode (DataFlavor df) { * * on encode: static convertToHTMLFormat is responsible for HTML clipboard header creation */ -class HTMLCodec extends InputStream { +final class HTMLCodec extends InputStream { public static final String VERSION = "Version:"; public static final String START_HTML = "StartHTML:"; diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDefaultFontCharset.java b/src/java.desktop/windows/classes/sun/awt/windows/WDefaultFontCharset.java index 7632e172b73c7..09c317c81f209 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDefaultFontCharset.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDefaultFontCharset.java @@ -46,7 +46,7 @@ public CharsetEncoder newEncoder() { return new Encoder(); } - private class Encoder extends AWTCharset.Encoder { + private final class Encoder extends AWTCharset.Encoder { @Override public boolean canEncode(char c){ return canConvert(c); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopProperties.java b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopProperties.java index d9b169160b4f2..6e3de090ee8f3 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopProperties.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopProperties.java @@ -198,7 +198,7 @@ private synchronized void setSoundProperty(String key, String winEventName) { */ private native void playWindowsSound(String winEventName); - class WinPlaySound implements Runnable { + final class WinPlaySound implements Runnable { String winEventName; WinPlaySound(String winEventName) { @@ -210,10 +210,12 @@ public void run() { WDesktopProperties.this.playWindowsSound(winEventName); } + @Override public String toString() { return "WinPlaySound("+winEventName+")"; } + @Override public boolean equals(Object o) { if (o == this) { return true; @@ -225,6 +227,7 @@ public boolean equals(Object o) { } } + @Override public int hashCode() { return winEventName.hashCode(); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDragSourceContextPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WDragSourceContextPeer.java index f4ab5fe0a34e7..43a91c3c7abbd 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDragSourceContextPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDragSourceContextPeer.java @@ -54,9 +54,11 @@ */ final class WDragSourceContextPeer extends SunDragSourceContextPeer { + @Override public void startSecondaryEventLoop(){ WToolkit.startSecondaryEventLoop(); } + @Override public void quitSecondaryEventLoop(){ WToolkit.quitSecondaryEventLoop(); } @@ -168,6 +170,7 @@ native void doDragDrop( int imgWidth, int imgHight, int offsetX, int offsetY); + @Override protected native void setNativeCursor(long nativeCtxt, Cursor c, int cType); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java b/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java index e5db1d432cf6d..ad0745a273314 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFrame.java @@ -36,7 +36,7 @@ import java.awt.peer.FramePeer; @SuppressWarnings("serial") // JDK-implementation class -public class WEmbeddedFrame extends EmbeddedFrame { +public final class WEmbeddedFrame extends EmbeddedFrame { static { initIDs(); @@ -79,6 +79,7 @@ public WEmbeddedFrame(long handle) { } } + @Override public void addNotify() { if (!isDisplayable()) { WToolkit toolkit = (WToolkit)Toolkit.getDefaultToolkit(); @@ -224,6 +225,7 @@ private native void printBand(long hdc, byte[] data, int offset, int sx, public void activateEmbeddingTopLevel() { } + @Override public void synthesizeWindowActivation(final boolean activate) { final FramePeer peer = AWTAccessor.getComponentAccessor().getPeer(this); if (!activate || EventQueue.isDispatchThread()) { @@ -241,7 +243,9 @@ public void run() { } } + @Override public void registerAccelerator(AWTKeyStroke stroke) {} + @Override public void unregisterAccelerator(AWTKeyStroke stroke) {} /** @@ -256,6 +260,7 @@ public void unregisterAccelerator(AWTKeyStroke stroke) {} * NOTE: This method may be called by privileged threads. * DO NOT INVOKE CLIENT CODE ON THIS THREAD! */ + @Override public void notifyModalBlocked(Dialog blocker, boolean blocked) { try { ComponentPeer thisPeer = (ComponentPeer)WToolkit.targetToPeer(this); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFramePeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFramePeer.java index 4d5219cf34e16..bab79c38ff4db 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFramePeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WEmbeddedFramePeer.java @@ -32,7 +32,7 @@ import sun.awt.EmbeddedFrame; import sun.awt.Win32GraphicsEnvironment; -public class WEmbeddedFramePeer extends WFramePeer { +public final class WEmbeddedFramePeer extends WFramePeer { public WEmbeddedFramePeer(EmbeddedFrame target) { super(target); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WLabelPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WLabelPeer.java index 259329d4f6d33..c5267e52793c9 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WLabelPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WLabelPeer.java @@ -31,6 +31,7 @@ final class WLabelPeer extends WComponentPeer implements LabelPeer { // ComponentPeer overrides + @Override public Dimension getMinimumSize() { FontMetrics fm = getFontMetrics(((Label)target).getFont()); String label = ((Label)target).getText(); @@ -40,6 +41,7 @@ public Dimension getMinimumSize() { } native void lazyPaint(); + @Override synchronized void start() { super.start(); // if need then paint label @@ -47,11 +49,14 @@ synchronized void start() { } // LabelPeer implementation + @Override public boolean shouldClearRectBeforePaint() { return false; } + @Override public native void setText(String label); + @Override public native void setAlignment(int alignment); // Toolkit & peer internals @@ -60,8 +65,10 @@ public boolean shouldClearRectBeforePaint() { super(target); } + @Override native void create(WComponentPeer parent); + @Override void initialize() { Label l = (Label)target; diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WLightweightFramePeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WLightweightFramePeer.java index ff77a5c188c94..994b81e372eb4 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WLightweightFramePeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WLightweightFramePeer.java @@ -36,7 +36,7 @@ import sun.swing.JLightweightFrame; import sun.swing.SwingAccessor; -public class WLightweightFramePeer extends WFramePeer implements OverrideNativeWindowHandle { +public final class WLightweightFramePeer extends WFramePeer implements OverrideNativeWindowHandle { public WLightweightFramePeer(LightweightFrame target) { super(target); @@ -100,6 +100,7 @@ public void updateCursorImmediately() { SwingAccessor.getJLightweightFrameAccessor().updateCursor((JLightweightFrame)getLwTarget()); } + @Override public boolean isLightweightFramePeer() { return true; } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java index 43d4d2b907a9b..f2961f79a0460 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WMenuItemPeer.java @@ -46,11 +46,13 @@ class WMenuItemPeer extends WObjectPeer implements MenuItemPeer { // MenuItemPeer implementation private synchronized native void _dispose(); + @Override protected void disposeImpl() { WToolkit.targetDisposedPeer(target, this); _dispose(); } + @Override public void setEnabled(boolean b) { enable(b); } @@ -69,6 +71,7 @@ private void readShortcutLabel() { } } + @Override public void setLabel(String label) { //Fix for 6288578: PIT. Windows: Shortcuts displayed for the menuitems in a popup menu readShortcutLabel(); @@ -165,6 +168,7 @@ static Font getDefaultFont() { private native void _setFont(Font f); + @Override public void setFont(final Font f) { _setFont(f); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WMouseInfoPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WMouseInfoPeer.java index e5bef4fe1b98b..4b785aaba6927 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WMouseInfoPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WMouseInfoPeer.java @@ -43,8 +43,10 @@ public final class WMouseInfoPeer implements MouseInfoPeer { WMouseInfoPeer() { } + @Override public native int fillPointWithCoords(Point point); + @Override public native boolean isWindowUnderMouse(Window w); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WPopupMenuPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WPopupMenuPeer.java index 69b6232567027..d89cbcfa53aaa 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WPopupMenuPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WPopupMenuPeer.java @@ -70,6 +70,7 @@ final class WPopupMenuPeer extends WMenuPeer implements PopupMenuPeer { private native void createMenu(WComponentPeer parent); + @Override @SuppressWarnings("deprecation") public void show(Event e) { Component origin = (Component)e.target; diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java index d7efc54ffd473..e50cfcff33b17 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java @@ -264,7 +264,7 @@ public final class WPrinterJob extends RasterPrinterJob /* The HandleRecord holds the native resources that need to be freed * when this WPrinterJob is GC'd. */ - static class HandleRecord implements DisposerRecord { + static final class HandleRecord implements DisposerRecord { /** * The Windows device context we will print into. * This variable is set after the Print dialog @@ -2269,7 +2269,7 @@ private void setPrinterNameAttrib(String printerName) { } @SuppressWarnings("serial") // JDK-implementation class -static class PrintToFileErrorDialog extends Dialog implements ActionListener { +static final class PrintToFileErrorDialog extends Dialog implements ActionListener { public PrintToFileErrorDialog(Frame parent, String title, String message, String buttonText) { super(parent, title, true); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WScrollPanePeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WScrollPanePeer.java index e305654d91f8d..3a0d7bf3a71c1 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WScrollPanePeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WScrollPanePeer.java @@ -167,7 +167,7 @@ private void postScrollEvent(int orient, int type, * operation. */ @SuppressWarnings("serial") // JDK-implementation class - static class ScrollEvent extends PeerEvent { + static final class ScrollEvent extends PeerEvent { ScrollEvent(Object source, Runnable runnable) { super(source, runnable, 0L); } @@ -187,7 +187,7 @@ public PeerEvent coalesceEvents(PeerEvent newEvent) { /* * Runnable for the ScrollEvent that performs the adjustment. */ - class Adjustor implements Runnable { + final class Adjustor implements Runnable { int orient; // selects scrollbar int type; // adjustment type int pos; // new position (only used for absolute) diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WScrollbarPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WScrollbarPeer.java index 055f2523cea26..cfc227d29d6e4 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WScrollbarPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WScrollbarPeer.java @@ -35,6 +35,7 @@ final class WScrollbarPeer extends WComponentPeer implements ScrollbarPeer { static native int getScrollbarSize(int orientation); // ComponentPeer overrides + @Override public Dimension getMinimumSize() { if (((Scrollbar)target).getOrientation() == Scrollbar.VERTICAL) { return new Dimension(getScrollbarSize(Scrollbar.VERTICAL), 50); @@ -46,9 +47,12 @@ public Dimension getMinimumSize() { // ScrollbarPeer implementation + @Override public native void setValues(int value, int visible, int minimum, int maximum); + @Override public native void setLineIncrement(int l); + @Override public native void setPageIncrement(int l); @@ -58,8 +62,10 @@ public native void setValues(int value, int visible, super(target); } + @Override native void create(WComponentPeer parent); + @Override void initialize() { Scrollbar sb = (Scrollbar)target; setValues(sb.getValue(), sb.getVisibleAmount(), @@ -136,6 +142,7 @@ public void run() { }); } + @Override public boolean shouldClearRectBeforePaint() { return false; } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java index e151a69a0f110..0fdc4c6005ba0 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WToolkit.java @@ -186,7 +186,7 @@ public static void loadLibraries() { } } - static class ToolkitDisposer implements sun.java2d.DisposerRecord { + static final class ToolkitDisposer implements sun.java2d.DisposerRecord { @Override public void dispose() { WToolkit.postDispose(); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java index 90fbd880446f3..d6c311d3f48c7 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WTrayIconPeer.java @@ -199,7 +199,7 @@ native void setNativeIcon(int[] rData, byte[] andMask, int nScanStride, native void _displayMessage(String caption, String text, String messageType); - class IconObserver implements ImageObserver { + final class IconObserver implements ImageObserver { @Override public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height) { if (image != ((TrayIcon)target).getImage() || // if the image has been changed diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java index 997db36beac06..0ba2217dde598 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java @@ -849,7 +849,7 @@ private static void initActiveWindowsTracking(Window w) { * it removes the list of the active windows from the disposed AppContext and * unregisters ActiveWindowListener listener. */ - private static class GuiDisposedListener implements PropertyChangeListener { + private static final class GuiDisposedListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent e) { boolean isDisposed = (Boolean)e.getNewValue(); @@ -875,7 +875,7 @@ public void propertyChange(PropertyChangeEvent e) { * window is always at the end of the list. The list is stored in AppContext. */ @SuppressWarnings("unchecked") - private static class ActiveWindowListener implements PropertyChangeListener { + private static final class ActiveWindowListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent e) { Window w = (Window)e.getNewValue(); diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WingDings.java b/src/java.desktop/windows/classes/sun/awt/windows/WingDings.java index 55c5236594c53..ae7573ac4e28a 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WingDings.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WingDings.java @@ -52,7 +52,7 @@ public boolean contains(Charset cs) { return cs instanceof WingDings; } - private static class Encoder extends CharsetEncoder { + private static final class Encoder extends CharsetEncoder { public Encoder(Charset cs) { super(cs, 1.0f, 1.0f); } diff --git a/src/java.desktop/windows/classes/sun/font/NativeFont.java b/src/java.desktop/windows/classes/sun/font/NativeFont.java index 7a5c29c7c73e0..9b0e6492d6738 100644 --- a/src/java.desktop/windows/classes/sun/font/NativeFont.java +++ b/src/java.desktop/windows/classes/sun/font/NativeFont.java @@ -37,7 +37,7 @@ * and the font is ignored. */ -public class NativeFont extends PhysicalFont { +public final class NativeFont extends PhysicalFont { /** * Verifies native font is accessible. @@ -53,6 +53,7 @@ static boolean hasExternalBitmaps(String platName) { return false; } + @Override public CharToGlyphMapper getMapper() { return null; } @@ -61,6 +62,7 @@ PhysicalFont getDelegateFont() { return null; } + @Override FontStrike createStrike(FontStrikeDesc desc) { return null; } @@ -69,16 +71,19 @@ public Rectangle2D getMaxCharBounds(FontRenderContext frc) { return null; } + @Override StrikeMetrics getFontMetrics(long pScalerContext) { return null; } + @Override public GeneralPath getGlyphOutline(long pScalerContext, int glyphCode, float x, float y) { return null; } + @Override public GeneralPath getGlyphVectorOutline(long pScalerContext, int[] glyphs, int numGlyphs, float x, float y) { @@ -86,20 +91,24 @@ public GeneralPath getGlyphVectorOutline(long pScalerContext, } + @Override long getGlyphImage(long pScalerContext, int glyphCode) { return 0L; } + @Override void getGlyphMetrics(long pScalerContext, int glyphCode, Point2D.Float metrics) { } + @Override float getGlyphAdvance(long pScalerContext, int glyphCode) { return 0f; } + @Override Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, int glyphCode) { return new Rectangle2D.Float(0f, 0f, 0f, 0f); diff --git a/src/java.desktop/windows/classes/sun/font/NativeStrike.java b/src/java.desktop/windows/classes/sun/font/NativeStrike.java index a28c47911ed47..7b2b947d722a7 100644 --- a/src/java.desktop/windows/classes/sun/font/NativeStrike.java +++ b/src/java.desktop/windows/classes/sun/font/NativeStrike.java @@ -48,9 +48,11 @@ public class NativeStrike extends PhysicalStrike { } + @Override void getGlyphImagePtrs(int[] glyphCodes, long[] images,int len) { } + @Override long getGlyphImagePtr(int glyphCode) { return 0L; } @@ -59,26 +61,32 @@ long getGlyphImagePtrNoCache(int glyphCode) { return 0L; } + @Override void getGlyphImageBounds(int glyphcode, Point2D.Float pt, Rectangle result) { } + @Override Point2D.Float getGlyphMetrics(int glyphCode) { return null; } + @Override float getGlyphAdvance(int glyphCode) { return 0f; } + @Override Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) { return null; } + @Override GeneralPath getGlyphOutline(int glyphCode, float x, float y) { return null; } + @Override GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) { return null; } diff --git a/src/java.desktop/windows/classes/sun/java2d/WindowsSurfaceManagerFactory.java b/src/java.desktop/windows/classes/sun/java2d/WindowsSurfaceManagerFactory.java deleted file mode 100644 index 4512492350d9a..0000000000000 --- a/src/java.desktop/windows/classes/sun/java2d/WindowsSurfaceManagerFactory.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. - * 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.java2d; - -import java.awt.GraphicsConfiguration; -import sun.awt.image.BufImgVolatileSurfaceManager; -import sun.awt.image.SunVolatileImage; -import sun.awt.image.VolatileSurfaceManager; -import sun.java2d.d3d.D3DGraphicsConfig; -import sun.java2d.d3d.D3DVolatileSurfaceManager; -import sun.java2d.opengl.WGLGraphicsConfig; -import sun.java2d.opengl.WGLVolatileSurfaceManager; - -/** - * The SurfaceManagerFactory that creates VolatileSurfaceManager - * implementations for the Windows volatile images. - */ -public class WindowsSurfaceManagerFactory extends SurfaceManagerFactory { - - /** - * Creates a new instance of a VolatileSurfaceManager given any - * arbitrary SunVolatileImage. An optional context Object can be supplied - * as a way for the caller to pass pipeline-specific context data to - * the VolatileSurfaceManager (such as a backbuffer handle, for example). - * - * For Windows platforms, this method returns a Windows-specific - * VolatileSurfaceManager. - */ - public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg, - Object context) - { - GraphicsConfiguration gc = vImg.getGraphicsConfig(); - if (gc instanceof D3DGraphicsConfig) { - return new D3DVolatileSurfaceManager(vImg, context); - } else if (gc instanceof WGLGraphicsConfig) { - return new WGLVolatileSurfaceManager(vImg, context); - } else { - return new BufImgVolatileSurfaceManager(vImg, context); - } - } - -} diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DBlitLoops.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DBlitLoops.java index 8f4da91eef26e..9aa9837b191d9 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DBlitLoops.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DBlitLoops.java @@ -358,7 +358,7 @@ static void IsoBlit(SurfaceData srcData, SurfaceData dstData, } } -class D3DSurfaceToSurfaceBlit extends Blit { +final class D3DSurfaceToSurfaceBlit extends Blit { D3DSurfaceToSurfaceBlit() { super(D3DSurfaceData.D3DSurface, @@ -366,6 +366,7 @@ class D3DSurfaceToSurfaceBlit extends Blit { D3DSurfaceData.D3DSurface); } + @Override public void Blit(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx, int sy, int dx, int dy, int w, int h) @@ -380,7 +381,7 @@ public void Blit(SurfaceData src, SurfaceData dst, } } -class D3DSurfaceToSurfaceScale extends ScaledBlit { +final class D3DSurfaceToSurfaceScale extends ScaledBlit { D3DSurfaceToSurfaceScale() { super(D3DSurfaceData.D3DSurface, @@ -388,6 +389,7 @@ class D3DSurfaceToSurfaceScale extends ScaledBlit { D3DSurfaceData.D3DSurface); } + @Override public void Scale(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx1, int sy1, @@ -405,7 +407,7 @@ public void Scale(SurfaceData src, SurfaceData dst, } } -class D3DSurfaceToSurfaceTransform extends TransformBlit { +final class D3DSurfaceToSurfaceTransform extends TransformBlit { D3DSurfaceToSurfaceTransform() { super(D3DSurfaceData.D3DSurface, @@ -413,6 +415,7 @@ class D3DSurfaceToSurfaceTransform extends TransformBlit { D3DSurfaceData.D3DSurface); } + @Override public void Transform(SurfaceData src, SurfaceData dst, Composite comp, Region clip, AffineTransform at, int hint, @@ -428,7 +431,7 @@ public void Transform(SurfaceData src, SurfaceData dst, } } -class D3DRTTSurfaceToSurfaceBlit extends Blit { +final class D3DRTTSurfaceToSurfaceBlit extends Blit { D3DRTTSurfaceToSurfaceBlit() { super(D3DSurfaceData.D3DSurfaceRTT, @@ -436,6 +439,7 @@ class D3DRTTSurfaceToSurfaceBlit extends Blit { D3DSurfaceData.D3DSurface); } + @Override public void Blit(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx, int sy, int dx, int dy, int w, int h) @@ -450,7 +454,7 @@ public void Blit(SurfaceData src, SurfaceData dst, } } -class D3DRTTSurfaceToSurfaceScale extends ScaledBlit { +final class D3DRTTSurfaceToSurfaceScale extends ScaledBlit { D3DRTTSurfaceToSurfaceScale() { super(D3DSurfaceData.D3DSurfaceRTT, @@ -458,6 +462,7 @@ class D3DRTTSurfaceToSurfaceScale extends ScaledBlit { D3DSurfaceData.D3DSurface); } + @Override public void Scale(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx1, int sy1, @@ -475,7 +480,7 @@ public void Scale(SurfaceData src, SurfaceData dst, } } -class D3DRTTSurfaceToSurfaceTransform extends TransformBlit { +final class D3DRTTSurfaceToSurfaceTransform extends TransformBlit { D3DRTTSurfaceToSurfaceTransform() { super(D3DSurfaceData.D3DSurfaceRTT, @@ -483,6 +488,7 @@ class D3DRTTSurfaceToSurfaceTransform extends TransformBlit { D3DSurfaceData.D3DSurface); } + @Override public void Transform(SurfaceData src, SurfaceData dst, Composite comp, Region clip, AffineTransform at, int hint, @@ -497,7 +503,7 @@ public void Transform(SurfaceData src, SurfaceData dst, } } -class D3DSurfaceToSwBlit extends Blit { +final class D3DSurfaceToSwBlit extends Blit { private int typeval; private WeakReference srcTmp; @@ -567,6 +573,7 @@ private synchronized void complexClipBlit(SurfaceData src, SurfaceData dst, } } + @Override public void Blit(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx, int sy, int dx, int dy, @@ -629,7 +636,7 @@ public void Blit(SurfaceData src, SurfaceData dst, } } -class D3DSwToSurfaceBlit extends Blit { +final class D3DSwToSurfaceBlit extends Blit { private int typeval; @@ -640,6 +647,7 @@ class D3DSwToSurfaceBlit extends Blit { this.typeval = typeval; } + @Override public void Blit(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx, int sy, int dx, int dy, int w, int h) @@ -653,7 +661,7 @@ public void Blit(SurfaceData src, SurfaceData dst, } } -class D3DSwToSurfaceScale extends ScaledBlit { +final class D3DSwToSurfaceScale extends ScaledBlit { private int typeval; @@ -664,6 +672,7 @@ class D3DSwToSurfaceScale extends ScaledBlit { this.typeval = typeval; } + @Override public void Scale(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx1, int sy1, @@ -680,7 +689,7 @@ public void Scale(SurfaceData src, SurfaceData dst, } } -class D3DSwToSurfaceTransform extends TransformBlit { +final class D3DSwToSurfaceTransform extends TransformBlit { private int typeval; @@ -691,6 +700,7 @@ class D3DSwToSurfaceTransform extends TransformBlit { this.typeval = typeval; } + @Override public void Transform(SurfaceData src, SurfaceData dst, Composite comp, Region clip, AffineTransform at, int hint, @@ -704,7 +714,7 @@ public void Transform(SurfaceData src, SurfaceData dst, } } -class D3DSwToTextureBlit extends Blit { +final class D3DSwToTextureBlit extends Blit { private int typeval; @@ -715,6 +725,7 @@ class D3DSwToTextureBlit extends Blit { this.typeval = typeval; } + @Override public void Blit(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx, int sy, int dx, int dy, int w, int h) @@ -728,7 +739,7 @@ public void Blit(SurfaceData src, SurfaceData dst, } } -class D3DTextureToSurfaceBlit extends Blit { +final class D3DTextureToSurfaceBlit extends Blit { D3DTextureToSurfaceBlit() { super(D3DSurfaceData.D3DTexture, @@ -736,6 +747,7 @@ class D3DTextureToSurfaceBlit extends Blit { D3DSurfaceData.D3DSurface); } + @Override public void Blit(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx, int sy, int dx, int dy, int w, int h) @@ -750,7 +762,7 @@ public void Blit(SurfaceData src, SurfaceData dst, } } -class D3DTextureToSurfaceScale extends ScaledBlit { +final class D3DTextureToSurfaceScale extends ScaledBlit { D3DTextureToSurfaceScale() { super(D3DSurfaceData.D3DTexture, @@ -758,6 +770,7 @@ class D3DTextureToSurfaceScale extends ScaledBlit { D3DSurfaceData.D3DSurface); } + @Override public void Scale(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx1, int sy1, @@ -775,7 +788,7 @@ public void Scale(SurfaceData src, SurfaceData dst, } } -class D3DTextureToSurfaceTransform extends TransformBlit { +final class D3DTextureToSurfaceTransform extends TransformBlit { D3DTextureToSurfaceTransform() { super(D3DSurfaceData.D3DTexture, @@ -783,6 +796,7 @@ class D3DTextureToSurfaceTransform extends TransformBlit { D3DSurfaceData.D3DSurface); } + @Override public void Transform(SurfaceData src, SurfaceData dst, Composite comp, Region clip, AffineTransform at, int hint, @@ -804,7 +818,7 @@ public void Transform(SurfaceData src, SurfaceData dst, * IntArgbPre->D3DSurface/Texture loop to get the intermediate * (premultiplied) surface down to D3D using simple blit. */ -class D3DGeneralBlit extends Blit { +final class D3DGeneralBlit extends Blit { private final Blit performop; private WeakReference srcTmp; @@ -817,6 +831,7 @@ class D3DGeneralBlit extends Blit { this.performop = performop; } + @Override public synchronized void Blit(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx, int sy, int dx, int dy, @@ -917,7 +932,7 @@ public synchronized void Transform(SurfaceData src, SurfaceData dst, * */ -class D3DSurfaceToGDIWindowSurfaceBlit extends Blit { +final class D3DSurfaceToGDIWindowSurfaceBlit extends Blit { D3DSurfaceToGDIWindowSurfaceBlit() { super(D3DSurfaceData.D3DSurface, @@ -935,7 +950,7 @@ public void Blit(SurfaceData src, SurfaceData dst, } -class D3DSurfaceToGDIWindowSurfaceScale extends ScaledBlit { +final class D3DSurfaceToGDIWindowSurfaceScale extends ScaledBlit { D3DSurfaceToGDIWindowSurfaceScale() { super(D3DSurfaceData.D3DSurface, @@ -955,7 +970,7 @@ public void Scale(SurfaceData src, SurfaceData dst, } } -class D3DSurfaceToGDIWindowSurfaceTransform extends TransformBlit { +final class D3DSurfaceToGDIWindowSurfaceTransform extends TransformBlit { D3DSurfaceToGDIWindowSurfaceTransform() { super(D3DSurfaceData.D3DSurface, diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DBufImgOps.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DBufImgOps.java index 890bb87d66b25..7ca0130391b2c 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DBufImgOps.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DBufImgOps.java @@ -37,7 +37,7 @@ import sun.java2d.pipe.BufferedBufImgOps; import static sun.java2d.d3d.D3DContext.D3DContextCaps.*; -class D3DBufImgOps extends BufferedBufImgOps { +final class D3DBufImgOps extends BufferedBufImgOps { /** * This method is called from D3DDrawImage.transformImage() only. It diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DContext.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DContext.java index 055e636bb6939..5d9b6c30b5bed 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DContext.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DContext.java @@ -106,7 +106,7 @@ D3DGraphicsDevice getDevice() { return device; } - static class D3DContextCaps extends ContextCapabilities { + static final class D3DContextCaps extends ContextCapabilities { /** * Indicates the presence of pixel shaders (v2.0 or greater). * This cap will only be set if the hardware supports the minimum number diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DDrawImage.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DDrawImage.java index f814dc2f6132e..e847c7ba7cd72 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DDrawImage.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DDrawImage.java @@ -37,7 +37,7 @@ import sun.java2d.loops.TransformBlit; import sun.java2d.pipe.DrawImage; -public class D3DDrawImage extends DrawImage { +public final class D3DDrawImage extends DrawImage { @Override protected void renderImageXform(SunGraphics2D sg, Image img, diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DGraphicsConfig.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DGraphicsConfig.java index 53d64017a3e4b..8b001317dcaff 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DGraphicsConfig.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DGraphicsConfig.java @@ -39,6 +39,7 @@ import sun.awt.Win32GraphicsConfig; import sun.awt.image.SunVolatileImage; import sun.awt.image.SurfaceManager; +import sun.awt.image.VolatileSurfaceManager; import sun.awt.windows.WComponentPeer; import sun.java2d.Surface; import sun.java2d.SurfaceData; @@ -51,7 +52,7 @@ public final class D3DGraphicsConfig extends Win32GraphicsConfig - implements AccelGraphicsConfig + implements AccelGraphicsConfig, SurfaceManager.Factory { private static ImageCapabilities imageCaps = new D3DImageCaps(); @@ -223,7 +224,7 @@ public void flip(WComponentPeer peer, } } - private static class D3DBufferCaps extends BufferCapabilities { + private static final class D3DBufferCaps extends BufferCapabilities { public D3DBufferCaps() { // REMIND: should we indicate that the front-buffer // (the on-screen rendering) is not accelerated? @@ -244,7 +245,7 @@ public BufferCapabilities getBufferCapabilities() { return bufferCaps; } - private static class D3DImageCaps extends ImageCapabilities { + private static final class D3DImageCaps extends ImageCapabilities { private D3DImageCaps() { super(true); } @@ -307,4 +308,10 @@ public D3DContext getContext() { public ContextCapabilities getContextCapabilities() { return device.getContextCapabilities(); } + + @Override + public VolatileSurfaceManager createVolatileManager(SunVolatileImage image, + Object context) { + return new D3DVolatileSurfaceManager(image, context); + } } diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DGraphicsDevice.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DGraphicsDevice.java index e9f02a5d98ce6..1c838d0d32d6c 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DGraphicsDevice.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DGraphicsDevice.java @@ -128,7 +128,7 @@ public void run() { return d3dCaps != null ? d3dCaps : new D3DContextCaps(CAPS_EMPTY, null); } - public final boolean isCapPresent(int cap) { + public boolean isCapPresent(int cap) { return ((d3dCaps.getCaps() & cap) != 0); } @@ -234,7 +234,7 @@ public void run() { * REMIND: we create an instance per each full-screen device while a single * instance would suffice (but requires more management). */ - private static class D3DFSWindowAdapter extends WindowAdapter { + private static final class D3DFSWindowAdapter extends WindowAdapter { @Override @SuppressWarnings("static") public void windowDeactivated(WindowEvent e) { diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DMaskBlit.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DMaskBlit.java index 835bcb733adaa..6ba766d2b1303 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DMaskBlit.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DMaskBlit.java @@ -36,7 +36,7 @@ import static sun.java2d.loops.CompositeType.*; import static sun.java2d.loops.SurfaceType.*; -class D3DMaskBlit extends BufferedMaskBlit { +final class D3DMaskBlit extends BufferedMaskBlit { static void register() { GraphicsPrimitive[] primitives = { diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DMaskFill.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DMaskFill.java index fb78616a98ff6..d00e3fe2ee132 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DMaskFill.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DMaskFill.java @@ -36,7 +36,7 @@ import static sun.java2d.loops.CompositeType.*; import static sun.java2d.loops.SurfaceType.*; -class D3DMaskFill extends BufferedMaskFill { +final class D3DMaskFill extends BufferedMaskFill { static void register() { GraphicsPrimitive[] primitives = { diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DPaints.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DPaints.java index 7d5bd4423eacf..74b7cfc69670a 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DPaints.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DPaints.java @@ -76,7 +76,7 @@ static boolean isValid(SunGraphics2D sg2d) { /************************* GradientPaint support ****************************/ - private static class Gradient extends D3DPaints { + private static final class Gradient extends D3DPaints { private Gradient() {} /** @@ -96,7 +96,7 @@ boolean isPaintValid(SunGraphics2D sg2d) { /************************** TexturePaint support ****************************/ - private static class Texture extends D3DPaints { + private static final class Texture extends D3DPaints { private Texture() {} /** @@ -201,7 +201,7 @@ boolean isPaintValid(SunGraphics2D sg2d) { /********************** LinearGradientPaint support *************************/ - private static class LinearGradient extends MultiGradient { + private static final class LinearGradient extends MultiGradient { private LinearGradient() {} @Override @@ -228,7 +228,7 @@ boolean isPaintValid(SunGraphics2D sg2d) { /********************** RadialGradientPaint support *************************/ - private static class RadialGradient extends MultiGradient { + private static final class RadialGradient extends MultiGradient { private RadialGradient() {} } } diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DRenderQueue.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DRenderQueue.java index d77e90456dfb6..be20e1c4e4c86 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DRenderQueue.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DRenderQueue.java @@ -33,7 +33,7 @@ /** * D3D-specific implementation of RenderQueue. */ -public class D3DRenderQueue extends RenderQueue { +public final class D3DRenderQueue extends RenderQueue { private static D3DRenderQueue theInstance; private static Thread rqThread; @@ -132,11 +132,13 @@ public static void disposeGraphicsConfig(long pConfigInfo) { } } + @Override public void flushNow() { // assert lock.isHeldByCurrentThread(); flushBuffer(null); } + @Override public void flushAndInvokeNow(Runnable r) { // assert lock.isHeldByCurrentThread(); flushBuffer(r); diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DRenderer.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DRenderer.java index fd63d758548a8..7361a09973b12 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DRenderer.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DRenderer.java @@ -87,6 +87,7 @@ void copyArea(SunGraphics2D sg2d, } } + @Override protected native void drawPoly(int[] xPoints, int[] yPoints, int nPoints, boolean isClosed, int transX, int transY); @@ -95,12 +96,13 @@ D3DRenderer traceWrap() { return new Tracer(this); } - private static class Tracer extends D3DRenderer { + private static final class Tracer extends D3DRenderer { private D3DRenderer d3dr; Tracer(D3DRenderer d3dr) { super(d3dr.rq); this.d3dr = d3dr; } + @Override public ParallelogramPipe getAAParallelogramPipe() { final ParallelogramPipe realpipe = d3dr.getAAParallelogramPipe(); return new ParallelogramPipe() { @@ -133,19 +135,23 @@ public void drawParallelogram(SunGraphics2D sg2d, }; } + @Override protected void validateContext(SunGraphics2D sg2d) { d3dr.validateContext(sg2d); } + @Override public void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) { GraphicsPrimitive.tracePrimitive("D3DDrawLine"); d3dr.drawLine(sg2d, x1, y1, x2, y2); } + @Override public void drawRect(SunGraphics2D sg2d, int x, int y, int w, int h) { GraphicsPrimitive.tracePrimitive("D3DDrawRect"); d3dr.drawRect(sg2d, x, y, w, h); } + @Override protected void drawPoly(SunGraphics2D sg2d, int[] xPoints, int[] yPoints, int nPoints, boolean isClosed) @@ -153,28 +159,33 @@ protected void drawPoly(SunGraphics2D sg2d, GraphicsPrimitive.tracePrimitive("D3DDrawPoly"); d3dr.drawPoly(sg2d, xPoints, yPoints, nPoints, isClosed); } + @Override public void fillRect(SunGraphics2D sg2d, int x, int y, int w, int h) { GraphicsPrimitive.tracePrimitive("D3DFillRect"); d3dr.fillRect(sg2d, x, y, w, h); } + @Override protected void drawPath(SunGraphics2D sg2d, Path2D.Float p2df, int transx, int transy) { GraphicsPrimitive.tracePrimitive("D3DDrawPath"); d3dr.drawPath(sg2d, p2df, transx, transy); } + @Override protected void fillPath(SunGraphics2D sg2d, Path2D.Float p2df, int transx, int transy) { GraphicsPrimitive.tracePrimitive("D3DFillPath"); d3dr.fillPath(sg2d, p2df, transx, transy); } + @Override protected void fillSpans(SunGraphics2D sg2d, SpanIterator si, int transx, int transy) { GraphicsPrimitive.tracePrimitive("D3DFillSpans"); d3dr.fillSpans(sg2d, si, transx, transy); } + @Override public void fillParallelogram(SunGraphics2D sg2d, double ux1, double uy1, double ux2, double uy2, @@ -187,6 +198,7 @@ public void fillParallelogram(SunGraphics2D sg2d, ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2); } + @Override public void drawParallelogram(SunGraphics2D sg2d, double ux1, double uy1, double ux2, double uy2, @@ -200,6 +212,7 @@ public void drawParallelogram(SunGraphics2D sg2d, ux1, uy1, ux2, uy2, x, y, dx1, dy1, dx2, dy2, lw1, lw2); } + @Override public void copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) { diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java index a1afb5cd4c465..58dfd17dea713 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java @@ -58,7 +58,7 @@ * There are some restrictions to which windows we would use this for. * @see #createScreenSurface */ -public class D3DScreenUpdateManager extends ScreenUpdateManager +public final class D3DScreenUpdateManager extends ScreenUpdateManager implements Runnable { /** @@ -401,6 +401,7 @@ public void runUpdateNow() { } } + @Override public void run() { while (!done) { synchronized (runLock) { diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java index c8e082b59d6da..374fce63db047 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceData.java @@ -441,6 +441,7 @@ public void run() { * Returns the D3DContext for the GraphicsConfig associated with this * surface. */ + @Override public final D3DContext getContext() { return graphicsDevice.getContext(); } @@ -448,6 +449,7 @@ public final D3DContext getContext() { /** * Returns one of the surface type constants defined above. */ + @Override public final int getType() { return type; } @@ -455,7 +457,7 @@ public final int getType() { private static native int dbGetPixelNative(long pData, int x, int y); private static native void dbSetPixelNative(long pData, int x, int y, int pixel); - static class D3DDataBufferNative extends DataBufferNative { + static final class D3DDataBufferNative extends DataBufferNative { int pixel; protected D3DDataBufferNative(SurfaceData sData, int type, int w, int h) @@ -463,6 +465,7 @@ protected D3DDataBufferNative(SurfaceData sData, super(sData, type, w, h); } + @Override protected int getElem(final int x, final int y, final SurfaceData sData) { @@ -486,6 +489,7 @@ public void run() { return retPixel; } + @Override protected void setElem(final int x, final int y, final int pixel, final SurfaceData sData) { @@ -508,6 +512,7 @@ public void run() { } } + @Override public synchronized Raster getRaster(int x, int y, int w, int h) { if (wrn == null) { DirectColorModel dcm = (DirectColorModel)getColorModel(); @@ -541,6 +546,7 @@ public synchronized Raster getRaster(int x, int y, int w, int h) { * - the source color is opaque * - and the destination is opaque */ + @Override public boolean canRenderLCDText(SunGraphics2D sg2d) { return graphicsDevice.isCapPresent(CAPS_LCD_SHADER) && @@ -564,6 +570,7 @@ void disableAccelerationForSurface() { } } + @Override public void validatePipe(SunGraphics2D sg2d) { TextPipe textpipe; boolean validated = false; @@ -803,10 +810,12 @@ public void run() { /** * Returns destination Image associated with this SurfaceData. */ + @Override public Object getDestination() { return offscreenImage; } + @Override public Rectangle getBounds() { if (type == FLIP_BACKBUFFER || type == WINDOW) { double scaleX = getDefaultScaleX(); @@ -821,6 +830,7 @@ public Rectangle getBounds() { } } + @Override public Rectangle getNativeBounds() { D3DRenderQueue rq = D3DRenderQueue.getInstance(); // need to lock to make sure nativeWidth and Height are consistent @@ -836,10 +846,12 @@ public Rectangle getNativeBounds() { } + @Override public GraphicsConfiguration getDeviceConfiguration() { return graphicsDevice.getDefaultConfiguration(); } + @Override public SurfaceData getReplacement() { return restoreContents(offscreenImage); } @@ -909,6 +921,7 @@ public void setSurfaceLost(boolean lost) { * such resource doesn't exist or can not be retrieved. * @see sun.java2d.pipe.hw.AccelSurface#getNativeResource */ + @Override public long getNativeResource(int resType) { return getNativeResourceNative(getNativeOps(), resType); } @@ -920,7 +933,7 @@ public long getNativeResource(int resType) { * * @see D3DScreenUpdateManager */ - public static class D3DWindowSurfaceData extends D3DSurfaceData { + public static final class D3DWindowSurfaceData extends D3DSurfaceData { StateTracker dirtyTracker; public D3DWindowSurfaceData(WComponentPeer peer, diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceDataProxy.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceDataProxy.java index 66e98882a19da..6ea095666b3e1 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceDataProxy.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DSurfaceDataProxy.java @@ -38,7 +38,7 @@ * SurfaceData with a cached D3D Texture and the code to create * the accelerated surfaces. */ -public class D3DSurfaceDataProxy extends SurfaceDataProxy { +public final class D3DSurfaceDataProxy extends SurfaceDataProxy { public static SurfaceDataProxy createProxy(SurfaceData srcData, D3DGraphicsConfig dstConfig) diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DTextRenderer.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DTextRenderer.java index b892ed4b10c51..31b2cc207cd68 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DTextRenderer.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DTextRenderer.java @@ -59,10 +59,11 @@ D3DTextRenderer traceWrap() { return new Tracer(this); } - private static class Tracer extends D3DTextRenderer { + private static final class Tracer extends D3DTextRenderer { Tracer(D3DTextRenderer d3dtr) { super(d3dtr.rq); } + @Override protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) { GraphicsPrimitive.tracePrimitive("D3DDrawGlyphs"); super.drawGlyphList(sg2d, gl); diff --git a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DVolatileSurfaceManager.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DVolatileSurfaceManager.java index 4aac1c248a6a5..0fedb47836200 100644 --- a/src/java.desktop/windows/classes/sun/java2d/d3d/D3DVolatileSurfaceManager.java +++ b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DVolatileSurfaceManager.java @@ -48,7 +48,7 @@ import static sun.java2d.pipe.hw.AccelSurface.TEXTURE; import static sun.java2d.pipe.hw.AccelSurface.UNDEFINED; -public class D3DVolatileSurfaceManager +public final class D3DVolatileSurfaceManager extends VolatileSurfaceManager { private boolean accelerationEnabled; @@ -75,6 +75,7 @@ public D3DVolatileSurfaceManager(SunVolatileImage vImg, Object context) { gd.isCapPresent(CAPS_RT_TEXTURE_ALPHA))); } + @Override protected boolean isAccelerationEnabled() { return accelerationEnabled; } @@ -86,6 +87,7 @@ public void setAccelerationEnabled(boolean accelerationEnabled) { * Create a pbuffer-based SurfaceData object (or init the backbuffer * of an existing window if this is a double buffered GraphicsConfig). */ + @Override protected SurfaceData initAcceleratedSurface() { SurfaceData sData; Component comp = vImg.getComponent(); @@ -124,6 +126,7 @@ protected SurfaceData initAcceleratedSurface() { return sData; } + @Override protected boolean isConfigValid(GraphicsConfiguration gc) { return ((gc == null) || (gc == vImg.getGraphicsConfig())); } diff --git a/src/java.desktop/windows/classes/sun/java2d/opengl/WGLGraphicsConfig.java b/src/java.desktop/windows/classes/sun/java2d/opengl/WGLGraphicsConfig.java index 768f2df08add7..40d2af5c12aa3 100644 --- a/src/java.desktop/windows/classes/sun/java2d/opengl/WGLGraphicsConfig.java +++ b/src/java.desktop/windows/classes/sun/java2d/opengl/WGLGraphicsConfig.java @@ -43,6 +43,7 @@ import sun.awt.Win32GraphicsDevice; import sun.awt.image.SunVolatileImage; import sun.awt.image.SurfaceManager; +import sun.awt.image.VolatileSurfaceManager; import sun.awt.windows.WComponentPeer; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; @@ -73,7 +74,8 @@ public final class WGLGraphicsConfig private ContextCapabilities oglCaps; private final OGLContext context; private Object disposerReferent = new Object(); - private final SurfaceManager.ProxyCache surfaceDataProxyCache = new SurfaceManager.ProxyCache(); + private final SurfaceManager.ProxyCache surfaceDataProxyCache = + new SurfaceManager.ProxyCache(); public static native int getDefaultPixFmt(int screennum); private static native boolean initWGL(); @@ -158,7 +160,7 @@ public void run() { * This is a small helper class that allows us to execute * getWGLConfigInfo() on the queue flushing thread. */ - private static class WGLGetConfigInfo implements Runnable { + private static final class WGLGetConfigInfo implements Runnable { private int screen; private int pixfmt; private long cfginfo; @@ -184,21 +186,21 @@ public static boolean isWGLAvailable() { * See OGLContext.java for a list of supported capabilities. */ @Override - public final boolean isCapPresent(int cap) { + public boolean isCapPresent(int cap) { return ((oglCaps.getCaps() & cap) != 0); } @Override - public final long getNativeConfigInfo() { + public long getNativeConfigInfo() { return pConfigInfo; } @Override - public final OGLContext getContext() { + public OGLContext getContext() { return context; } - private static class WGLGCDisposerRecord implements DisposerRecord { + private static final class WGLGCDisposerRecord implements DisposerRecord { private long pCfgInfo; public WGLGCDisposerRecord(long pCfgInfo) { this.pCfgInfo = pCfgInfo; @@ -374,7 +376,7 @@ public void flip(WComponentPeer peer, } } - private static class WGLBufferCaps extends BufferCapabilities { + private static final class WGLBufferCaps extends BufferCapabilities { public WGLBufferCaps(boolean dblBuf) { super(imageCaps, imageCaps, dblBuf ? FlipContents.UNDEFINED : null); @@ -390,7 +392,7 @@ public BufferCapabilities getBufferCapabilities() { return bufferCaps; } - private static class WGLImageCaps extends ImageCapabilities { + private static final class WGLImageCaps extends ImageCapabilities { private WGLImageCaps() { super(true); } @@ -432,4 +434,10 @@ public ImageCapabilities getImageCapabilities() { public ContextCapabilities getContextCapabilities() { return oglCaps; } + + @Override + public VolatileSurfaceManager createVolatileManager(SunVolatileImage image, + Object context) { + return new WGLVolatileSurfaceManager(image, context); + } } diff --git a/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java b/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java index fa4cb6a3cc275..8f97980811f36 100644 --- a/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java +++ b/src/java.desktop/windows/classes/sun/java2d/opengl/WGLSurfaceData.java @@ -75,6 +75,7 @@ public double getDefaultScaleY() { return scaleY; } + @Override public GraphicsConfiguration getDeviceConfiguration() { return graphicsConfig; } @@ -151,7 +152,7 @@ public static WGLGraphicsConfig getGC(WComponentPeer peer) { } } - public static class WGLWindowSurfaceData extends WGLSurfaceData { + public static final class WGLWindowSurfaceData extends WGLSurfaceData { public WGLWindowSurfaceData(WComponentPeer peer, WGLGraphicsConfig gc) @@ -159,10 +160,12 @@ public WGLWindowSurfaceData(WComponentPeer peer, super(peer, gc, peer.getColorModel(), WINDOW); } + @Override public SurfaceData getReplacement() { return peer.getSurfaceData(); } + @Override public Rectangle getBounds() { Rectangle r = peer.getBounds(); r.x = r.y = 0; @@ -174,6 +177,7 @@ public Rectangle getBounds() { /** * Returns destination Component associated with this SurfaceData. */ + @Override public Object getDestination() { return peer.getTarget(); } @@ -188,7 +192,7 @@ public Object getDestination() { * belongs to is showed, it is first copied to the real private * FLIP_BACKBUFFER, which is then flipped. */ - public static class WGLVSyncOffScreenSurfaceData extends + public static final class WGLVSyncOffScreenSurfaceData extends WGLOffScreenSurfaceData { private WGLOffScreenSurfaceData flipSurface; @@ -235,10 +239,12 @@ public WGLOffScreenSurfaceData(WComponentPeer peer, initSurface(this.width, this.height); } + @Override public SurfaceData getReplacement() { return restoreContents(offscreenImage); } + @Override public Rectangle getBounds() { if (type == FLIP_BACKBUFFER) { Rectangle r = peer.getBounds(); @@ -254,6 +260,7 @@ public Rectangle getBounds() { /** * Returns destination Image associated with this SurfaceData. */ + @Override public Object getDestination() { return offscreenImage; } diff --git a/src/java.desktop/windows/classes/sun/java2d/opengl/WGLVolatileSurfaceManager.java b/src/java.desktop/windows/classes/sun/java2d/opengl/WGLVolatileSurfaceManager.java index 750bb94df0889..c90f8102e7ecd 100644 --- a/src/java.desktop/windows/classes/sun/java2d/opengl/WGLVolatileSurfaceManager.java +++ b/src/java.desktop/windows/classes/sun/java2d/opengl/WGLVolatileSurfaceManager.java @@ -43,7 +43,7 @@ import sun.java2d.pipe.hw.ExtendedBufferCapabilities; import static sun.java2d.pipe.hw.ExtendedBufferCapabilities.VSyncType.*; -public class WGLVolatileSurfaceManager extends VolatileSurfaceManager { +public final class WGLVolatileSurfaceManager extends VolatileSurfaceManager { private final boolean accelerationEnabled; @@ -62,6 +62,7 @@ public WGLVolatileSurfaceManager(SunVolatileImage vImg, Object context) { && transparency != Transparency.BITMASK; } + @Override protected boolean isAccelerationEnabled() { return accelerationEnabled; } @@ -70,6 +71,7 @@ protected boolean isAccelerationEnabled() { * Create a FBO-based SurfaceData object (or init the backbuffer * of an existing window if this is a double buffered GraphicsConfig). */ + @Override protected SurfaceData initAcceleratedSurface() { SurfaceData sData; Component comp = vImg.getComponent(); diff --git a/src/java.desktop/windows/classes/sun/java2d/windows/GDIBlitLoops.java b/src/java.desktop/windows/classes/sun/java2d/windows/GDIBlitLoops.java index 3f1996309cd22..18d44ea6a498b 100644 --- a/src/java.desktop/windows/classes/sun/java2d/windows/GDIBlitLoops.java +++ b/src/java.desktop/windows/classes/sun/java2d/windows/GDIBlitLoops.java @@ -43,7 +43,7 @@ * that is faster than our current fallback (which creates * a temporary GDI DIB) */ -public class GDIBlitLoops extends Blit { +public final class GDIBlitLoops extends Blit { // Store these values to be passed to native code int rmask, gmask, bmask; @@ -134,6 +134,7 @@ public native void nativeBlit(SurfaceData src, SurfaceData dst, * Composite data because we only register these loops for * SrcNoEa composite operations. */ + @Override public void Blit(SurfaceData src, SurfaceData dst, Composite comp, Region clip, int sx, int sy, int dx, int dy, int w, int h) diff --git a/src/java.desktop/windows/classes/sun/java2d/windows/GDIRenderer.java b/src/java.desktop/windows/classes/sun/java2d/windows/GDIRenderer.java index f08273c76fb19..9bb3ba38b20db 100644 --- a/src/java.desktop/windows/classes/sun/java2d/windows/GDIRenderer.java +++ b/src/java.desktop/windows/classes/sun/java2d/windows/GDIRenderer.java @@ -50,6 +50,7 @@ native void doDrawLine(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x1, int y1, int x2, int y2); + @Override public void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) { @@ -68,6 +69,7 @@ native void doDrawRect(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h); + @Override public void drawRect(SunGraphics2D sg2d, int x, int y, int width, int height) { @@ -85,6 +87,7 @@ native void doDrawRoundRect(GDIWindowSurfaceData sData, int x, int y, int w, int h, int arcW, int arcH); + @Override public void drawRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) @@ -103,6 +106,7 @@ native void doDrawOval(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h); + @Override public void drawOval(SunGraphics2D sg2d, int x, int y, int width, int height) { @@ -120,6 +124,7 @@ native void doDrawArc(GDIWindowSurfaceData sData, int x, int y, int w, int h, int angleStart, int angleExtent); + @Override public void drawArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) @@ -140,6 +145,7 @@ native void doDrawPoly(GDIWindowSurfaceData sData, int[] xpoints, int[] ypoints, int npoints, boolean isclosed); + @Override public void drawPolyline(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints) @@ -153,6 +159,7 @@ public void drawPolyline(SunGraphics2D sg2d, } } + @Override public void drawPolygon(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints) @@ -170,6 +177,7 @@ native void doFillRect(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h); + @Override public void fillRect(SunGraphics2D sg2d, int x, int y, int width, int height) { @@ -187,6 +195,7 @@ native void doFillRoundRect(GDIWindowSurfaceData sData, int x, int y, int w, int h, int arcW, int arcH); + @Override public void fillRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight) @@ -205,6 +214,7 @@ native void doFillOval(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h); + @Override public void fillOval(SunGraphics2D sg2d, int x, int y, int width, int height) { @@ -222,6 +232,7 @@ native void doFillArc(GDIWindowSurfaceData sData, int x, int y, int w, int h, int angleStart, int angleExtent); + @Override public void fillArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle) @@ -242,6 +253,7 @@ native void doFillPoly(GDIWindowSurfaceData sData, int[] xpoints, int[] ypoints, int npoints); + @Override public void fillPolygon(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints) @@ -303,6 +315,7 @@ public void doFillSpans(SunGraphics2D sg2d, SpanIterator si) { } } + @Override public void draw(SunGraphics2D sg2d, Shape s) { if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) { doShape(sg2d, s, false); @@ -318,6 +331,7 @@ public void draw(SunGraphics2D sg2d, Shape s) { } } + @Override public void fill(SunGraphics2D sg2d, Shape s) { doShape(sg2d, s, true); } @@ -331,7 +345,8 @@ public GDIRenderer traceWrap() { return new Tracer(); } - public static class Tracer extends GDIRenderer { + public static final class Tracer extends GDIRenderer { + @Override void doDrawLine(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x1, int y1, int x2, int y2) @@ -339,6 +354,7 @@ void doDrawLine(GDIWindowSurfaceData sData, GraphicsPrimitive.tracePrimitive("GDIDrawLine"); super.doDrawLine(sData, clip, comp, color, x1, y1, x2, y2); } + @Override void doDrawRect(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h) @@ -346,6 +362,7 @@ void doDrawRect(GDIWindowSurfaceData sData, GraphicsPrimitive.tracePrimitive("GDIDrawRect"); super.doDrawRect(sData, clip, comp, color, x, y, w, h); } + @Override void doDrawRoundRect(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h, @@ -355,6 +372,7 @@ void doDrawRoundRect(GDIWindowSurfaceData sData, super.doDrawRoundRect(sData, clip, comp, color, x, y, w, h, arcW, arcH); } + @Override void doDrawOval(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h) @@ -362,6 +380,7 @@ void doDrawOval(GDIWindowSurfaceData sData, GraphicsPrimitive.tracePrimitive("GDIDrawOval"); super.doDrawOval(sData, clip, comp, color, x, y, w, h); } + @Override void doDrawArc(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h, @@ -371,6 +390,7 @@ void doDrawArc(GDIWindowSurfaceData sData, super.doDrawArc(sData, clip, comp, color, x, y, w, h, angleStart, angleExtent); } + @Override void doDrawPoly(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int transx, int transy, @@ -381,6 +401,7 @@ void doDrawPoly(GDIWindowSurfaceData sData, super.doDrawPoly(sData, clip, comp, color, transx, transy, xpoints, ypoints, npoints, isclosed); } + @Override void doFillRect(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h) @@ -388,6 +409,7 @@ void doFillRect(GDIWindowSurfaceData sData, GraphicsPrimitive.tracePrimitive("GDIFillRect"); super.doFillRect(sData, clip, comp, color, x, y, w, h); } + @Override void doFillRoundRect(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h, @@ -397,6 +419,7 @@ void doFillRoundRect(GDIWindowSurfaceData sData, super.doFillRoundRect(sData, clip, comp, color, x, y, w, h, arcW, arcH); } + @Override void doFillOval(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h) @@ -404,6 +427,7 @@ void doFillOval(GDIWindowSurfaceData sData, GraphicsPrimitive.tracePrimitive("GDIFillOval"); super.doFillOval(sData, clip, comp, color, x, y, w, h); } + @Override void doFillArc(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int x, int y, int w, int h, @@ -413,6 +437,7 @@ void doFillArc(GDIWindowSurfaceData sData, super.doFillArc(sData, clip, comp, color, x, y, w, h, angleStart, angleExtent); } + @Override void doFillPoly(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int transx, int transy, @@ -423,6 +448,7 @@ void doFillPoly(GDIWindowSurfaceData sData, super.doFillPoly(sData, clip, comp, color, transx, transy, xpoints, ypoints, npoints); } + @Override void doShape(GDIWindowSurfaceData sData, Region clip, Composite comp, int color, int transX, int transY, @@ -434,6 +460,7 @@ void doShape(GDIWindowSurfaceData sData, super.doShape(sData, clip, comp, color, transX, transY, p2df, isfill); } + @Override public void devCopyArea(GDIWindowSurfaceData sData, int srcx, int srcy, int dx, int dy, diff --git a/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java b/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java index 52e0d47c55d14..38b57f77bce4c 100644 --- a/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java +++ b/src/java.desktop/windows/classes/sun/java2d/windows/GDIWindowSurfaceData.java @@ -51,7 +51,7 @@ import sun.java2d.loops.RenderLoops; import sun.java2d.loops.XORComposite; -public class GDIWindowSurfaceData extends SurfaceData { +public final class GDIWindowSurfaceData extends SurfaceData { private WComponentPeer peer; private Win32GraphicsConfig graphicsConfig; private RenderLoops solidloops; @@ -139,6 +139,7 @@ public SurfaceDataProxy makeProxyFor(SurfaceData srcData) { return SurfaceDataProxy.UNCACHED; } + @Override public Raster getRaster(int x, int y, int w, int h) { throw new InternalError("not implemented yet"); } @@ -155,6 +156,7 @@ public Raster getRaster(int x, int y, int w, int h) { } + @Override public void validatePipe(SunGraphics2D sg2d) { if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON && sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR && @@ -223,6 +225,7 @@ public void validatePipe(SunGraphics2D sg2d) { } } + @Override public RenderLoops getRenderLoops(SunGraphics2D sg2d) { if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR && sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY) @@ -232,6 +235,7 @@ public RenderLoops getRenderLoops(SunGraphics2D sg2d) { return super.getRenderLoops(sg2d); } + @Override public GraphicsConfiguration getDeviceConfiguration() { return graphicsConfig; } @@ -299,6 +303,7 @@ public SurfaceData getReplacement() { return mgr.getReplacementScreenSurface(peer, this); } + @Override public Rectangle getBounds() { Rectangle r = peer.getBounds(); r.x = r.y = 0; @@ -307,6 +312,7 @@ public Rectangle getBounds() { return r; } + @Override public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, int dx, int dy) { diff --git a/src/java.desktop/windows/classes/sun/java2d/windows/WindowsFlags.java b/src/java.desktop/windows/classes/sun/java2d/windows/WindowsFlags.java index 7333eeda3dd92..b5b1e6376e960 100644 --- a/src/java.desktop/windows/classes/sun/java2d/windows/WindowsFlags.java +++ b/src/java.desktop/windows/classes/sun/java2d/windows/WindowsFlags.java @@ -28,7 +28,7 @@ import sun.awt.windows.WToolkit; import sun.java2d.opengl.WGLGraphicsConfig; -public class WindowsFlags { +public final class WindowsFlags { /** * Description of command-line flags. All flags with [true|false] diff --git a/src/java.desktop/windows/classes/sun/print/PlatformPrinterJobProxy.java b/src/java.desktop/windows/classes/sun/print/PlatformPrinterJobProxy.java index f9ac7621aa44c..677831a92450d 100644 --- a/src/java.desktop/windows/classes/sun/print/PlatformPrinterJobProxy.java +++ b/src/java.desktop/windows/classes/sun/print/PlatformPrinterJobProxy.java @@ -27,7 +27,7 @@ import java.awt.print.PrinterJob; -public class PlatformPrinterJobProxy { +public final class PlatformPrinterJobProxy { public static PrinterJob getPrinterJob() { return new sun.awt.windows.WPrinterJob(); diff --git a/src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java b/src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java index 4dbcf3a4701e8..7af6f1b61f878 100644 --- a/src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java +++ b/src/java.desktop/windows/classes/sun/print/PrintServiceLookupProvider.java @@ -43,7 +43,7 @@ import sun.awt.util.ThreadGroupUtils; -public class PrintServiceLookupProvider extends PrintServiceLookup { +public final class PrintServiceLookupProvider extends PrintServiceLookup { private PrintService defaultPrintService; private PrintService[] printServices; /* includes the default printer */ @@ -105,6 +105,7 @@ public PrintServiceLookupProvider() { * This isn't required by the API and there's a risk doing this will * lead people to assume its guaranteed. */ + @Override public synchronized PrintService[] getPrintServices() { if (printServices == null) { refreshServices(); @@ -199,6 +200,7 @@ boolean matchingService(PrintService service, return true; } + @Override public PrintService[] getPrintServices(DocFlavor flavor, AttributeSet attributes) { @@ -260,6 +262,7 @@ public PrintService[] getPrintServices(DocFlavor flavor, /* * return empty array as don't support multi docs */ + @Override public MultiDocPrintService[] getMultiDocPrintServices(DocFlavor[] flavors, AttributeSet attributes) { @@ -267,6 +270,7 @@ public PrintService[] getPrintServices(DocFlavor flavor, } + @Override public synchronized PrintService getDefaultPrintService() { // Windows does not have notification for a change in default diff --git a/src/java.desktop/windows/classes/sun/print/Win32MediaTray.java b/src/java.desktop/windows/classes/sun/print/Win32MediaTray.java index 43ce6a19d6a34..3cb8c858e3935 100644 --- a/src/java.desktop/windows/classes/sun/print/Win32MediaTray.java +++ b/src/java.desktop/windows/classes/sun/print/Win32MediaTray.java @@ -35,7 +35,7 @@ * It also implements driver-defined trays. **/ @SuppressWarnings("serial") // JDK implementation class -public class Win32MediaTray extends MediaTray { +public final class Win32MediaTray extends MediaTray { static final Win32MediaTray ENVELOPE_MANUAL = new Win32MediaTray(0, 6); //DMBIN_ENVMANUAL @@ -96,6 +96,7 @@ protected static int getTraySize() { return (myStringTable.length+winStringTable.size()); } + @Override protected String[] getStringTable() { ArrayList completeList = new ArrayList<>(); for (int i=0; i < myStringTable.length; i++) { @@ -106,6 +107,7 @@ protected String[] getStringTable() { return completeList.toArray(nameTable); } + @Override protected EnumSyntax[] getEnumValueTable() { ArrayList completeList = new ArrayList<>(); for (int i=0; i < myEnumValueTable.length; i++) { diff --git a/src/java.desktop/windows/classes/sun/print/Win32PrintJob.java b/src/java.desktop/windows/classes/sun/print/Win32PrintJob.java index f8ee2d3db0b21..cd4eec9926bc0 100644 --- a/src/java.desktop/windows/classes/sun/print/Win32PrintJob.java +++ b/src/java.desktop/windows/classes/sun/print/Win32PrintJob.java @@ -73,7 +73,7 @@ import java.awt.print.*; -public class Win32PrintJob implements CancelablePrintJob { +public final class Win32PrintJob implements CancelablePrintJob { private transient ArrayList jobListeners; private transient ArrayList attrListeners; @@ -113,10 +113,12 @@ public class Win32PrintJob implements CancelablePrintJob { this.service = service; } + @Override public PrintService getPrintService() { return service; } + @Override public PrintJobAttributeSet getAttributes() { synchronized (this) { if (jobAttrSet == null) { @@ -129,6 +131,7 @@ public PrintJobAttributeSet getAttributes() { } } + @Override public void addPrintJobListener(PrintJobListener listener) { synchronized (this) { if (listener == null) { @@ -141,6 +144,7 @@ public void addPrintJobListener(PrintJobListener listener) { } } + @Override public void removePrintJobListener(PrintJobListener listener) { synchronized (this) { if (listener == null || jobListeners == null ) { @@ -254,6 +258,7 @@ private void notifyEvent(int reason) { } } + @Override public void addPrintJobAttributeListener( PrintJobAttributeListener listener, PrintJobAttributeSet attributes) { @@ -273,6 +278,7 @@ public void addPrintJobAttributeListener( } } + @Override public void removePrintJobAttributeListener( PrintJobAttributeListener listener) { synchronized (this) { @@ -293,6 +299,7 @@ public void removePrintJobAttributeListener( } } + @Override public void print(Doc doc, PrintRequestAttributeSet attributes) throws PrintException { @@ -697,6 +704,7 @@ private native boolean startPrintRawData(String printerName, private native boolean endPrintRawData(); /* Cancel PrinterJob jobs that haven't yet completed. */ + @Override public void cancel() throws PrintException { synchronized (this) { if (!printing) { diff --git a/src/java.desktop/windows/classes/sun/print/Win32PrintService.java b/src/java.desktop/windows/classes/sun/print/Win32PrintService.java index 7d85755bf0cb9..3bca5b581bbc7 100644 --- a/src/java.desktop/windows/classes/sun/print/Win32PrintService.java +++ b/src/java.desktop/windows/classes/sun/print/Win32PrintService.java @@ -78,7 +78,7 @@ import javax.print.event.PrintServiceAttributeListener; import sun.awt.windows.WPrinterJob; -public class Win32PrintService implements PrintService, AttributeUpdater, +public final class Win32PrintService implements PrintService, AttributeUpdater, SunPrinterJobService { public static MediaSize[] predefMedia = Win32MediaSize.getPredefMedia(); @@ -242,6 +242,7 @@ public void invalidateService() { isInvalid = true; } + @Override public String getName() { return printer; } @@ -857,6 +858,7 @@ private boolean isSupportedResolution(PrinterResolution res) { return false; } + @Override public DocPrintJob createPrintJob() { return new Win32PrintJob(this); } @@ -868,6 +870,7 @@ private PrintServiceAttributeSet getDynamicAttributes() { return attrs; } + @Override public PrintServiceAttributeSet getUpdatedAttributes() { PrintServiceAttributeSet currSet = getDynamicAttributes(); if (lastSet == null) { @@ -896,6 +899,7 @@ public void wakeNotifier() { } } + @Override public void addPrintServiceAttributeListener(PrintServiceAttributeListener listener) { synchronized (this) { @@ -909,6 +913,7 @@ public void addPrintServiceAttributeListener(PrintServiceAttributeListener } } + @Override public void removePrintServiceAttributeListener( PrintServiceAttributeListener listener) { synchronized (this) { @@ -923,6 +928,7 @@ public void removePrintServiceAttributeListener( } } + @Override @SuppressWarnings("unchecked") public T getAttribute(Class category) @@ -955,6 +961,7 @@ public void removePrintServiceAttributeListener( } } + @Override public PrintServiceAttributeSet getAttributes() { PrintServiceAttributeSet attrs = new HashPrintServiceAttributeSet(); @@ -979,6 +986,7 @@ public PrintServiceAttributeSet getAttributes() { return AttributeSetUtilities.unmodifiableView(attrs); } + @Override public DocFlavor[] getSupportedDocFlavors() { int len = supportedFlavors.length; DocFlavor[] supportedDocFlavors; @@ -998,6 +1006,7 @@ public DocFlavor[] getSupportedDocFlavors() { return supportedDocFlavors; } + @Override public boolean isDocFlavorSupported(DocFlavor flavor) { /* To avoid a native query which may be time-consuming * do not invoke native unless postscript support is being queried. @@ -1017,6 +1026,7 @@ public boolean isDocFlavorSupported(DocFlavor flavor) { return false; } + @Override public Class[] getSupportedAttributeCategories() { ArrayList> categList = new ArrayList<>(otherAttrCats.length+3); for (int i=0; i < otherAttrCats.length; i++) { @@ -1049,6 +1059,7 @@ public Class[] getSupportedAttributeCategories() { return categList.toArray(new Class[categList.size()]); } + @Override public boolean isAttributeCategorySupported(Class category) { @@ -1072,6 +1083,7 @@ public Class[] getSupportedAttributeCategories() { return false; } + @Override public Object getDefaultAttributeValue(Class category) { @@ -1255,6 +1267,7 @@ private boolean isAutoSense(DocFlavor flavor) { } } + @Override public Object getSupportedAttributeValues(Class category, DocFlavor flavor, @@ -1457,6 +1470,7 @@ private boolean isAutoSense(DocFlavor flavor) { } } + @Override public boolean isAttributeValueSupported(Attribute attr, DocFlavor flavor, AttributeSet attributes) { @@ -1586,6 +1600,7 @@ else if (category == Chromaticity.class) { return true; } + @Override public AttributeSet getUnsupportedAttributes(DocFlavor flavor, AttributeSet attributes) { @@ -1622,7 +1637,7 @@ else if (!isAttributeValueSupported(attr, flavor, attributes)) { private Win32DocumentPropertiesUI docPropertiesUI = null; - private static class Win32DocumentPropertiesUI + private static final class Win32DocumentPropertiesUI extends DocumentPropertiesUI { Win32PrintService service; @@ -1631,6 +1646,7 @@ private Win32DocumentPropertiesUI(Win32PrintService s) { service = s; } + @Override public PrintRequestAttributeSet showDocumentProperties(PrinterJob job, Window owner, @@ -1649,7 +1665,7 @@ private synchronized DocumentPropertiesUI getDocumentPropertiesUI() { return new Win32DocumentPropertiesUI(this); } - private static class Win32ServiceUIFactory extends ServiceUIFactory { + private static final class Win32ServiceUIFactory extends ServiceUIFactory { Win32PrintService service; @@ -1657,6 +1673,7 @@ private static class Win32ServiceUIFactory extends ServiceUIFactory { service = s; } + @Override public Object getUI(int role, String ui) { if (role <= ServiceUIFactory.MAIN_UIROLE) { return null; @@ -1669,6 +1686,7 @@ public Object getUI(int role, String ui) { throw new IllegalArgumentException("Unsupported role"); } + @Override public String[] getUIClassNamesForRole(int role) { if (role <= ServiceUIFactory.MAIN_UIROLE) { @@ -1685,6 +1703,7 @@ public String[] getUIClassNamesForRole(int role) { private Win32ServiceUIFactory uiFactory = null; + @Override public synchronized ServiceUIFactory getServiceUIFactory() { if (uiFactory == null) { uiFactory = new Win32ServiceUIFactory(this); @@ -1692,20 +1711,24 @@ public synchronized ServiceUIFactory getServiceUIFactory() { return uiFactory; } + @Override public String toString() { return "Win32 Printer : " + getName(); } + @Override public boolean equals(Object obj) { return (obj == this || (obj instanceof Win32PrintService && ((Win32PrintService)obj).getName().equals(getName()))); } + @Override public int hashCode() { return this.getClass().hashCode()+getName().hashCode(); } + @Override public boolean usesClass(Class c) { return (c == sun.awt.windows.WPrinterJob.class); } @@ -1727,7 +1750,7 @@ private native float[] getMediaPrintableArea(String printerName, } @SuppressWarnings("serial") // JDK implementation class -class Win32MediaSize extends MediaSizeName { +final class Win32MediaSize extends MediaSizeName { private static ArrayList winStringTable = new ArrayList<>(); private static ArrayList winEnumTable = new ArrayList<>(); private static MediaSize[] predefMedia; @@ -1787,11 +1810,13 @@ int getDMPaper() { return dmPaperID; } + @Override protected String[] getStringTable() { String[] nameTable = new String[winStringTable.size()]; return winStringTable.toArray(nameTable); } + @Override protected EnumSyntax[] getEnumValueTable() { MediaSizeName[] enumTable = new MediaSizeName[winEnumTable.size()]; return winEnumTable.toArray(enumTable); diff --git a/src/java.desktop/windows/classes/sun/swing/plaf/windows/ClassicSortArrowIcon.java b/src/java.desktop/windows/classes/sun/swing/plaf/windows/ClassicSortArrowIcon.java index 6430a71f06ebc..f1180b3ae38a9 100644 --- a/src/java.desktop/windows/classes/sun/swing/plaf/windows/ClassicSortArrowIcon.java +++ b/src/java.desktop/windows/classes/sun/swing/plaf/windows/ClassicSortArrowIcon.java @@ -37,7 +37,7 @@ * */ @SuppressWarnings("serial") // JDK-implementation class -public class ClassicSortArrowIcon implements Icon, UIResource, Serializable{ +public final class ClassicSortArrowIcon implements Icon, UIResource, Serializable{ private static final int X_OFFSET = 9; private boolean ascending; @@ -45,6 +45,7 @@ public ClassicSortArrowIcon(boolean ascending) { this.ascending = ascending; } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { x += X_OFFSET; if (ascending) { @@ -89,9 +90,11 @@ private void drawSide(Graphics g, int x, int y, int xIncrement) { g.fillRect(x, y, 1, 2); } + @Override public int getIconWidth() { return X_OFFSET + 8; } + @Override public int getIconHeight() { return 9; } diff --git a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/functions/FuncExtFunction.java b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/functions/FuncExtFunction.java index 6fe57d28d2f48..9f2a7e78951ac 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/functions/FuncExtFunction.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/functions/FuncExtFunction.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -41,7 +41,7 @@ * the expression executes, it calls ExtensionsTable#extFunction, and then * converts the result to the appropriate XObject. * @xsl.usage advanced - * @LastModified: May 2022 + * @LastModified: Apr 2025 */ public class FuncExtFunction extends Function { @@ -186,12 +186,6 @@ public FuncExtFunction(java.lang.String namespace, */ public XObject execute(XPathContext xctxt) throws TransformerException { - if (xctxt.isSecureProcessing()) - throw new javax.xml.transform.TransformerException( - XPATHMessages.createXPATHMessage( - XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED, - new Object[] {toString()})); - XObject result; List argVec = new ArrayList<>(); int nArgs = m_argVec.size(); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/JAXPExtensionsProvider.java b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/JAXPExtensionsProvider.java index 6d9d882f075ed..5622a0e3db081 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/JAXPExtensionsProvider.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/JAXPExtensionsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -37,26 +37,16 @@ /** * * @author Ramesh Mandava - * @LastModified: Nov 2017 + * @LastModified: Apr 2025 */ public class JAXPExtensionsProvider implements ExtensionsProvider { private final XPathFunctionResolver resolver; - private boolean extensionInvocationDisabled = false; public JAXPExtensionsProvider(XPathFunctionResolver resolver) { this.resolver = resolver; - this.extensionInvocationDisabled = false; } - public JAXPExtensionsProvider(XPathFunctionResolver resolver, - boolean featureSecureProcessing, JdkXmlFeatures featureManager ) { - this.resolver = resolver; - if (featureSecureProcessing && - !featureManager.getFeature(JdkXmlFeatures.XmlFeature.ENABLE_EXTENSION_FUNCTION)) { - this.extensionInvocationDisabled = true; - } - } /** * Is the extension function available? @@ -111,16 +101,6 @@ public Object extFunction(String ns, String funcName, List argVec, //Find the XPathFunction corresponding to namespace and funcName javax.xml.namespace.QName myQName = new QName( ns, funcName ); - // JAXP 1.3 spec says When XMLConstants.FEATURE_SECURE_PROCESSING - // feature is set then invocation of extension functions need to - // throw XPathFunctionException - if ( extensionInvocationDisabled ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED, - new Object[] { myQName.toString() } ); - throw new XPathFunctionException ( fmsg ); - } - // Assuming user is passing all the needed parameters ( including // default values ) int arity = argVec.size(); @@ -167,16 +147,6 @@ public Object extFunction(FuncExtFunction extFunction, List argVec) javax.xml.namespace.QName myQName = new javax.xml.namespace.QName( namespace, functionName ); - // JAXP 1.3 spec says When XMLConstants.FEATURE_SECURE_PROCESSING - // feature is set then invocation of extension functions need to - // throw XPathFunctionException - if ( extensionInvocationDisabled ) { - String fmsg = XSLMessages.createXPATHMessage( - XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED, - new Object[] { myQName.toString() } ); - throw new XPathFunctionException ( fmsg ); - } - XPathFunction xpathFunction = resolver.resolveFunction( myQName, arity ); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java index e81523b560752..a92090900facb 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,7 @@ * This class contains several utility methods used by XPathImpl and * XPathExpressionImpl * - * @LastModified: Jan 2022 + * @LastModified: Apr 2025 */ class XPathImplUtil { XPathFunctionResolver functionResolver; @@ -85,8 +85,7 @@ XObject eval(Object contextItem, com.sun.org.apache.xpath.internal.XPath xpath) new Object[] {})); } if (functionResolver != null) { - JAXPExtensionsProvider jep = new JAXPExtensionsProvider( - functionResolver, featureSecureProcessing, featureManager); + JAXPExtensionsProvider jep = new JAXPExtensionsProvider(functionResolver); xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(jep); } else { xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(); diff --git a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java index 433a0f853890e..716a4bcf63bda 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -31,7 +31,7 @@ * Also you need to update the count of messages(MAX_CODE)or * the count of warnings(MAX_WARNING) [ Information purpose only] * @xsl.usage advanced - * @LastModified: Nov 2024 + * @LastModified: Apr 2025 */ public class XPATHErrorResources extends ListResourceBundle { @@ -305,7 +305,6 @@ public class XPATHErrorResources extends ListResourceBundle public static final String ER_XPATH_ERROR = "ER_XPATH_ERROR"; //BEGIN: Keys needed for exception messages of JAXP 1.3 XPath API implementation - public static final String ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED = "ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED"; public static final String ER_RESOLVE_VARIABLE_RETURNS_NULL = "ER_RESOLVE_VARIABLE_RETURNS_NULL"; public static final String ER_NO_XPATH_VARIABLE_RESOLVER = "ER_NO_XPATH_VARIABLE_RESOLVER"; public static final String ER_NO_XPATH_FUNCTION_PROVIDER = "ER_NO_XPATH_FUNCTION_PROVIDER"; @@ -766,11 +765,6 @@ public class XPATHErrorResources extends ListResourceBundle //BEGIN: Definitions of error keys used in exception messages of JAXP 1.3 XPath API implementation - /** Field ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED */ - - { ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED, - "Extension function: ''{0}'' can not be invoked when the XMLConstants.FEATURE_SECURE_PROCESSING feature is set to true."}, - /** Field ER_RESOLVE_VARIABLE_RETURNS_NULL */ { ER_RESOLVE_VARIABLE_RETURNS_NULL, diff --git a/src/java.xml/share/classes/module-info.java b/src/java.xml/share/classes/module-info.java index a12fd3e8f4536..1b4f345eb7274 100644 --- a/src/java.xml/share/classes/module-info.java +++ b/src/java.xml/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -868,19 +868,19 @@ * * * {@systemProperty jdk.xml.enableExtensionFunctions} - * Determines if XSLT and XPath extension functions are to be allowed. + * Determines whether extension functions in the Transform API are to be allowed. + * The extension functions in the XPath API are not affected by this property. * * yes * Boolean * * true or false. True indicates that extension functions are allowed; False otherwise. * - * true + * false * false * Yes * * Transform
- * XPath * * Method 2 * 8 diff --git a/src/jdk.compiler/share/classes/com/sun/source/tree/ImportTree.java b/src/jdk.compiler/share/classes/com/sun/source/tree/ImportTree.java index 83a6c3eb87b66..ac4dc6258acab 100644 --- a/src/jdk.compiler/share/classes/com/sun/source/tree/ImportTree.java +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/ImportTree.java @@ -25,8 +25,6 @@ package com.sun.source.tree; -import jdk.internal.javac.PreviewFeature; - /** * A tree node for an import declaration. * @@ -49,11 +47,11 @@ public interface ImportTree extends Tree { * @return true if this is a static import */ boolean isStatic(); + /** * {@return true if this is an module import declaration.} - * @since 23 + * @since 25 */ - @PreviewFeature(feature=PreviewFeature.Feature.MODULE_IMPORTS, reflective=true) boolean isModule(); /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index 9d7b1e6535245..2d0f44200c110 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -153,9 +153,7 @@ public boolean participatesInPreview(Symtab syms, ModuleSymbol m) { // s participates in the preview API return syms.java_base.exports.stream() .filter(ed -> ed.packge.fullname == names.jdk_internal_javac) - .anyMatch(ed -> ed.modules.contains(m)) || - //the specification lists the java.se module as participating in preview: - m.name == names.java_se; + .anyMatch(ed -> ed.modules.contains(m)); } /** @@ -228,11 +226,8 @@ public boolean isEnabled() { */ public boolean isPreview(Feature feature) { return switch (feature) { - case IMPLICIT_CLASSES -> true; case FLEXIBLE_CONSTRUCTORS -> true; case PRIMITIVE_PATTERNS -> true; - case MODULE_IMPORTS -> true; - case JAVA_BASE_TRANSITIVE -> true; //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). //When real preview features will be added, this method can be implemented to return 'true' //for those selected features, and 'false' for all the others. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java index f1c25d032d05c..e3585dd398b90 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -262,13 +262,13 @@ public enum Feature { REDUNDANT_STRICTFP(JDK17), UNCONDITIONAL_PATTERN_IN_INSTANCEOF(JDK21, Fragments.FeatureUnconditionalPatternsInInstanceof, DiagKind.PLURAL), RECORD_PATTERNS(JDK21, Fragments.FeatureDeconstructionPatterns, DiagKind.PLURAL), - IMPLICIT_CLASSES(JDK21, Fragments.FeatureImplicitClasses, DiagKind.PLURAL), + IMPLICIT_CLASSES(JDK25, Fragments.FeatureImplicitClasses, DiagKind.PLURAL), WARN_ON_ILLEGAL_UTF8(MIN, JDK21), UNNAMED_VARIABLES(JDK22, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL), PRIMITIVE_PATTERNS(JDK23, Fragments.FeaturePrimitivePatterns, DiagKind.PLURAL), FLEXIBLE_CONSTRUCTORS(JDK22, Fragments.FeatureFlexibleConstructors, DiagKind.NORMAL), - MODULE_IMPORTS(JDK23, Fragments.FeatureModuleImports, DiagKind.PLURAL), - JAVA_BASE_TRANSITIVE(JDK24, Fragments.FeatureJavaBaseTransitive, DiagKind.PLURAL), + MODULE_IMPORTS(JDK25, Fragments.FeatureModuleImports, DiagKind.PLURAL), + JAVA_BASE_TRANSITIVE(JDK25, Fragments.FeatureJavaBaseTransitive, DiagKind.PLURAL), PRIVATE_MEMBERS_IN_PERMITS_CLAUSE(JDK19), ERASE_POLY_SIG_RETURN_TYPE(JDK24), ; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java index cfe4ef662e5cb..74c740656f4a3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, 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 @@ -230,7 +230,6 @@ public static Symtab instance(Context context) { public final Type valueBasedInternalType; public final Type classDescType; public final Type enumDescType; - public final Type ioType; // For serialization lint checking public final Type objectStreamFieldType; @@ -613,7 +612,6 @@ public R accept(ElementVisitor v, P p) { valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation"); classDescType = enterClass("java.lang.constant.ClassDesc"); enumDescType = enterClass("java.lang.Enum$EnumDesc"); - ioType = enterClass("java.io.IO"); // For serialization lint checking objectStreamFieldType = enterClass("java.io.ObjectStreamField"); objectInputStreamType = enterClass("java.io.ObjectInputStream"); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 6dc0238f87e54..53ce8b1b4944c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -3343,6 +3343,10 @@ TargetInfo getTargetInfo(JCPolyExpression that, ResultInfo resultInfo, List env) { (cls.mods.flags & IMPLICIT_CLASS) != 0; if (isImplicitClass) { doModuleImport(make.ModuleImport(make.QualIdent(syms.java_base))); - if (peekTypeExists(syms.ioType.tsym)) { - doImport(make.Import(make.Select(make.QualIdent(syms.ioType.tsym), - names.asterisk), true), false); - } - } - } - - private boolean peekTypeExists(TypeSymbol type) { - try { - type.complete(); - return !type.type.isErroneous(); - } catch (CompletionFailure cf) { - //does not exist - return false; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 6f55903f489ab..0173ab109d7d3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1202,11 +1202,6 @@ protected void read(Symbol sym, int attrLen) { ModuleSymbol rsym = poolReader.getModule(nextChar()); Set flags = readRequiresFlags(nextChar()); if (rsym == syms.java_base && majorVersion >= V54.major) { - if (flags.contains(RequiresFlag.TRANSITIVE) && - (majorVersion != Version.MAX().major || !previewClassFile) && - !preview.participatesInPreview(syms, msym)) { - throw badClassFile("bad.requires.flag", RequiresFlag.TRANSITIVE); - } if (flags.contains(RequiresFlag.STATIC_PHASE)) { throw badClassFile("bad.requires.flag", RequiresFlag.STATIC_PHASE); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index d0b9fccde4887..1383baa57b8ee 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -153,6 +153,7 @@ public class JavacParser implements Parser { * is true). */ private boolean permitTypeAnnotationsPushBack = false; + private JCDiagnostic.Error unexpectedTopLevelDefinitionStartError; interface ErrorRecoveryAction { JCTree doRecover(JavacParser parser); @@ -200,6 +201,7 @@ protected JavacParser(ParserFactory fac, this.allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source); this.allowRecords = Feature.RECORDS.allowedInSource(source); this.allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source); + updateUnexpectedTopLevelDefinitionStartError(false); } /** Construct a parser from an existing parser, with minimal overhead. @@ -224,6 +226,7 @@ protected JavacParser(JavacParser parser, this.allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source); this.allowRecords = Feature.RECORDS.allowedInSource(source); this.allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source); + updateUnexpectedTopLevelDefinitionStartError(false); } protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) { @@ -4021,6 +4024,7 @@ public JCTree.JCCompilationUnit parseCompilationUnit() { attach(pd, firstToken.docComment()); consumedToplevelDoc = true; defs.append(pd); + updateUnexpectedTopLevelDefinitionStartError(true); } boolean firstTypeDecl = true; // have we seen a class, enum, or interface declaration yet? @@ -4094,7 +4098,7 @@ public JCTree.JCCompilationUnit parseCompilationUnit() { // it is parsed. Otherwise, parsing continues as though // implicitly declared classes did not exist and error reporting // is the same as in the past. - if (Feature.IMPLICIT_CLASSES.allowedInSource(source) && !isDeclaration()) { + if (!isDeclaration(true)) { final JCModifiers finalMods = mods; JavacParser speculative = new VirtualParser(this); List speculativeResult = @@ -4348,18 +4352,7 @@ protected JCStatement classOrRecordOrInterfaceOrEnumDeclaration(JCModifiers mods errs = List.of(mods); } - JCDiagnostic.Error error; - if (parseModuleInfo) { - error = Errors.ExpectedModuleOrOpen; - } else if (Feature.IMPLICIT_CLASSES.allowedInSource(source) && - (!preview.isPreview(Feature.IMPLICIT_CLASSES) || preview.isEnabled())) { - error = Errors.ClassMethodOrFieldExpected; - } else if (allowRecords) { - error = Errors.Expected4(CLASS, INTERFACE, ENUM, "record"); - } else { - error = Errors.Expected3(CLASS, INTERFACE, ENUM); - } - return toP(F.Exec(syntaxError(pos, errs, error))); + return toP(F.Exec(syntaxError(pos, errs, unexpectedTopLevelDefinitionStartError))); } } @@ -4948,6 +4941,10 @@ private List topLevelMethodOrFieldDeclaration(JCModifiers mods, Comment } protected boolean isDeclaration() { + return isDeclaration(allowRecords); + } + + private boolean isDeclaration(boolean allowRecords) { return token.kind == CLASS || token.kind == INTERFACE || token.kind == ENUM || @@ -5612,6 +5609,19 @@ protected void checkSourceLevel(int pos, Feature feature) { } } + private void updateUnexpectedTopLevelDefinitionStartError(boolean hasPackageDecl) { + //TODO: proper tests for this logic (and updates): + if (parseModuleInfo) { + unexpectedTopLevelDefinitionStartError = Errors.ExpectedModuleOrOpen; + } else if (Feature.IMPLICIT_CLASSES.allowedInSource(source) && !hasPackageDecl) { + unexpectedTopLevelDefinitionStartError = Errors.ClassMethodOrFieldExpected; + } else if (allowRecords) { + unexpectedTopLevelDefinitionStartError = Errors.Expected4(CLASS, INTERFACE, ENUM, "record"); + } else { + unexpectedTopLevelDefinitionStartError = Errors.Expected3(CLASS, INTERFACE, ENUM); + } + } + /* * a functional source tree and end position mappings */ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavadocTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavadocTokenizer.java index 5dcb4cc36d4f3..a2925c69d94e2 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavadocTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavadocTokenizer.java @@ -307,12 +307,13 @@ private void ensure(int need) { while (need > grow) { grow <<= 1; + // Handle overflow. + if (grow <= 0) { + throw new IndexOutOfBoundsException(); + } } - // Handle overflow. - if (grow < map.length) { - throw new IndexOutOfBoundsException(); - } else if (grow != map.length) { + if (grow != map.length) { map = Arrays.copyOf(map, grow); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 79b6a4fd1da4a..79d15a96a6e01 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -337,7 +337,7 @@ compiler.misc.bad.intersection.target.for.functional.expr=\ # 0: symbol or type compiler.misc.not.an.intf.component=\ - component type {0} is not an interface + component type {0} is not an interface or java.lang.Object # 0: kind name, 1: message segment compiler.err.invalid.mref=\ @@ -529,10 +529,10 @@ compiler.err.bad.file.name=\ bad file name: {0} compiler.err.implicit.class.should.not.have.package.declaration=\ - implicitly declared class should not have package declaration + compact source file should not have package declaration compiler.err.implicit.class.does.not.have.main.method=\ - implicitly declared class does not have main method in the form of void main() or void main(String[] args) + compact source file does not have main method in the form of void main() or void main(String[] args) # 0: name, 1: name compiler.err.same.binary.name=\ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java index fa81ce7ba75c9..512fa0e07e3be 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java @@ -83,8 +83,37 @@ public class RichDiagnosticFormatter extends /* type/symbol printer used by this formatter */ private RichPrinter printer; + private static class WhereClauses { + private final Map> whereClauses; + + WhereClauses() { + Map> whereClauses = new EnumMap<>(WhereClauseKind.class); + for (WhereClauseKind kind : WhereClauseKind.values()) { + whereClauses.put(kind, new LinkedHashMap<>()); + } + this.whereClauses = whereClauses; + } + + public Map get(WhereClauseKind kind) { + return whereClauses.get(kind); + } + } + /* map for keeping track of a where clause associated to a given type */ - Map> whereClauses; + WhereClauses whereClauses; + + private void enter() { + if (nameSimplifier != null || whereClauses != null) { + throw new IllegalStateException(); + } + nameSimplifier = new ClassNameSimplifier(); + whereClauses = new WhereClauses(); + } + + private void exit() { + nameSimplifier = null; + whereClauses = null; + } /** Get the DiagnosticFormatter instance for this context. */ public static RichDiagnosticFormatter instance(Context context) { @@ -102,39 +131,42 @@ protected RichDiagnosticFormatter(Context context) { this.diags = JCDiagnostic.Factory.instance(context); this.types = Types.instance(context); this.messages = JavacMessages.instance(context); - whereClauses = new EnumMap<>(WhereClauseKind.class); configuration = new RichConfiguration(Options.instance(context), formatter); - for (WhereClauseKind kind : WhereClauseKind.values()) - whereClauses.put(kind, new LinkedHashMap()); } @Override public String format(JCDiagnostic diag, Locale l) { - StringBuilder sb = new StringBuilder(); - nameSimplifier = new ClassNameSimplifier(); - for (WhereClauseKind kind : WhereClauseKind.values()) - whereClauses.get(kind).clear(); - preprocessDiagnostic(diag); - sb.append(formatter.format(diag, l)); - if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) { - List clauses = getWhereClauses(); - String indent = formatter.isRaw() ? "" : - formatter.indentString(DetailsInc); - for (JCDiagnostic d : clauses) { - String whereClause = formatter.format(d, l); - if (whereClause.length() > 0) { - sb.append('\n' + indent + whereClause); + enter(); + try { + StringBuilder sb = new StringBuilder(); + preprocessDiagnostic(diag); + sb.append(formatter.format(diag, l)); + if (getConfiguration().isEnabled(RichFormatterFeature.WHERE_CLAUSES)) { + List clauses = getWhereClauses(); + String indent = formatter.isRaw() ? "" : + formatter.indentString(DetailsInc); + for (JCDiagnostic d : clauses) { + String whereClause = formatter.format(d, l); + if (whereClause.length() > 0) { + sb.append('\n' + indent + whereClause); + } } } + return sb.toString(); + } finally { + exit(); } - return sb.toString(); } @Override public String formatMessage(JCDiagnostic diag, Locale l) { - nameSimplifier = new ClassNameSimplifier(); - preprocessDiagnostic(diag); - return super.formatMessage(diag, l); + enter(); + try { + preprocessDiagnostic(diag); + return super.formatMessage(diag, l); + } finally { + exit(); + } } /** @@ -556,7 +588,7 @@ public Void visitClassType(ClassType t, Void ignored) { @Override public Void visitTypeVar(TypeVar t, Void ignored) { - t = (TypeVar)t.stripMetadataIfNeeded(); + t = (TypeVar)t.stripMetadata(); if (indexOf(t, WhereClauseKind.TYPEVAR) == -1) { //access the bound type and skip error types Type bound = t.getUpperBound(); diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11HKDF.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11HKDF.java index 43a036da6f35c..e8bef222d88d4 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11HKDF.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11HKDF.java @@ -38,6 +38,7 @@ import static sun.security.pkcs11.TemplateManager.*; import sun.security.pkcs11.wrapper.*; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; +import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.CKR_KEY_SIZE_RANGE; final class P11HKDF extends KDFSpi { private final Token token; @@ -157,6 +158,15 @@ private T derive(String alg, AlgorithmParameterSpec derivationSpec, " instance, instead of " + derivationSpec.getClass()); } + P11SecretKeyFactory.KeyInfo ki = P11SecretKeyFactory.getKeyInfo(alg); + if (ki == null) { + throw new InvalidAlgorithmParameterException("A PKCS #11 key " + + "type (CKK_*) was not found for a key of the algorithm '" + + alg + "'."); + } + checkDerivedKeyType(ki, alg); + P11KeyGenerator.checkKeySize(ki.keyGenMech, outLen * 8, token); + P11Key p11BaseKey = convertKey(baseKey, (isExtract ? "IKM" : "PRK") + " could not be converted to a token key for HKDF derivation."); @@ -174,17 +184,10 @@ private T derive(String alg, AlgorithmParameterSpec derivationSpec, "token as service."; } - P11SecretKeyFactory.KeyInfo ki = P11SecretKeyFactory.getKeyInfo(alg); - if (ki == null) { - throw new InvalidAlgorithmParameterException("A PKCS #11 key " + - "type (CKK_*) was not found for a key of the algorithm '" + - alg + "'."); - } - long derivedKeyType = ki.keyType; long derivedKeyClass = isData ? CKO_DATA : CKO_SECRET_KEY; CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_CLASS, derivedKeyClass), - new CK_ATTRIBUTE(CKA_KEY_TYPE, derivedKeyType), + new CK_ATTRIBUTE(CKA_KEY_TYPE, ki.keyType), new CK_ATTRIBUTE(CKA_VALUE_LEN, outLen) }; Session session = null; @@ -195,7 +198,7 @@ private T derive(String alg, AlgorithmParameterSpec derivationSpec, svcKi.hmacMech, saltType, saltBytes, p11SaltKey != null ? p11SaltKey.getKeyID() : 0L, info); attrs = token.getAttributes(O_GENERATE, derivedKeyClass, - derivedKeyType, attrs); + ki.keyType, attrs); long derivedObjectID = token.p11.C_DeriveKey(session.id(), new CK_MECHANISM(mechanism, params), baseKeyID, attrs); Object ret; @@ -216,6 +219,11 @@ private T derive(String alg, AlgorithmParameterSpec derivationSpec, } return retType.cast(ret); } catch (PKCS11Exception e) { + if (e.match(CKR_KEY_SIZE_RANGE)) { + throw new InvalidAlgorithmParameterException("Invalid key " + + "size (" + outLen + " bytes) for algorithm '" + alg + + "'.", e); + } throw new ProviderException("HKDF derivation for algorithm '" + alg + "' failed.", e); } finally { @@ -227,6 +235,23 @@ private T derive(String alg, AlgorithmParameterSpec derivationSpec, } } + private static boolean canDeriveKeyInfoType(long t) { + return (t == CKK_DES || t == CKK_DES3 || t == CKK_AES || + t == CKK_RC4 || t == CKK_BLOWFISH || t == CKK_CHACHA20 || + t == CKK_GENERIC_SECRET); + } + + private void checkDerivedKeyType(P11SecretKeyFactory.KeyInfo ki, String alg) + throws InvalidAlgorithmParameterException { + Class kiClass = ki.getClass(); + if (!kiClass.equals(P11SecretKeyFactory.TLSKeyInfo.class) && + !(kiClass.equals(P11SecretKeyFactory.KeyInfo.class) && + canDeriveKeyInfoType(ki.keyType))) { + throw new InvalidAlgorithmParameterException("A key of algorithm " + + "'" + alg + "' is not valid for derivation."); + } + } + private P11Key.P11SecretKey convertKey(SecretKey key, String errorMessage) { try { return (P11Key.P11SecretKey) P11SecretKeyFactory.convertKey(token, diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java index bb2211ab9c16c..b0caa413f0e06 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Key.java @@ -356,7 +356,9 @@ static SecretKey secretKey(Session session, long keyID, String algorithm, return new P11SecretKey(session, keyID, algorithm, keyLength, attrs); } - static SecretKey pbeKey(Session session, long keyID, String algorithm, + // for PBKDF2 and the deprecated PBE-based key derivation method defined + // in RFC 7292 PKCS#12 B.2 + static SecretKey pbkdfKey(Session session, long keyID, String algorithm, int keyLength, CK_ATTRIBUTE[] attrs, char[] password, byte[] salt, int iterationCount) { attrs = getAttributes(session, keyID, attrs, new CK_ATTRIBUTE[] { @@ -364,7 +366,7 @@ static SecretKey pbeKey(Session session, long keyID, String algorithm, new CK_ATTRIBUTE(CKA_SENSITIVE), new CK_ATTRIBUTE(CKA_EXTRACTABLE), }); - return new P11PBEKey(session, keyID, algorithm, keyLength, + return new P11PBKDFKey(session, keyID, algorithm, keyLength, attrs, password, salt, iterationCount); } @@ -502,16 +504,16 @@ private abstract static class P11PublicKey extends P11Key implements } } - static final class P11PBEKey extends P11SecretKey + static final class P11PBKDFKey extends P11SecretKey implements PBEKey { private static final long serialVersionUID = 6847576994253634876L; private char[] password; private final byte[] salt; private final int iterationCount; - P11PBEKey(Session session, long keyID, String algorithm, + P11PBKDFKey(Session session, long keyID, String keyAlgo, int keyLength, CK_ATTRIBUTE[] attributes, char[] password, byte[] salt, int iterationCount) { - super(session, keyID, algorithm, keyLength, attributes); + super(session, keyID, keyAlgo, keyLength, attributes); this.password = password.clone(); this.salt = salt.clone(); this.iterationCount = iterationCount; diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java index 3ec2cfba6553e..9bfe55b2509d4 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,9 +75,10 @@ final class P11KeyGenerator extends KeyGeneratorSpi { // java-specific lower limit; returned values are in bits private static CK_MECHANISM_INFO getSupportedRange(Token token, long mech) throws ProviderException { - // No need to query for fix-length algorithms - if (mech == CKM_DES_KEY_GEN || mech == CKM_DES2_KEY_GEN || - mech == CKM_DES3_KEY_GEN) { + // No need to query if the mechanism is not available or for + // fix-length algorithms + if (mech == CK_UNAVAILABLE_INFORMATION || mech == CKM_DES_KEY_GEN || + mech == CKM_DES2_KEY_GEN || mech == CKM_DES3_KEY_GEN) { return null; } @@ -115,7 +116,7 @@ private static CK_MECHANISM_INFO getSupportedRange(Token token, * and within the supported range. Return the significant key size * upon successful validation. * @param keyGenMech the PKCS#11 key generation mechanism. - * @param keySize the to-be-checked key size for this mechanism. + * @param keySize the to-be-checked key size (in bits) for this mechanism. * @param token token which provides this mechanism. * @return the significant key size (in bits) corresponding to the * specified key size. @@ -123,7 +124,7 @@ private static CK_MECHANISM_INFO getSupportedRange(Token token, * @throws ProviderException if this mechanism isn't supported by SunPKCS11 * or underlying native impl. */ - // called by P11SecretKeyFactory to check key size + // called by P11SecretKeyFactory and P11HKDF to check key size static int checkKeySize(long keyGenMech, int keySize, Token token) throws InvalidAlgorithmParameterException, ProviderException { CK_MECHANISM_INFO range = getSupportedRange(token, keyGenMech); @@ -136,8 +137,8 @@ private static int checkKeySize(long keyGenMech, int keySize, switch ((int)keyGenMech) { case (int)CKM_DES_KEY_GEN: if ((keySize != 64) && (keySize != 56)) { - throw new InvalidAlgorithmParameterException - ("DES key length must be 56 bits"); + throw new InvalidAlgorithmParameterException("DES key " + + "length was " + keySize + " but must be 56 bits"); } sigKeySize = 56; break; @@ -148,23 +149,26 @@ private static int checkKeySize(long keyGenMech, int keySize, } else if ((keySize == 168) || (keySize == 192)) { sigKeySize = 168; } else { - throw new InvalidAlgorithmParameterException - ("DESede key length must be 112, or 168 bits"); + throw new InvalidAlgorithmParameterException("DESede key " + + "length was " + keySize + " but must be 112, or " + + "168 bits"); } break; default: // Handle all variable-key-length algorithms here - if (range != null && keySize < range.iMinKeySize - || keySize > range.iMaxKeySize) { - throw new InvalidAlgorithmParameterException - ("Key length must be between " + range.iMinKeySize + - " and " + range.iMaxKeySize + " bits"); + if (range != null && (keySize < range.iMinKeySize + || keySize > range.iMaxKeySize)) { + throw new InvalidAlgorithmParameterException("Key length " + + "was " + keySize + " but must be between " + + range.iMinKeySize + " and " + range.iMaxKeySize + + " bits"); } if (keyGenMech == CKM_AES_KEY_GEN) { if ((keySize != 128) && (keySize != 192) && (keySize != 256)) { - throw new InvalidAlgorithmParameterException - ("AES key length must be 128, 192, or 256 bits"); + throw new InvalidAlgorithmParameterException("AES key" + + " length was " + keySize + " but must be 128," + + " 192, or 256 bits"); } } sigKeySize = keySize; diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java index 8fd6c484ea483..f684b13fcd8fe 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Mac.java @@ -196,33 +196,23 @@ protected void engineInit(Key key, AlgorithmParameterSpec params) reset(true); p11Key = null; if (svcPbeKi != null) { - if (key instanceof P11Key) { - // If the key is a P11Key, it must come from a PBE derivation - // because this is a PBE Mac service. In addition to checking - // the key, check that params (if passed) are consistent. - PBEUtil.checkKeyAndParams(key, params, algorithm); - } else { - // If the key is not a P11Key, a derivation is needed. Data for - // derivation has to be carried either as part of the key or - // params. Use SunPKCS11 PBE key derivation to obtain a P11Key. - // Assign the derived key to p11Key because conversion is never - // needed for this case. - PBEKeySpec pbeKeySpec = PBEUtil.getPBAKeySpec(key, params); - try { - P11Key.P11PBEKey p11PBEKey = - P11SecretKeyFactory.derivePBEKey(token, - pbeKeySpec, svcPbeKi); - // This Mac service uses the token where the derived key - // lives so there won't be any need to re-derive and use - // the password. The p11Key cannot be accessed out of this - // class. - p11PBEKey.clearPassword(); - p11Key = p11PBEKey; - } catch (InvalidKeySpecException e) { - throw new InvalidKeyException(e); - } finally { - pbeKeySpec.clearPassword(); - } + // Do key derivation using P11SecretKeyFactory, then store the + // derived key to p11Key + PBEKeySpec pbeKeySpec = PBEUtil.getPBAKeySpec(key, params); + try { + P11Key.P11PBKDFKey derivedKey = + P11SecretKeyFactory.derivePBEKey(token, + pbeKeySpec, svcPbeKi); + // This Mac service uses the token where the derived key + // lives so there won't be any need to re-derive and use + // the password. The p11Key cannot be accessed out of this + // class. + derivedKey.clearPassword(); + p11Key = derivedKey; + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } finally { + pbeKeySpec.clearPassword(); } if (params instanceof PBEParameterSpec pbeParams) { // For PBE services, reassign params to the underlying @@ -230,16 +220,13 @@ protected void engineInit(Key key, AlgorithmParameterSpec params) // value to be null. params = pbeParams.getParameterSpec(); } + } else { // for the non-PBE case + p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm); } if (params != null) { throw new InvalidAlgorithmParameterException( "Parameters not supported"); } - // In non-PBE cases and PBE cases where we didn't derive, - // a key conversion might be needed. - if (p11Key == null) { - p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm); - } try { initialize(); } catch (PKCS11Exception e) { diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java index dc4233793afbd..de49036d8e12c 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PBECipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Red Hat, Inc. + * Copyright (c) 2023, 2025, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package sun.security.pkcs11; import java.security.AlgorithmParameters; @@ -39,7 +38,6 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; import javax.crypto.spec.PBEKeySpec; -import javax.crypto.spec.PBEParameterSpec; import sun.security.jca.JCAUtil; import static sun.security.pkcs11.wrapper.PKCS11Constants.*; @@ -128,46 +126,23 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - if (key instanceof P11Key) { - // If the key is a P11Key, it must come from a PBE derivation - // because this is a PBE Cipher service. In addition to checking the - // key, check that params (if passed) are consistent. - PBEUtil.checkKeyAndParams(key, params, pbeAlg); - // At this point, we know that the key is a P11PBEKey. - P11Key.P11PBEKey p11PBEKey = (P11Key.P11PBEKey) key; - // PBE services require a PBE key of the same algorithm and the - // underlying service (non-PBE) won't check it. - if (!pbeAlg.equals(p11PBEKey.getAlgorithm())) { - throw new InvalidKeyException("Cannot use a " + - p11PBEKey.getAlgorithm() + " key for a " + pbeAlg + - " service"); - } - if (params instanceof PBEParameterSpec pbeParams) { - params = pbeParams.getParameterSpec(); - } - pbes2Params.initialize(blkSize, opmode, - p11PBEKey.getIterationCount(), p11PBEKey.getSalt(), params, - random); - } else { - // If the key is not a P11Key, a derivation is needed. Data for - // derivation has to be carried either as part of the key or params. - // Use SunPKCS11 PBE key derivation to obtain a P11Key. - PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec( - blkSize, svcPbeKi.keyLen, opmode, key, params, random); - try { - P11Key.P11PBEKey p11PBEKey = P11SecretKeyFactory.derivePBEKey( - token, pbeSpec, svcPbeKi); - // The internal Cipher service uses the token where the - // derived key lives so there won't be any need to re-derive - // and use the password. The key cannot be accessed out of this - // class. - p11PBEKey.clearPassword(); - key = p11PBEKey; - } catch (InvalidKeySpecException e) { - throw new InvalidKeyException(e); - } finally { - pbeSpec.clearPassword(); - } + // do key derivation, use P11SecretKeyFactory + PBEKeySpec pbeSpec = pbes2Params.getPBEKeySpec( + blkSize, svcPbeKi.keyLen, opmode, key, params, random); + try { + P11Key.P11PBKDFKey derivedKey = + P11SecretKeyFactory.derivePBEKey( + token, pbeSpec, svcPbeKi); + // The internal Cipher service uses the token where the + // derived key lives so there won't be any need to re-derive + // and use the password. The key cannot be accessed out of this + // class. + derivedKey.clearPassword(); + key = derivedKey; + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } finally { + pbeSpec.clearPassword(); } cipher.engineInit(opmode, key, pbes2Params.getIvSpec(), random); } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java index 6c67e64143851..3e32b0c7c2daf 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11SecretKeyFactory.java @@ -103,13 +103,37 @@ private static void putKeyInfo(KeyInfo ki) { keyInfo.put(ki.algo.toUpperCase(Locale.ENGLISH), ki); } - static sealed class KeyInfo permits PBEKeyInfo, HMACKeyInfo, HKDFKeyInfo { + /* + * The KeyInfo class represents information about a symmetric PKCS #11 key + * type or about the output of a key-based computation (e.g. HMAC). A + * KeyInfo instance may describe the key/output itself, or the type of + * key/output that a service accepts/produces. Used by P11SecretKeyFactory, + * P11PBECipher, P11Mac, and P11HKDF. + */ + static sealed class KeyInfo permits PBEKeyInfo, HMACKeyInfo, HKDFKeyInfo, + TLSKeyInfo { + // Java Standard Algorithm Name. public final String algo; + + // Key type (CKK_*). public final long keyType; + // Mechanism for C_GenerateKey to generate a key of this type (CKM_*). + // While keys may be generated with other APIs and mechanisms (e.g. AES + // key generated with C_DeriveKey and CKM_HKDF_DERIVE instead of + // C_GenerateKey and CKM_AES_KEY_GEN), this information is used by + // P11KeyGenerator::checkKeySize in a best-effort attempt to validate + // that the key size is within a valid range (see CK_MECHANISM_INFO). + public final long keyGenMech; + KeyInfo(String algo, long keyType) { + this(algo, keyType, CK_UNAVAILABLE_INFORMATION); + } + + KeyInfo(String algo, long keyType, long keyGenMech) { this.algo = algo; this.keyType = keyType; + this.keyGenMech = keyGenMech; } // The P11SecretKeyFactory::convertKey method needs to know if a service @@ -134,8 +158,26 @@ static boolean checkUse(KeyInfo ki, KeyInfo si) { } } + /* + * KeyInfo specialization for keys that are either input or result of a TLS + * key derivation. Keys of this type are typically handled by JSSE and their + * algorithm name start with "Tls". Used by P11HKDF. + */ + static final class TLSKeyInfo extends KeyInfo { + TLSKeyInfo(String algo) { + super(algo, CKK_GENERIC_SECRET); + } + } + + /* + * KeyInfo specialization for outputs of a HMAC computation. Used by + * P11SecretKeyFactory and P11Mac. + */ static final class HMACKeyInfo extends KeyInfo { + // HMAC mechanism (CKM_*) to generate the output. public final long mech; + + // HMAC output length (in bits). public final int keyLen; HMACKeyInfo(String algo, long mech, int keyLen) { @@ -145,6 +187,10 @@ static final class HMACKeyInfo extends KeyInfo { } } + /* + * KeyInfo specialization for HKDF key derivation. Used by + * P11SecretKeyFactory and P11HKDF. + */ static final class HKDFKeyInfo extends KeyInfo { public static final long UNKNOWN_KEY_TYPE = -1; public final long hmacMech; @@ -157,6 +203,10 @@ static final class HKDFKeyInfo extends KeyInfo { } } + /* + * KeyInfo specialization for PBE key derivation. Used by + * P11SecretKeyFactory, P11PBECipher and P11Mac. + */ abstract static sealed class PBEKeyInfo extends KeyInfo permits AESPBEKeyInfo, PBKDF2KeyInfo, P12MacPBEKeyInfo { public static final long INVALID_PRF = -1; @@ -204,24 +254,39 @@ static final class P12MacPBEKeyInfo extends PBEKeyInfo { } static { - putKeyInfo(new KeyInfo("RC4", CKK_RC4)); - putKeyInfo(new KeyInfo("ARCFOUR", CKK_RC4)); - putKeyInfo(new KeyInfo("DES", CKK_DES)); - putKeyInfo(new KeyInfo("DESede", CKK_DES3)); - putKeyInfo(new KeyInfo("AES", CKK_AES)); - putKeyInfo(new KeyInfo("Blowfish", CKK_BLOWFISH)); - putKeyInfo(new KeyInfo("ChaCha20", CKK_CHACHA20)); - putKeyInfo(new KeyInfo("ChaCha20-Poly1305", CKK_CHACHA20)); + putKeyInfo(new KeyInfo("RC4", CKK_RC4, CKM_RC4_KEY_GEN)); + putKeyInfo(new KeyInfo("ARCFOUR", CKK_RC4, CKM_RC4_KEY_GEN)); + putKeyInfo(new KeyInfo("DES", CKK_DES, CKM_DES_KEY_GEN)); + putKeyInfo(new KeyInfo("DESede", CKK_DES3, CKM_DES3_KEY_GEN)); + putKeyInfo(new KeyInfo("AES", CKK_AES, CKM_AES_KEY_GEN)); + putKeyInfo(new KeyInfo("Blowfish", CKK_BLOWFISH, CKM_BLOWFISH_KEY_GEN)); + putKeyInfo(new KeyInfo("ChaCha20", CKK_CHACHA20, CKM_CHACHA20_KEY_GEN)); + putKeyInfo(new KeyInfo("ChaCha20-Poly1305", CKK_CHACHA20, + CKM_CHACHA20_KEY_GEN)); // we don't implement RC2 or IDEA, but we want to be able to generate // keys for those SSL/TLS ciphersuites. - putKeyInfo(new KeyInfo("RC2", CKK_RC2)); - putKeyInfo(new KeyInfo("IDEA", CKK_IDEA)); - - putKeyInfo(new KeyInfo("TlsPremasterSecret", PCKK_TLSPREMASTER)); - putKeyInfo(new KeyInfo("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER)); - putKeyInfo(new KeyInfo("TlsMasterSecret", PCKK_TLSMASTER)); - putKeyInfo(new KeyInfo("Generic", CKK_GENERIC_SECRET)); + putKeyInfo(new KeyInfo("RC2", CKK_RC2, CKM_RC2_KEY_GEN)); + putKeyInfo(new KeyInfo("IDEA", CKK_IDEA, CKM_IDEA_KEY_GEN)); + + putKeyInfo(new TLSKeyInfo("TlsPremasterSecret")); + putKeyInfo(new TLSKeyInfo("TlsRsaPremasterSecret")); + putKeyInfo(new TLSKeyInfo("TlsMasterSecret")); + putKeyInfo(new TLSKeyInfo("TlsBinderKey")); + putKeyInfo(new TLSKeyInfo("TlsClientAppTrafficSecret")); + putKeyInfo(new TLSKeyInfo("TlsClientHandshakeTrafficSecret")); + putKeyInfo(new TLSKeyInfo("TlsEarlySecret")); + putKeyInfo(new TLSKeyInfo("TlsFinishedSecret")); + putKeyInfo(new TLSKeyInfo("TlsHandshakeSecret")); + putKeyInfo(new TLSKeyInfo("TlsKey")); + putKeyInfo(new TLSKeyInfo("TlsResumptionMasterSecret")); + putKeyInfo(new TLSKeyInfo("TlsSaltSecret")); + putKeyInfo(new TLSKeyInfo("TlsServerAppTrafficSecret")); + putKeyInfo(new TLSKeyInfo("TlsServerHandshakeTrafficSecret")); + putKeyInfo(new TLSKeyInfo("TlsUpdateNplus1")); + + putKeyInfo(new KeyInfo("Generic", CKK_GENERIC_SECRET, + CKM_GENERIC_SECRET_KEY_GEN)); HMACKeyInfo hmacSHA1 = new HMACKeyInfo("HmacSHA1", CKM_SHA_1_HMAC, 160); @@ -335,23 +400,30 @@ static P11Key convertKey(Token token, Key key, String svcAlgo, if (keyAlgo == null) { throw new InvalidKeyException("Key must specify its algorithm"); } + KeyInfo ki = getKeyInfo(keyAlgo); + if (ki == null) { + throw new InvalidKeyException("Unknown algorithm " + keyAlgo); + } + + KeyInfo si; if (svcAlgo == null) { svcAlgo = keyAlgo; + si = ki; + } else { + si = getKeyInfo(svcAlgo); + if (si == null) { + throw new InvalidKeyException("Unknown algorithm " + svcAlgo); + } } - KeyInfo ki = null; - KeyInfo si = getKeyInfo(svcAlgo); - if (si == null) { - throw new InvalidKeyException("Unknown algorithm " + svcAlgo); - } + // Check if the key can be used for the service. // Any key can be used for a MAC service. - if (svcAlgo != keyAlgo && !(si instanceof HMACKeyInfo)) { - ki = getKeyInfo(keyAlgo); - if (ki == null || !KeyInfo.checkUse(ki, si)) { - throw new InvalidKeyException("Cannot use a " + keyAlgo + + if (svcAlgo != keyAlgo && !(si instanceof HMACKeyInfo) && + !KeyInfo.checkUse(ki, si)) { + throw new InvalidKeyException("Cannot use a " + keyAlgo + " key for a " + svcAlgo + " service"); - } } + if (key instanceof P11Key p11Key) { if (p11Key.token == token) { if (extraAttrs != null) { @@ -382,7 +454,6 @@ static P11Key convertKey(Token token, Key key, String svcAlgo, return p11Key; } if (key instanceof PBEKey pbeKey) { - ki = ki == null ? getKeyInfo(keyAlgo) : ki; if (ki instanceof PBEKeyInfo pbeKi) { PBEKeySpec keySpec = getPbeKeySpec(pbeKey); try { @@ -414,7 +485,9 @@ static P11Key convertKey(Token token, Key key, String svcAlgo, return p11Key; } - static P11Key.P11PBEKey derivePBEKey(Token token, PBEKeySpec keySpec, + // utility method for deriving secret keys using PBKDF2 or the legacy + // PKCS#12 B.2 method. + static P11Key.P11PBKDFKey derivePBEKey(Token token, PBEKeySpec keySpec, PBEKeyInfo pbeKi) throws InvalidKeySpecException { token.ensureValid(); if (keySpec == null) { @@ -429,7 +502,7 @@ static P11Key.P11PBEKey derivePBEKey(Token token, PBEKeySpec keySpec, password = keySpec.getPassword(); byte[] salt = keySpec.getSalt(); int itCount = keySpec.getIterationCount(); - int keySize = keySpec.getKeyLength(); + int keySize = keySpec.getKeyLength(); // in bits assert password != null : "PBEKeySpec does not allow a null password"; if (salt == null) { @@ -495,7 +568,7 @@ static P11Key.P11PBEKey derivePBEKey(Token token, PBEKeySpec keySpec, CK_ATTRIBUTE[] attr = token.getAttributes( O_GENERATE, CKO_SECRET_KEY, pbeKi.keyType, attrs); long keyID = token.p11.C_GenerateKey(session.id(), ckMech, attr); - return (P11Key.P11PBEKey) P11Key.pbeKey(session, keyID, pbeKi.algo, + return (P11Key.P11PBKDFKey) P11Key.pbkdfKey(session, keyID, pbeKi.algo, keySize, attr, password, salt, itCount); } catch (PKCS11Exception e) { throw new InvalidKeySpecException("Could not create key", e); @@ -549,34 +622,23 @@ private static P11Key createKey(Token token, byte[] encoded, long keyType = ki.keyType; try { switch ((int) keyType) { - case (int) CKK_DES -> { - keyLength = - P11KeyGenerator.checkKeySize(CKM_DES_KEY_GEN, n, token); - fixDESParity(encoded, 0); - } - case (int) CKK_DES3 -> { - keyLength = - P11KeyGenerator.checkKeySize(CKM_DES3_KEY_GEN, n, token); - fixDESParity(encoded, 0); - fixDESParity(encoded, 8); - if (keyLength == 112) { - keyType = CKK_DES2; - } else { - keyType = CKK_DES3; - fixDESParity(encoded, 16); + case (int) CKK_DES, (int) CKK_DES3, (int) CKK_AES, (int) CKK_RC4, + (int) CKK_BLOWFISH, (int) CKK_CHACHA20 -> { + keyLength = P11KeyGenerator.checkKeySize(ki.keyGenMech, n, + token); + if (keyType == CKK_DES || keyType == CKK_DES3) { + fixDESParity(encoded, 0); + if (keyType == CKK_DES3) { + fixDESParity(encoded, 8); + if (keyLength == 112) { + keyType = CKK_DES2; + } else { + fixDESParity(encoded, 16); + } + } } } - case (int) CKK_AES -> keyLength = - P11KeyGenerator.checkKeySize(CKM_AES_KEY_GEN, n, token); - case (int) CKK_RC4 -> keyLength = - P11KeyGenerator.checkKeySize(CKM_RC4_KEY_GEN, n, token); - case (int) CKK_BLOWFISH -> keyLength = - P11KeyGenerator.checkKeySize(CKM_BLOWFISH_KEY_GEN, n, - token); - case (int) CKK_CHACHA20 -> keyLength = P11KeyGenerator.checkKeySize( - CKM_CHACHA20_KEY_GEN, n, token); - case (int) CKK_GENERIC_SECRET, (int) PCKK_TLSPREMASTER, (int) PCKK_TLSRSAPREMASTER, (int) PCKK_TLSMASTER -> - keyType = CKK_GENERIC_SECRET; + case (int) CKK_GENERIC_SECRET -> {} default -> throw new InvalidKeyException("Unknown algorithm " + algorithm); } diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java index 7664d8cc041bc..eb0569ec0d465 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -736,46 +736,6 @@ private static void register(Descriptor d) { d(SKF, "Generic", P11SecretKeyFactory, m(CKM_GENERIC_SECRET_KEY_GEN)); - /* - * PBE Secret Key Factories - * - * KeyDerivationPrf must be supported for these services - * to be available. - * - */ - d(SKF, "PBEWithHmacSHA1AndAES_128", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA_1_HMAC)); - d(SKF, "PBEWithHmacSHA224AndAES_128", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA224_HMAC)); - d(SKF, "PBEWithHmacSHA256AndAES_128", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA256_HMAC)); - d(SKF, "PBEWithHmacSHA384AndAES_128", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA384_HMAC)); - d(SKF, "PBEWithHmacSHA512AndAES_128", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA512_HMAC)); - d(SKF, "PBEWithHmacSHA1AndAES_256", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA_1_HMAC)); - d(SKF, "PBEWithHmacSHA224AndAES_256", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA224_HMAC)); - d(SKF, "PBEWithHmacSHA256AndAES_256", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA256_HMAC)); - d(SKF, "PBEWithHmacSHA384AndAES_256", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA384_HMAC)); - d(SKF, "PBEWithHmacSHA512AndAES_256", - P11SecretKeyFactory, m(CKM_PKCS5_PBKD2), m(CKM_SHA512_HMAC)); - /* - * PBA Secret Key Factories - */ - d(SKF, "HmacPBESHA1", P11SecretKeyFactory, - m(CKM_PBA_SHA1_WITH_SHA1_HMAC)); - d(SKF, "HmacPBESHA224", P11SecretKeyFactory, - m(CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN)); - d(SKF, "HmacPBESHA256", P11SecretKeyFactory, - m(CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN)); - d(SKF, "HmacPBESHA384", P11SecretKeyFactory, - m(CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN)); - d(SKF, "HmacPBESHA512", P11SecretKeyFactory, - m(CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN)); /* * PBKDF2 Secret Key Factories */ diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java index 3cd1ca125631a..924bfbcc22670 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/PKCS11Constants.java @@ -311,10 +311,6 @@ public interface PKCS11Constants { // pseudo key type ANY (for template manager) public static final long PCKK_ANY = 0x7FFFFF22L; - public static final long PCKK_TLSPREMASTER = 0x7FFFFF25L; - public static final long PCKK_TLSRSAPREMASTER = 0x7FFFFF26L; - public static final long PCKK_TLSMASTER = 0x7FFFFF27L; - /* Uncomment when actually used public static final long CK_CERTIFICATE_CATEGORY_UNSPECIFIED = 0L; public static final long CK_CERTIFICATE_CATEGORY_TOKEN_USER = 1L; diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c index 977347048a3a4..74563aa0d6c3d 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -349,7 +349,7 @@ const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* p thread_info* add_thread_info(struct ps_prochandle* ph, lwpid_t lwp_id) { thread_info* newthr; if ( (newthr = (thread_info*) calloc(1, sizeof(thread_info))) == NULL) { - print_debug("can't allocate memory for thread_info\n"); + print_error("can't allocate memory for thread_info\n"); return NULL; } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c index b1b69c81e2e0c..808ef42e06906 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ #include #include #include +#include #include "libproc_impl.h" #include "ps_core_common.h" #include "proc_service.h" @@ -67,7 +68,7 @@ static bool sort_map_array(struct ps_prochandle* ph) { // allocate map_array map_info** array; if ( (array = (map_info**) malloc(sizeof(map_info*) * num_maps)) == NULL) { - print_debug("can't allocate memory for map array\n"); + print_error("can't allocate memory for map array\n"); return false; } @@ -189,8 +190,10 @@ static bool core_handle_prstatus(struct ps_prochandle* ph, const char* buf, size prstatus_t* prstat = (prstatus_t*) buf; thread_info* newthr; print_debug("got integer regset for lwp %d\n", prstat->pr_pid); - if((newthr = add_thread_info(ph, prstat->pr_pid)) == NULL) + if((newthr = add_thread_info(ph, prstat->pr_pid)) == NULL) { + print_error("failed to add thread info\n"); return false; + } // copy regs memcpy(&newthr->regs, prstat->pr_reg, sizeof(struct user_regs_struct)); @@ -256,20 +259,20 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { // we are interested in just prstatus entries. we will ignore the rest. // Advance the seek pointer to the start of the PT_NOTE data if (lseek(ph->core->core_fd, note_phdr->p_offset, SEEK_SET) == (off_t)-1) { - print_debug("failed to lseek to PT_NOTE data\n"); + print_error("failed to lseek to PT_NOTE data\n"); return false; } // Now process the PT_NOTE structures. Each one is preceded by // an Elf{32/64}_Nhdr structure describing its type and size. if ( (buf = (char*) malloc(size)) == NULL) { - print_debug("can't allocate memory for reading core notes\n"); + print_error("can't allocate memory for reading core notes\n"); goto err; } // read notes into buffer if (read(ph->core->core_fd, buf, size) != size) { - print_debug("failed to read notes, core file must have been truncated\n"); + print_error("failed to read notes, core file must have been truncated\n"); goto err; } @@ -282,6 +285,7 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { if (notep->n_type == NT_PRSTATUS) { if (core_handle_prstatus(ph, descdata, notep->n_descsz) != true) { + print_error("failed to handle NT_PRSTATUS note\n"); return false; } } else if (notep->n_type == NT_AUXV) { @@ -314,8 +318,10 @@ static bool read_core_segments(struct ps_prochandle* ph, ELF_EHDR* core_ehdr) { ELF_PHDR* phbuf = NULL; ELF_PHDR* core_php = NULL; - if ((phbuf = read_program_header_table(ph->core->core_fd, core_ehdr)) == NULL) + if ((phbuf = read_program_header_table(ph->core->core_fd, core_ehdr)) == NULL) { + print_error("failed to read program header table\n"); return false; + } /* * Now iterate through the program headers in the core file. @@ -344,6 +350,7 @@ static bool read_core_segments(struct ps_prochandle* ph, ELF_EHDR* core_ehdr) { switch (core_php->p_type) { case PT_NOTE: if (core_handle_note(ph, core_php) != true) { + print_error("failed to read note segment\n"); goto err; } break; @@ -351,7 +358,10 @@ static bool read_core_segments(struct ps_prochandle* ph, ELF_EHDR* core_ehdr) { case PT_LOAD: { if (core_php->p_filesz != 0) { if (add_map_info(ph, ph->core->core_fd, core_php->p_offset, - core_php->p_vaddr, core_php->p_filesz, core_php->p_flags) == NULL) goto err; + core_php->p_vaddr, core_php->p_filesz, core_php->p_flags) == NULL) { + print_error("failed to add map info\n"); + goto err; + } } break; } @@ -376,6 +386,7 @@ static bool read_lib_segments(struct ps_prochandle* ph, int lib_fd, ELF_EHDR* li int page_size = sysconf(_SC_PAGE_SIZE); if ((phbuf = read_program_header_table(lib_fd, lib_ehdr)) == NULL) { + print_error("failed to read program header table\n"); return false; } @@ -391,6 +402,7 @@ static bool read_lib_segments(struct ps_prochandle* ph, int lib_fd, ELF_EHDR* li if (existing_map == NULL){ if (add_map_info(ph, lib_fd, lib_php->p_offset, target_vaddr, lib_php->p_memsz, lib_php->p_flags) == NULL) { + print_error("failed to add map info\n"); goto err; } } else if (lib_php->p_flags != existing_map->flags) { @@ -412,7 +424,7 @@ static bool read_lib_segments(struct ps_prochandle* ph, int lib_fd, ELF_EHDR* li (existing_map->fd != lib_fd) && (ROUNDUP(existing_map->memsz, page_size) != ROUNDUP(lib_php->p_memsz, page_size))) { - print_debug("address conflict @ 0x%lx (existing map size = %ld, size = %ld, flags = %d)\n", + print_error("address conflict @ 0x%lx (existing map size = %ld, size = %ld, flags = %d)\n", target_vaddr, existing_map->memsz, lib_php->p_memsz, lib_php->p_flags); goto err; } @@ -442,12 +454,12 @@ static bool read_interp_segments(struct ps_prochandle* ph) { ELF_EHDR interp_ehdr; if (read_elf_header(ph->core->interp_fd, &interp_ehdr) != true) { - print_debug("interpreter is not a valid ELF file\n"); + print_error("interpreter is not a valid ELF file\n"); return false; } if (read_lib_segments(ph, ph->core->interp_fd, &interp_ehdr, ph->core->ld_base_addr) != true) { - print_debug("can't read segments of interpreter\n"); + print_error("can't read segments of interpreter\n"); return false; } @@ -463,6 +475,7 @@ static uintptr_t read_exec_segments(struct ps_prochandle* ph, ELF_EHDR* exec_ehd uintptr_t result = 0L; if ((phbuf = read_program_header_table(ph->core->exec_fd, exec_ehdr)) == NULL) { + print_error("failed to read program header table\n"); return 0L; } @@ -473,7 +486,10 @@ static uintptr_t read_exec_segments(struct ps_prochandle* ph, ELF_EHDR* exec_ehd case PT_LOAD: { // add only non-writable segments of non-zero filesz if (!(exec_php->p_flags & PF_W) && exec_php->p_filesz != 0) { - if (add_map_info(ph, ph->core->exec_fd, exec_php->p_offset, exec_php->p_vaddr, exec_php->p_filesz, exec_php->p_flags) == NULL) goto err; + if (add_map_info(ph, ph->core->exec_fd, exec_php->p_offset, exec_php->p_vaddr, exec_php->p_filesz, exec_php->p_flags) == NULL) { + print_error("failed to add map info\n"); + goto err; + } } break; } @@ -484,18 +500,19 @@ static uintptr_t read_exec_segments(struct ps_prochandle* ph, ELF_EHDR* exec_ehd // BUF_SIZE is PATH_MAX + NAME_MAX + 1. if (exec_php->p_filesz > BUF_SIZE) { + print_error("Invalid ELF interpreter info\n"); goto err; } if (pread(ph->core->exec_fd, interp_name, exec_php->p_filesz, exec_php->p_offset) != exec_php->p_filesz) { - print_debug("Unable to read in the ELF interpreter\n"); + print_error("Unable to read in the ELF interpreter\n"); goto err; } interp_name[exec_php->p_filesz] = '\0'; print_debug("ELF interpreter %s\n", interp_name); // read interpreter segments as well if ((ph->core->interp_fd = pathmap_open(interp_name)) < 0) { - print_debug("can't open runtime loader\n"); + print_error("can't open runtime loader\n"); goto err; } break; @@ -555,7 +572,7 @@ static uintptr_t calc_prelinked_load_address(struct ps_prochandle* ph, int lib_f phbuf = read_program_header_table(lib_fd, elf_ehdr); if (phbuf == NULL) { - print_debug("can't read program header of shared object\n"); + print_error("can't read program header of shared object\n"); return INVALID_LOAD_ADDRESS; } @@ -571,7 +588,7 @@ static uintptr_t calc_prelinked_load_address(struct ps_prochandle* ph, int lib_f if (ps_pdread(ph, (psaddr_t)link_map_addr + LINK_MAP_LD_OFFSET, &lib_ld, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read address of dynamic section in shared object\n"); + print_error("can't read address of dynamic section in shared object\n"); return INVALID_LOAD_ADDRESS; } @@ -607,7 +624,7 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { dyn.d_tag = DT_NULL; while (dyn.d_tag != DT_DEBUG) { if (ps_pdread(ph, (psaddr_t) addr, &dyn, sizeof(ELF_DYN)) != PS_OK) { - print_debug("can't read debug info from _DYNAMIC\n"); + print_error("can't read debug info from _DYNAMIC\n"); return false; } addr += sizeof(ELF_DYN); @@ -618,14 +635,14 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { // at debug_base we have struct r_debug. This has first link map in r_map field if (ps_pdread(ph, (psaddr_t) debug_base + FIRST_LINK_MAP_OFFSET, &first_link_map_addr, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read first link map address\n"); + print_error("can't read first link map address\n"); return false; } // read ld_base address from struct r_debug if (ps_pdread(ph, (psaddr_t) debug_base + LD_BASE_OFFSET, &ld_base_addr, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read ld base address\n"); + print_error("can't read ld base address\n"); return false; } ph->core->ld_base_addr = ld_base_addr; @@ -634,11 +651,13 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { // now read segments from interp (i.e ld.so or ld-linux.so or ld-elf.so) if (read_interp_segments(ph) != true) { + print_error("failed to read interp segments\n"); return false; } // after adding interpreter (ld.so) mappings sort again if (sort_map_array(ph) != true) { + print_error("failed to sort segment map array\n"); return false; } @@ -654,14 +673,14 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { if (ps_pdread(ph, (psaddr_t) link_map_addr + LINK_MAP_ADDR_OFFSET, &lib_base_diff, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read shared object base address diff\n"); + print_error("can't read shared object base address diff\n"); return false; } // read address of the name if (ps_pdread(ph, (psaddr_t) link_map_addr + LINK_MAP_NAME_OFFSET, &lib_name_addr, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read address of shared object name\n"); + print_error("can't read address of shared object name\n"); return false; } @@ -687,6 +706,7 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { lib_base_diff = calc_prelinked_load_address(ph, lib_fd, &elf_ehdr, link_map_addr); if (lib_base_diff == INVALID_LOAD_ADDRESS) { close(lib_fd); + print_error("failed to calculate load address\n"); return false; } } @@ -696,15 +716,17 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { lib_name, lib_base, lib_base_diff); // while adding library mappings we need to use "base difference". if (! read_lib_segments(ph, lib_fd, &elf_ehdr, lib_base_diff)) { - print_debug("can't read shared object's segments\n"); + print_error("can't read shared object's segments\n"); close(lib_fd); return false; } add_lib_info_fd(ph, lib_name, lib_fd, lib_base); // Map info is added for the library (lib_name) so // we need to re-sort it before calling the p_pdread. - if (sort_map_array(ph) != true) + if (sort_map_array(ph) != true) { + print_error("failed to sort segment map array\n"); return false; + } } else { print_debug("can't read ELF header for shared object %s\n", lib_name); close(lib_fd); @@ -716,7 +738,7 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { // read next link_map address if (ps_pdread(ph, (psaddr_t) link_map_addr + LINK_MAP_NEXT_OFFSET, &link_map_addr, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read next link in link_map\n"); + print_error("can't read next link in link_map\n"); return false; } } @@ -732,13 +754,13 @@ Pgrab_core(const char* exec_file, const char* core_file) { struct ps_prochandle* ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle)); if (ph == NULL) { - print_debug("can't allocate ps_prochandle\n"); + print_error("can't allocate ps_prochandle\n"); return NULL; } if ((ph->core = (struct core_data*) calloc(1, sizeof(struct core_data))) == NULL) { free(ph); - print_debug("can't allocate ps_prochandle\n"); + print_error("can't allocate ps_prochandle\n"); return NULL; } @@ -750,39 +772,42 @@ Pgrab_core(const char* exec_file, const char* core_file) { // open the core file if ((ph->core->core_fd = open(core_file, O_RDONLY)) < 0) { - print_debug("can't open core file\n"); + print_error("can't open core file: %s\n", strerror(errno)); goto err; } // read core file ELF header if (read_elf_header(ph->core->core_fd, &core_ehdr) != true || core_ehdr.e_type != ET_CORE) { - print_debug("core file is not a valid ELF ET_CORE file\n"); + print_error("core file is not a valid ELF ET_CORE file\n"); goto err; } if ((ph->core->exec_fd = open(exec_file, O_RDONLY)) < 0) { - print_debug("can't open executable file\n"); + print_error("can't open executable file: %s\n", strerror(errno)); goto err; } if (read_elf_header(ph->core->exec_fd, &exec_ehdr) != true || ((exec_ehdr.e_type != ET_EXEC) && (exec_ehdr.e_type != ET_DYN))) { - print_debug("executable file is not a valid ELF file\n"); + print_error("executable file is not a valid ELF file\n"); goto err; } // process core file segments if (read_core_segments(ph, &core_ehdr) != true) { + print_error("failed to read core segments\n"); goto err; } // process exec file segments uintptr_t exec_base_addr = read_exec_segments(ph, &exec_ehdr); if (exec_base_addr == 0L) { + print_error("failed to read exec segments\n"); goto err; } print_debug("exec_base_addr = 0x%lx\n", exec_base_addr); if (add_lib_info_fd(ph, exec_file, ph->core->exec_fd, exec_base_addr) == NULL) { + print_error("failed to add lib info\n"); goto err; } @@ -790,19 +815,23 @@ Pgrab_core(const char* exec_file, const char* core_file) { // here because read_shared_lib_info needs to read from debuggee // address space if (sort_map_array(ph) != true) { + print_error("failed to sort segment map array\n"); goto err; } if (read_shared_lib_info(ph) != true) { + print_error("failed to read libraries\n"); goto err; } // sort again because we have added more mappings from shared objects if (sort_map_array(ph) != true) { + print_error("failed to sort segment map array\n"); goto err; } if (init_classsharing_workaround(ph) != true) { + print_error("failed to workaround class sharing\n"); goto err; } diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c index de81e962d8a24..fdaa30c3f5d05 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_proc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -349,7 +349,7 @@ static bool read_lib_info(struct ps_prochandle* ph) { snprintf(fname, sizeof(fname), "/proc/%d/maps", ph->pid); fp = fopen(fname, "r"); if (fp == NULL) { - print_debug("can't open /proc/%d/maps file\n", ph->pid); + print_error("can't open /proc/%d/maps file\n", ph->pid); return false; } @@ -447,13 +447,14 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { if ( (ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle))) == NULL) { snprintf(err_buf, err_buf_len, "can't allocate memory for ps_prochandle"); - print_debug("%s\n", err_buf); + print_error("%s\n", err_buf); return NULL; } if ((attach_status = ptrace_attach(pid, err_buf, err_buf_len)) != ATTACH_SUCCESS) { if (attach_status == ATTACH_THREAD_DEAD) { - print_error("The process with pid %d does not exist.\n", pid); + snprintf(err_buf, err_buf_len, "The process with pid %d does not exist.", pid); + print_error("%s\n", err_buf); } free(ph); return NULL; @@ -461,7 +462,12 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { // initialize ps_prochandle ph->pid = pid; - add_thread_info(ph, ph->pid); + if (add_thread_info(ph, ph->pid) == NULL) { + snprintf(err_buf, err_buf_len, "failed to add thread info"); + print_error("%s\n", err_buf); + free(ph); + return NULL; + } // initialize vtable ph->ops = &process_ops; @@ -469,7 +475,10 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { // read library info and symbol tables, must do this before attaching threads, // as the symbols in the pthread library will be used to figure out // the list of threads within the same process. - read_lib_info(ph); + if (read_lib_info(ph) == false) { + snprintf(err_buf, err_buf_len, "failed to read lib info"); + goto err; + } /* * Read thread info. @@ -491,7 +500,10 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { continue; } if (!process_doesnt_exist(lwp_id)) { - add_thread_info(ph, lwp_id); + if (add_thread_info(ph, lwp_id) == NULL) { + snprintf(err_buf, err_buf_len, "failed to add thread info"); + goto err; + } } } closedir(dirp); @@ -510,11 +522,15 @@ Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { delete_thread_info(ph, current_thr); } else { - Prelease(ph); - return NULL; + snprintf(err_buf, err_buf_len, "Failed to attach to the thread with lwp_id %d.", current_thr->lwp_id); + goto err; } // ATTACH_THREAD_DEAD } // !ATTACH_SUCCESS } } return ph; +err: + print_error("%s\n", err_buf); + Prelease(ph); + return NULL; } diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c index ff48c402f8d20..8bcd09d40ced5 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/libproc_impl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -323,7 +323,7 @@ const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* p sa_thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) { sa_thread_info* newthr; if ( (newthr = (sa_thread_info*) calloc(1, sizeof(sa_thread_info))) == NULL) { - print_debug("can't allocate memory for thread_info\n"); + print_error("can't allocate memory for thread_info\n"); return NULL; } diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c index e07b3a8fd9776..149997dc4bb1b 100644 --- a/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c +++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/ps_core.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -74,7 +74,7 @@ static bool sort_map_array(struct ps_prochandle* ph) { // allocate map_array map_info** array; if ( (array = (map_info**) malloc(sizeof(map_info*) * num_maps)) == NULL) { - print_debug("can't allocate memory for map array\n"); + print_error("can't allocate memory for map array\n"); return false; } @@ -280,6 +280,7 @@ static bool read_core_segments(struct ps_prochandle* ph) { lseek(fd, offset, SEEK_SET); if(read(fd, (void *)&fhead, sizeof(mach_header_64)) != sizeof(mach_header_64)) { + print_error("Failed to read program header table\n"); goto err; } print_debug("total commands: %d\n", fhead.ncmds); @@ -287,6 +288,7 @@ static bool read_core_segments(struct ps_prochandle* ph) { for (i = 0; i < fhead.ncmds; i++) { lseek(fd, offset, SEEK_SET); if (read(fd, (void *)&lcmd, sizeof(load_command)) != sizeof(load_command)) { + print_error("Failed to read command\n"); goto err; } offset += lcmd.cmdsize; // next command position @@ -294,14 +296,14 @@ static bool read_core_segments(struct ps_prochandle* ph) { if (lcmd.cmd == LC_SEGMENT_64) { lseek(fd, -sizeof(load_command), SEEK_CUR); if (read(fd, (void *)&segcmd, sizeof(segment_command_64)) != sizeof(segment_command_64)) { - print_debug("failed to read LC_SEGMENT_64 i = %d!\n", i); + print_error("failed to read LC_SEGMENT_64 i = %d!\n", i); goto err; } // The base of the library is offset by a random amount which ends up as a load command with a // filesize of 0. This must be ignored otherwise the base address of the library is wrong. if (segcmd.filesize != 0) { if (add_map_info(ph, fd, segcmd.fileoff, segcmd.vmaddr, segcmd.vmsize, segcmd.flags) == NULL) { - print_debug("Failed to add map_info at i = %d\n", i); + print_error("Failed to add map_info at i = %d\n", i); goto err; } } @@ -318,7 +320,7 @@ static bool read_core_segments(struct ps_prochandle* ph) { uint32_t size = sizeof(load_command); while (size < lcmd.cmdsize) { if (read(fd, (void *)&fc, sizeof(thread_fc)) != sizeof(thread_fc)) { - printf("Reading flavor, count failed.\n"); + print_error("Reading flavor, count failed.\n"); goto err; } size += sizeof(thread_fc); @@ -326,14 +328,14 @@ static bool read_core_segments(struct ps_prochandle* ph) { if (fc.flavor == x86_THREAD_STATE) { x86_thread_state_t thrstate; if (read(fd, (void *)&thrstate, sizeof(x86_thread_state_t)) != sizeof(x86_thread_state_t)) { - printf("Reading flavor, count failed.\n"); + print_error("Reading flavor, count failed.\n"); goto err; } size += sizeof(x86_thread_state_t); // create thread info list, update lwp_id later sa_thread_info* newthr = add_thread_info(ph, (pthread_t) -1, (lwpid_t) num_threads++); if (newthr == NULL) { - printf("create thread_info failed\n"); + print_error("create thread_info failed\n"); goto err; } @@ -370,14 +372,14 @@ static bool read_core_segments(struct ps_prochandle* ph) { } else if (fc.flavor == x86_FLOAT_STATE) { x86_float_state_t flstate; if (read(fd, (void *)&flstate, sizeof(x86_float_state_t)) != sizeof(x86_float_state_t)) { - print_debug("Reading flavor, count failed.\n"); + print_error("Reading flavor, count failed.\n"); goto err; } size += sizeof(x86_float_state_t); } else if (fc.flavor == x86_EXCEPTION_STATE) { x86_exception_state_t excpstate; if (read(fd, (void *)&excpstate, sizeof(x86_exception_state_t)) != sizeof(x86_exception_state_t)) { - printf("Reading flavor, count failed.\n"); + print_error("Reading flavor, count failed.\n"); goto err; } size += sizeof(x86_exception_state_t); @@ -387,14 +389,14 @@ static bool read_core_segments(struct ps_prochandle* ph) { if (fc.flavor == ARM_THREAD_STATE64) { arm_thread_state64_t thrstate; if (read(fd, (void *)&thrstate, sizeof(arm_thread_state64_t)) != sizeof(arm_thread_state64_t)) { - printf("Reading flavor, count failed.\n"); + print_error("Reading flavor, count failed.\n"); goto err; } size += sizeof(arm_thread_state64_t); // create thread info list, update lwp_id later sa_thread_info* newthr = add_thread_info(ph, (pthread_t) -1, (lwpid_t) num_threads++); if (newthr == NULL) { - printf("create thread_info failed\n"); + print_error("create thread_info failed\n"); goto err; } @@ -443,21 +445,21 @@ static bool read_core_segments(struct ps_prochandle* ph) { } else if (fc.flavor == ARM_NEON_STATE64) { arm_neon_state64_t flstate; if (read(fd, (void *)&flstate, sizeof(arm_neon_state64_t)) != sizeof(arm_neon_state64_t)) { - printf("Reading flavor, count failed.\n"); + print_error("Reading flavor, count failed.\n"); goto err; } size += sizeof(arm_neon_state64_t); } else if (fc.flavor == ARM_EXCEPTION_STATE64) { arm_exception_state64_t excpstate; if (read(fd, (void *)&excpstate, sizeof(arm_exception_state64_t)) != sizeof(arm_exception_state64_t)) { - printf("Reading flavor, count failed.\n"); + print_error("Reading flavor, count failed.\n"); goto err; } size += sizeof(arm_exception_state64_t); } else if (fc.flavor == ARM_DEBUG_STATE64) { arm_debug_state64_t dbgstate; if (read(fd, (void *)&dbgstate, sizeof(arm_debug_state64_t)) != sizeof(arm_debug_state64_t)) { - printf("Reading flavor, count failed.\n"); + print_error("Reading flavor, count failed.\n"); goto err; } size += sizeof(arm_debug_state64_t); @@ -631,6 +633,7 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { lseek(fd, -sizeof(uint32_t), SEEK_CUR); // This is the beginning of the mach-o file in the segment. if (read(fd, (void *)&header, sizeof(mach_header_64)) != sizeof(mach_header_64)) { + print_error("Failed to file header\n"); goto err; } fpos = ltell(fd); @@ -641,6 +644,7 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { // LC_ID_DYLIB is the file itself for a .dylib lseek(fd, fpos, SEEK_SET); if (read(fd, (void *)&lcmd, sizeof(load_command)) != sizeof(load_command)) { + print_error("Failed to read command\n"); return false; // error } fpos += lcmd.cmdsize; // next command position @@ -652,6 +656,7 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { if (lcmd.cmd == LC_ID_DYLIB) { lseek(fd, -sizeof(load_command), SEEK_CUR); if (read(fd, (void *)&dylibcmd, sizeof(dylib_command)) != sizeof(dylib_command)) { + print_error("Failed to read command\n"); return false; } /**** name stored at dylib_command.dylib.name.offset, is a C string */ @@ -710,13 +715,13 @@ struct ps_prochandle* Pgrab_core(const char* exec_file, const char* core_file) { struct ps_prochandle* ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle)); if (ph == NULL) { - print_debug("can't allocate ps_prochandle\n"); + print_error("can't allocate ps_prochandle\n"); return NULL; } if ((ph->core = (struct core_data*) calloc(1, sizeof(struct core_data))) == NULL) { free(ph); - print_debug("can't allocate ps_prochandle\n"); + print_error("can't allocate ps_prochandle\n"); return NULL; } @@ -738,12 +743,12 @@ struct ps_prochandle* Pgrab_core(const char* exec_file, const char* core_file) { // read core file header if (read_macho64_header(ph->core->core_fd, &core_header) != true || core_header.filetype != MH_CORE) { - print_debug("core file is not a valid Mach-O file\n"); + print_error("core file is not a valid Mach-O file\n"); goto err; } if ((ph->core->exec_fd = open(exec_file, O_RDONLY)) < 0) { - print_error("can't open executable file\n"); + print_error("can't open executable file: %s\n", strerror(errno)); goto err; } @@ -779,7 +784,7 @@ struct ps_prochandle* Pgrab_core(const char* exec_file, const char* core_file) { } if (init_classsharing_workaround(ph) != true) { - print_error("failed to workaround classshareing\n"); + print_error("failed to workaround class sharing\n"); goto err; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/compiler/CompileTask.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/compiler/CompileTask.java deleted file mode 100644 index e54facaeb72bb..0000000000000 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/compiler/CompileTask.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -package sun.jvm.hotspot.compiler; - -import java.io.PrintStream; -import java.util.*; -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.runtime.*; -import sun.jvm.hotspot.oops.*; -import sun.jvm.hotspot.prims.JvmtiExport; -import sun.jvm.hotspot.types.*; -import sun.jvm.hotspot.utilities.Observable; -import sun.jvm.hotspot.utilities.Observer; - -public class CompileTask extends VMObject { - static { - VM.registerVMInitializedObserver(new Observer() { - public void update(Observable o, Object data) { - initialize(VM.getVM().getTypeDataBase()); - } - }); - } - - private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { - Type type = db.lookupType("CompileTask"); - methodField = type.getAddressField("_method"); - osrBciField = new CIntField(type.getCIntegerField("_osr_bci"), 0); - compLevelField = new CIntField(type.getCIntegerField("_comp_level"), 0); - } - - private static AddressField methodField; - private static CIntField osrBciField; - private static CIntField compLevelField; - - public CompileTask(Address addr) { - super(addr); - } - - public Method method() { - Address oh = methodField.getValue(getAddress()); - return (Method)Metadata.instantiateWrapperFor(oh); - } - - public int osrBci() { - return (int)osrBciField.getValue(getAddress()); - } - - public int compLevel() { - return (int)compLevelField.getValue(getAddress()); - } -} diff --git a/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c b/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c index 3c244aab0f379..40757a346fe2c 100644 --- a/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c +++ b/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c @@ -141,6 +141,7 @@ map_info* add_map_info(struct ps_prochandle* ph, int fd, off_t offset, uintptr_t vaddr, size_t memsz, uint32_t flags) { map_info* map; if ((map = allocate_init_map(fd, offset, vaddr, memsz, flags)) == NULL) { + print_error("failed to allocate map\n"); return NULL; } @@ -158,6 +159,7 @@ static map_info* add_class_share_map_info(struct ps_prochandle* ph, off_t offset map_info* map; if ((map = allocate_init_map(ph->core->classes_jsa_fd, offset, vaddr, memsz, MAP_R_FLAG)) == NULL) { + print_debug("failed to allocate class share map\n"); return NULL; } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index ed8d273ff37db..2b1cc879e6638 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java @@ -689,7 +689,7 @@ ByteVector lanewiseTemplate(VectorOperators.Unary op) { if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0)); } - if (op == NOT) { + else if (op == NOT) { return broadcast(-1).lanewise(XOR, this); } } @@ -717,7 +717,7 @@ ByteVector lanewiseTemplate(VectorOperators.Unary op, if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0, m)); } - if (op == NOT) { + else if (op == NOT) { return lanewise(XOR, broadcast(-1), m); } } @@ -728,6 +728,7 @@ opc, getClass(), maskClass, byte.class, length(), UN_IMPL.find(op, opc, ByteVector::unaryOperations)); } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, ByteVector.class); @@ -824,6 +825,7 @@ ByteVector lanewiseTemplate(VectorOperators.Binary op, = this.compare(EQ, (byte) 0, m); return this.blend(that, mask); } + if (opKind(op, VO_SHIFT)) { // As per shift specification for Java, mask the shift count. // This allows the JIT to ignore some ISA details. @@ -850,6 +852,7 @@ opc, getClass(), maskClass, byte.class, length(), BIN_IMPL.find(op, opc, ByteVector::binaryOperations)); } + private static final ImplCache>> BIN_IMPL = new ImplCache<>(Binary.class, ByteVector.class); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java new file mode 100644 index 0000000000000..c0d8ef03ada4c --- /dev/null +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java @@ -0,0 +1,86 @@ +/* + * 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 jdk.incubator.vector; + +import jdk.internal.vm.vector.VectorSupport; + +import java.util.Locale; +import java.util.Set; + +import static jdk.incubator.vector.Util.requires; +import static jdk.internal.util.Architecture.isX64; +import static jdk.internal.vm.vector.Utils.debug; + +/** + * Enumerates CPU ISA extensions supported by the JVM on the current hardware. + */ +/*package-private*/ class CPUFeatures { + private static final Set features = getCPUFeatures(); + + private static Set getCPUFeatures() { + String featuresString = VectorSupport.getCPUFeatures(); + debug(featuresString); + + if (featuresString.equals("")) return Set.of(); + + String[] features = featuresString.toLowerCase(Locale.ROOT) + .split(",? "); // " " or ", " are used as a delimiter by JVM + assert validateFeatures(features); + return Set.of(features); + } + + private static boolean validateFeatures(String[] features) { + for (String s : features) { + assert s != null && s.matches("[a-z0-9._]+") : String.format("Invalid CPU feature name: '%s'", s); + } + return true; + } + + private static boolean hasFeature(String feature) { + return features.contains(feature.toLowerCase(Locale.ROOT)); + } + + public static class X64 { + public static boolean SUPPORTS_AVX = hasFeature("avx"); + public static boolean SUPPORTS_AVX2 = hasFeature("avx2"); + public static boolean SUPPORTS_AVX512F = hasFeature("avx512f"); + public static boolean SUPPORTS_AVX512DQ = hasFeature("avx512dq"); + + static { + requires(isX64(), "unsupported platform"); + + debug("AVX=%b; AVX2=%b; AVX512F=%b; AVX512DQ=%b", + SUPPORTS_AVX, SUPPORTS_AVX2, SUPPORTS_AVX512F, SUPPORTS_AVX512DQ); + + assert SUPPORTS_AVX512F == (VectorShape.getMaxVectorBitSize(int.class) == 512); + assert SUPPORTS_AVX2 == (VectorShape.getMaxVectorBitSize(byte.class) >= 256); + assert SUPPORTS_AVX == (VectorShape.getMaxVectorBitSize(float.class) >= 256); + } + } + + public static Set features() { + return features; + } +} diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 5fbf02f87bd93..48446c6fa0129 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -678,6 +678,9 @@ DoubleVector lanewiseTemplate(VectorOperators.Unary op) { if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0)); } + else if (opKind(op, VO_MATHLIB)) { + return unaryMathOp(op); + } } int opc = opCode(op); return VectorSupport.unaryOp( @@ -703,6 +706,9 @@ DoubleVector lanewiseTemplate(VectorOperators.Unary op, if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0, m)); } + else if (opKind(op, VO_MATHLIB)) { + return blend(unaryMathOp(op), m); + } } int opc = opCode(op); return VectorSupport.unaryOp( @@ -711,6 +717,13 @@ opc, getClass(), maskClass, double.class, length(), UN_IMPL.find(op, opc, DoubleVector::unaryOperations)); } + @ForceInline + final + DoubleVector unaryMathOp(VectorOperators.Unary op) { + return VectorMathLibrary.unaryMathOp(op, opCode(op), species(), DoubleVector::unaryOperations, + this); + } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, DoubleVector.class); @@ -781,6 +794,9 @@ DoubleVector lanewiseTemplate(VectorOperators.Binary op, = this.viewAsIntegralLanes().compare(EQ, (long) 0); return this.blend(that, mask.cast(vspecies())); } + else if (opKind(op, VO_MATHLIB)) { + return binaryMathOp(op, that); + } } int opc = opCode(op); @@ -815,6 +831,10 @@ DoubleVector lanewiseTemplate(VectorOperators.Binary op, = bits.compare(EQ, (long) 0, m.cast(bits.vspecies())); return this.blend(that, mask.cast(vspecies())); } + else if (opKind(op, VO_MATHLIB)) { + return this.blend(binaryMathOp(op, that), m); + } + } int opc = opCode(op); @@ -824,6 +844,13 @@ opc, getClass(), maskClass, double.class, length(), BIN_IMPL.find(op, opc, DoubleVector::binaryOperations)); } + @ForceInline + final + DoubleVector binaryMathOp(VectorOperators.Binary op, DoubleVector that) { + return VectorMathLibrary.binaryMathOp(op, opCode(op), species(), DoubleVector::binaryOperations, + this, that); + } + private static final ImplCache>> BIN_IMPL = new ImplCache<>(Binary.class, DoubleVector.class); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index 26fbe64742d6f..1e0829a3b1c0c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -678,6 +678,9 @@ FloatVector lanewiseTemplate(VectorOperators.Unary op) { if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0)); } + else if (opKind(op, VO_MATHLIB)) { + return unaryMathOp(op); + } } int opc = opCode(op); return VectorSupport.unaryOp( @@ -703,6 +706,9 @@ FloatVector lanewiseTemplate(VectorOperators.Unary op, if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0, m)); } + else if (opKind(op, VO_MATHLIB)) { + return blend(unaryMathOp(op), m); + } } int opc = opCode(op); return VectorSupport.unaryOp( @@ -711,6 +717,13 @@ opc, getClass(), maskClass, float.class, length(), UN_IMPL.find(op, opc, FloatVector::unaryOperations)); } + @ForceInline + final + FloatVector unaryMathOp(VectorOperators.Unary op) { + return VectorMathLibrary.unaryMathOp(op, opCode(op), species(), FloatVector::unaryOperations, + this); + } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, FloatVector.class); @@ -781,6 +794,9 @@ FloatVector lanewiseTemplate(VectorOperators.Binary op, = this.viewAsIntegralLanes().compare(EQ, (int) 0); return this.blend(that, mask.cast(vspecies())); } + else if (opKind(op, VO_MATHLIB)) { + return binaryMathOp(op, that); + } } int opc = opCode(op); @@ -815,6 +831,10 @@ FloatVector lanewiseTemplate(VectorOperators.Binary op, = bits.compare(EQ, (int) 0, m.cast(bits.vspecies())); return this.blend(that, mask.cast(vspecies())); } + else if (opKind(op, VO_MATHLIB)) { + return this.blend(binaryMathOp(op, that), m); + } + } int opc = opCode(op); @@ -824,6 +844,13 @@ opc, getClass(), maskClass, float.class, length(), BIN_IMPL.find(op, opc, FloatVector::binaryOperations)); } + @ForceInline + final + FloatVector binaryMathOp(VectorOperators.Binary op, FloatVector that) { + return VectorMathLibrary.binaryMathOp(op, opCode(op), species(), FloatVector::binaryOperations, + this, that); + } + private static final ImplCache>> BIN_IMPL = new ImplCache<>(Binary.class, FloatVector.class); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java index 076a66ed6a543..b691527bec680 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java @@ -689,7 +689,7 @@ IntVector lanewiseTemplate(VectorOperators.Unary op) { if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0)); } - if (op == NOT) { + else if (op == NOT) { return broadcast(-1).lanewise(XOR, this); } } @@ -717,7 +717,7 @@ IntVector lanewiseTemplate(VectorOperators.Unary op, if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0, m)); } - if (op == NOT) { + else if (op == NOT) { return lanewise(XOR, broadcast(-1), m); } } @@ -728,6 +728,7 @@ opc, getClass(), maskClass, int.class, length(), UN_IMPL.find(op, opc, IntVector::unaryOperations)); } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, IntVector.class); @@ -824,6 +825,7 @@ IntVector lanewiseTemplate(VectorOperators.Binary op, = this.compare(EQ, (int) 0, m); return this.blend(that, mask); } + if (opKind(op, VO_SHIFT)) { // As per shift specification for Java, mask the shift count. // This allows the JIT to ignore some ISA details. @@ -850,6 +852,7 @@ opc, getClass(), maskClass, int.class, length(), BIN_IMPL.find(op, opc, IntVector::binaryOperations)); } + private static final ImplCache>> BIN_IMPL = new ImplCache<>(Binary.class, IntVector.class); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java index 21903aa6794e8..9e4dcd23d677a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java @@ -647,7 +647,7 @@ LongVector lanewiseTemplate(VectorOperators.Unary op) { if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0)); } - if (op == NOT) { + else if (op == NOT) { return broadcast(-1).lanewise(XOR, this); } } @@ -675,7 +675,7 @@ LongVector lanewiseTemplate(VectorOperators.Unary op, if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0, m)); } - if (op == NOT) { + else if (op == NOT) { return lanewise(XOR, broadcast(-1), m); } } @@ -686,6 +686,7 @@ opc, getClass(), maskClass, long.class, length(), UN_IMPL.find(op, opc, LongVector::unaryOperations)); } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, LongVector.class); @@ -782,6 +783,7 @@ LongVector lanewiseTemplate(VectorOperators.Binary op, = this.compare(EQ, (long) 0, m); return this.blend(that, mask); } + if (opKind(op, VO_SHIFT)) { // As per shift specification for Java, mask the shift count. // This allows the JIT to ignore some ISA details. @@ -808,6 +810,7 @@ opc, getClass(), maskClass, long.class, length(), BIN_IMPL.find(op, opc, LongVector::binaryOperations)); } + private static final ImplCache>> BIN_IMPL = new ImplCache<>(Binary.class, LongVector.class); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java index 0bb97da824459..46df27309ae3b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java @@ -689,7 +689,7 @@ ShortVector lanewiseTemplate(VectorOperators.Unary op) { if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0)); } - if (op == NOT) { + else if (op == NOT) { return broadcast(-1).lanewise(XOR, this); } } @@ -717,7 +717,7 @@ ShortVector lanewiseTemplate(VectorOperators.Unary op, if (op == ZOMO) { return blend(broadcast(-1), compare(NE, 0, m)); } - if (op == NOT) { + else if (op == NOT) { return lanewise(XOR, broadcast(-1), m); } } @@ -728,6 +728,7 @@ opc, getClass(), maskClass, short.class, length(), UN_IMPL.find(op, opc, ShortVector::unaryOperations)); } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, ShortVector.class); @@ -824,6 +825,7 @@ ShortVector lanewiseTemplate(VectorOperators.Binary op, = this.compare(EQ, (short) 0, m); return this.blend(that, mask); } + if (opKind(op, VO_SHIFT)) { // As per shift specification for Java, mask the shift count. // This allows the JIT to ignore some ISA details. @@ -850,6 +852,7 @@ opc, getClass(), maskClass, short.class, length(), BIN_IMPL.find(op, opc, ShortVector::binaryOperations)); } + private static final ImplCache>> BIN_IMPL = new ImplCache<>(Binary.class, ShortVector.class); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java new file mode 100644 index 0000000000000..8562d4b5d7a2a --- /dev/null +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java @@ -0,0 +1,33 @@ +/* + * 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 jdk.incubator.vector; + +/*package-private*/ class Util { + public static void requires(boolean cond, String message) { + if (!cond) { + throw new InternalError(message); + } + } +} diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java new file mode 100644 index 0000000000000..4729235e2d9b8 --- /dev/null +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java @@ -0,0 +1,325 @@ +/* + * 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 jdk.incubator.vector; + +import jdk.internal.util.StaticProperty; +import jdk.internal.vm.annotation.DontInline; +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; +import jdk.internal.vm.vector.VectorSupport; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SymbolLookup; +import java.util.function.IntFunction; + +import static jdk.incubator.vector.Util.requires; +import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.util.Architecture.*; +import static jdk.internal.vm.vector.Utils.debug; + +/** + * A wrapper for native vector math libraries bundled with the JDK (SVML and SLEEF). + * Binds vector operations to native implementations provided by the libraries. + */ +/*package-private*/ class VectorMathLibrary { + private static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup(); + + interface Library { + String symbolName(Operator op, VectorSpecies vspecies); + boolean isSupported(Operator op, VectorSpecies vspecies); + + String SVML = "svml"; + String SLEEF = "sleef"; + String JAVA = "java"; + + static Library getInstance() { + String libraryName = System.getProperty("jdk.incubator.vector.VectorMathLibrary", getDefaultName()); + try { + return switch (libraryName) { + case SVML -> new SVML(); + case SLEEF -> new SLEEF(); + case JAVA -> new Java(); + default -> throw new IllegalArgumentException("Unsupported vector math library: " + libraryName); + }; + } catch (Throwable e) { + debug("Error during initialization of %s library: %s", libraryName, e); + return new Java(); // fallback + } + } + + static String getDefaultName() { + return switch (StaticProperty.osArch()) { + case "amd64", "x86_64" -> SVML; + case "aarch64", "riscv64" -> SLEEF; + default -> JAVA; + }; + } + } + + private static final Library LIBRARY = Library.getInstance(); + + static { + debug("%s library is used (cpu features: %s)", LIBRARY.getClass().getSimpleName(), CPUFeatures.features()); + } + + private static class Java implements Library { + @Override + public String symbolName(Operator op, VectorSpecies vspecies) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSupported(Operator op, VectorSpecies vspecies) { + return false; // always use default implementation + } + } + + /** + * Naming convention in SVML vector math library. + * All the methods are named as __jsvml__ha_ where: + * ha stands for high accuracy + * is optional to indicate float/double + * Set to f for vector float operation + * Omitted for vector double operation + * is the number of elements in the vector + * 1, 2, 4, 8, 16 + * e.g. 128 bit float vector has 4 float elements + * indicates the avx/sse level: + * z0 is AVX512, l9 is AVX2, e9 is AVX1 and ex is for SSE2 + * e.g. __jsvml_expf16_ha_z0 is the method for computing 16 element vector float exp using AVX 512 insns + * __jsvml_exp8_ha_z0 is the method for computing 8 element vector double exp using AVX 512 insns + */ + private static class SVML implements Library { + static { + loadNativeLibrary(); + } + + private static void loadNativeLibrary() { + requires(isX64(), "SVML library is x64-specific"); + VectorSupport.loadNativeLibrary("jsvml"); + } + + private static String suffix(VectorSpecies vspecies) { + assert vspecies.vectorBitSize() <= VectorShape.getMaxVectorBitSize(vspecies.elementType()); + + if (vspecies.vectorBitSize() == 512) { + assert CPUFeatures.X64.SUPPORTS_AVX512F; + return "z0"; + } else if (CPUFeatures.X64.SUPPORTS_AVX2) { + return "l9"; + } else if (CPUFeatures.X64.SUPPORTS_AVX) { + return "e9"; + } else { + return "ex"; + } + } + + @Override + public String symbolName(Operator op, VectorSpecies vspecies) { + String suffix = suffix(vspecies); + String elemType = (vspecies.elementType() == float.class ? "f" : ""); + boolean isFloat64Vector = (vspecies.elementType() == float.class) && (vspecies.length() == 2); // Float64Vector or FloatMaxVector + int vlen = (isFloat64Vector ? 4 : vspecies.length()); // reuse 128-bit variant for 64-bit float vectors + return String.format("__jsvml_%s%s%d_ha_%s", op.operatorName(), elemType, vlen, suffix); + } + + @Override + public boolean isSupported(Operator op, VectorSpecies vspecies) { + Class etype = vspecies.elementType(); + if (etype != float.class && etype != double.class) { + return false; // only FP types are supported + } + int maxLaneCount = VectorSupport.getMaxLaneCount(vspecies.elementType()); + if (vspecies.length() > maxLaneCount) { + return false; // lacking vector support (either hardware or disabled on JVM side) + } + if (vspecies == DoubleVector.SPECIES_64) { + return false; // 64-bit double vectors are not supported + } + if (vspecies.vectorBitSize() == 512) { + if (op == LOG || op == LOG10 || op == POW) { + return CPUFeatures.X64.SUPPORTS_AVX512DQ; // requires AVX512DQ CPU support + } + } else if (op == POW) { + return false; // not supported + } + return true; + } + } + + /** + * Naming convention in SLEEF-based vector math library . + * All the methods are named as _ where: + * is the operation name, e.g. sin + * is optional to indicate float/double + * "f/d" for vector float/double operation + * is the number of elements in the vector + * "2/4" for neon, and "x" for sve/rvv + * is the precision level + * "u10/u05" represents 1.0/0.5 ULP error bounds + * We use "u10" for all operations by default + * But for those functions do not have u10 support, we use "u05" instead + * indicates neon/sve/rvv + * "sve/advsimd/rvv" for sve/neon/rvv implementations + * e.g. sinfx_u10sve is the method for computing vector float sin using SVE instructions + * cosd2_u10advsimd is the method for computing 2 elements vector double cos using NEON instructions + */ + private static class SLEEF implements Library { + static { + VectorSupport.loadNativeLibrary("sleef"); + } + + private static String suffix(VectorShape vshape, boolean isShapeAgnostic) { + if (isAARCH64()) { + if (isShapeAgnostic) { + return "sve"; + } else { + return "advsimd"; + } + } else if (isRISCV64()) { + assert isShapeAgnostic : "not supported"; + return "rvv"; + } else { + throw new InternalError("unsupported platform"); + } + } + + private static String precisionLevel(Operator op) { + return (op == HYPOT ? "u05" : "u10"); + } + + @Override + public String symbolName(Operator op, VectorSpecies vspecies) { + boolean isFloat64Vector = (vspecies.elementType() == float.class) && (vspecies.length() == 2); // Float64Vector or FloatMaxVector + int vlen = (isFloat64Vector ? 4 : vspecies.length()); // reuse 128-bit variant for 64-bit float vectors + boolean isShapeAgnostic = isRISCV64() || (isAARCH64() && vspecies.vectorBitSize() > 128); + return String.format("%s%s%s_%s%s", op.operatorName(), + (vspecies.elementType() == float.class ? "f" : "d"), + (isShapeAgnostic ? "x" : Integer.toString(vlen)), + precisionLevel(op), + suffix(vspecies.vectorShape(), isShapeAgnostic)); + } + + @Override + public boolean isSupported(Operator op, VectorSpecies vspecies) { + Class etype = vspecies.elementType(); + if (etype != float.class && etype != double.class) { + return false; // only FP element types are supported + } + int maxLaneCount = VectorSupport.getMaxLaneCount(vspecies.elementType()); + if (vspecies.length() > maxLaneCount) { + return false; // lacking vector support (either hardware or disabled on JVM side) + } + if (vspecies == DoubleVector.SPECIES_64) { + return false; // 64-bit double vectors are not supported + } + if (op == TANH) { + return false; // skip due to performance considerations + } + return true; + } + } + + private static final int SIZE = VectorSupport.VECTOR_OP_MATHLIB_LAST - VectorSupport.VECTOR_OP_MATHLIB_FIRST + 1; + + private record Entry (String name, MemorySegment entry, T impl) {} + + private static final @Stable Entry[][][] LIBRARY_ENTRIES = new Entry[SIZE][LaneType.SK_LIMIT][VectorShape.SK_LIMIT]; // OP x SHAPE x TYPE + + @ForceInline + private static Entry lookup(Operator op, int opc, VectorSpecies vspecies, IntFunction implSupplier) { + int idx = opc - VectorSupport.VECTOR_OP_MATHLIB_FIRST; + int elem_idx = ((AbstractSpecies)vspecies).laneType.switchKey; + int shape_idx = vspecies.vectorShape().switchKey; + @SuppressWarnings({"unchecked"}) + Entry entry = (Entry)LIBRARY_ENTRIES[idx][elem_idx][shape_idx]; + if (entry == null) { + entry = constructEntry(op, opc, vspecies, implSupplier); + LIBRARY_ENTRIES[idx][elem_idx][shape_idx] = entry; + } + return entry; + } + + @DontInline + private static + + Entry constructEntry(Operator op, int opc, VectorSpecies vspecies, IntFunction implSupplier) { + if (LIBRARY.isSupported(op, vspecies)) { + String symbol = LIBRARY.symbolName(op, vspecies); + try { + MemorySegment addr = LOOKUP.findOrThrow(symbol); + debug("%s %s => 0x%016x\n", op, symbol, addr.address()); + T impl = implSupplier.apply(opc); // TODO: should call the very same native implementation eventually (once FFM API supports vectors) + return new Entry<>(symbol, addr, impl); + } catch (RuntimeException e) { + throw new InternalError("not supported: " + op + " " + vspecies + " " + symbol, e); + } + } else { + return new Entry<>(null, MemorySegment.NULL, implSupplier.apply(opc)); + } + } + + @ForceInline + /*package-private*/ static + > + V unaryMathOp(Unary op, int opc, VectorSpecies vspecies, + IntFunction> implSupplier, + V v) { + var entry = lookup(op, opc, vspecies, implSupplier); + + long entryAddress = entry.entry.address(); + if (entryAddress != 0) { + @SuppressWarnings({"unchecked"}) + Class vt = (Class)vspecies.vectorType(); + return VectorSupport.libraryUnaryOp( + entry.entry.address(), vt, vspecies.elementType(), vspecies.length(), entry.name, + v, + entry.impl); + } else { + return entry.impl.apply(v, null); + } + } + + @ForceInline + /*package-private*/ static + > + V binaryMathOp(Binary op, int opc, VectorSpecies vspecies, + IntFunction> implSupplier, + V v1, V v2) { + var entry = lookup(op, opc, vspecies, implSupplier); + + long entryAddress = entry.entry.address(); + if (entryAddress != 0) { + @SuppressWarnings({"unchecked"}) + Class vt = (Class)vspecies.vectorType(); + return VectorSupport.libraryBinaryOp( + entry.entry.address(), vt, vspecies.elementType(), vspecies.length(), entry.name, + v1, v2, + entry.impl); + } else { + return entry.impl.apply(v1, v2, null); + } + } +} diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java index c5fe6dd444128..0e231bd5174f0 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java @@ -33,6 +33,8 @@ import jdk.internal.vm.vector.VectorSupport; +import static jdk.internal.vm.vector.Utils.isNonCapturingLambda; + /** * This class consists solely of static constants * that describe lane-wise vector operations, plus nested interfaces @@ -426,6 +428,7 @@ static boolean opKind(Operator op, int bit) { VO_SPECIAL = 0x080, // random special handling VO_NOFP = 0x100, VO_ONLYFP = 0x200, + VO_MATHLIB = 0x400, VO_OPCODE_VALID = 0x800, VO_OPCODE_SHIFT = 12, VO_OPCODE_LIMIT = 0x400, @@ -476,67 +479,67 @@ static boolean opKind(Operator op, int bit) { /** Produce {@code sin(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary SIN = unary("SIN", "sin", VectorSupport.VECTOR_OP_SIN, VO_ONLYFP); + public static final /*float*/ Unary SIN = unary("SIN", "sin", VectorSupport.VECTOR_OP_SIN, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code cos(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary COS = unary("COS", "cos", VectorSupport.VECTOR_OP_COS, VO_ONLYFP); + public static final /*float*/ Unary COS = unary("COS", "cos", VectorSupport.VECTOR_OP_COS, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code tan(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary TAN = unary("TAN", "tan", VectorSupport.VECTOR_OP_TAN, VO_ONLYFP); + public static final /*float*/ Unary TAN = unary("TAN", "tan", VectorSupport.VECTOR_OP_TAN, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code asin(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary ASIN = unary("ASIN", "asin", VectorSupport.VECTOR_OP_ASIN, VO_ONLYFP); + public static final /*float*/ Unary ASIN = unary("ASIN", "asin", VectorSupport.VECTOR_OP_ASIN, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code acos(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary ACOS = unary("ACOS", "acos", VectorSupport.VECTOR_OP_ACOS, VO_ONLYFP); + public static final /*float*/ Unary ACOS = unary("ACOS", "acos", VectorSupport.VECTOR_OP_ACOS, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code atan(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary ATAN = unary("ATAN", "atan", VectorSupport.VECTOR_OP_ATAN, VO_ONLYFP); + public static final /*float*/ Unary ATAN = unary("ATAN", "atan", VectorSupport.VECTOR_OP_ATAN, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code exp(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary EXP = unary("EXP", "exp", VectorSupport.VECTOR_OP_EXP, VO_ONLYFP); + public static final /*float*/ Unary EXP = unary("EXP", "exp", VectorSupport.VECTOR_OP_EXP, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code log(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary LOG = unary("LOG", "log", VectorSupport.VECTOR_OP_LOG, VO_ONLYFP); + public static final /*float*/ Unary LOG = unary("LOG", "log", VectorSupport.VECTOR_OP_LOG, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code log10(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary LOG10 = unary("LOG10", "log10", VectorSupport.VECTOR_OP_LOG10, VO_ONLYFP); + public static final /*float*/ Unary LOG10 = unary("LOG10", "log10", VectorSupport.VECTOR_OP_LOG10, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code sqrt(a)}. Floating only. See section "Operations on floating point vectors" above */ public static final /*float*/ Unary SQRT = unary("SQRT", "sqrt", VectorSupport.VECTOR_OP_SQRT, VO_ONLYFP); /** Produce {@code cbrt(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary CBRT = unary("CBRT", "cbrt", VectorSupport.VECTOR_OP_CBRT, VO_ONLYFP); + public static final /*float*/ Unary CBRT = unary("CBRT", "cbrt", VectorSupport.VECTOR_OP_CBRT, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code sinh(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary SINH = unary("SINH", "sinh", VectorSupport.VECTOR_OP_SINH, VO_ONLYFP); + public static final /*float*/ Unary SINH = unary("SINH", "sinh", VectorSupport.VECTOR_OP_SINH, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code cosh(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary COSH = unary("COSH", "cosh", VectorSupport.VECTOR_OP_COSH, VO_ONLYFP); + public static final /*float*/ Unary COSH = unary("COSH", "cosh", VectorSupport.VECTOR_OP_COSH, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code tanh(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary TANH = unary("TANH", "tanh", VectorSupport.VECTOR_OP_TANH, VO_ONLYFP); + public static final /*float*/ Unary TANH = unary("TANH", "tanh", VectorSupport.VECTOR_OP_TANH, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code expm1(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary EXPM1 = unary("EXPM1", "expm1", VectorSupport.VECTOR_OP_EXPM1, VO_ONLYFP); + public static final /*float*/ Unary EXPM1 = unary("EXPM1", "expm1", VectorSupport.VECTOR_OP_EXPM1, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code log1p(a)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Unary LOG1P = unary("LOG1P", "log1p", VectorSupport.VECTOR_OP_LOG1P, VO_ONLYFP); + public static final /*float*/ Unary LOG1P = unary("LOG1P", "log1p", VectorSupport.VECTOR_OP_LOG1P, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); // Binary operators @@ -615,15 +618,15 @@ static boolean opKind(Operator op, int bit) { /** Produce {@code atan2(a,b)}. See Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Binary ATAN2 = binary("ATAN2", "atan2", VectorSupport.VECTOR_OP_ATAN2, VO_ONLYFP); + public static final /*float*/ Binary ATAN2 = binary("ATAN2", "atan2", VectorSupport.VECTOR_OP_ATAN2, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code pow(a,b)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Binary POW = binary("POW", "pow", VectorSupport.VECTOR_OP_POW, VO_ONLYFP); + public static final /*float*/ Binary POW = binary("POW", "pow", VectorSupport.VECTOR_OP_POW, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); /** Produce {@code hypot(a,b)}. Floating only. * Not guaranteed to be semi-monotonic. See section "Operations on floating point vectors" above */ - public static final /*float*/ Binary HYPOT = binary("HYPOT", "hypot", VectorSupport.VECTOR_OP_HYPOT, VO_ONLYFP); + public static final /*float*/ Binary HYPOT = binary("HYPOT", "hypot", VectorSupport.VECTOR_OP_HYPOT, VO_ONLYFP | VO_SPECIAL | VO_MATHLIB); // Ternary operators @@ -1373,7 +1376,7 @@ public T find(OP op, int opc, IntFunction supplier) { if (fn != null) return fn; fn = supplier.apply(opc); if (fn == null) throw badOp(op); - assert(VectorSupport.isNonCapturingLambda(fn)) : fn; + assert(isNonCapturingLambda(fn)) : fn; // The JIT can see into this cache: cache[opc] = fn; return fn; diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index 8084cc307e867..6d9db65a1ba83 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -712,10 +712,15 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { return blend(broadcast(-1), compare(NE, 0)); } #if[BITWISE] - if (op == NOT) { + else if (op == NOT) { return broadcast(-1).lanewise(XOR, this); } #end[BITWISE] +#if[FP] + else if (opKind(op, VO_MATHLIB)) { + return unaryMathOp(op); + } +#end[FP] } int opc = opCode(op); return VectorSupport.unaryOp( @@ -742,10 +747,15 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { return blend(broadcast(-1), compare(NE, 0, m)); } #if[BITWISE] - if (op == NOT) { + else if (op == NOT) { return lanewise(XOR, broadcast(-1), m); } #end[BITWISE] +#if[FP] + else if (opKind(op, VO_MATHLIB)) { + return blend(unaryMathOp(op), m); + } +#end[FP] } int opc = opCode(op); return VectorSupport.unaryOp( @@ -754,6 +764,15 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { UN_IMPL.find(op, opc, $abstractvectortype$::unaryOperations)); } +#if[FP] + @ForceInline + final + $abstractvectortype$ unaryMathOp(VectorOperators.Unary op) { + return VectorMathLibrary.unaryMathOp(op, opCode(op), species(), $abstractvectortype$::unaryOperations, + this); + } +#end[FP] + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, $Type$Vector.class); @@ -856,6 +875,11 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { = this{#if[FP]?.viewAsIntegralLanes()}.compare(EQ, ($bitstype$) 0); return this.blend(that, mask{#if[FP]?.cast(vspecies())}); } +#if[FP] + else if (opKind(op, VO_MATHLIB)) { + return binaryMathOp(op, that); + } +#end[FP] #if[BITWISE] #if[!FP] if (opKind(op, VO_SHIFT)) { @@ -915,6 +939,12 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { return this.blend(that, mask); #end[FP] } +#if[FP] + else if (opKind(op, VO_MATHLIB)) { + return this.blend(binaryMathOp(op, that), m); + } +#end[FP] + #if[BITWISE] #if[!FP] if (opKind(op, VO_SHIFT)) { @@ -945,6 +975,15 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { BIN_IMPL.find(op, opc, $abstractvectortype$::binaryOperations)); } +#if[FP] + @ForceInline + final + $abstractvectortype$ binaryMathOp(VectorOperators.Binary op, $abstractvectortype$ that) { + return VectorMathLibrary.binaryMathOp(op, opCode(op), species(), $abstractvectortype$::binaryOperations, + this, that); + } +#end[FP] + private static final ImplCache>> BIN_IMPL = new ImplCache<>(Binary.class, $Type$Vector.class); diff --git a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/JdkConsoleProviderImpl.java b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/JdkConsoleProviderImpl.java index 11de96b7fc339..03a9da22771f9 100644 --- a/src/jdk.internal.le/share/classes/jdk/internal/org/jline/JdkConsoleProviderImpl.java +++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/JdkConsoleProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -93,16 +93,6 @@ public JdkConsole print(Object obj) { return this; } - @Override - public String readln(String prompt) { - return getDelegate(true).readln(prompt); - } - - @Override - public String readln() { - return getDelegate(true).readln(); - } - @Override public JdkConsole format(Locale locale, String format, Object... args) { JdkConsole delegate = getDelegate(false); @@ -219,21 +209,6 @@ public JdkConsole print(Object obj) { return this; } - @Override - public String readln(String prompt) { - try { - initJLineIfNeeded(); - return jline.readLine(prompt == null ? "null" : prompt.replace("%", "%%")); - } catch (EndOfFileException eofe) { - return null; - } - } - - @Override - public String readln() { - return readLine(); - } - @Override public JdkConsole format(Locale locale, String format, Object ... args) { writer().format(locale, format, args).flush(); diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java index 2a8959e5d7fe9..dbd83b314c9eb 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/aarch64/AArch64.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -183,6 +183,8 @@ public enum CPUFeature implements CPUFeatureName { SVEBITPERM, SVE2, A53MAC, + FPHP, + ASIMDHP, } private final EnumSet features; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java index b768aee081fe9..c6f583de3618e 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java @@ -311,6 +311,12 @@ protected void generateOtherFiles(ClassTree classTree) copyFontResources(); } + var syntaxHighlight = options.syntaxHighlight(); + if (syntaxHighlight) { + copyResource(DocPaths.HIGHLIGHT_CSS, DocPaths.RESOURCE_FILES.resolve(DocPaths.HIGHLIGHT_CSS), true); + copyResource(DocPaths.HIGHLIGHT_JS, DocPaths.SCRIPT_FILES.resolve(DocPaths.HIGHLIGHT_JS), true); + } + // If a stylesheet file is not specified, copy the default stylesheet // and replace newline with platform-specific newline. if (options.stylesheetFile().isEmpty()) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java index 448a68fd5a1f1..34ef40ed650a4 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java @@ -511,6 +511,7 @@ public void printHtmlDocument(List metakeywords, .setStylesheets(configuration.getMainStylesheet(), additionalStylesheets, localStylesheets) .setAdditionalScripts(configuration.getAdditionalScripts()) .setIndex(options.createIndex(), mainBodyScript) + .setSyntaxHighlight(options.syntaxHighlight()) .addContent(extraHeadContent); HtmlDocument htmlDocument = new HtmlDocument( diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlOptions.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlOptions.java index 10483fb4a4c19..faf1f8c897d51 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlOptions.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -198,6 +198,11 @@ public class HtmlOptions extends BaseOptions { */ private String stylesheetFile = ""; + /** + * Argument for command line option {@code --syntax-highlight}. + */ + private boolean syntaxHighlight = false; + /** * Argument for command-line option {@code -tagletpath}. * The path to Taglets @@ -423,6 +428,14 @@ public boolean process(String opt, List args) { } }, + new Option(resources, "--syntax-highlight") { + @Override + public boolean process(String opt, List args) { + syntaxHighlight = true; + return true; + } + }, + new Option(resources, "-tag", 1) { @Override public boolean process(String opt, List args) { @@ -806,6 +819,13 @@ String stylesheetFile() { return stylesheetFile; } + /** + * Argument for command line option {@code --syntax-highlight}. + * True if command line option "--syntax-highlight" is used and syntax + * highlighting should be enabled. Default value is false. + */ + public boolean syntaxHighlight() { return syntaxHighlight; } + /** * Argument for command-line option {@code -tagletpath}. * The path to Taglets diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java index 7170c6bcc8a2e..0c54fc1e29c1a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Head.java @@ -70,6 +70,7 @@ public class Head extends Content { private final List extraContent; private boolean addDefaultScript = true; private DocPath canonicalLink; + private boolean syntaxHighlight = false; /** * Creates a {@code Head} object, for a given file and HTML version. @@ -238,6 +239,16 @@ public void setCanonicalLink(DocPath link) { this.canonicalLink = link; } + /** + * Enables or disables support for syntax highlighting. + * @param value {@code true} to enable syntax highligting + * @return this object + */ + public Head setSyntaxHighlight(boolean value) { + this.syntaxHighlight = value; + return this; + } + /** * Adds additional content to be included in the HEAD element. * @@ -339,6 +350,11 @@ private void addStylesheets(HtmlTree head) { addStylesheet(head, DocPaths.RESOURCE_FILES.resolve(path)); } + if (syntaxHighlight) { + addStylesheet(head, DocPaths.RESOURCE_FILES.resolve(DocPaths.HIGHLIGHT_CSS)); + addScriptElement(head, DocPaths.HIGHLIGHT_JS); + } + for (DocPath path : localStylesheets) { // Local stylesheets are contained in doc-files, so omit resource-files prefix addStylesheet(head, path); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css new file mode 100644 index 0000000000000..145c1c7f5d281 --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ +/* Syntax highlight style sheet */ +.hljs-title.function_, +.hljs-template-variable { + color: #00738F; +} +.hljs-code, +.hljs-comment, +.hljs-quote { + color: #6e6e71; + font-style: italic; +} +.hljs-meta { + color: #836F00; +} +.hljs-symbol, +.hljs-template-tag, +.hljs-keyword, +.hljs-literal, +.hljs-name, +.hljs-built_in, +.hljs-char.escape_ { + color: #0C40C2; +} +.hljs-variable, +.hljs-property, +.hljs-attr, +.hljs-section { + color: #841191; +} +.hljs-attribute { + color: #164ad9; +} +.hljs-regexp, +.hljs-number { + color: #104BEB; +} +.hljs-link { + color: #47688a; +} +.hljs-string { + color: #008313; +} +.hljs-doctag { + text-decoration: underline; +} +.hljs-emphasis { + font-style: italic; +} +.hljs-strong { + font-weight: bold; +} +.hljs-subst, +.hljs-title, +.hljs-params, +.hljs-bullet, +.hljs-formula, +.hljs-tag, +.hljs-type { + /* ignored */ +} + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.js b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.js new file mode 100644 index 0000000000000..efa14c8c306d5 --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.js @@ -0,0 +1,3278 @@ +/*! + Highlight.js v11.11.1 (git: 08cb242e7d) + (c) 2006-2025 Josh Goebel and other contributors + License: BSD-3-Clause + */ +var hljs = (function () { + 'use strict'; + + /* eslint-disable no-multi-assign */ + + function deepFreeze(obj) { + if (obj instanceof Map) { + obj.clear = + obj.delete = + obj.set = + function () { + throw new Error('map is read-only'); + }; + } else if (obj instanceof Set) { + obj.add = + obj.clear = + obj.delete = + function () { + throw new Error('set is read-only'); + }; + } + + // Freeze self + Object.freeze(obj); + + Object.getOwnPropertyNames(obj).forEach((name) => { + const prop = obj[name]; + const type = typeof prop; + + // Freeze prop if it is an object or function and also not already frozen + if ((type === 'object' || type === 'function') && !Object.isFrozen(prop)) { + deepFreeze(prop); + } + }); + + return obj; + } + + /** @typedef {import('highlight.js').CallbackResponse} CallbackResponse */ + /** @typedef {import('highlight.js').CompiledMode} CompiledMode */ + /** @implements CallbackResponse */ + + class Response { + /** + * @param {CompiledMode} mode + */ + constructor(mode) { + // eslint-disable-next-line no-undefined + if (mode.data === undefined) mode.data = {}; + + this.data = mode.data; + this.isMatchIgnored = false; + } + + ignoreMatch() { + this.isMatchIgnored = true; + } + } + + /** + * @param {string} value + * @returns {string} + */ + function escapeHTML(value) { + return value + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + /** + * performs a shallow merge of multiple objects into one + * + * @template T + * @param {T} original + * @param {Record[]} objects + * @returns {T} a single new object + */ + function inherit$1(original, ...objects) { + /** @type Record */ + const result = Object.create(null); + + for (const key in original) { + result[key] = original[key]; + } + objects.forEach(function(obj) { + for (const key in obj) { + result[key] = obj[key]; + } + }); + return /** @type {T} */ (result); + } + + /** + * @typedef {object} Renderer + * @property {(text: string) => void} addText + * @property {(node: Node) => void} openNode + * @property {(node: Node) => void} closeNode + * @property {() => string} value + */ + + /** @typedef {{scope?: string, language?: string, sublanguage?: boolean}} Node */ + /** @typedef {{walk: (r: Renderer) => void}} Tree */ + /** */ + + const SPAN_CLOSE = ''; + + /** + * Determines if a node needs to be wrapped in + * + * @param {Node} node */ + const emitsWrappingTags = (node) => { + // rarely we can have a sublanguage where language is undefined + // TODO: track down why + return !!node.scope; + }; + + /** + * + * @param {string} name + * @param {{prefix:string}} options + */ + const scopeToCSSClass = (name, { prefix }) => { + // sub-language + if (name.startsWith("language:")) { + return name.replace("language:", "language-"); + } + // tiered scope: comment.line + if (name.includes(".")) { + const pieces = name.split("."); + return [ + `${prefix}${pieces.shift()}`, + ...(pieces.map((x, i) => `${x}${"_".repeat(i + 1)}`)) + ].join(" "); + } + // simple scope + return `${prefix}${name}`; + }; + + /** @type {Renderer} */ + class HTMLRenderer { + /** + * Creates a new HTMLRenderer + * + * @param {Tree} parseTree - the parse tree (must support `walk` API) + * @param {{classPrefix: string}} options + */ + constructor(parseTree, options) { + this.buffer = ""; + this.classPrefix = options.classPrefix; + parseTree.walk(this); + } + + /** + * Adds texts to the output stream + * + * @param {string} text */ + addText(text) { + this.buffer += escapeHTML(text); + } + + /** + * Adds a node open to the output stream (if needed) + * + * @param {Node} node */ + openNode(node) { + if (!emitsWrappingTags(node)) return; + + const className = scopeToCSSClass(node.scope, + { prefix: this.classPrefix }); + this.span(className); + } + + /** + * Adds a node close to the output stream (if needed) + * + * @param {Node} node */ + closeNode(node) { + if (!emitsWrappingTags(node)) return; + + this.buffer += SPAN_CLOSE; + } + + /** + * returns the accumulated buffer + */ + value() { + return this.buffer; + } + + // helpers + + /** + * Builds a span element + * + * @param {string} className */ + span(className) { + this.buffer += ``; + } + } + + /** @typedef {{scope?: string, language?: string, children: Node[]} | string} Node */ + /** @typedef {{scope?: string, language?: string, children: Node[]} } DataNode */ + /** @typedef {import('highlight.js').Emitter} Emitter */ + /** */ + + /** @returns {DataNode} */ + const newNode = (opts = {}) => { + /** @type DataNode */ + const result = { children: [] }; + Object.assign(result, opts); + return result; + }; + + class TokenTree { + constructor() { + /** @type DataNode */ + this.rootNode = newNode(); + this.stack = [this.rootNode]; + } + + get top() { + return this.stack[this.stack.length - 1]; + } + + get root() { return this.rootNode; } + + /** @param {Node} node */ + add(node) { + this.top.children.push(node); + } + + /** @param {string} scope */ + openNode(scope) { + /** @type Node */ + const node = newNode({ scope }); + this.add(node); + this.stack.push(node); + } + + closeNode() { + if (this.stack.length > 1) { + return this.stack.pop(); + } + // eslint-disable-next-line no-undefined + return undefined; + } + + closeAllNodes() { + while (this.closeNode()); + } + + toJSON() { + return JSON.stringify(this.rootNode, null, 4); + } + + /** + * @typedef { import("./html_renderer").Renderer } Renderer + * @param {Renderer} builder + */ + walk(builder) { + // this does not + return this.constructor._walk(builder, this.rootNode); + // this works + // return TokenTree._walk(builder, this.rootNode); + } + + /** + * @param {Renderer} builder + * @param {Node} node + */ + static _walk(builder, node) { + if (typeof node === "string") { + builder.addText(node); + } else if (node.children) { + builder.openNode(node); + node.children.forEach((child) => this._walk(builder, child)); + builder.closeNode(node); + } + return builder; + } + + /** + * @param {Node} node + */ + static _collapse(node) { + if (typeof node === "string") return; + if (!node.children) return; + + if (node.children.every(el => typeof el === "string")) { + // node.text = node.children.join(""); + // delete node.children; + node.children = [node.children.join("")]; + } else { + node.children.forEach((child) => { + TokenTree._collapse(child); + }); + } + } + } + + /** + Currently this is all private API, but this is the minimal API necessary + that an Emitter must implement to fully support the parser. + + Minimal interface: + + - addText(text) + - __addSublanguage(emitter, subLanguageName) + - startScope(scope) + - endScope() + - finalize() + - toHTML() + + */ + + /** + * @implements {Emitter} + */ + class TokenTreeEmitter extends TokenTree { + /** + * @param {*} options + */ + constructor(options) { + super(); + this.options = options; + } + + /** + * @param {string} text + */ + addText(text) { + if (text === "") { return; } + + this.add(text); + } + + /** @param {string} scope */ + startScope(scope) { + this.openNode(scope); + } + + endScope() { + this.closeNode(); + } + + /** + * @param {Emitter & {root: DataNode}} emitter + * @param {string} name + */ + __addSublanguage(emitter, name) { + /** @type DataNode */ + const node = emitter.root; + if (name) node.scope = `language:${name}`; + + this.add(node); + } + + toHTML() { + const renderer = new HTMLRenderer(this, this.options); + return renderer.value(); + } + + finalize() { + this.closeAllNodes(); + return true; + } + } + + /** + * @param {string} value + * @returns {RegExp} + * */ + + /** + * @param {RegExp | string } re + * @returns {string} + */ + function source(re) { + if (!re) return null; + if (typeof re === "string") return re; + + return re.source; + } + + /** + * @param {RegExp | string } re + * @returns {string} + */ + function lookahead(re) { + return concat('(?=', re, ')'); + } + + /** + * @param {RegExp | string } re + * @returns {string} + */ + function anyNumberOfTimes(re) { + return concat('(?:', re, ')*'); + } + + /** + * @param {RegExp | string } re + * @returns {string} + */ + function optional(re) { + return concat('(?:', re, ')?'); + } + + /** + * @param {...(RegExp | string) } args + * @returns {string} + */ + function concat(...args) { + const joined = args.map((x) => source(x)).join(""); + return joined; + } + + /** + * @param { Array } args + * @returns {object} + */ + function stripOptionsFromArgs(args) { + const opts = args[args.length - 1]; + + if (typeof opts === 'object' && opts.constructor === Object) { + args.splice(args.length - 1, 1); + return opts; + } else { + return {}; + } + } + + /** @typedef { {capture?: boolean} } RegexEitherOptions */ + + /** + * Any of the passed expresssions may match + * + * Creates a huge this | this | that | that match + * @param {(RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]} args + * @returns {string} + */ + function either(...args) { + /** @type { object & {capture?: boolean} } */ + const opts = stripOptionsFromArgs(args); + const joined = '(' + + (opts.capture ? "" : "?:") + + args.map((x) => source(x)).join("|") + ")"; + return joined; + } + + /** + * @param {RegExp | string} re + * @returns {number} + */ + function countMatchGroups(re) { + return (new RegExp(re.toString() + '|')).exec('').length - 1; + } + + /** + * Does lexeme start with a regular expression match at the beginning + * @param {RegExp} re + * @param {string} lexeme + */ + function startsWith(re, lexeme) { + const match = re && re.exec(lexeme); + return match && match.index === 0; + } + + // BACKREF_RE matches an open parenthesis or backreference. To avoid + // an incorrect parse, it additionally matches the following: + // - [...] elements, where the meaning of parentheses and escapes change + // - other escape sequences, so we do not misparse escape sequences as + // interesting elements + // - non-matching or lookahead parentheses, which do not capture. These + // follow the '(' with a '?'. + const BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./; + + // **INTERNAL** Not intended for outside usage + // join logically computes regexps.join(separator), but fixes the + // backreferences so they continue to match. + // it also places each individual regular expression into it's own + // match group, keeping track of the sequencing of those match groups + // is currently an exercise for the caller. :-) + /** + * @param {(string | RegExp)[]} regexps + * @param {{joinWith: string}} opts + * @returns {string} + */ + function _rewriteBackreferences(regexps, { joinWith }) { + let numCaptures = 0; + + return regexps.map((regex) => { + numCaptures += 1; + const offset = numCaptures; + let re = source(regex); + let out = ''; + + while (re.length > 0) { + const match = BACKREF_RE.exec(re); + if (!match) { + out += re; + break; + } + out += re.substring(0, match.index); + re = re.substring(match.index + match[0].length); + if (match[0][0] === '\\' && match[1]) { + // Adjust the backreference. + out += '\\' + String(Number(match[1]) + offset); + } else { + out += match[0]; + if (match[0] === '(') { + numCaptures++; + } + } + } + return out; + }).map(re => `(${re})`).join(joinWith); + } + + /** @typedef {import('highlight.js').Mode} Mode */ + /** @typedef {import('highlight.js').ModeCallback} ModeCallback */ + + // Common regexps + const MATCH_NOTHING_RE = /\b\B/; + const IDENT_RE = '[a-zA-Z]\\w*'; + const UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*'; + const NUMBER_RE = '\\b\\d+(\\.\\d+)?'; + const C_NUMBER_RE = '(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float + const BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b... + const RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~'; + + /** + * @param { Partial & {binary?: string | RegExp} } opts + */ + const SHEBANG = (opts = {}) => { + const beginShebang = /^#![ ]*\//; + if (opts.binary) { + opts.begin = concat( + beginShebang, + /.*\b/, + opts.binary, + /\b.*/); + } + return inherit$1({ + scope: 'meta', + begin: beginShebang, + end: /$/, + relevance: 0, + /** @type {ModeCallback} */ + "on:begin": (m, resp) => { + if (m.index !== 0) resp.ignoreMatch(); + } + }, opts); + }; + + // Common modes + const BACKSLASH_ESCAPE = { + begin: '\\\\[\\s\\S]', relevance: 0 + }; + const APOS_STRING_MODE = { + scope: 'string', + begin: '\'', + end: '\'', + illegal: '\\n', + contains: [BACKSLASH_ESCAPE] + }; + const QUOTE_STRING_MODE = { + scope: 'string', + begin: '"', + end: '"', + illegal: '\\n', + contains: [BACKSLASH_ESCAPE] + }; + const PHRASAL_WORDS_MODE = { + begin: /\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ + }; + /** + * Creates a comment mode + * + * @param {string | RegExp} begin + * @param {string | RegExp} end + * @param {Mode | {}} [modeOptions] + * @returns {Partial} + */ + const COMMENT = function(begin, end, modeOptions = {}) { + const mode = inherit$1( + { + scope: 'comment', + begin, + end, + contains: [] + }, + modeOptions + ); + mode.contains.push({ + scope: 'doctag', + // hack to avoid the space from being included. the space is necessary to + // match here to prevent the plain text rule below from gobbling up doctags + begin: '[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)', + end: /(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/, + excludeBegin: true, + relevance: 0 + }); + const ENGLISH_WORD = either( + // list of common 1 and 2 letter words in English + "I", + "a", + "is", + "so", + "us", + "to", + "at", + "if", + "in", + "it", + "on", + // note: this is not an exhaustive list of contractions, just popular ones + /[A-Za-z]+['](d|ve|re|ll|t|s|n)/, // contractions - can't we'd they're let's, etc + /[A-Za-z]+[-][a-z]+/, // `no-way`, etc. + /[A-Za-z][a-z]{2,}/ // allow capitalized words at beginning of sentences + ); + // looking like plain text, more likely to be a comment + mode.contains.push( + { + // TODO: how to include ", (, ) without breaking grammars that use these for + // comment delimiters? + // begin: /[ ]+([()"]?([A-Za-z'-]{3,}|is|a|I|so|us|[tT][oO]|at|if|in|it|on)[.]?[()":]?([.][ ]|[ ]|\))){3}/ + // --- + + // this tries to find sequences of 3 english words in a row (without any + // "programming" type syntax) this gives us a strong signal that we've + // TRULY found a comment - vs perhaps scanning with the wrong language. + // It's possible to find something that LOOKS like the start of the + // comment - but then if there is no readable text - good chance it is a + // false match and not a comment. + // + // for a visual example please see: + // https://github.com/highlightjs/highlight.js/issues/2827 + + begin: concat( + /[ ]+/, // necessary to prevent us gobbling up doctags like /* @author Bob Mcgill */ + '(', + ENGLISH_WORD, + /[.]?[:]?([.][ ]|[ ])/, + '){3}') // look for 3 words in a row + } + ); + return mode; + }; + const C_LINE_COMMENT_MODE = COMMENT('//', '$'); + const C_BLOCK_COMMENT_MODE = COMMENT('/\\*', '\\*/'); + const HASH_COMMENT_MODE = COMMENT('#', '$'); + const NUMBER_MODE = { + scope: 'number', + begin: NUMBER_RE, + relevance: 0 + }; + const C_NUMBER_MODE = { + scope: 'number', + begin: C_NUMBER_RE, + relevance: 0 + }; + const BINARY_NUMBER_MODE = { + scope: 'number', + begin: BINARY_NUMBER_RE, + relevance: 0 + }; + const REGEXP_MODE = { + scope: "regexp", + begin: /\/(?=[^/\n]*\/)/, + end: /\/[gimuy]*/, + contains: [ + BACKSLASH_ESCAPE, + { + begin: /\[/, + end: /\]/, + relevance: 0, + contains: [BACKSLASH_ESCAPE] + } + ] + }; + const TITLE_MODE = { + scope: 'title', + begin: IDENT_RE, + relevance: 0 + }; + const UNDERSCORE_TITLE_MODE = { + scope: 'title', + begin: UNDERSCORE_IDENT_RE, + relevance: 0 + }; + const METHOD_GUARD = { + // excludes method names from keyword processing + begin: '\\.\\s*' + UNDERSCORE_IDENT_RE, + relevance: 0 + }; + + /** + * Adds end same as begin mechanics to a mode + * + * Your mode must include at least a single () match group as that first match + * group is what is used for comparison + * @param {Partial} mode + */ + const END_SAME_AS_BEGIN = function(mode) { + return Object.assign(mode, + { + /** @type {ModeCallback} */ + 'on:begin': (m, resp) => { resp.data._beginMatch = m[1]; }, + /** @type {ModeCallback} */ + 'on:end': (m, resp) => { if (resp.data._beginMatch !== m[1]) resp.ignoreMatch(); } + }); + }; + + var MODES = /*#__PURE__*/Object.freeze({ + __proto__: null, + APOS_STRING_MODE: APOS_STRING_MODE, + BACKSLASH_ESCAPE: BACKSLASH_ESCAPE, + BINARY_NUMBER_MODE: BINARY_NUMBER_MODE, + BINARY_NUMBER_RE: BINARY_NUMBER_RE, + COMMENT: COMMENT, + C_BLOCK_COMMENT_MODE: C_BLOCK_COMMENT_MODE, + C_LINE_COMMENT_MODE: C_LINE_COMMENT_MODE, + C_NUMBER_MODE: C_NUMBER_MODE, + C_NUMBER_RE: C_NUMBER_RE, + END_SAME_AS_BEGIN: END_SAME_AS_BEGIN, + HASH_COMMENT_MODE: HASH_COMMENT_MODE, + IDENT_RE: IDENT_RE, + MATCH_NOTHING_RE: MATCH_NOTHING_RE, + METHOD_GUARD: METHOD_GUARD, + NUMBER_MODE: NUMBER_MODE, + NUMBER_RE: NUMBER_RE, + PHRASAL_WORDS_MODE: PHRASAL_WORDS_MODE, + QUOTE_STRING_MODE: QUOTE_STRING_MODE, + REGEXP_MODE: REGEXP_MODE, + RE_STARTERS_RE: RE_STARTERS_RE, + SHEBANG: SHEBANG, + TITLE_MODE: TITLE_MODE, + UNDERSCORE_IDENT_RE: UNDERSCORE_IDENT_RE, + UNDERSCORE_TITLE_MODE: UNDERSCORE_TITLE_MODE + }); + + /** + @typedef {import('highlight.js').CallbackResponse} CallbackResponse + @typedef {import('highlight.js').CompilerExt} CompilerExt + */ + + // Grammar extensions / plugins + // See: https://github.com/highlightjs/highlight.js/issues/2833 + + // Grammar extensions allow "syntactic sugar" to be added to the grammar modes + // without requiring any underlying changes to the compiler internals. + + // `compileMatch` being the perfect small example of now allowing a grammar + // author to write `match` when they desire to match a single expression rather + // than being forced to use `begin`. The extension then just moves `match` into + // `begin` when it runs. Ie, no features have been added, but we've just made + // the experience of writing (and reading grammars) a little bit nicer. + + // ------ + + // TODO: We need negative look-behind support to do this properly + /** + * Skip a match if it has a preceding dot + * + * This is used for `beginKeywords` to prevent matching expressions such as + * `bob.keyword.do()`. The mode compiler automatically wires this up as a + * special _internal_ 'on:begin' callback for modes with `beginKeywords` + * @param {RegExpMatchArray} match + * @param {CallbackResponse} response + */ + function skipIfHasPrecedingDot(match, response) { + const before = match.input[match.index - 1]; + if (before === ".") { + response.ignoreMatch(); + } + } + + /** + * + * @type {CompilerExt} + */ + function scopeClassName(mode, _parent) { + // eslint-disable-next-line no-undefined + if (mode.className !== undefined) { + mode.scope = mode.className; + delete mode.className; + } + } + + /** + * `beginKeywords` syntactic sugar + * @type {CompilerExt} + */ + function beginKeywords(mode, parent) { + if (!parent) return; + if (!mode.beginKeywords) return; + + // for languages with keywords that include non-word characters checking for + // a word boundary is not sufficient, so instead we check for a word boundary + // or whitespace - this does no harm in any case since our keyword engine + // doesn't allow spaces in keywords anyways and we still check for the boundary + // first + mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?!\\.)(?=\\b|\\s)'; + mode.__beforeBegin = skipIfHasPrecedingDot; + mode.keywords = mode.keywords || mode.beginKeywords; + delete mode.beginKeywords; + + // prevents double relevance, the keywords themselves provide + // relevance, the mode doesn't need to double it + // eslint-disable-next-line no-undefined + if (mode.relevance === undefined) mode.relevance = 0; + } + + /** + * Allow `illegal` to contain an array of illegal values + * @type {CompilerExt} + */ + function compileIllegal(mode, _parent) { + if (!Array.isArray(mode.illegal)) return; + + mode.illegal = either(...mode.illegal); + } + + /** + * `match` to match a single expression for readability + * @type {CompilerExt} + */ + function compileMatch(mode, _parent) { + if (!mode.match) return; + if (mode.begin || mode.end) throw new Error("begin & end are not supported with match"); + + mode.begin = mode.match; + delete mode.match; + } + + /** + * provides the default 1 relevance to all modes + * @type {CompilerExt} + */ + function compileRelevance(mode, _parent) { + // eslint-disable-next-line no-undefined + if (mode.relevance === undefined) mode.relevance = 1; + } + + // allow beforeMatch to act as a "qualifier" for the match + // the full match begin must be [beforeMatch][begin] + const beforeMatchExt = (mode, parent) => { + if (!mode.beforeMatch) return; + // starts conflicts with endsParent which we need to make sure the child + // rule is not matched multiple times + if (mode.starts) throw new Error("beforeMatch cannot be used with starts"); + + const originalMode = Object.assign({}, mode); + Object.keys(mode).forEach((key) => { delete mode[key]; }); + + mode.keywords = originalMode.keywords; + mode.begin = concat(originalMode.beforeMatch, lookahead(originalMode.begin)); + mode.starts = { + relevance: 0, + contains: [ + Object.assign(originalMode, { endsParent: true }) + ] + }; + mode.relevance = 0; + + delete originalMode.beforeMatch; + }; + + // keywords that should have no default relevance value + const COMMON_KEYWORDS = [ + 'of', + 'and', + 'for', + 'in', + 'not', + 'or', + 'if', + 'then', + 'parent', // common variable name + 'list', // common variable name + 'value' // common variable name + ]; + + const DEFAULT_KEYWORD_SCOPE = "keyword"; + + /** + * Given raw keywords from a language definition, compile them. + * + * @param {string | Record | Array} rawKeywords + * @param {boolean} caseInsensitive + */ + function compileKeywords(rawKeywords, caseInsensitive, scopeName = DEFAULT_KEYWORD_SCOPE) { + /** @type {import("highlight.js/private").KeywordDict} */ + const compiledKeywords = Object.create(null); + + // input can be a string of keywords, an array of keywords, or a object with + // named keys representing scopeName (which can then point to a string or array) + if (typeof rawKeywords === 'string') { + compileList(scopeName, rawKeywords.split(" ")); + } else if (Array.isArray(rawKeywords)) { + compileList(scopeName, rawKeywords); + } else { + Object.keys(rawKeywords).forEach(function(scopeName) { + // collapse all our objects back into the parent object + Object.assign( + compiledKeywords, + compileKeywords(rawKeywords[scopeName], caseInsensitive, scopeName) + ); + }); + } + return compiledKeywords; + + // --- + + /** + * Compiles an individual list of keywords + * + * Ex: "for if when while|5" + * + * @param {string} scopeName + * @param {Array} keywordList + */ + function compileList(scopeName, keywordList) { + if (caseInsensitive) { + keywordList = keywordList.map(x => x.toLowerCase()); + } + keywordList.forEach(function(keyword) { + const pair = keyword.split('|'); + compiledKeywords[pair[0]] = [scopeName, scoreForKeyword(pair[0], pair[1])]; + }); + } + } + + /** + * Returns the proper score for a given keyword + * + * Also takes into account comment keywords, which will be scored 0 UNLESS + * another score has been manually assigned. + * @param {string} keyword + * @param {string} [providedScore] + */ + function scoreForKeyword(keyword, providedScore) { + // manual scores always win over common keywords + // so you can force a score of 1 if you really insist + if (providedScore) { + return Number(providedScore); + } + + return commonKeyword(keyword) ? 0 : 1; + } + + /** + * Determines if a given keyword is common or not + * + * @param {string} keyword */ + function commonKeyword(keyword) { + return COMMON_KEYWORDS.includes(keyword.toLowerCase()); + } + + /* + + For the reasoning behind this please see: + https://github.com/highlightjs/highlight.js/issues/2880#issuecomment-747275419 + + */ + + /** + * @type {Record} + */ + const seenDeprecations = {}; + + /** + * @param {string} message + */ + const error = (message) => { + console.error(message); + }; + + /** + * @param {string} message + * @param {any} args + */ + const warn = (message, ...args) => { + console.log(`WARN: ${message}`, ...args); + }; + + /** + * @param {string} version + * @param {string} message + */ + const deprecated = (version, message) => { + if (seenDeprecations[`${version}/${message}`]) return; + + console.log(`Deprecated as of ${version}. ${message}`); + seenDeprecations[`${version}/${message}`] = true; + }; + + /* eslint-disable no-throw-literal */ + + /** + @typedef {import('highlight.js').CompiledMode} CompiledMode + */ + + const MultiClassError = new Error(); + + /** + * Renumbers labeled scope names to account for additional inner match + * groups that otherwise would break everything. + * + * Lets say we 3 match scopes: + * + * { 1 => ..., 2 => ..., 3 => ... } + * + * So what we need is a clean match like this: + * + * (a)(b)(c) => [ "a", "b", "c" ] + * + * But this falls apart with inner match groups: + * + * (a)(((b)))(c) => ["a", "b", "b", "b", "c" ] + * + * Our scopes are now "out of alignment" and we're repeating `b` 3 times. + * What needs to happen is the numbers are remapped: + * + * { 1 => ..., 2 => ..., 5 => ... } + * + * We also need to know that the ONLY groups that should be output + * are 1, 2, and 5. This function handles this behavior. + * + * @param {CompiledMode} mode + * @param {Array} regexes + * @param {{key: "beginScope"|"endScope"}} opts + */ + function remapScopeNames(mode, regexes, { key }) { + let offset = 0; + const scopeNames = mode[key]; + /** @type Record */ + const emit = {}; + /** @type Record */ + const positions = {}; + + for (let i = 1; i <= regexes.length; i++) { + positions[i + offset] = scopeNames[i]; + emit[i + offset] = true; + offset += countMatchGroups(regexes[i - 1]); + } + // we use _emit to keep track of which match groups are "top-level" to avoid double + // output from inside match groups + mode[key] = positions; + mode[key]._emit = emit; + mode[key]._multi = true; + } + + /** + * @param {CompiledMode} mode + */ + function beginMultiClass(mode) { + if (!Array.isArray(mode.begin)) return; + + if (mode.skip || mode.excludeBegin || mode.returnBegin) { + error("skip, excludeBegin, returnBegin not compatible with beginScope: {}"); + throw MultiClassError; + } + + if (typeof mode.beginScope !== "object" || mode.beginScope === null) { + error("beginScope must be object"); + throw MultiClassError; + } + + remapScopeNames(mode, mode.begin, { key: "beginScope" }); + mode.begin = _rewriteBackreferences(mode.begin, { joinWith: "" }); + } + + /** + * @param {CompiledMode} mode + */ + function endMultiClass(mode) { + if (!Array.isArray(mode.end)) return; + + if (mode.skip || mode.excludeEnd || mode.returnEnd) { + error("skip, excludeEnd, returnEnd not compatible with endScope: {}"); + throw MultiClassError; + } + + if (typeof mode.endScope !== "object" || mode.endScope === null) { + error("endScope must be object"); + throw MultiClassError; + } + + remapScopeNames(mode, mode.end, { key: "endScope" }); + mode.end = _rewriteBackreferences(mode.end, { joinWith: "" }); + } + + /** + * this exists only to allow `scope: {}` to be used beside `match:` + * Otherwise `beginScope` would necessary and that would look weird + + { + match: [ /def/, /\w+/ ] + scope: { 1: "keyword" , 2: "title" } + } + + * @param {CompiledMode} mode + */ + function scopeSugar(mode) { + if (mode.scope && typeof mode.scope === "object" && mode.scope !== null) { + mode.beginScope = mode.scope; + delete mode.scope; + } + } + + /** + * @param {CompiledMode} mode + */ + function MultiClass(mode) { + scopeSugar(mode); + + if (typeof mode.beginScope === "string") { + mode.beginScope = { _wrap: mode.beginScope }; + } + if (typeof mode.endScope === "string") { + mode.endScope = { _wrap: mode.endScope }; + } + + beginMultiClass(mode); + endMultiClass(mode); + } + + /** + @typedef {import('highlight.js').Mode} Mode + @typedef {import('highlight.js').CompiledMode} CompiledMode + @typedef {import('highlight.js').Language} Language + @typedef {import('highlight.js').HLJSPlugin} HLJSPlugin + @typedef {import('highlight.js').CompiledLanguage} CompiledLanguage + */ + + // compilation + + /** + * Compiles a language definition result + * + * Given the raw result of a language definition (Language), compiles this so + * that it is ready for highlighting code. + * @param {Language} language + * @returns {CompiledLanguage} + */ + function compileLanguage(language) { + /** + * Builds a regex with the case sensitivity of the current language + * + * @param {RegExp | string} value + * @param {boolean} [global] + */ + function langRe(value, global) { + return new RegExp( + source(value), + 'm' + + (language.case_insensitive ? 'i' : '') + + (language.unicodeRegex ? 'u' : '') + + (global ? 'g' : '') + ); + } + + /** + Stores multiple regular expressions and allows you to quickly search for + them all in a string simultaneously - returning the first match. It does + this by creating a huge (a|b|c) regex - each individual item wrapped with () + and joined by `|` - using match groups to track position. When a match is + found checking which position in the array has content allows us to figure + out which of the original regexes / match groups triggered the match. + + The match object itself (the result of `Regex.exec`) is returned but also + enhanced by merging in any meta-data that was registered with the regex. + This is how we keep track of which mode matched, and what type of rule + (`illegal`, `begin`, end, etc). + */ + class MultiRegex { + constructor() { + this.matchIndexes = {}; + // @ts-ignore + this.regexes = []; + this.matchAt = 1; + this.position = 0; + } + + // @ts-ignore + addRule(re, opts) { + opts.position = this.position++; + // @ts-ignore + this.matchIndexes[this.matchAt] = opts; + this.regexes.push([opts, re]); + this.matchAt += countMatchGroups(re) + 1; + } + + compile() { + if (this.regexes.length === 0) { + // avoids the need to check length every time exec is called + // @ts-ignore + this.exec = () => null; + } + const terminators = this.regexes.map(el => el[1]); + this.matcherRe = langRe(_rewriteBackreferences(terminators, { joinWith: '|' }), true); + this.lastIndex = 0; + } + + /** @param {string} s */ + exec(s) { + this.matcherRe.lastIndex = this.lastIndex; + const match = this.matcherRe.exec(s); + if (!match) { return null; } + + // eslint-disable-next-line no-undefined + const i = match.findIndex((el, i) => i > 0 && el !== undefined); + // @ts-ignore + const matchData = this.matchIndexes[i]; + // trim off any earlier non-relevant match groups (ie, the other regex + // match groups that make up the multi-matcher) + match.splice(0, i); + + return Object.assign(match, matchData); + } + } + + /* + Created to solve the key deficiently with MultiRegex - there is no way to + test for multiple matches at a single location. Why would we need to do + that? In the future a more dynamic engine will allow certain matches to be + ignored. An example: if we matched say the 3rd regex in a large group but + decided to ignore it - we'd need to started testing again at the 4th + regex... but MultiRegex itself gives us no real way to do that. + + So what this class creates MultiRegexs on the fly for whatever search + position they are needed. + + NOTE: These additional MultiRegex objects are created dynamically. For most + grammars most of the time we will never actually need anything more than the + first MultiRegex - so this shouldn't have too much overhead. + + Say this is our search group, and we match regex3, but wish to ignore it. + + regex1 | regex2 | regex3 | regex4 | regex5 ' ie, startAt = 0 + + What we need is a new MultiRegex that only includes the remaining + possibilities: + + regex4 | regex5 ' ie, startAt = 3 + + This class wraps all that complexity up in a simple API... `startAt` decides + where in the array of expressions to start doing the matching. It + auto-increments, so if a match is found at position 2, then startAt will be + set to 3. If the end is reached startAt will return to 0. + + MOST of the time the parser will be setting startAt manually to 0. + */ + class ResumableMultiRegex { + constructor() { + // @ts-ignore + this.rules = []; + // @ts-ignore + this.multiRegexes = []; + this.count = 0; + + this.lastIndex = 0; + this.regexIndex = 0; + } + + // @ts-ignore + getMatcher(index) { + if (this.multiRegexes[index]) return this.multiRegexes[index]; + + const matcher = new MultiRegex(); + this.rules.slice(index).forEach(([re, opts]) => matcher.addRule(re, opts)); + matcher.compile(); + this.multiRegexes[index] = matcher; + return matcher; + } + + resumingScanAtSamePosition() { + return this.regexIndex !== 0; + } + + considerAll() { + this.regexIndex = 0; + } + + // @ts-ignore + addRule(re, opts) { + this.rules.push([re, opts]); + if (opts.type === "begin") this.count++; + } + + /** @param {string} s */ + exec(s) { + const m = this.getMatcher(this.regexIndex); + m.lastIndex = this.lastIndex; + let result = m.exec(s); + + // The following is because we have no easy way to say "resume scanning at the + // existing position but also skip the current rule ONLY". What happens is + // all prior rules are also skipped which can result in matching the wrong + // thing. Example of matching "booger": + + // our matcher is [string, "booger", number] + // + // ....booger.... + + // if "booger" is ignored then we'd really need a regex to scan from the + // SAME position for only: [string, number] but ignoring "booger" (if it + // was the first match), a simple resume would scan ahead who knows how + // far looking only for "number", ignoring potential string matches (or + // future "booger" matches that might be valid.) + + // So what we do: We execute two matchers, one resuming at the same + // position, but the second full matcher starting at the position after: + + // /--- resume first regex match here (for [number]) + // |/---- full match here for [string, "booger", number] + // vv + // ....booger.... + + // Which ever results in a match first is then used. So this 3-4 step + // process essentially allows us to say "match at this position, excluding + // a prior rule that was ignored". + // + // 1. Match "booger" first, ignore. Also proves that [string] does non match. + // 2. Resume matching for [number] + // 3. Match at index + 1 for [string, "booger", number] + // 4. If #2 and #3 result in matches, which came first? + if (this.resumingScanAtSamePosition()) { + if (result && result.index === this.lastIndex) ; else { // use the second matcher result + const m2 = this.getMatcher(0); + m2.lastIndex = this.lastIndex + 1; + result = m2.exec(s); + } + } + + if (result) { + this.regexIndex += result.position + 1; + if (this.regexIndex === this.count) { + // wrap-around to considering all matches again + this.considerAll(); + } + } + + return result; + } + } + + /** + * Given a mode, builds a huge ResumableMultiRegex that can be used to walk + * the content and find matches. + * + * @param {CompiledMode} mode + * @returns {ResumableMultiRegex} + */ + function buildModeRegex(mode) { + const mm = new ResumableMultiRegex(); + + mode.contains.forEach(term => mm.addRule(term.begin, { rule: term, type: "begin" })); + + if (mode.terminatorEnd) { + mm.addRule(mode.terminatorEnd, { type: "end" }); + } + if (mode.illegal) { + mm.addRule(mode.illegal, { type: "illegal" }); + } + + return mm; + } + + /** skip vs abort vs ignore + * + * @skip - The mode is still entered and exited normally (and contains rules apply), + * but all content is held and added to the parent buffer rather than being + * output when the mode ends. Mostly used with `sublanguage` to build up + * a single large buffer than can be parsed by sublanguage. + * + * - The mode begin ands ends normally. + * - Content matched is added to the parent mode buffer. + * - The parser cursor is moved forward normally. + * + * @abort - A hack placeholder until we have ignore. Aborts the mode (as if it + * never matched) but DOES NOT continue to match subsequent `contains` + * modes. Abort is bad/suboptimal because it can result in modes + * farther down not getting applied because an earlier rule eats the + * content but then aborts. + * + * - The mode does not begin. + * - Content matched by `begin` is added to the mode buffer. + * - The parser cursor is moved forward accordingly. + * + * @ignore - Ignores the mode (as if it never matched) and continues to match any + * subsequent `contains` modes. Ignore isn't technically possible with + * the current parser implementation. + * + * - The mode does not begin. + * - Content matched by `begin` is ignored. + * - The parser cursor is not moved forward. + */ + + /** + * Compiles an individual mode + * + * This can raise an error if the mode contains certain detectable known logic + * issues. + * @param {Mode} mode + * @param {CompiledMode | null} [parent] + * @returns {CompiledMode | never} + */ + function compileMode(mode, parent) { + const cmode = /** @type CompiledMode */ (mode); + if (mode.isCompiled) return cmode; + + [ + scopeClassName, + // do this early so compiler extensions generally don't have to worry about + // the distinction between match/begin + compileMatch, + MultiClass, + beforeMatchExt + ].forEach(ext => ext(mode, parent)); + + language.compilerExtensions.forEach(ext => ext(mode, parent)); + + // __beforeBegin is considered private API, internal use only + mode.__beforeBegin = null; + + [ + beginKeywords, + // do this later so compiler extensions that come earlier have access to the + // raw array if they wanted to perhaps manipulate it, etc. + compileIllegal, + // default to 1 relevance if not specified + compileRelevance + ].forEach(ext => ext(mode, parent)); + + mode.isCompiled = true; + + let keywordPattern = null; + if (typeof mode.keywords === "object" && mode.keywords.$pattern) { + // we need a copy because keywords might be compiled multiple times + // so we can't go deleting $pattern from the original on the first + // pass + mode.keywords = Object.assign({}, mode.keywords); + keywordPattern = mode.keywords.$pattern; + delete mode.keywords.$pattern; + } + keywordPattern = keywordPattern || /\w+/; + + if (mode.keywords) { + mode.keywords = compileKeywords(mode.keywords, language.case_insensitive); + } + + cmode.keywordPatternRe = langRe(keywordPattern, true); + + if (parent) { + if (!mode.begin) mode.begin = /\B|\b/; + cmode.beginRe = langRe(cmode.begin); + if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/; + if (mode.end) cmode.endRe = langRe(cmode.end); + cmode.terminatorEnd = source(cmode.end) || ''; + if (mode.endsWithParent && parent.terminatorEnd) { + cmode.terminatorEnd += (mode.end ? '|' : '') + parent.terminatorEnd; + } + } + if (mode.illegal) cmode.illegalRe = langRe(/** @type {RegExp | string} */ (mode.illegal)); + if (!mode.contains) mode.contains = []; + + mode.contains = [].concat(...mode.contains.map(function(c) { + return expandOrCloneMode(c === 'self' ? mode : c); + })); + mode.contains.forEach(function(c) { compileMode(/** @type Mode */ (c), cmode); }); + + if (mode.starts) { + compileMode(mode.starts, parent); + } + + cmode.matcher = buildModeRegex(cmode); + return cmode; + } + + if (!language.compilerExtensions) language.compilerExtensions = []; + + // self is not valid at the top-level + if (language.contains && language.contains.includes('self')) { + throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation."); + } + + // we need a null object, which inherit will guarantee + language.classNameAliases = inherit$1(language.classNameAliases || {}); + + return compileMode(/** @type Mode */ (language)); + } + + /** + * Determines if a mode has a dependency on it's parent or not + * + * If a mode does have a parent dependency then often we need to clone it if + * it's used in multiple places so that each copy points to the correct parent, + * where-as modes without a parent can often safely be re-used at the bottom of + * a mode chain. + * + * @param {Mode | null} mode + * @returns {boolean} - is there a dependency on the parent? + * */ + function dependencyOnParent(mode) { + if (!mode) return false; + + return mode.endsWithParent || dependencyOnParent(mode.starts); + } + + /** + * Expands a mode or clones it if necessary + * + * This is necessary for modes with parental dependenceis (see notes on + * `dependencyOnParent`) and for nodes that have `variants` - which must then be + * exploded into their own individual modes at compile time. + * + * @param {Mode} mode + * @returns {Mode | Mode[]} + * */ + function expandOrCloneMode(mode) { + if (mode.variants && !mode.cachedVariants) { + mode.cachedVariants = mode.variants.map(function(variant) { + return inherit$1(mode, { variants: null }, variant); + }); + } + + // EXPAND + // if we have variants then essentially "replace" the mode with the variants + // this happens in compileMode, where this function is called from + if (mode.cachedVariants) { + return mode.cachedVariants; + } + + // CLONE + // if we have dependencies on parents then we need a unique + // instance of ourselves, so we can be reused with many + // different parents without issue + if (dependencyOnParent(mode)) { + return inherit$1(mode, { starts: mode.starts ? inherit$1(mode.starts) : null }); + } + + if (Object.isFrozen(mode)) { + return inherit$1(mode); + } + + // no special dependency issues, just return ourselves + return mode; + } + + var version = "11.11.1"; + + class HTMLInjectionError extends Error { + constructor(reason, html) { + super(reason); + this.name = "HTMLInjectionError"; + this.html = html; + } + } + + /* + Syntax highlighting with language autodetection. + https://highlightjs.org/ + */ + + + + /** + @typedef {import('highlight.js').Mode} Mode + @typedef {import('highlight.js').CompiledMode} CompiledMode + @typedef {import('highlight.js').CompiledScope} CompiledScope + @typedef {import('highlight.js').Language} Language + @typedef {import('highlight.js').HLJSApi} HLJSApi + @typedef {import('highlight.js').HLJSPlugin} HLJSPlugin + @typedef {import('highlight.js').PluginEvent} PluginEvent + @typedef {import('highlight.js').HLJSOptions} HLJSOptions + @typedef {import('highlight.js').LanguageFn} LanguageFn + @typedef {import('highlight.js').HighlightedHTMLElement} HighlightedHTMLElement + @typedef {import('highlight.js').BeforeHighlightContext} BeforeHighlightContext + @typedef {import('highlight.js/private').MatchType} MatchType + @typedef {import('highlight.js/private').KeywordData} KeywordData + @typedef {import('highlight.js/private').EnhancedMatch} EnhancedMatch + @typedef {import('highlight.js/private').AnnotatedError} AnnotatedError + @typedef {import('highlight.js').AutoHighlightResult} AutoHighlightResult + @typedef {import('highlight.js').HighlightOptions} HighlightOptions + @typedef {import('highlight.js').HighlightResult} HighlightResult + */ + + + const escape = escapeHTML; + const inherit = inherit$1; + const NO_MATCH = Symbol("nomatch"); + const MAX_KEYWORD_HITS = 7; + + /** + * @param {any} hljs - object that is extended (legacy) + * @returns {HLJSApi} + */ + const HLJS = function(hljs) { + // Global internal variables used within the highlight.js library. + /** @type {Record} */ + const languages = Object.create(null); + /** @type {Record} */ + const aliases = Object.create(null); + /** @type {HLJSPlugin[]} */ + const plugins = []; + + // safe/production mode - swallows more errors, tries to keep running + // even if a single syntax or parse hits a fatal error + let SAFE_MODE = true; + const LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?"; + /** @type {Language} */ + const PLAINTEXT_LANGUAGE = { disableAutodetect: true, name: 'Plain text', contains: [] }; + + // Global options used when within external APIs. This is modified when + // calling the `hljs.configure` function. + /** @type HLJSOptions */ + let options = { + ignoreUnescapedHTML: false, + throwUnescapedHTML: false, + noHighlightRe: /^(no-?highlight)$/i, + languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i, + classPrefix: 'hljs-', + cssSelector: 'pre code', + languages: null, + // beta configuration options, subject to change, welcome to discuss + // https://github.com/highlightjs/highlight.js/issues/1086 + __emitter: TokenTreeEmitter + }; + + /* Utility functions */ + + /** + * Tests a language name to see if highlighting should be skipped + * @param {string} languageName + */ + function shouldNotHighlight(languageName) { + return options.noHighlightRe.test(languageName); + } + + /** + * @param {HighlightedHTMLElement} block - the HTML element to determine language for + */ + function blockLanguage(block) { + let classes = block.className + ' '; + + classes += block.parentNode ? block.parentNode.className : ''; + + // language-* takes precedence over non-prefixed class names. + const match = options.languageDetectRe.exec(classes); + if (match) { + const language = getLanguage(match[1]); + if (!language) { + warn(LANGUAGE_NOT_FOUND.replace("{}", match[1])); + warn("Falling back to no-highlight mode for this block.", block); + } + return language ? match[1] : 'no-highlight'; + } + + return classes + .split(/\s+/) + .find((_class) => shouldNotHighlight(_class) || getLanguage(_class)); + } + + /** + * Core highlighting function. + * + * OLD API + * highlight(lang, code, ignoreIllegals, continuation) + * + * NEW API + * highlight(code, {lang, ignoreIllegals}) + * + * @param {string} codeOrLanguageName - the language to use for highlighting + * @param {string | HighlightOptions} optionsOrCode - the code to highlight + * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail + * + * @returns {HighlightResult} Result - an object that represents the result + * @property {string} language - the language name + * @property {number} relevance - the relevance score + * @property {string} value - the highlighted HTML code + * @property {string} code - the original raw code + * @property {CompiledMode} top - top of the current mode stack + * @property {boolean} illegal - indicates whether any illegal matches were found + */ + function highlight(codeOrLanguageName, optionsOrCode, ignoreIllegals) { + let code = ""; + let languageName = ""; + if (typeof optionsOrCode === "object") { + code = codeOrLanguageName; + ignoreIllegals = optionsOrCode.ignoreIllegals; + languageName = optionsOrCode.language; + } else { + // old API + deprecated("10.7.0", "highlight(lang, code, ...args) has been deprecated."); + deprecated("10.7.0", "Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"); + languageName = codeOrLanguageName; + code = optionsOrCode; + } + + // https://github.com/highlightjs/highlight.js/issues/3149 + // eslint-disable-next-line no-undefined + if (ignoreIllegals === undefined) { ignoreIllegals = true; } + + /** @type {BeforeHighlightContext} */ + const context = { + code, + language: languageName + }; + // the plugin can change the desired language or the code to be highlighted + // just be changing the object it was passed + fire("before:highlight", context); + + // a before plugin can usurp the result completely by providing it's own + // in which case we don't even need to call highlight + const result = context.result + ? context.result + : _highlight(context.language, context.code, ignoreIllegals); + + result.code = context.code; + // the plugin can change anything in result to suite it + fire("after:highlight", result); + + return result; + } + + /** + * private highlight that's used internally and does not fire callbacks + * + * @param {string} languageName - the language to use for highlighting + * @param {string} codeToHighlight - the code to highlight + * @param {boolean?} [ignoreIllegals] - whether to ignore illegal matches, default is to bail + * @param {CompiledMode?} [continuation] - current continuation mode, if any + * @returns {HighlightResult} - result of the highlight operation + */ + function _highlight(languageName, codeToHighlight, ignoreIllegals, continuation) { + const keywordHits = Object.create(null); + + /** + * Return keyword data if a match is a keyword + * @param {CompiledMode} mode - current mode + * @param {string} matchText - the textual match + * @returns {KeywordData | false} + */ + function keywordData(mode, matchText) { + return mode.keywords[matchText]; + } + + function processKeywords() { + if (!top.keywords) { + emitter.addText(modeBuffer); + return; + } + + let lastIndex = 0; + top.keywordPatternRe.lastIndex = 0; + let match = top.keywordPatternRe.exec(modeBuffer); + let buf = ""; + + while (match) { + buf += modeBuffer.substring(lastIndex, match.index); + const word = language.case_insensitive ? match[0].toLowerCase() : match[0]; + const data = keywordData(top, word); + if (data) { + const [kind, keywordRelevance] = data; + emitter.addText(buf); + buf = ""; + + keywordHits[word] = (keywordHits[word] || 0) + 1; + if (keywordHits[word] <= MAX_KEYWORD_HITS) relevance += keywordRelevance; + if (kind.startsWith("_")) { + // _ implied for relevance only, do not highlight + // by applying a class name + buf += match[0]; + } else { + const cssClass = language.classNameAliases[kind] || kind; + emitKeyword(match[0], cssClass); + } + } else { + buf += match[0]; + } + lastIndex = top.keywordPatternRe.lastIndex; + match = top.keywordPatternRe.exec(modeBuffer); + } + buf += modeBuffer.substring(lastIndex); + emitter.addText(buf); + } + + function processSubLanguage() { + if (modeBuffer === "") return; + /** @type HighlightResult */ + let result = null; + + if (typeof top.subLanguage === 'string') { + if (!languages[top.subLanguage]) { + emitter.addText(modeBuffer); + return; + } + result = _highlight(top.subLanguage, modeBuffer, true, continuations[top.subLanguage]); + continuations[top.subLanguage] = /** @type {CompiledMode} */ (result._top); + } else { + result = highlightAuto(modeBuffer, top.subLanguage.length ? top.subLanguage : null); + } + + // Counting embedded language score towards the host language may be disabled + // with zeroing the containing mode relevance. Use case in point is Markdown that + // allows XML everywhere and makes every XML snippet to have a much larger Markdown + // score. + if (top.relevance > 0) { + relevance += result.relevance; + } + emitter.__addSublanguage(result._emitter, result.language); + } + + function processBuffer() { + if (top.subLanguage != null) { + processSubLanguage(); + } else { + processKeywords(); + } + modeBuffer = ''; + } + + /** + * @param {string} text + * @param {string} scope + */ + function emitKeyword(keyword, scope) { + if (keyword === "") return; + + emitter.startScope(scope); + emitter.addText(keyword); + emitter.endScope(); + } + + /** + * @param {CompiledScope} scope + * @param {RegExpMatchArray} match + */ + function emitMultiClass(scope, match) { + let i = 1; + const max = match.length - 1; + while (i <= max) { + if (!scope._emit[i]) { i++; continue; } + const klass = language.classNameAliases[scope[i]] || scope[i]; + const text = match[i]; + if (klass) { + emitKeyword(text, klass); + } else { + modeBuffer = text; + processKeywords(); + modeBuffer = ""; + } + i++; + } + } + + /** + * @param {CompiledMode} mode - new mode to start + * @param {RegExpMatchArray} match + */ + function startNewMode(mode, match) { + if (mode.scope && typeof mode.scope === "string") { + emitter.openNode(language.classNameAliases[mode.scope] || mode.scope); + } + if (mode.beginScope) { + // beginScope just wraps the begin match itself in a scope + if (mode.beginScope._wrap) { + emitKeyword(modeBuffer, language.classNameAliases[mode.beginScope._wrap] || mode.beginScope._wrap); + modeBuffer = ""; + } else if (mode.beginScope._multi) { + // at this point modeBuffer should just be the match + emitMultiClass(mode.beginScope, match); + modeBuffer = ""; + } + } + + top = Object.create(mode, { parent: { value: top } }); + return top; + } + + /** + * @param {CompiledMode } mode - the mode to potentially end + * @param {RegExpMatchArray} match - the latest match + * @param {string} matchPlusRemainder - match plus remainder of content + * @returns {CompiledMode | void} - the next mode, or if void continue on in current mode + */ + function endOfMode(mode, match, matchPlusRemainder) { + let matched = startsWith(mode.endRe, matchPlusRemainder); + + if (matched) { + if (mode["on:end"]) { + const resp = new Response(mode); + mode["on:end"](match, resp); + if (resp.isMatchIgnored) matched = false; + } + + if (matched) { + while (mode.endsParent && mode.parent) { + mode = mode.parent; + } + return mode; + } + } + // even if on:end fires an `ignore` it's still possible + // that we might trigger the end node because of a parent mode + if (mode.endsWithParent) { + return endOfMode(mode.parent, match, matchPlusRemainder); + } + } + + /** + * Handle matching but then ignoring a sequence of text + * + * @param {string} lexeme - string containing full match text + */ + function doIgnore(lexeme) { + if (top.matcher.regexIndex === 0) { + // no more regexes to potentially match here, so we move the cursor forward one + // space + modeBuffer += lexeme[0]; + return 1; + } else { + // no need to move the cursor, we still have additional regexes to try and + // match at this very spot + resumeScanAtSamePosition = true; + return 0; + } + } + + /** + * Handle the start of a new potential mode match + * + * @param {EnhancedMatch} match - the current match + * @returns {number} how far to advance the parse cursor + */ + function doBeginMatch(match) { + const lexeme = match[0]; + const newMode = match.rule; + + const resp = new Response(newMode); + // first internal before callbacks, then the public ones + const beforeCallbacks = [newMode.__beforeBegin, newMode["on:begin"]]; + for (const cb of beforeCallbacks) { + if (!cb) continue; + cb(match, resp); + if (resp.isMatchIgnored) return doIgnore(lexeme); + } + + if (newMode.skip) { + modeBuffer += lexeme; + } else { + if (newMode.excludeBegin) { + modeBuffer += lexeme; + } + processBuffer(); + if (!newMode.returnBegin && !newMode.excludeBegin) { + modeBuffer = lexeme; + } + } + startNewMode(newMode, match); + return newMode.returnBegin ? 0 : lexeme.length; + } + + /** + * Handle the potential end of mode + * + * @param {RegExpMatchArray} match - the current match + */ + function doEndMatch(match) { + const lexeme = match[0]; + const matchPlusRemainder = codeToHighlight.substring(match.index); + + const endMode = endOfMode(top, match, matchPlusRemainder); + if (!endMode) { return NO_MATCH; } + + const origin = top; + if (top.endScope && top.endScope._wrap) { + processBuffer(); + emitKeyword(lexeme, top.endScope._wrap); + } else if (top.endScope && top.endScope._multi) { + processBuffer(); + emitMultiClass(top.endScope, match); + } else if (origin.skip) { + modeBuffer += lexeme; + } else { + if (!(origin.returnEnd || origin.excludeEnd)) { + modeBuffer += lexeme; + } + processBuffer(); + if (origin.excludeEnd) { + modeBuffer = lexeme; + } + } + do { + if (top.scope) { + emitter.closeNode(); + } + if (!top.skip && !top.subLanguage) { + relevance += top.relevance; + } + top = top.parent; + } while (top !== endMode.parent); + if (endMode.starts) { + startNewMode(endMode.starts, match); + } + return origin.returnEnd ? 0 : lexeme.length; + } + + function processContinuations() { + const list = []; + for (let current = top; current !== language; current = current.parent) { + if (current.scope) { + list.unshift(current.scope); + } + } + list.forEach(item => emitter.openNode(item)); + } + + /** @type {{type?: MatchType, index?: number, rule?: Mode}}} */ + let lastMatch = {}; + + /** + * Process an individual match + * + * @param {string} textBeforeMatch - text preceding the match (since the last match) + * @param {EnhancedMatch} [match] - the match itself + */ + function processLexeme(textBeforeMatch, match) { + const lexeme = match && match[0]; + + // add non-matched text to the current mode buffer + modeBuffer += textBeforeMatch; + + if (lexeme == null) { + processBuffer(); + return 0; + } + + // we've found a 0 width match and we're stuck, so we need to advance + // this happens when we have badly behaved rules that have optional matchers to the degree that + // sometimes they can end up matching nothing at all + // Ref: https://github.com/highlightjs/highlight.js/issues/2140 + if (lastMatch.type === "begin" && match.type === "end" && lastMatch.index === match.index && lexeme === "") { + // spit the "skipped" character that our regex choked on back into the output sequence + modeBuffer += codeToHighlight.slice(match.index, match.index + 1); + if (!SAFE_MODE) { + /** @type {AnnotatedError} */ + const err = new Error(`0 width match regex (${languageName})`); + err.languageName = languageName; + err.badRule = lastMatch.rule; + throw err; + } + return 1; + } + lastMatch = match; + + if (match.type === "begin") { + return doBeginMatch(match); + } else if (match.type === "illegal" && !ignoreIllegals) { + // illegal match, we do not continue processing + /** @type {AnnotatedError} */ + const err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.scope || '') + '"'); + err.mode = top; + throw err; + } else if (match.type === "end") { + const processed = doEndMatch(match); + if (processed !== NO_MATCH) { + return processed; + } + } + + // edge case for when illegal matches $ (end of line) which is technically + // a 0 width match but not a begin/end match so it's not caught by the + // first handler (when ignoreIllegals is true) + if (match.type === "illegal" && lexeme === "") { + // advance so we aren't stuck in an infinite loop + modeBuffer += "\n"; + return 1; + } + + // infinite loops are BAD, this is a last ditch catch all. if we have a + // decent number of iterations yet our index (cursor position in our + // parsing) still 3x behind our index then something is very wrong + // so we bail + if (iterations > 100000 && iterations > match.index * 3) { + const err = new Error('potential infinite loop, way more iterations than matches'); + throw err; + } + + /* + Why might be find ourselves here? An potential end match that was + triggered but could not be completed. IE, `doEndMatch` returned NO_MATCH. + (this could be because a callback requests the match be ignored, etc) + + This causes no real harm other than stopping a few times too many. + */ + + modeBuffer += lexeme; + return lexeme.length; + } + + const language = getLanguage(languageName); + if (!language) { + error(LANGUAGE_NOT_FOUND.replace("{}", languageName)); + throw new Error('Unknown language: "' + languageName + '"'); + } + + const md = compileLanguage(language); + let result = ''; + /** @type {CompiledMode} */ + let top = continuation || md; + /** @type Record */ + const continuations = {}; // keep continuations for sub-languages + const emitter = new options.__emitter(options); + processContinuations(); + let modeBuffer = ''; + let relevance = 0; + let index = 0; + let iterations = 0; + let resumeScanAtSamePosition = false; + + try { + if (!language.__emitTokens) { + top.matcher.considerAll(); + + for (;;) { + iterations++; + if (resumeScanAtSamePosition) { + // only regexes not matched previously will now be + // considered for a potential match + resumeScanAtSamePosition = false; + } else { + top.matcher.considerAll(); + } + top.matcher.lastIndex = index; + + const match = top.matcher.exec(codeToHighlight); + // console.log("match", match[0], match.rule && match.rule.begin) + + if (!match) break; + + const beforeMatch = codeToHighlight.substring(index, match.index); + const processedCount = processLexeme(beforeMatch, match); + index = match.index + processedCount; + } + processLexeme(codeToHighlight.substring(index)); + } else { + language.__emitTokens(codeToHighlight, emitter); + } + + emitter.finalize(); + result = emitter.toHTML(); + + return { + language: languageName, + value: result, + relevance, + illegal: false, + _emitter: emitter, + _top: top + }; + } catch (err) { + if (err.message && err.message.includes('Illegal')) { + return { + language: languageName, + value: escape(codeToHighlight), + illegal: true, + relevance: 0, + _illegalBy: { + message: err.message, + index, + context: codeToHighlight.slice(index - 100, index + 100), + mode: err.mode, + resultSoFar: result + }, + _emitter: emitter + }; + } else if (SAFE_MODE) { + return { + language: languageName, + value: escape(codeToHighlight), + illegal: false, + relevance: 0, + errorRaised: err, + _emitter: emitter, + _top: top + }; + } else { + throw err; + } + } + } + + /** + * returns a valid highlight result, without actually doing any actual work, + * auto highlight starts with this and it's possible for small snippets that + * auto-detection may not find a better match + * @param {string} code + * @returns {HighlightResult} + */ + function justTextHighlightResult(code) { + const result = { + value: escape(code), + illegal: false, + relevance: 0, + _top: PLAINTEXT_LANGUAGE, + _emitter: new options.__emitter(options) + }; + result._emitter.addText(code); + return result; + } + + /** + Highlighting with language detection. Accepts a string with the code to + highlight. Returns an object with the following properties: + + - language (detected language) + - relevance (int) + - value (an HTML string with highlighting markup) + - secondBest (object with the same structure for second-best heuristically + detected language, may be absent) + + @param {string} code + @param {Array} [languageSubset] + @returns {AutoHighlightResult} + */ + function highlightAuto(code, languageSubset) { + languageSubset = languageSubset || options.languages || Object.keys(languages); + const plaintext = justTextHighlightResult(code); + + const results = languageSubset.filter(getLanguage).filter(autoDetection).map(name => + _highlight(name, code, false) + ); + results.unshift(plaintext); // plaintext is always an option + + const sorted = results.sort((a, b) => { + // sort base on relevance + if (a.relevance !== b.relevance) return b.relevance - a.relevance; + + // always award the tie to the base language + // ie if C++ and Arduino are tied, it's more likely to be C++ + if (a.language && b.language) { + if (getLanguage(a.language).supersetOf === b.language) { + return 1; + } else if (getLanguage(b.language).supersetOf === a.language) { + return -1; + } + } + + // otherwise say they are equal, which has the effect of sorting on + // relevance while preserving the original ordering - which is how ties + // have historically been settled, ie the language that comes first always + // wins in the case of a tie + return 0; + }); + + const [best, secondBest] = sorted; + + /** @type {AutoHighlightResult} */ + const result = best; + result.secondBest = secondBest; + + return result; + } + + /** + * Builds new class name for block given the language name + * + * @param {HTMLElement} element + * @param {string} [currentLang] + * @param {string} [resultLang] + */ + function updateClassName(element, currentLang, resultLang) { + const language = (currentLang && aliases[currentLang]) || resultLang; + + element.classList.add("hljs"); + element.classList.add(`language-${language}`); + } + + /** + * Applies highlighting to a DOM node containing code. + * + * @param {HighlightedHTMLElement} element - the HTML element to highlight + */ + function highlightElement(element) { + /** @type HTMLElement */ + let node = null; + const language = blockLanguage(element); + + if (shouldNotHighlight(language)) return; + + fire("before:highlightElement", + { el: element, language }); + + if (element.dataset.highlighted) { + console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.", element); + return; + } + + // we should be all text, no child nodes (unescaped HTML) - this is possibly + // an HTML injection attack - it's likely too late if this is already in + // production (the code has likely already done its damage by the time + // we're seeing it)... but we yell loudly about this so that hopefully it's + // more likely to be caught in development before making it to production + if (element.children.length > 0) { + if (!options.ignoreUnescapedHTML) { + console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."); + console.warn("https://github.com/highlightjs/highlight.js/wiki/security"); + console.warn("The element with unescaped HTML:"); + console.warn(element); + } + if (options.throwUnescapedHTML) { + const err = new HTMLInjectionError( + "One of your code blocks includes unescaped HTML.", + element.innerHTML + ); + throw err; + } + } + + node = element; + const text = node.textContent; + const result = language ? highlight(text, { language, ignoreIllegals: true }) : highlightAuto(text); + + element.innerHTML = result.value; + element.dataset.highlighted = "yes"; + updateClassName(element, language, result.language); + element.result = { + language: result.language, + // TODO: remove with version 11.0 + re: result.relevance, + relevance: result.relevance + }; + if (result.secondBest) { + element.secondBest = { + language: result.secondBest.language, + relevance: result.secondBest.relevance + }; + } + + fire("after:highlightElement", { el: element, result, text }); + } + + /** + * Updates highlight.js global options with the passed options + * + * @param {Partial} userOptions + */ + function configure(userOptions) { + options = inherit(options, userOptions); + } + + // TODO: remove v12, deprecated + const initHighlighting = () => { + highlightAll(); + deprecated("10.6.0", "initHighlighting() deprecated. Use highlightAll() now."); + }; + + // TODO: remove v12, deprecated + function initHighlightingOnLoad() { + highlightAll(); + deprecated("10.6.0", "initHighlightingOnLoad() deprecated. Use highlightAll() now."); + } + + let wantsHighlight = false; + + /** + * auto-highlights all pre>code elements on the page + */ + function highlightAll() { + function boot() { + // if a highlight was requested before DOM was loaded, do now + highlightAll(); + } + + // if we are called too early in the loading process + if (document.readyState === "loading") { + // make sure the event listener is only added once + if (!wantsHighlight) { + window.addEventListener('DOMContentLoaded', boot, false); + } + wantsHighlight = true; + return; + } + + const blocks = document.querySelectorAll(options.cssSelector); + blocks.forEach(highlightElement); + } + + /** + * Register a language grammar module + * + * @param {string} languageName + * @param {LanguageFn} languageDefinition + */ + function registerLanguage(languageName, languageDefinition) { + let lang = null; + try { + lang = languageDefinition(hljs); + } catch (error$1) { + error("Language definition for '{}' could not be registered.".replace("{}", languageName)); + // hard or soft error + if (!SAFE_MODE) { throw error$1; } else { error(error$1); } + // languages that have serious errors are replaced with essentially a + // "plaintext" stand-in so that the code blocks will still get normal + // css classes applied to them - and one bad language won't break the + // entire highlighter + lang = PLAINTEXT_LANGUAGE; + } + // give it a temporary name if it doesn't have one in the meta-data + if (!lang.name) lang.name = languageName; + languages[languageName] = lang; + lang.rawDefinition = languageDefinition.bind(null, hljs); + + if (lang.aliases) { + registerAliases(lang.aliases, { languageName }); + } + } + + /** + * Remove a language grammar module + * + * @param {string} languageName + */ + function unregisterLanguage(languageName) { + delete languages[languageName]; + for (const alias of Object.keys(aliases)) { + if (aliases[alias] === languageName) { + delete aliases[alias]; + } + } + } + + /** + * @returns {string[]} List of language internal names + */ + function listLanguages() { + return Object.keys(languages); + } + + /** + * @param {string} name - name of the language to retrieve + * @returns {Language | undefined} + */ + function getLanguage(name) { + name = (name || '').toLowerCase(); + return languages[name] || languages[aliases[name]]; + } + + /** + * + * @param {string|string[]} aliasList - single alias or list of aliases + * @param {{languageName: string}} opts + */ + function registerAliases(aliasList, { languageName }) { + if (typeof aliasList === 'string') { + aliasList = [aliasList]; + } + aliasList.forEach(alias => { aliases[alias.toLowerCase()] = languageName; }); + } + + /** + * Determines if a given language has auto-detection enabled + * @param {string} name - name of the language + */ + function autoDetection(name) { + const lang = getLanguage(name); + return lang && !lang.disableAutodetect; + } + + /** + * Upgrades the old highlightBlock plugins to the new + * highlightElement API + * @param {HLJSPlugin} plugin + */ + function upgradePluginAPI(plugin) { + // TODO: remove with v12 + if (plugin["before:highlightBlock"] && !plugin["before:highlightElement"]) { + plugin["before:highlightElement"] = (data) => { + plugin["before:highlightBlock"]( + Object.assign({ block: data.el }, data) + ); + }; + } + if (plugin["after:highlightBlock"] && !plugin["after:highlightElement"]) { + plugin["after:highlightElement"] = (data) => { + plugin["after:highlightBlock"]( + Object.assign({ block: data.el }, data) + ); + }; + } + } + + /** + * @param {HLJSPlugin} plugin + */ + function addPlugin(plugin) { + upgradePluginAPI(plugin); + plugins.push(plugin); + } + + /** + * @param {HLJSPlugin} plugin + */ + function removePlugin(plugin) { + const index = plugins.indexOf(plugin); + if (index !== -1) { + plugins.splice(index, 1); + } + } + + /** + * + * @param {PluginEvent} event + * @param {any} args + */ + function fire(event, args) { + const cb = event; + plugins.forEach(function(plugin) { + if (plugin[cb]) { + plugin[cb](args); + } + }); + } + + /** + * DEPRECATED + * @param {HighlightedHTMLElement} el + */ + function deprecateHighlightBlock(el) { + deprecated("10.7.0", "highlightBlock will be removed entirely in v12.0"); + deprecated("10.7.0", "Please use highlightElement now."); + + return highlightElement(el); + } + + /* Interface definition */ + Object.assign(hljs, { + highlight, + highlightAuto, + highlightAll, + highlightElement, + // TODO: Remove with v12 API + highlightBlock: deprecateHighlightBlock, + configure, + initHighlighting, + initHighlightingOnLoad, + registerLanguage, + unregisterLanguage, + listLanguages, + getLanguage, + registerAliases, + autoDetection, + inherit, + addPlugin, + removePlugin + }); + + hljs.debugMode = function() { SAFE_MODE = false; }; + hljs.safeMode = function() { SAFE_MODE = true; }; + hljs.versionString = version; + + hljs.regex = { + concat: concat, + lookahead: lookahead, + either: either, + optional: optional, + anyNumberOfTimes: anyNumberOfTimes + }; + + for (const key in MODES) { + // @ts-ignore + if (typeof MODES[key] === "object") { + // @ts-ignore + deepFreeze(MODES[key]); + } + } + + // merge all the modes/regexes into our main object + Object.assign(hljs, MODES); + + return hljs; + }; + + // Other names for the variable may break build script + const highlight = HLJS({}); + + // returns a new instance of the highlighter to be used for extensions + // check https://github.com/wooorm/lowlight/issues/47 + highlight.newInstance = () => HLJS({}); + + // https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10 + var decimalDigits = '[0-9](_*[0-9])*'; + var frac = `\\.(${decimalDigits})`; + var hexDigits = '[0-9a-fA-F](_*[0-9a-fA-F])*'; + var NUMERIC = { + className: 'number', + variants: [ + // DecimalFloatingPointLiteral + // including ExponentPart + { begin: `(\\b(${decimalDigits})((${frac})|\\.)?|(${frac}))` + + `[eE][+-]?(${decimalDigits})[fFdD]?\\b` }, + // excluding ExponentPart + { begin: `\\b(${decimalDigits})((${frac})[fFdD]?\\b|\\.([fFdD]\\b)?)` }, + { begin: `(${frac})[fFdD]?\\b` }, + { begin: `\\b(${decimalDigits})[fFdD]\\b` }, + + // HexadecimalFloatingPointLiteral + { begin: `\\b0[xX]((${hexDigits})\\.?|(${hexDigits})?\\.(${hexDigits}))` + + `[pP][+-]?(${decimalDigits})[fFdD]?\\b` }, + + // DecimalIntegerLiteral + { begin: '\\b(0|[1-9](_*[0-9])*)[lL]?\\b' }, + + // HexIntegerLiteral + { begin: `\\b0[xX](${hexDigits})[lL]?\\b` }, + + // OctalIntegerLiteral + { begin: '\\b0(_*[0-7])*[lL]?\\b' }, + + // BinaryIntegerLiteral + { begin: '\\b0[bB][01](_*[01])*[lL]?\\b' }, + ], + relevance: 0 + }; + + /* + Language: Java + Author: Vsevolod Solovyov + Category: common, enterprise + Website: https://www.java.com/ + */ + + + /** + * Allows recursive regex expressions to a given depth + * + * ie: recurRegex("(abc~~~)", /~~~/g, 2) becomes: + * (abc(abc(abc))) + * + * @param {string} re + * @param {RegExp} substitution (should be a g mode regex) + * @param {number} depth + * @returns {string}`` + */ + function recurRegex(re, substitution, depth) { + if (depth === -1) return ""; + + return re.replace(substitution, _ => { + return recurRegex(re, substitution, depth - 1); + }); + } + + /** @type LanguageFn */ + function java(hljs) { + const regex = hljs.regex; + const JAVA_IDENT_RE = '[\u00C0-\u02B8a-zA-Z_$][\u00C0-\u02B8a-zA-Z_$0-9]*'; + const TYPE_ARG_RE = '(?:(?:' + JAVA_IDENT_RE + '~~~)|(?:\\?\\s+(?:extends|super)\\s+' + JAVA_IDENT_RE + '~~~)|(?:\\?))'; + const GENERIC_RE = recurRegex('(?:<' + TYPE_ARG_RE + '(?:\\s*,\\s*' + TYPE_ARG_RE + ')*>)?', /~~~/g, 2); + const ARRAY_RE = '(?:(?:\\[])+)?'; + const MAIN_KEYWORDS = [ + 'synchronized', + 'abstract', + 'private', + 'var', + 'static', + 'if', + 'const ', + 'for', + 'while', + 'strictfp', + 'finally', + 'protected', + 'import', + 'native', + 'final', + 'void', + 'enum', + 'else', + 'break', + 'transient', + 'catch', + 'instanceof', + 'volatile', + 'case', + 'assert', + 'package', + 'default', + 'public', + 'try', + 'switch', + 'continue', + 'throws', + 'protected', + 'public', + 'private', + 'module', + 'requires', + 'exports', + 'do', + 'sealed', + 'yield', + 'permits', + 'goto', + 'when' + ]; + + const BUILT_INS = [ + 'super', + 'this' + ]; + + const LITERALS = [ + 'false', + 'true', + 'null' + ]; + + const TYPES = [ + 'char', + 'boolean', + 'long', + 'float', + 'int', + 'byte', + 'short', + 'double' + ]; + + const KEYWORDS = { + keyword: MAIN_KEYWORDS, + literal: LITERALS, + type: TYPES, + built_in: BUILT_INS + }; + + const ANNOTATION = { + className: 'meta', + begin: '@' + JAVA_IDENT_RE, + contains: [ + { + begin: /\(/, + end: /\)/, + contains: [ "self" ] // allow nested () inside our annotation + } + ] + }; + const PARAMS = { + className: 'params', + begin: /\(/, + end: /\)/, + keywords: KEYWORDS, + relevance: 0, + contains: [ hljs.C_BLOCK_COMMENT_MODE ], + endsParent: true + }; + + return { + name: 'Java', + aliases: [ 'jsp' ], + keywords: KEYWORDS, + illegal: /<\/|#/, + contains: [ + hljs.COMMENT( + '/\\*\\*', + '\\*/', + { + relevance: 0, + contains: [ + { + // eat up @'s in emails to prevent them to be recognized as doctags + begin: /\w+@/, + relevance: 0 + }, + { + className: 'doctag', + begin: '@[A-Za-z]+' + } + ] + } + ), + // relevance boost + { + begin: /import java\.[a-z]+\./, + keywords: "import", + relevance: 2 + }, + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE, + { + begin: /"""/, + end: /"""/, + className: "string", + contains: [ hljs.BACKSLASH_ESCAPE ] + }, + hljs.APOS_STRING_MODE, + hljs.QUOTE_STRING_MODE, + { + match: [ + /\b(?:class|interface|enum|extends|implements|new)/, + /\s+/, + JAVA_IDENT_RE + ], + className: { + 1: "keyword", + 3: "title.class" + } + }, + { + // Exceptions for hyphenated keywords + match: /non-sealed/, + scope: "keyword" + }, + { + begin: [ + regex.concat(/(?!else)/, JAVA_IDENT_RE), + GENERIC_RE, + ARRAY_RE, + /\s+/, + JAVA_IDENT_RE, + /\s+/, + /=(?!=)/ + ], + className: { + 1: "type", + 5: "variable", + 7: "operator" + } + }, + { + begin: [ + /record/, + /\s+/, + JAVA_IDENT_RE + ], + className: { + 1: "keyword", + 3: "title.class" + }, + contains: [ + PARAMS, + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE + ] + }, + { + // Expression keywords prevent 'keyword Name(...)' from being + // recognized as a function definition + beginKeywords: 'new throw return else', + relevance: 0 + }, + { + begin: [ + '(?:' + JAVA_IDENT_RE + GENERIC_RE + ARRAY_RE + '\\s+)', + hljs.UNDERSCORE_IDENT_RE, + /\s*(?=\()/ + ], + className: { 2: "title.function" }, + keywords: KEYWORDS, + contains: [ + { + className: 'params', + begin: /\(/, + end: /\)/, + keywords: KEYWORDS, + relevance: 0, + contains: [ + ANNOTATION, + hljs.APOS_STRING_MODE, + hljs.QUOTE_STRING_MODE, + NUMERIC, + hljs.C_BLOCK_COMMENT_MODE + ] + }, + hljs.C_LINE_COMMENT_MODE, + hljs.C_BLOCK_COMMENT_MODE + ] + }, + NUMERIC, + ANNOTATION + ] + }; + } + + /* + Language: .properties + Contributors: Valentin Aitken , Egor Rogov + Website: https://en.wikipedia.org/wiki/.properties + Category: config + */ + + /** @type LanguageFn */ + function properties(hljs) { + // whitespaces: space, tab, formfeed + const WS0 = '[ \\t\\f]*'; + const WS1 = '[ \\t\\f]+'; + // delimiter + const EQUAL_DELIM = WS0 + '[:=]' + WS0; + const WS_DELIM = WS1; + const DELIM = '(' + EQUAL_DELIM + '|' + WS_DELIM + ')'; + const KEY = '([^\\\\:= \\t\\f\\n]|\\\\.)+'; + + const DELIM_AND_VALUE = { + // skip DELIM + end: DELIM, + relevance: 0, + starts: { + // value: everything until end of line (again, taking into account backslashes) + className: 'string', + end: /$/, + relevance: 0, + contains: [ + { begin: '\\\\\\\\' }, + { begin: '\\\\\\n' } + ] + } + }; + + return { + name: '.properties', + disableAutodetect: true, + case_insensitive: true, + illegal: /\S/, + contains: [ + hljs.COMMENT('^\\s*[!#]', '$'), + // key: everything until whitespace or = or : (taking into account backslashes) + // case of a key-value pair + { + returnBegin: true, + variants: [ + { begin: KEY + EQUAL_DELIM }, + { begin: KEY + WS_DELIM } + ], + contains: [ + { + className: 'attr', + begin: KEY, + endsParent: true + } + ], + starts: DELIM_AND_VALUE + }, + // case of an empty key + { + className: 'attr', + begin: KEY + WS0 + '$' + } + ] + }; + } + + /* + Language: HTML, XML + Website: https://www.w3.org/XML/ + Category: common, web + Audit: 2020 + */ + + /** @type LanguageFn */ + function xml(hljs) { + const regex = hljs.regex; + // XML names can have the following additional letters: https://www.w3.org/TR/xml/#NT-NameChar + // OTHER_NAME_CHARS = /[:\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]/; + // Element names start with NAME_START_CHAR followed by optional other Unicode letters, ASCII digits, hyphens, underscores, and periods + // const TAG_NAME_RE = regex.concat(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/, regex.optional(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*:/), /[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*/);; + // const XML_IDENT_RE = /[A-Z_a-z:\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]+/; + // const TAG_NAME_RE = regex.concat(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/, regex.optional(/[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*:/), /[A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*/); + // however, to cater for performance and more Unicode support rely simply on the Unicode letter class + const TAG_NAME_RE = regex.concat(/[\p{L}_]/u, regex.optional(/[\p{L}0-9_.-]*:/u), /[\p{L}0-9_.-]*/u); + const XML_IDENT_RE = /[\p{L}0-9._:-]+/u; + const XML_ENTITIES = { + className: 'symbol', + begin: /&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/ + }; + const XML_META_KEYWORDS = { + begin: /\s/, + contains: [ + { + className: 'keyword', + begin: /#?[a-z_][a-z1-9_-]+/, + illegal: /\n/ + } + ] + }; + const XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, { + begin: /\(/, + end: /\)/ + }); + const APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, { className: 'string' }); + const QUOTE_META_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, { className: 'string' }); + const TAG_INTERNALS = { + endsWithParent: true, + illegal: /`]+/ } + ] + } + ] + } + ] + }; + return { + name: 'HTML, XML', + aliases: [ + 'html', + 'xhtml', + 'rss', + 'atom', + 'xjb', + 'xsd', + 'xsl', + 'plist', + 'wsf', + 'svg' + ], + case_insensitive: true, + unicodeRegex: true, + contains: [ + { + className: 'meta', + begin: //, + relevance: 10, + contains: [ + XML_META_KEYWORDS, + QUOTE_META_STRING_MODE, + APOS_META_STRING_MODE, + XML_META_PAR_KEYWORDS, + { + begin: /\[/, + end: /\]/, + contains: [ + { + className: 'meta', + begin: //, + contains: [ + XML_META_KEYWORDS, + XML_META_PAR_KEYWORDS, + QUOTE_META_STRING_MODE, + APOS_META_STRING_MODE + ] + } + ] + } + ] + }, + hljs.COMMENT( + //, + { relevance: 10 } + ), + { + begin: //, + relevance: 10 + }, + XML_ENTITIES, + // xml processing instructions + { + className: 'meta', + end: /\?>/, + variants: [ + { + begin: /<\?xml/, + relevance: 10, + contains: [ + QUOTE_META_STRING_MODE + ] + }, + { + begin: /<\?[a-z][a-z0-9]+/, + } + ] + + }, + { + className: 'tag', + /* + The lookahead pattern (?=...) ensures that 'begin' only matches + ')/, + end: />/, + keywords: { name: 'style' }, + contains: [ TAG_INTERNALS ], + starts: { + end: /<\/style>/, + returnEnd: true, + subLanguage: [ + 'css', + 'xml' + ] + } + }, + { + className: 'tag', + // See the comment in the