diff --git a/.editorconfig b/.editorconfig index 0e6c17e7674..98f07b3c5fb 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 1e6f99e97c9..a085f10c48e 100644 --- a/doc/building.html +++ b/doc/building.html @@ -594,7 +594,7 @@ to compile successfully without issues.

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 18e030baa9e..a31cd5fe775 100644 --- a/doc/building.md +++ b/doc/building.md @@ -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 14.3.1 (using clang 14.0.3) | +| 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 8962b596278..ba8ec0c864b 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 60c029ce8f9..965ae689e21 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/GenerateLinkOptData.gmk b/make/GenerateLinkOptData.gmk index 5fc745ba223..54022e4e27a 100644 --- a/make/GenerateLinkOptData.gmk +++ b/make/GenerateLinkOptData.gmk @@ -66,7 +66,7 @@ endif # default classlist is minimal, let's filter out the '@cp' lines until we can # find a proper solution. CLASSLIST_FILE_VM_OPTS = \ - -Duser.language=en -Duser.country=US + -Duser.language=en -Duser.country=US --enable-native-access=ALL-UNNAMED # Save the stderr output of the command and print it along with stdout in case # something goes wrong. diff --git a/make/Main.gmk b/make/Main.gmk index eda3b79265a..3535ad16aae 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/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 79e44dd4ad1..f8a512f503d 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 907a60290ec..ebaa487b40a 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 aa4d846280e..40fa453f954 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -1090,7 +1090,7 @@ 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", + 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 011d1a675a9..05e4722e45a 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/createWindowsDevkit.sh b/make/devkit/createWindowsDevkit.sh index 0646cb68ef4..757fb157ad4 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/jdk/src/classes/build/tools/classlist/HelloClasslist.java b/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java index 1b930ca7527..9434889e0bb 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 @@ -31,6 +31,8 @@ */ package build.tools.classlist; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -59,6 +61,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 +144,7 @@ public class HelloClasslist { 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 +164,9 @@ public class HelloClasslist { 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 @@ -167,6 +174,10 @@ public class HelloClasslist { // an inconsistency in the classlist between builds (see JDK-8295951). // To avoid the problem, load the class explicitly. Class striped64Class = Class.forName("java.util.concurrent.atomic.Striped64$Cell"); + + // Initialize FFM linkers + var signature = FunctionDescriptor.ofVoid(); + Linker.nativeLinker().downcallHandle(signature); } public HelloClasslist() {} diff --git a/make/modules/java.base/Lib.gmk b/make/modules/java.base/Lib.gmk index 84ee309dadd..51d323a0344 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/src/hotspot/.editorconfig b/src/hotspot/.editorconfig deleted file mode 100644 index 48e63362b54..00000000000 --- 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 76e3c92ddc2..1b128f2a0ce 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 3db7d308844..5c02e30963e 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 @@ public: #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); \ + data_processing(op31, type, opcode, op21, 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(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(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); + 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 @@ public: 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 2334cbdff24..2e53ecb8058 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 56a91310dcd..585812a99ee 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 e0eaa0b76e6..70e4265c7cc 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 0c2d9a32c8c..3874c8cd54e 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/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index f231caeba9f..fd6af0b9b4b 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -1003,9 +1003,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)); } @@ -5520,9 +5517,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 af09f97e938..e6075b0073b 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1472,16 +1472,6 @@ public: 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); diff --git a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp index a6cd0557758..0fbc2ef141e 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/runtime_aarch64.cpp b/src/hotspot/cpu/aarch64/runtime_aarch64.cpp index 83e43c3ebd2..2361d584f42 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/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index a6cc757f6a0..f5567dcc03a 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -11166,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 @@ -11392,8 +11319,6 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_montgomerySquare = g.generate_multiply(); } - generate_vector_math_stubs(); - #endif // COMPILER2 if (UseChaCha20Intrinsics) { diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index b2d34553487..6ed7a6be585 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 04cf9c9c2a0..373f8da5405 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 f3b97d23ad3..4a0b557968c 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 bca6c7ca30c..5683bc59d5c 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/shared/barrierSetNMethod_arm.cpp b/src/hotspot/cpu/arm/gc/shared/barrierSetNMethod_arm.cpp index c6cc0ce406e..52d71ca65c2 100644 --- a/src/hotspot/cpu/arm/gc/shared/barrierSetNMethod_arm.cpp +++ b/src/hotspot/cpu/arm/gc/shared/barrierSetNMethod_arm.cpp @@ -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 20c1bc199d3..615a63eac19 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/vm_version_arm_32.cpp b/src/hotspot/cpu/arm/vm_version_arm_32.cpp index 148786a55da..d0941936035 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 d4f5faa29a8..a390a6eeed4 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 e4684613e25..8ce324a570b 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 1b44a169e67..d3bb9cc3c04 100644 --- a/src/hotspot/cpu/ppc/gc/shared/barrierSetNMethod_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetNMethod_ppc.cpp @@ -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/runtime_ppc.cpp b/src/hotspot/cpu/ppc/runtime_ppc.cpp index 94e8c08ebf5..6d9a1dfcb1e 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 5a94d469434..1c9c88b3c30 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -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/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index 8ec69bffe15..3cb0bf9bf72 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(); } @@ -519,7 +519,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 +726,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 e036cb6b1ec..4773043e1ba 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 d55521823ec..ea299181ca7 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/nativeInst_riscv.hpp b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp index 295e92bbc1b..d8f5fa57816 100644 --- a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp +++ b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp @@ -300,7 +300,7 @@ public: 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 2e9d25c8156..f6fb2e195d3 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: @@ -1950,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; diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad index 6fea439954c..58c78874797 100644 --- a/src/hotspot/cpu/riscv/riscv_v.ad +++ b/src/hotspot/cpu/riscv/riscv_v.ad @@ -415,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)); @@ -430,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), @@ -444,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)); @@ -459,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), @@ -473,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)); @@ -488,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), @@ -502,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)); @@ -517,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), @@ -595,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)); @@ -610,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), @@ -624,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)); @@ -639,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), @@ -685,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); @@ -716,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)); @@ -763,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), @@ -778,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)); @@ -794,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), @@ -841,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); @@ -872,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)); @@ -919,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), @@ -934,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)); @@ -950,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), @@ -997,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); @@ -1028,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)); @@ -1075,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), @@ -1090,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)); @@ -1106,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), @@ -1123,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)); @@ -1141,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 %{ @@ -1154,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), @@ -1173,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 %{ @@ -1187,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)); @@ -1222,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)); @@ -1349,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) %{ @@ -1737,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)); @@ -1752,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), @@ -1766,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)); @@ -1781,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), @@ -1857,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, @@ -1891,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, @@ -1927,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, @@ -1961,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, @@ -1997,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, @@ -2031,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, @@ -2067,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, @@ -2173,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, @@ -2239,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, @@ -2273,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, @@ -2309,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, @@ -2343,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, @@ -3058,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)); @@ -3076,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)); @@ -3094,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)); @@ -3111,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)); @@ -3129,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) { @@ -3146,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) { @@ -3163,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) { @@ -3179,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) { @@ -3196,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)); @@ -3218,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)); @@ -3240,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)); @@ -3257,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)); @@ -3275,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) { @@ -3296,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) { @@ -3317,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) { @@ -3333,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) { @@ -3350,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)); @@ -3367,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)); @@ -3384,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)); @@ -3396,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)); @@ -3409,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)); @@ -3427,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)); @@ -3445,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)); @@ -3458,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)); @@ -3502,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)); @@ -3514,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; @@ -3534,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)); @@ -3545,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)); @@ -3557,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; @@ -3589,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)); @@ -3601,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; @@ -3622,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)); @@ -3633,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)); @@ -3645,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; @@ -4243,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" %} @@ -4257,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" %} @@ -4355,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 %{ @@ -5093,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)); @@ -5112,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 44a8e35e285..7c8ca853bc4 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/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 4527a32926f..fb4539267ae 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 8dcffc9c646..28c32ed33c8 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -468,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 c858a4b8cb1..430928a66ed 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 9fa6da8341f..ddba445154a 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 66ccc8de876..721995f41fe 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/shared/barrierSetNMethod_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetNMethod_s390.cpp index 85dcc0a4e73..88b3199e4e1 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 4ba99eb9e88..09995334330 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 4eedb3877d2..8f96ff55ccb 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 f4487ccabec..099e28a3adc 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -2768,6 +2768,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 157b945e6e1..8261fbd083a 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 49e6f5686f6..6c6eb76bf7b 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 3ea00681ada..7a4d7c6d6f3 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: diff --git a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp index 73262b21365..7c0d3ff624d 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 574bc081fce..19e25cde2ec 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 dd2880d88c3..713eb73d68f 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -44,6 +44,9 @@ public: 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 @@ public: 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/globalDefinitions_x86.hpp b/src/hotspot/cpu/x86/globalDefinitions_x86.hpp index 873cfbdcea0..3c1474ae861 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 5b5f5683961..a1d4a71874f 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); diff --git a/src/hotspot/cpu/x86/nativeInst_x86.cpp b/src/hotspot/cpu/x86/nativeInst_x86.cpp index 4ee741077dc..c3345be2172 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 d02387aa9ff..b2448cb99fd 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 a063c7aeb37..027a523b33d 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/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index b88a2bd1f8e..1a16416787d 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/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 19ae2dc8d2b..5306f0becef 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 && @@ -1114,15 +1080,19 @@ void VM_Version::get_processor_features() { } 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()) { @@ -1206,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; @@ -1217,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)) { @@ -1246,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 @@ -1262,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); } @@ -1308,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; } @@ -1335,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); } @@ -1377,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) { @@ -1405,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]); @@ -1418,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; } @@ -1458,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 @@ -1766,7 +1668,6 @@ void VM_Version::get_processor_features() { } #endif -#ifdef _LP64 if (UseSSE42Intrinsics) { if (FLAG_IS_DEFAULT(UseVectorizedMismatchIntrinsic)) { UseVectorizedMismatchIntrinsic = true; @@ -1783,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()) { @@ -1945,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 @@ -1964,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)) @@ -2195,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; @@ -2217,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(); @@ -2991,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; @@ -3168,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. @@ -3324,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 cc5c6c1c639..7eb627c8714 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -642,7 +642,7 @@ public: 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 @@ public: // 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 @@ public: // 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 078150c61fb..25cee7a3094 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/vm_version_zero.cpp b/src/hotspot/cpu/zero/vm_version_zero.cpp index e38561e19c5..3ce9227c193 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/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 49dcebd0083..1bbaf29125d 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -132,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 @@ -906,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; @@ -2571,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 0b008a197de..8444002b871 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/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp b/src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp index 86549e878cb..861fda7a71d 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 193c7cb0689..9a9954b3eb9 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/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 1afece719cb..9bd45b0fec8 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/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index df084ae6898..1da0eb219b6 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -492,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; } @@ -1326,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(). @@ -1473,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 248a30d04ad..5c3b1f35bd1 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 @@ public: 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 4d6fc1e4b8c..cbbecea3a6a 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/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 555ac832aae..e900d5695ae 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -147,7 +147,7 @@ public: }; -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/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 959a0616834..c6107b95259 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -112,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 @@ -1225,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 @@ -3020,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) @@ -3081,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 @@ -3099,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 @@ -3241,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()), @@ -3255,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; } @@ -3272,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, @@ -3281,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) { @@ -4813,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 @@ -5188,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 dda0acde793..322b844f413 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_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index dabc69403f3..9725c6cd6c0 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/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index b6095c279cb..506c78cacca 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/share/asm/assembler.hpp b/src/hotspot/share/asm/assembler.hpp index 9abd3eb7171..961b5fab700 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 917569e2be6..fc8f72a881e 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. @@ -939,11 +939,11 @@ 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 @@ -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; diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp index f855d41b181..b38cc74cc3b 100644 --- a/src/hotspot/share/asm/codeBuffer.hpp +++ b/src/hotspot/share/asm/codeBuffer.hpp @@ -121,8 +121,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) { @@ -535,7 +535,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. diff --git a/src/hotspot/share/c1/c1_FrameMap.hpp b/src/hotspot/share/c1/c1_FrameMap.hpp index 4e4fde0cb4a..f10c4d3f226 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 9d4b35024ed..0f87a90a417 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 65eb06ca7f0..d87b501150b 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(); diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 297f8109eb4..be7e1f31d8a 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 00000000000..e948a6cefa3 --- /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 00000000000..3d645c2df17 --- /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 c309de17b4c..ef75bd42323 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -309,7 +309,8 @@ address ArchiveBuilder::reserve_buffer() { size_t buffer_size = LP64_ONLY(CompressedClassSpaceSize) NOT_LP64(256 * M); 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(); @@ -1201,7 +1202,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 +1211,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); } diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index 5684066105f..2b6d403d835 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/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 64ad07b0cf8..9af83653351 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -536,9 +536,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 diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 580699b60b5..1b5d455485c 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -1066,10 +1066,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 +1094,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. @@ -1620,7 +1620,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/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index ce98b2b93b7..ef71d5895f6 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 04c9ae91381..f4e86aa5895 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -164,8 +164,8 @@ private: 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 @@ private: 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 ef2a6dcb8e6..bdec6441080 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" @@ -962,22 +963,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 @@ -1345,7 +1338,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 +1530,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 +1599,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 +1618,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 diff --git a/src/hotspot/share/ci/ciInstance.cpp b/src/hotspot/share/ci/ciInstance.cpp index ad456ba6726..9591298e3ab 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 3af07edcd9e..1fb09985930 100644 --- a/src/hotspot/share/ci/ciInstance.hpp +++ b/src/hotspot/share/ci/ciInstance.hpp @@ -67,6 +67,7 @@ public: 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 3b2670c3eb0..6b6f1b6f0a8 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 234b4611ea1..6df090a7ce5 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 92db6253aa0..adfb85dc17f 100644 --- a/src/hotspot/share/ci/ciTypeFlow.hpp +++ b/src/hotspot/share/ci/ciTypeFlow.hpp @@ -253,7 +253,7 @@ public: 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 05e73d8ca64..91f848368ac 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 3e6246d4aee..757f0e72c8c 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/compactHashtable.cpp b/src/hotspot/share/classfile/compactHashtable.cpp index 8d50e8136a3..15ae5ba8013 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/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index a2ad1dce5e4..c7cca9682fe 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 3f2ff90ccab..a506c4502a4 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 @@ public: }; 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 68de40f1788..2959f35ef2c 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 e66066738ef..f9f6bd07254 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/debugInfoRec.cpp b/src/hotspot/share/code/debugInfoRec.cpp index 02cd23407bf..8449f5d6929 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 2b3253030c5..a8ef707978d 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 56ba76a806e..4576a348a4f 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) { diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 7b19cf75a76..1c536a5d7e1 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 af23bf12b43..c37651892cc 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 ad194c71bb2..a828a8356aa 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])) { @@ -342,7 +342,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. diff --git a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp index 25cca49e50b..a10f653bbb1 100644 --- a/src/hotspot/share/code/relocInfo.hpp +++ b/src/hotspot/share/code/relocInfo.hpp @@ -574,7 +574,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; diff --git a/src/hotspot/share/code/stubs.cpp b/src/hotspot/share/code/stubs.cpp index 074241ff611..6ae71f93709 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 0c2822c94d2..3138eb3a8c0 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 ea2c770d66f..88249fc8555 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() || diff --git a/src/hotspot/share/compiler/oopMap.hpp b/src/hotspot/share/compiler/oopMap.hpp index 634fb8b0bfa..191996f0f05 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; } diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp index b1cf9fd3046..bd156a69fe6 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/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 4ce45d06d1d..ad52388a64f 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -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 = diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index 288ddae48fb..03610ab520b 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 62804b47d93..1962d3173b7 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.hpp @@ -316,6 +316,7 @@ public: } 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/parallel/objectStartArray.cpp b/src/hotspot/share/gc/parallel/objectStartArray.cpp index e8d94b28d18..d2c91b302b5 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 f33d7f93f1d..d2d168cc2c0 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/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index e63ff686312..ed8df5d42f6 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -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/psPromotionLAB.cpp b/src/hotspot/share/gc/parallel/psPromotionLAB.cpp index e3ed819ceb1..a6612d5cd2d 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 e8e42d3754b..60bd4ec250a 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/serial/serialBlockOffsetTable.cpp b/src/hotspot/share/gc/serial/serialBlockOffsetTable.cpp index 5baad7f995a..afaf1aba538 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/shared/cardTable.cpp b/src/hotspot/share/gc/shared/cardTable.cpp index e5dbbcc0746..76fe73abaf6 100644 --- a/src/hotspot/share/gc/shared/cardTable.cpp +++ b/src/hotspot/share/gc/shared/cardTable.cpp @@ -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.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index 439563a4b62..8bf47d6b0bb 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -179,7 +179,7 @@ protected: 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 { diff --git a/src/hotspot/share/gc/shared/hSpaceCounters.cpp b/src/hotspot/share/gc/shared/hSpaceCounters.cpp index de5dd2912a5..818d7422fba 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 63aabf1479b..01310e456f6 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/memAllocator.cpp b/src/hotspot/share/gc/shared/memAllocator.cpp index 74ef1f66184..a8ba2074cd1 100644 --- a/src/hotspot/share/gc/shared/memAllocator.cpp +++ b/src/hotspot/share/gc/shared/memAllocator.cpp @@ -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/scavengableNMethods.cpp b/src/hotspot/share/gc/shared/scavengableNMethods.cpp index 0dff5526911..887ac5f43a2 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/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index d71e84d33b0..f12b3dc5fa8 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -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/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index cf1a76ff4ff..2d0bbfd5e4a 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 ba09eeb8794..3aca436104b 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -120,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/shenandoahCardTable.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCardTable.cpp index 00f11938489..e9e52475fb9 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/shenandoahCollectionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp index 1d353163463..25b900f8d77 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/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 6975bd9f350..d8eb8e0a4e1 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -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/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index fe4679bbf05..6b33d5207d0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -482,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/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index b5b8e4b9e1a..41faf3efa24 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -161,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"); } @@ -375,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()); @@ -384,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"); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp index bf309af9743..774c4f7d941 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/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index 342b599caf5..af661fd1dc4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -309,14 +309,14 @@ private: 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 @@ public: 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/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp index 62ce860594b..e577ce42c1e 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 8dbc3d4fcea..7736d3f4565 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 a7a21f65944..ca54b297b38 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 97f2b8a1990..0ba54fc79be 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 f97838c7869..faf0baf2bcb 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 4e66af7f478..814f15017e0 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 8d381a646ee..4b5ca80470e 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 ad47c954fd7..af87f2bc5fb 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 55229e5d732..ad16b7abde3 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 cffac62c1c8..a4a8f3bb1d0 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/jvmci_globals.cpp b/src/hotspot/share/jvmci/jvmci_globals.cpp index dfc7e6b5970..a41351218f8 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 4da49b24e6e..5bcead9ff7b 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 662c1f1c2bb..b883dbccacf 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/memory/allocation.inline.hpp b/src/hotspot/share/memory/allocation.inline.hpp index 8d531c6dd23..01af1616ce1 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 43cc71355ff..457818139cd 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 1e16ec252a9..f8f642cca95 100644 --- a/src/hotspot/share/memory/memoryReserver.hpp +++ b/src/hotspot/share/memory/memoryReserver.hpp @@ -58,12 +58,12 @@ public: 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 ab7202d046a..c28360e5553 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -598,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 @@ -771,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 76fa1e36c45..f06f6c855be 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 66644c805a9..bb59192cf16 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 e846eb3ddde..156c96d621f 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; diff --git a/src/hotspot/share/memory/universe.hpp b/src/hotspot/share/memory/universe.hpp index 69f8642d6da..35c31330f08 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 fa1de208804..2c2d629f032 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; diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index c7327782a4a..512e1975929 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 05b1588f38b..2238d42f15f 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 981e991a41e..3918e81dab7 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/virtualMemoryTracker.hpp b/src/hotspot/share/nmt/virtualMemoryTracker.hpp index 74d299e6637..2b3b5722571 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 6490a012e9a..83af4b88e32 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 a3db976046d..a17d1ca4e37 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 715c8f473d0..00ef75d8486 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 eb507495cf5..b8327492493 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 ac0e125ab4c..b0d2e84335e 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() { diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index 0c4430b44c3..07f2c559604 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -1256,7 +1256,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; } diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 5d4ac471303..c33f656047d 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 4ac6399d3a0..5baa72dfffb 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 @@ protected: 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 @@ public: { 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 f135df21114..675113163e8 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 41347313a8c..1fb7714153d 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 41b08161097..2246bd62f4e 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 f39937b9cdd..272692446ae 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 bb30dcb2976..3527e46c24b 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 198634af71b..6b3058d94f6 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 4885d44a13d..92f6c938dba 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 11e1797d034..7832cbd42f8 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 4b74420f996..cc3d3479c81 100644 --- a/src/hotspot/share/opto/chaitin.hpp +++ b/src/hotspot/share/opto/chaitin.hpp @@ -82,11 +82,11 @@ public: // 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 @@ public: // 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 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 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 e4e82cbddab..effc7e21354 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 1fb34e799b2..3a6a81f656e 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -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 df9177a4938..20feca26ede 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 d1b58526a6d..28773d75333 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 8c26e1cc39d..dd7e9ae52b7 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/indexSet.cpp b/src/hotspot/share/opto/indexSet.cpp index 7f02f01c83f..367f5b78af2 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 017564173a8..65635001e13 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 cb755267ec5..1be08df32ae 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/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index c3f411eea94..195d48b37c6 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 } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 1bd35f9dcc9..1fe59c9156b 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 e34a43cc1e2..0849b40ad7e 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.hpp b/src/hotspot/share/opto/memnode.hpp index 157cc1866a0..a751022f752 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -71,7 +71,7 @@ protected: _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 @@ protected: _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 @@ protected: _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; } @@ -273,7 +273,7 @@ public: // 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"); @@ -1497,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 1d046583ee0..72562a657ea 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 25dad70a50a..dff2caed38d 100644 --- a/src/hotspot/share/opto/multnode.hpp +++ b/src/hotspot/share/opto/multnode.hpp @@ -71,7 +71,7 @@ public: // 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 76e19360910..a635abebec1 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 1cb9009ef27..e1a4ee22661 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -427,11 +427,11 @@ public: 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 @@ private: } 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 @@ public: } // 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 @@ protected: 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 9fe5ad562b0..1cd6ebabc4b 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 f7330d10df3..6fa0b0f497d 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 9be15b153b3..82e5b3f9d85 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 } } diff --git a/src/hotspot/share/opto/regalloc.hpp b/src/hotspot/share/opto/regalloc.hpp index 86877e18325..c724406a168 100644 --- a/src/hotspot/share/opto/regalloc.hpp +++ b/src/hotspot/share/opto/regalloc.hpp @@ -60,12 +60,12 @@ public: // 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 6d24fb26cd2..fcb0ac38ace 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/type.cpp b/src/hotspot/share/opto/type.cpp index 4556555acea..163f94ee959 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 e33d7b19686..13acc0469eb 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 084b70a6906..05ef64af704 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 ae817598d39..e72c3880c79 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; }; diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index d2916fad185..228244bbd05 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 aa158490eab..14d9c36c9fd 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 bbca1ccc6e1..d1274158be4 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 7a0c9a428c5..b2af12b08a6 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 32dd6fc13e7..13822f73f77 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 e9d496fc696..b4119825bd3 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 246e2cdbb13..1ab5d6ab7f7 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 f5f91e4614f..0b051bbc60f 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 911f0f3ad2c..bc6a56dab05 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 a00656f30ee..c907ddb4885 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 688fb595099..5ba18cdfaa8 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 e08a5ba5ebd..2e277ffadab 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 e95c96b4e9c..c0667d739ce 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 8cfc7031f97..6f1b886bc98 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 b3e0600df34..69d37a11e45 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/handles.cpp b/src/hotspot/share/runtime/handles.cpp index f747b4dc76e..cd3bd3eb3e0 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 8ed16d33a2f..d2020e34121 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 669a940eca9..4d3dd527c0d 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/interfaceSupport.inline.hpp b/src/hotspot/share/runtime/interfaceSupport.inline.hpp index 403ff1d9ea2..c52c2664faa 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 012cea19bb0..04bd0871073 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 e4f377a1217..8a6da52d762 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 e44fb064813..68db2e36d45 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 b4fc32947dd..e7564467a81 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/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 48e068714f5..44899225691 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 67b25be8eb9..8e85c0a8c02 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -243,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())) { @@ -598,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; } @@ -1168,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); } @@ -1968,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); @@ -1979,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); @@ -2185,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)); } @@ -2362,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); @@ -2401,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 16a74cd7ea9..dde80806912 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 a5a67686f2c..37dfe6fcc3d 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 6d8f67edb9b..f353ce34b74 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 51038d764bb..71224bbff4c 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/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index b1b1f1d6056..358434938f2 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 3189415a6c5..7548a97ced8 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -305,10 +305,6 @@ public: /* 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 a42576b562d..400d69ad510 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 d0c0e4d5f73..81307c4acab 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -605,7 +605,7 @@ protected: // 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 f865380fdb7..77fae3becb5 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/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index 7a2c8d52969..a042a390925 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 89ee69242c9..d320e17fafb 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/compilerWarnings.hpp b/src/hotspot/share/utilities/compilerWarnings.hpp index e2c9fc289f6..bf5ca5b4893 100644 --- a/src/hotspot/share/utilities/compilerWarnings.hpp +++ b/src/hotspot/share/utilities/compilerWarnings.hpp @@ -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 diff --git a/src/hotspot/share/utilities/compilerWarnings_gcc.hpp b/src/hotspot/share/utilities/compilerWarnings_gcc.hpp index 863cc512cca..c10650598cd 100644 --- a/src/hotspot/share/utilities/compilerWarnings_gcc.hpp +++ b/src/hotspot/share/utilities/compilerWarnings_gcc.hpp @@ -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) diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 9413f1f72d7..abe3d6757b5 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/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index 724797eb9f3..a3ad480f50c 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/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 085939b0131..cc5f3ebb291 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -269,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); diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 31e797fc192..86b7ed5f917 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 @@ public: // 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 5c3cfaa0dd5..a5caf316aa3 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/java.base/aix/native/libsyslookup/syslookup.c b/src/java.base/aix/native/libsyslookup/syslookup.c index 66bd50e01a0..95aff2d4300 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/java/io/FilePermission.java b/src/java.base/share/classes/java/io/FilePermission.java index aa6f11e00ee..883f0410b6d 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.StringJoiner; 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 @@ public final class FilePermission extends Permission implements Serializable { } } - 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 @@ public final class FilePermission extends Permission implements Serializable { if (mask == NONE) throw new IllegalArgumentException("invalid actions mask"); - if (FilePermCompat.nb) { + if (nb) { String name = getName(); if (name == null) @@ -567,7 +583,7 @@ public final class FilePermission extends Permission implements Serializable { 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 final class FilePermission extends Permission implements Serializable { 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 final class FilePermission extends Permission implements Serializable { */ @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/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index c2f90e22802..b8d209b8a6e 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.DoubleToDecimal; 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 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence */ @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 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence * 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 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence } 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 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence 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 aa64d799b66..4c24e98a549 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 final class Boolean implements java.io.Serializable, * {@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 5835f65366f..accd448a0cd 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 final class Byte extends Number implements Comparable, Constable { * {@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 final class Byte extends Number implements Comparable, Constable { * {@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 4347a3bc8aa..a439a90761d 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -9232,7 +9232,7 @@ class Character implements java.io.Serializable, Comparable, Constabl * {@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; } diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index d86baaac362..8be5ae8fa15 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 final class Class implements java.io.Serializable, 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 final class Class implements java.io.Serializable, * 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 b890ba51651..6082bc53297 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 @@ public abstract class ClassLoader { */ 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 54be998cac5..5ab6dce080b 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 final class Double extends Number * {@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 final class Double extends Number * {@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 746d90db81e..4344d9657b4 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 final class Float extends Number * {@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 final class Float extends Number * 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 final class Float extends Number * {@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/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 99b056ec18a..1350a66b66c 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 final class Integer extends Number * {@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 final class Integer extends Number * {@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 c401fcd8027..3093f37e99a 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 @@ public final class Long extends Number * {@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 final class Long extends Number * {@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 dcc92d012de..065e1ac4620 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.lang.reflect.AnnotatedElement; 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 final class Module implements AnnotatedElement { 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 @@ public final class Module implements AnnotatedElement { 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 @@ public final class Module implements AnnotatedElement { * 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 @@ public final class Module implements AnnotatedElement { * 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 @@ public final class Module implements AnnotatedElement { * 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 @@ public final class Module implements AnnotatedElement { * 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 @@ public final class Module implements AnnotatedElement { } } - // 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()); - } 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); + 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 { + // 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 57e88442b27..f0ae8b28e45 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 final class Short extends Number implements Comparable, Constable * {@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 final class Short extends Number implements Comparable, Constable * {@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 00000000000..b12d6f5e921 --- /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/System.java b/src/java.base/share/classes/java/lang/System.java index 903f6e34e2e..a60958f6f82 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 @@ public final class 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 final class System { * {@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 final class System { * 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 final class System { 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 final class System { 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 5017838d68e..108c47c8089 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 @@ public sealed interface ClassEntry * 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 5da36502678..2f56ce2e6ab 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 @@ public sealed interface MethodTypeEntry /** * {@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 fd920aa1231..169927868b6 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 ec56d0a4870..19f4560b5e6 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 8a0bbb4b015..b49e74df404 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 1d885051b2b..81a4e973d3f 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 6e7aae9f72b..8f1cab22b86 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 @@ public sealed interface Linker permits AbstractLinker { * @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 0a943d253af..ad6387e8c9e 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 @@ sealed class DirectMethodHandle extends MethodHandle { 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 @@ sealed class DirectMethodHandle extends MethodHandle { 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 @@ sealed class DirectMethodHandle extends MethodHandle { // 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 @@ sealed class DirectMethodHandle extends MethodHandle { 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 @@ sealed class DirectMethodHandle extends MethodHandle { return lform; } - private static final Wrapper[] ALL_WRAPPERS = Wrapper.values(); + private static final @Stable 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; - } + // 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 @@ sealed class DirectMethodHandle extends MethodHandle { 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 @@ sealed class DirectMethodHandle extends MethodHandle { 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 @@ sealed class DirectMethodHandle extends MethodHandle { } 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 e52438544ae..561f9122398 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 @@ class GenerateJLIClassesHelper { 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 f5998c46aa1..b3d2ff2c880 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 @@ class InvokerBytecodeGenerator { 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 7ba66a473fe..c1a07d8d0de 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 @@ class LambdaForm { 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 6a25bf9c0a8..471de5aa48f 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.LambdaForm.BasicType.*; 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 @@ class LambdaFormEditor { * 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 @@ class LambdaFormEditor { } 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 35fd26331b1..918d1b10791 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 @@ final class MemberName implements Member, Cloneable { } @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 9df7d25258d..0db7a6a8ddb 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 @@ class MethodHandleNatives { 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/MethodType.java b/src/java.base/share/classes/java/lang/invoke/MethodType.java index 5e1c0f8581c..3d15ce68710 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.lang.constant.MethodTypeDesc; 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 java.util.concurrent.ConcurrentHashMap; 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 @@ class MethodType 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 @@ class MethodType } 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 @@ s.writeObject(this.parameterArray()); 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 8bbc4dd5f72..d5272337585 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 sun.invoke.util.Wrapper; 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 @@ final class MethodTypeForm { 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 @@ final class MethodTypeForm { 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 @@ final class MethodTypeForm { 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 @@ final class MethodTypeForm { * 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 @@ final class MethodTypeForm { 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 13ba76e5dd2..e109b974adc 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 abstract sealed class Reference */ 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 @@ public abstract sealed class Reference 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 @@ public abstract sealed class Reference 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 07110a19fa1..4dcaf796e25 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 @@ public class ReferenceQueue { } } - 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 @@ public class ReferenceQueue { // 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 @@ public class ReferenceQueue { 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 @@ public class ReferenceQueue { // 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 bc6b20be9a8..4314c8c410d 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 @@ import static java.util.Map.entry; */ @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 enum AccessFlag { * 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 enum AccessFlag { * 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 enum AccessFlag { * {@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 enum AccessFlag { */ 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 enum AccessFlag { * 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 enum AccessFlag { * @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 enum AccessFlag { * @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 enum AccessFlag { * @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 enum AccessFlag { * @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 enum AccessFlag { */ 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 enum AccessFlag { * 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 enum AccessFlag { * @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 enum AccessFlag { * @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 enum AccessFlag { * @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 enum AccessFlag { 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 enum AccessFlag { } /** - * {@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 AccessFlag { 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 AccessFlag { // 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 b2978d73c8b..2f6f6cca89f 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 @@ public abstract sealed class Executable extends AccessibleObject */ @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 bffa211fe12..e26d8b03ff8 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 @@ class Field extends AccessibleObject implements Member { */ @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 1b7bc3bf65b..624f32b4e04 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 @@ public final class Modifier { * @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 final class Modifier { * @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 final class Modifier { * @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 final class Modifier { * @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 final class Modifier { * @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 final class Modifier { * @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 eac02001294..b8a57a9790b 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 final class Parameter implements AnnotatedElement { * @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/time/Clock.java b/src/java.base/share/classes/java/time/Clock.java index 731a117fcea..7fab5d27fb3 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 abstract class Clock implements InstantSource { } @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 d640121f32d..88d49fa9e45 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 final class Duration */ @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 82ff18c1421..1c7a41d7f0e 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 final class Instant */ @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 13478530301..016bdab5394 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 final class LocalDate */ @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 a79ecebe0c2..ca226b70d24 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 @@ abstract class ChronoLocalDateImpl @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 0253dbc7e1a..43e8db55d7f 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 @@ import java.util.stream.StreamSupport; * 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 205a6be6f89..9becf167176 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.BiConsumer; 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 @@ class ImmutableCollections { 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 @@ class ImmutableCollections { } } + 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 @@ class ImmutableCollections { 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 @@ class ImmutableCollections { } 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 @@ class ImmutableCollections { } 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 @@ class ImmutableCollections { } } + @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 @@ class ImmutableCollections { // ---------- 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 @@ class ImmutableCollections { } // 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 @@ class ImmutableCollections { * @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 @@ class ImmutableCollections { 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 28468165fab..14f3fe13918 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 final class Locale implements Cloneable, Serializable { 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 21128fa980d..e19dde6383e 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 final class Optional { * @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/javax/crypto/spec/RC2ParameterSpec.java b/src/java.base/share/classes/javax/crypto/spec/RC2ParameterSpec.java index e37e3872ff2..0e664510324 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 class RC2ParameterSpec implements AlgorithmParameterSpec { */ 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 8edeb731cee..20b6cd23682 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 f88d57521ac..cac8785b158 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 3ec9d6d4bae..bc955a76abb 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 class SharedSecrets { 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 3e50773d59e..bf558ddef16 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 java.util.Arrays; 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 @@ public abstract sealed class AbstractPoolEntry { 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 abstract sealed class AbstractPoolEntry { 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 abstract sealed class AbstractPoolEntry { 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 abstract sealed class AbstractPoolEntry { 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 abstract sealed class AbstractPoolEntry { if (hash != 0) return hash; - return this.hash = hashClassFromUtf8(isArrayDescriptor(ref1), ref1); + return this.hash = hashClassFromUtf8(ref1.mayBeArrayDescriptor(), ref1); } } @@ -596,6 +688,11 @@ public abstract sealed class AbstractPoolEntry { 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 abstract sealed class AbstractPoolEntry { 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 abstract sealed class AbstractPoolEntry { 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 abstract sealed class AbstractPoolEntry { 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/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index 6dc62511639..1e12969f204 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/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index 5ba81fc2927..2ea6fa943a7 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 @@ public final class SplitConstantPool implements ConstantPoolBuilder { @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/SystemLookup.java b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java index bf5371d43a0..640c7580d15 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 @@ public final class SystemLookup implements SymbolLookup { 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 @@ public final class SystemLookup implements SymbolLookup { 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 @@ public final class SystemLookup implements SymbolLookup { 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 @@ public final class SystemLookup implements SymbolLookup { }; } - /* - * Returns the path of the given library name from JDK - */ - private static Path jdkLibraryPath(String name) { - Path javahome = Path.of(StaticProperty.javaHome()); - String lib = OperatingSystem.isWindows() ? "bin" : "lib"; - String libname = System.mapLibraryName(name); - return javahome.resolve(lib).resolve(libname); + private static SymbolLookup stdLibLookup(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 19b235963a3..401d3e9a9c5 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 abstract sealed class AbstractLinker implements Linker permits LinuxAArch 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 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch 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 7fb62c56bf1..d95e713541f 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 jdk.internal.util.OperatingSystem; 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 5ca410f40e2..4ada4b23417 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 @@ package jdk.internal.foreign.abi; 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 class LinkerOptions { 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 abcc6027919..3940f996e0d 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/VMStorage.java b/src/java.base/share/classes/jdk/internal/foreign/abi/VMStorage.java index 1237650e026..0be1675ccd8 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 @@ package jdk.internal.foreign.abi; * @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 record VMStorage(byte type, 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/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index deb786b42bd..6455664b985 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 @@ -76,10 +76,11 @@ public @interface PreviewFeature { 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 00000000000..88be2cea1b6 --- /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 00000000000..1b10593e5e8 --- /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 00000000000..8bb046a762d --- /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 00000000000..bdb40648db6 --- /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 00000000000..f6f33f9b1e8 --- /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 00000000000..88c80eb6b39 --- /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 f070738e0c7..94696551645 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 class CDS { 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/module/ModuleBootstrap.java b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java index 0a9044178a4..39c52f25514 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 @@ public final class ModuleBootstrap { 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 @@ public final class ModuleBootstrap { // --add-reads, --add-exports/--add-opens addExtraReads(bootLayer); - boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer); + addExtraExportsAndOpens(bootLayer); // add enable native access addEnableNativeAccess(bootLayer); @@ -722,15 +721,13 @@ public final class ModuleBootstrap { * 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 @@ public final class ModuleBootstrap { 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 ff3ffada22a..ed97937e39a 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 @@ public final class ModuleInfo { 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 20b390855c9..19be5e53798 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.ObjectStreamClass; 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 class ReflectionFactory { } } + 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 766aca20b21..150b9d752bb 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.Collectors; 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 final class ReferencedKeyMap implements Map { } } + @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 355af10aa7c..960b63b12d3 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 final class ReferencedKeySet extends AbstractSet { 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 26110a1dab3..5760f04831e 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 final class SystemProps { 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 final class SystemProps { @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 ed9f88685d6..de0bb83d39d 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 cbf30da2289..4a8ad79b50c 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 jdk.internal.misc.Unsafe; 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 @@ public class VectorSupport { /* ============================================================================ */ +// 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 @@ public class VectorSupport { 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 @@ public class VectorSupport { /* ============================================================================ */ + // 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 22f40d9cead..6775dda269a 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -272,6 +272,7 @@ module java.base { 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/security/ssl/SSLSessionImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java index 2e7d99ee293..566415e23c7 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 @@ package sun.security.ssl; 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 @@ final class SSLSessionImpl extends ExtendedSSLSession { /** * 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 @@ final class SSLSessionImpl extends ExtendedSSLSession { * 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 80029e73afb..a95b31583bb 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.text.MessageFormat; 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 @@ final class SignatureAlgorithmsExtension { 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 @@ final class SignatureAlgorithmsExtension { 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 @@ final class SignatureAlgorithmsExtension { "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 651c84a462b..00000000000 --- 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/validator/CamerfirmaTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/CamerfirmaTLSPolicy.java index 591a7a26c68..984f5a0ab47 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 @@ final class CamerfirmaTLSPolicy { 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 2763ac30ca7..dced9151d5b 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 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"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 b92255f770c..00000000000 --- 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 935eea9c212..00000000000 --- 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 b1feaf3dff7..725c1e30227 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 95fb6d9e7f9..774a7435e54 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 b1f543bfdb7..fa7ddca4156 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/native/libjava/java_props_md.c b/src/java.base/unix/native/libjava/java_props_md.c index 6db307088b0..1d5435d5229 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 275dd3795c5..9495faf81e5 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 c7ddce46a79..3216632c53d 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 1e98cb19ba4..9da55002b72 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 final class JRSUIConstants { @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 1d11a0a8d96..60ceb4893a8 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 abstract class AquaButtonBorder extends AquaBorder implements Border, UIR 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 063923ff807..17818d09121 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.LookAndFeel; 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 @@ public class AquaButtonUI extends BasicButtonUI implements Sizeable { // 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 @@ public class AquaButtonUI extends BasicButtonUI implements Sizeable { 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 class AquaButtonUI extends BasicButtonUI implements Sizeable { } } - class AquaButtonListener extends BasicButtonListener implements AncestorListener { + class AquaButtonListener extends BasicButtonListener { protected final AbstractButton b; public AquaButtonListener(final AbstractButton b) { @@ -658,27 +650,5 @@ public class AquaButtonUI extends BasicButtonUI implements Sizeable { } } } - - 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 2b4e1bb0867..6bd472b7605 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 @@ import com.apple.laf.AquaUtils.RecyclableSingletonFromDefaultConstructor; /** * 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 class AquaRootPaneUI extends BasicRootPaneUI implements AncestorListener, 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 class AquaRootPaneUI extends BasicRootPaneUI implements AncestorListener, } public void uninstallUI(final JComponent c) { - stopTimer(); c.removeAncestorListener(this); if (sUseScreenMenuBar) { @@ -161,73 +149,6 @@ public class AquaRootPaneUI extends BasicRootPaneUI implements AncestorListener, } } - /** - * 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 class AquaRootPaneUI extends BasicRootPaneUI implements AncestorListener, 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 3c09758e87f..0ca85104817 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 fe4431e3091..e11dabc7a10 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.List; 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 final class CGraphicsEnvironment extends SunGraphicsEnvironment { 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 93fac3fb22a..00000000000 --- 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 82adac72c03..e4afec42482 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.CGraphicsConfig; 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.pipe.hw.ContextCapabilities.*; 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 final class MTLGraphicsConfig extends CGraphicsConfig 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 ee9b0aec3a0..6022d516bf9 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.CGraphicsConfig; 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 final class CGLGraphicsConfig extends CGraphicsConfig 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/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index ddd6c8ec354..8be635a6c49 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 class CSS implements Serializable { 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 55dfc8db766..4bcd5f6b443 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.BufferedImage; 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 class SunVolatileImage extends VolatileImage 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 9f605eed4e3..9b86d469d98 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 abstract class SurfaceManager { } } + /** + * 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 abstract class SurfaceManager { } 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 abstract class SurfaceManager { 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 39b6d2e33cf..00000000000 --- 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 252644b7e49..a0904588019 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 @@ import sun.java2d.pipe.hw.AccelGraphicsConfig; * 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/native/libsplashscreen/splashscreen_gif.c b/src/java.desktop/share/native/libsplashscreen/splashscreen_gif.c index 3654c677493..cbdad61f78e 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 ba06574cdd7..9c7a7d5c376 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 java.awt.image.WritableRaster; 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.RenderLoops; 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 @@ import sun.java2d.x11.X11SurfaceData; * @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 class X11GraphicsConfig extends GraphicsConfiguration } 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 39ecb13c6a4..a7ec9ccc9fd 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 final class X11GraphicsDevice extends GraphicsDevice } 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 82490e88a99..c24e311017e 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 java.util.Map; 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 @@ public final class X11GraphicsEnvironment extends SunGraphicsEnvironment { 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 5ab78ee39ee..00000000000 --- 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 96b0dc49183..2ea076f8051 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.X11GraphicsEnvironment; 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 final class GLXGraphicsConfig 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 fb2c8ad4801..43f17e11ae1 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.X11GraphicsConfig; 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 class XRGraphicsConfig extends X11GraphicsConfig implements 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/windows/classes/sun/awt/Win32GraphicsDevice.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java index 865fe480853..fedc7f38980 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 { diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsEnvironment.java index 09768148f3d..2e11d551059 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 java.util.ListIterator; 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()) { 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 4abb8c823f6..00000000000 --- a/src/java.desktop/windows/classes/sun/java2d/WindowsSurfaceManagerFactory.java +++ /dev/null @@ -1,66 +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 final 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. - */ - @Override - 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/D3DGraphicsConfig.java b/src/java.desktop/windows/classes/sun/java2d/d3d/D3DGraphicsConfig.java index aab6b720d6a..8b001317dca 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 java.awt.image.VolatileImage; 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 @@ import static sun.java2d.d3d.D3DContext.D3DContextCaps.*; public final class D3DGraphicsConfig extends Win32GraphicsConfig - implements AccelGraphicsConfig + implements AccelGraphicsConfig, SurfaceManager.Factory { private static ImageCapabilities imageCaps = new D3DImageCaps(); @@ -307,4 +308,10 @@ public final class D3DGraphicsConfig 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/opengl/WGLGraphicsConfig.java b/src/java.desktop/windows/classes/sun/java2d/opengl/WGLGraphicsConfig.java index c6658bf2914..40d2af5c12a 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.Win32GraphicsConfig; 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(); @@ -432,4 +434,10 @@ public final class WGLGraphicsConfig public ContextCapabilities getContextCapabilities() { return oglCaps; } + + @Override + public VolatileSurfaceManager createVolatileManager(SunVolatileImage image, + Object context) { + return new WGLVolatileSurfaceManager(image, context); + } } 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 6fe57d28d2f..9f2a7e78951 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 @@ import javax.xml.transform.TransformerException; * 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 class FuncExtFunction extends Function */ 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 6d9d882f075..5622a0e3db0 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 @@ import jdk.xml.internal.JdkXmlFeatures; /** * * @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 class JAXPExtensionsProvider implements ExtensionsProvider { //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 class JAXPExtensionsProvider implements ExtensionsProvider { 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 e81523b5607..a92090900fa 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 @@ import org.xml.sax.SAXException; * 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 @@ class XPathImplUtil { 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 433a0f85389..716a4bcf63b 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 @@ import java.util.ListResourceBundle; * 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 static final String ER_IGNORABLE_WHITESPACE_NOT_HANDLED = 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 static final String ER_IGNORABLE_WHITESPACE_NOT_HANDLED = //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 a12fd3e8f45..1b4f345eb72 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 83a6c3eb87b..ac4dc6258ac 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 7927930a526..ba2ed8ce04b 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 @@ -145,9 +145,7 @@ public class Preview { // 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)); } /** @@ -219,8 +217,6 @@ public class Preview { 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 f1c25d032d0..eed0ceaade0 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 @@ -267,8 +267,8 @@ public enum Source { 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/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index b5f94f903b0..d894941e14f 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 @@ -1195,11 +1195,6 @@ public class ClassReader { 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/JavadocTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavadocTokenizer.java index 5dcb4cc36d4..a2925c69d94 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 @@ public class JavadocTokenizer extends JavaTokenizer { 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/util/RichDiagnosticFormatter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java index fa81ce7ba75..512fa0e07e3 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 @@ public class RichDiagnosticFormatter extends 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 class RichDiagnosticFormatter extends @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.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index ed8d273ff37..2b1cc879e66 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 @@ public abstract class ByteVector extends AbstractVector { 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 @@ public abstract class ByteVector extends AbstractVector { 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 @@ public abstract class ByteVector extends AbstractVector { UN_IMPL.find(op, opc, ByteVector::unaryOperations)); } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, ByteVector.class); @@ -824,6 +825,7 @@ public abstract class ByteVector extends AbstractVector { = 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 @@ public abstract class ByteVector extends AbstractVector { 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 00000000000..c0d8ef03ada --- /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 5fbf02f87bd..48446c6fa01 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 @@ public abstract class DoubleVector extends AbstractVector { 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 @@ public abstract class DoubleVector extends AbstractVector { 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 @@ public abstract class DoubleVector extends AbstractVector { 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 @@ public abstract class DoubleVector extends AbstractVector { = 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 @@ public abstract class DoubleVector extends AbstractVector { = 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 @@ public abstract class DoubleVector extends AbstractVector { 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 26fbe64742d..1e0829a3b1c 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 @@ public abstract class FloatVector extends AbstractVector { 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 @@ public abstract class FloatVector extends AbstractVector { 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 @@ public abstract class FloatVector extends AbstractVector { 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 @@ public abstract class FloatVector extends AbstractVector { = 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 @@ public abstract class FloatVector extends AbstractVector { = 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 @@ public abstract class FloatVector extends AbstractVector { 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 076a66ed6a5..b691527bec6 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 @@ public abstract class IntVector extends AbstractVector { 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 @@ public abstract class IntVector extends AbstractVector { 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 @@ public abstract class IntVector extends AbstractVector { UN_IMPL.find(op, opc, IntVector::unaryOperations)); } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, IntVector.class); @@ -824,6 +825,7 @@ public abstract class IntVector extends AbstractVector { = 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 @@ public abstract class IntVector extends AbstractVector { 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 21903aa6794..9e4dcd23d67 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 @@ public abstract class LongVector extends AbstractVector { 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 @@ public abstract class LongVector extends AbstractVector { 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 @@ public abstract class LongVector extends AbstractVector { UN_IMPL.find(op, opc, LongVector::unaryOperations)); } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, LongVector.class); @@ -782,6 +783,7 @@ public abstract class LongVector extends AbstractVector { = 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 @@ public abstract class LongVector extends AbstractVector { 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 0bb97da8244..46df27309ae 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 @@ public abstract class ShortVector extends AbstractVector { 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 @@ public abstract class ShortVector extends AbstractVector { 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 @@ public abstract class ShortVector extends AbstractVector { UN_IMPL.find(op, opc, ShortVector::unaryOperations)); } + private static final ImplCache>> UN_IMPL = new ImplCache<>(Unary.class, ShortVector.class); @@ -824,6 +825,7 @@ public abstract class ShortVector extends AbstractVector { = 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 @@ public abstract class ShortVector extends AbstractVector { 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 00000000000..8562d4b5d7a --- /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 00000000000..4729235e2d9 --- /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 c5fe6dd4441..0e231bd5174 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.annotation.Stable; 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 @@ public abstract class VectorOperators { 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 @@ public abstract class VectorOperators { /** 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 @@ public abstract class VectorOperators { /** 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 abstract class VectorOperators { 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 8084cc307e8..6d9db65a1ba 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.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 2a8959e5d7f..dbd83b314c9 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 class AArch64 extends Architecture { 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 b768aee081f..c6f583de361 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 @@ public class HtmlDoclet extends AbstractDoclet { 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 448a68fd5a1..34ef40ed650 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 abstract class HtmlDocletWriter { .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 10483fb4a4c..faf1f8c897d 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 class HtmlOptions extends BaseOptions { } }, + 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 @@ public class HtmlOptions extends BaseOptions { 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 7170c6bcc8a..0c54fc1e29c 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 class Head extends Content { 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 @@ public class Head extends Content { 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 00000000000..145c1c7f5d2 --- /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 00000000000..efa14c8c306 --- /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