Go to Debian
Package Search , search for the following packages for your
diff --git a/doc/building.md b/doc/building.md
index b626027f101..d653d36eb55 100644
--- a/doc/building.md
+++ b/doc/building.md
@@ -1178,10 +1178,8 @@ Note that alsa is needed even if you only want to build a headless JDK.
#### X11
-You will need X11 libraries suitable for your *target* system. In most cases,
-using Debian's pre-built libraries work fine.
-
-Note that X11 is needed even if you only want to build a headless JDK.
+When not building a headless JDK, you will need X11 libraries suitable for your
+*target* system. In most cases, using Debian's pre-built libraries work fine.
* Go to [Debian Package Search](https://www.debian.org/distrib/packages),
search for the following packages for your *target* system, and download them
diff --git a/doc/hotspot-style.html b/doc/hotspot-style.html
index 362245cd00a..c7126622c7d 100644
--- a/doc/hotspot-style.html
+++ b/doc/hotspot-style.html
@@ -965,9 +965,8 @@ rather than NULL. See the paper for reasons to avoid
NULL.
Don't use (constant expression or literal) 0 for pointers. Note that
C++14 removed non-literal 0 constants from null pointer
-constants , though some compilers continue to treat them as such.
-For historical reasons there may be lingering uses of 0 as a
-pointer.
+constants, though some compilers continue to treat them as
+such.
<atomic>
Do not use facilities provided by the <atomic>
header ( echo#s"##trala#a"
+Quote = $(subst :=,$(AssignQ),$(subst $(SPACE),$(SpaceQ),$(subst $(Q),$(QQ),$1)))
+
+# $(call Unquote,echo#s"##trala#a") -> echo "#trala:="
+Unquote = $(subst $(QQ),$(Q),$(subst $(SpaceQ),$(SPACE),$(subst $(AssignQ),:=,$1)))
+
+# $(call QuoteAppend,name,some value) -> name#+some#svalue
+# $(call QuoteAppend,bad+=name,some value) -> error
+QuoteAppend = $(if $(findstring +=,$1),$(error you can not have += in a variable name: "$1"),$(call Quote,$1)$(AppendQ)$(call Quote,$2))
+
+# $(call UnquoteAppendIndex,name#+some#svalue,1) -> name
+# $(call UnquoteAppendIndex,name#+some#svalue,2) -> some value
+UnquoteAppendIndex = $(call Unquote,$(word $2,$(subst $(AppendQ),$(SPACE),$1)))
+
+# $(call FilterFiles,dir,%.cpp) -> file1.cpp file2.cpp (without path)
+FilterFiles = $(filter $2,$(notdir $(call FindFiles,$1)))
+
+# $(call Unpack module_,file1.cpp_CXXFLAGS#+-Wconversion file2.cpp_CXXFLAGS#+-Wconversion) -> module_file1.cpp_CXXFLAGS += -Wconversion
+# module_file2.cpp_CXXFLAGS += -Wconversion
+Unpack = $(foreach pair,$2,$1$(call UnquoteAppendIndex,$(pair),1) += $(call UnquoteAppendIndex,$(pair),2)$(NEWLINE))
+
+# This macro takes four arguments:
+# $1: directory where to find files (striped), example: $(TOPDIR)/src/hotspot/share/gc/g1
+# $2: filter to match what to keep (striped), example: g1Concurrent%.cpp
+# $3: what flags to override (striped), example: _CXXFLAGS
+# $4: what value to append to the flag (striped), example: $(CFLAGS_CONVERSION_WARNINGS)
+#
+# The result will be a quoted string that can be unpacked to a list of
+# variable appendings (see macro Unpack above). You do not need to take
+# care of unpacking, it is done in NamedParamsMacroTemplate.
+#
+# This feature should only be used for warnings that we want to
+# incrementally add to the rest of the code base.
+#
+# $(call ExtendFlags,dir,%.cpp,_CXXFLAGS,-Wconversion) -> file1.cpp_CXXFLAGS#+-Wconversion file2.cpp_CXXFLAGS#+-Wconversion
+ExtendFlags = $(foreach file,$(call FilterFiles,$(strip $1),$(strip $2)),$(call QuoteAppend,$(file)$(strip $3),$(strip $4)))
+
################################################################################
MAX_PARAMS := 96
@@ -166,7 +226,10 @@ define NamedParamsMacroTemplate
Too many named arguments to macro, please update MAX_PARAMS in MakeBase.gmk))
# Iterate over 2 3 4... and evaluate the named parameters with $1_ as prefix
$(foreach i, $(PARAM_SEQUENCE), $(if $(strip $($i)), \
- $(strip $1)_$(strip $(call EscapeHash, $(call DoubleDollar, $($i))))$(NEWLINE)))
+ $(if $(findstring :=,$($i)), \
+ $(strip $1)_$(strip $(call EscapeHash, $(call DoubleDollar, $($i))))$(NEWLINE), \
+ $(call Unpack,$(strip $1)_,$($i)))))
+
# Debug print all named parameter names and values
$(if $(findstring $(LOG_LEVEL), trace), \
$(info $0 $(strip $1) $(foreach i, $(PARAM_SEQUENCE), \
diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf
index bd73e909062..ebfc9191535 100644
--- a/make/conf/github-actions.conf
+++ b/make/conf/github-actions.conf
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
# Versions and download locations for dependencies used by GitHub Actions (GHA)
GTEST_VERSION=1.14.0
-JTREG_VERSION=8.1+1
+JTREG_VERSION=8.2.1+1
LINUX_X64_BOOT_JDK_EXT=tar.gz
LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_linux-x64_bin.tar.gz
diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js
index 93aeebc0dd6..76a94b7789e 100644
--- a/make/conf/jib-profiles.js
+++ b/make/conf/jib-profiles.js
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1174,9 +1174,9 @@ var getJibProfilesDependencies = function (input, common) {
jtreg: {
server: "jpg",
product: "jtreg",
- version: "8.1",
+ version: "8.2.1",
build_number: "1",
- file: "bundles/jtreg-8.1+1.zip",
+ file: "bundles/jtreg-8.2.1+1.zip",
environment_name: "JT_HOME",
environment_path: input.get("jtreg", "home_path") + "/bin",
configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"),
diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk
index db77f2f3f74..74c6d861777 100644
--- a/make/devkit/Tools.gmk
+++ b/make/devkit/Tools.gmk
@@ -78,7 +78,7 @@ else ifeq ($(BASE_OS), Fedora)
endif
BASE_URL := http://fedora.riscv.rocks/repos-dist/f$(BASE_OS_VERSION)/latest/$(ARCH)/Packages/
else
- LATEST_ARCHIVED_OS_VERSION := 36
+ LATEST_ARCHIVED_OS_VERSION := 41
ifeq ($(filter aarch64 armhfp x86_64, $(ARCH)), )
FEDORA_TYPE := fedora-secondary
else
diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk
index 327014b1e9d..4b21d481049 100644
--- a/make/hotspot/lib/CompileGtest.gmk
+++ b/make/hotspot/lib/CompileGtest.gmk
@@ -63,6 +63,10 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBGTEST, \
unused-result zero-as-null-pointer-constant, \
DISABLED_WARNINGS_clang := format-nonliteral undef unused-result \
zero-as-null-pointer-constant, \
+ $(comment Disable deprecated-declarations warnings to workaround) \
+ $(comment clang18+glibc12 bug https://github.com/llvm/llvm-project/issues/76515) \
+ $(comment until the clang bug has been fixed) \
+ DISABLED_WARNINGS_clang_gtest-all.cc := deprecated-declarations, \
DISABLED_WARNINGS_microsoft := 4530, \
DEFAULT_CFLAGS := false, \
CFLAGS := $(JVM_CFLAGS) \
diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk
index 39a549b7db0..f41693e05fb 100644
--- a/make/hotspot/lib/CompileJvm.gmk
+++ b/make/hotspot/lib/CompileJvm.gmk
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -105,7 +105,7 @@ DISABLED_WARNINGS_gcc := array-bounds comment delete-non-virtual-dtor \
DISABLED_WARNINGS_clang := delete-non-abstract-non-virtual-dtor \
invalid-offsetof missing-braces \
sometimes-uninitialized unknown-pragmas unused-but-set-variable \
- unused-function unused-local-typedef unused-private-field unused-variable
+ unused-local-typedef unused-private-field unused-variable
ifneq ($(DEBUG_LEVEL), release)
# Assert macro gives warning
@@ -190,6 +190,8 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \
abstract_vm_version.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \
arguments.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \
whitebox.cpp_CXXFLAGS := $(CFLAGS_SHIP_DEBUGINFO), \
+ $(call ExtendFlags, $(TOPDIR)/src/hotspot/share/gc/g1, \
+ g1Numa.cpp, _CXXFLAGS, $(CFLAGS_CONVERSION_WARNINGS)), \
DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc), \
DISABLED_WARNINGS_gcc_ad_$(HOTSPOT_TARGET_CPU_ARCH).cpp := nonnull, \
DISABLED_WARNINGS_gcc_bytecodeInterpreter.cpp := unused-label, \
diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java
index 55dd6a8d6ad..ab878a4d2a5 100644
--- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java
+++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -293,8 +293,10 @@ public class CLDRConverter {
bundleGenerator = new ResourceBundleGenerator();
// Parse data independent of locales
- parseSupplemental();
+ // parseBCP47() must precede parseSupplemental(). The latter depends
+ // on IANA alias map, which is produced by the former.
parseBCP47();
+ parseSupplemental();
// rules maps
pluralRules = generateRules(handlerPlurals);
@@ -536,6 +538,12 @@ public class CLDRConverter {
// canonical tz name map
// alias -> primary
+ //
+ // Note that CLDR meta zones do not necessarily align with IANA's
+ // current time zone identifiers. For example, the CLDR "India"
+ // meta zone maps to "Asia/Calcutta", whereas IANA now uses
+ // "Asia/Kolkata" for the zone. Accordingly, "canonical" here is
+ // defined in terms of CLDR's zone mappings.
handlerTimeZone.getData().forEach((k, v) -> {
String[] ids = ((String)v).split("\\s");
for (int i = 1; i < ids.length; i++) {
diff --git a/make/jdk/src/classes/build/tools/cldrconverter/TimeZoneParseHandler.java b/make/jdk/src/classes/build/tools/cldrconverter/TimeZoneParseHandler.java
index 66e94e5ca06..8203a9f0b91 100644
--- a/make/jdk/src/classes/build/tools/cldrconverter/TimeZoneParseHandler.java
+++ b/make/jdk/src/classes/build/tools/cldrconverter/TimeZoneParseHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,9 @@ package build.tools.cldrconverter;
import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
@@ -40,6 +43,10 @@ import org.xml.sax.SAXException;
class TimeZoneParseHandler extends AbstractLDMLHandler {
private static final String PREF_PREFIX = "preferred:";
+ // CLDR aliases to IANA ids map. The initial capacity is estimated
+ // from the number of aliases in timezone.xml as of CLDR v48
+ private final Map ianaAliasMap = HashMap.newHashMap(32);
+
@Override
public InputSource resolveEntity(String publicID, String systemID) throws IOException, SAXException {
// avoid HTTP traffic to unicode.org
@@ -61,7 +68,16 @@ class TimeZoneParseHandler extends AbstractLDMLHandler {
put(attributes.getValue("name"), PREF_PREFIX + preferred);
}
} else {
- put(attributes.getValue("name"), attributes.getValue("alias"));
+ var alias = attributes.getValue("alias");
+ var iana = attributes.getValue("iana");
+ if (iana != null) {
+ for (var a : alias.split("\\s+")) {
+ if (!a.equals(iana)) {
+ ianaAliasMap.put(a, iana);
+ }
+ }
+ }
+ put(attributes.getValue("name"), alias);
}
}
break;
@@ -80,4 +96,8 @@ class TimeZoneParseHandler extends AbstractLDMLHandler {
.forEach(e -> map.put(e.getKey(),
map.get(e.getValue().toString().substring(PREF_PREFIX.length()))));
}
+
+ Map getIanaAliasMap() {
+ return ianaAliasMap;
+ }
}
diff --git a/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java b/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java
index a584358f0cb..343e143b6ad 100644
--- a/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java
+++ b/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -56,6 +56,7 @@ class WinZonesParseHandler extends AbstractLDMLHandler {
String zoneName = attributes.getValue("other");
String territory = attributes.getValue("territory");
String javatz = attributes.getValue("type").replaceFirst("\\s.*", "");
+ javatz = CLDRConverter.handlerTimeZone.getIanaAliasMap().getOrDefault(javatz, javatz);
put(zoneName + ":" + territory, javatz);
pushIgnoredContainer(qName);
break;
diff --git a/make/modules/java.base/Copy.gmk b/make/modules/java.base/Copy.gmk
index b8c1f2c05fa..43b9db651e0 100644
--- a/make/modules/java.base/Copy.gmk
+++ b/make/modules/java.base/Copy.gmk
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -172,6 +172,10 @@ ifeq ($(USE_EXTERNAL_LIBZ), true)
LEGAL_EXCLUDES += zlib.md
endif
+ifneq ($(TOOLCHAIN_TYPE), gcc)
+ LEGAL_EXCLUDES += gcc.md
+endif
+
$(eval $(call SetupCopyLegalFiles, COPY_LEGAL, \
EXCLUDES := $(LEGAL_EXCLUDES), \
))
diff --git a/make/modules/java.desktop/lib/AwtLibraries.gmk b/make/modules/java.desktop/lib/AwtLibraries.gmk
index 463e09e12dc..8b6b50b9e62 100644
--- a/make/modules/java.desktop/lib/AwtLibraries.gmk
+++ b/make/modules/java.desktop/lib/AwtLibraries.gmk
@@ -88,6 +88,10 @@ LIBAWT_EXTRA_HEADER_DIRS := \
LIBAWT_CFLAGS := -D__MEDIALIB_OLD_NAMES -D__USE_J2D_NAMES -DMLIB_NO_LIBSUNMATH
+ifeq ($(ENABLE_HEADLESS_ONLY), true)
+ LIBAWT_CFLAGS += -DHEADLESS
+endif
+
ifeq ($(call isTargetOs, windows), true)
LIBAWT_CFLAGS += -EHsc -DUNICODE -D_UNICODE -DMLIB_OS64BIT
LIBAWT_RCFLAGS ?= -I$(TOPDIR)/src/java.base/windows/native/launcher/icons
@@ -167,11 +171,18 @@ ifeq ($(call isTargetOs, windows macosx), false)
$(TOPDIR)/src/$(MODULE)/$(OPENJDK_TARGET_OS_TYPE)/native/common/awt \
#
+ LIBAWT_HEADLESS_EXCLUDE_FILES := \
+ GLXGraphicsConfig.c \
+ GLXSurfaceData.c \
+ X11PMBlitLoops.c \
+ X11Renderer.c \
+ X11SurfaceData.c \
+ #
+
LIBAWT_HEADLESS_EXTRA_HEADER_DIRS := \
$(LIBAWT_DEFAULT_HEADER_DIRS) \
common/awt/debug \
common/font \
- common/java2d/opengl \
java.base:libjvm \
#
@@ -191,7 +202,8 @@ ifeq ($(call isTargetOs, windows macosx), false)
$(eval $(call SetupJdkLibrary, BUILD_LIBAWT_HEADLESS, \
NAME := awt_headless, \
EXTRA_SRC := $(LIBAWT_HEADLESS_EXTRA_SRC), \
- EXCLUDES := medialib, \
+ EXCLUDES := medialib opengl, \
+ EXCLUDE_FILES := $(LIBAWT_HEADLESS_EXCLUDE_FILES), \
ONLY_EXPORTED := $(LIBAWT_HEADLESS_ONLY_EXPORTED), \
OPTIMIZATION := LOW, \
CFLAGS := -DHEADLESS=true $(CUPS_CFLAGS) $(FONTCONFIG_CFLAGS) \
diff --git a/make/modules/jdk.jpackage/Lib.gmk b/make/modules/jdk.jpackage/Lib.gmk
index 704436bbde6..86b11bdafee 100644
--- a/make/modules/jdk.jpackage/Lib.gmk
+++ b/make/modules/jdk.jpackage/Lib.gmk
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -54,7 +54,7 @@ $(eval $(call SetupJdkExecutable, BUILD_JPACKAGEAPPLAUNCHER, \
SRC := applauncher, \
EXTRA_SRC := common, \
INCLUDE_FILES := $(JPACKAGEAPPLAUNCHER_INCLUDE_FILES), \
- OPTIMIZATION := LOW, \
+ OPTIMIZATION := SIZE, \
DISABLED_WARNINGS_clang_JvmLauncherLib.c := format-nonliteral, \
DISABLED_WARNINGS_clang_LinuxPackage.c := format-nonliteral, \
DISABLED_WARNINGS_clang_Log.cpp := unused-const-variable, \
@@ -91,7 +91,7 @@ ifeq ($(call isTargetOs, linux), true)
common, \
EXCLUDE_FILES := LinuxLauncher.c LinuxPackage.c, \
LINK_TYPE := C++, \
- OPTIMIZATION := LOW, \
+ OPTIMIZATION := SIZE, \
DISABLED_WARNINGS_gcc_Log.cpp := unused-const-variable, \
DISABLED_WARNINGS_clang_JvmLauncherLib.c := format-nonliteral, \
DISABLED_WARNINGS_clang_tstrings.cpp := format-nonliteral, \
diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad
index a9ca91d9309..9734c6845ea 100644
--- a/src/hotspot/cpu/aarch64/aarch64.ad
+++ b/src/hotspot/cpu/aarch64/aarch64.ad
@@ -3403,11 +3403,13 @@ encode %{
} else if (rtype == relocInfo::metadata_type) {
__ mov_metadata(dst_reg, (Metadata*)con);
} else {
- assert(rtype == relocInfo::none, "unexpected reloc type");
+ assert(rtype == relocInfo::none || rtype == relocInfo::external_word_type, "unexpected reloc type");
+ // load fake address constants using a normal move
if (! __ is_valid_AArch64_address(con) ||
con < (address)(uintptr_t)os::vm_page_size()) {
__ mov(dst_reg, con);
} else {
+ // no reloc so just use adrp and add
uint64_t offset;
__ adrp(dst_reg, con, offset);
__ add(dst_reg, dst_reg, offset);
@@ -4535,6 +4537,18 @@ operand immP_1()
interface(CONST_INTER);
%}
+// AOT Runtime Constants Address
+operand immAOTRuntimeConstantsAddress()
+%{
+ // Check if the address is in the range of AOT Runtime Constants
+ predicate(AOTRuntimeConstants::contains((address)(n->get_ptr())));
+ match(ConP);
+
+ op_cost(0);
+ format %{ %}
+ interface(CONST_INTER);
+%}
+
// Float and Double operands
// Double Immediate
operand immD()
@@ -6898,6 +6912,20 @@ instruct loadConP1(iRegPNoSp dst, immP_1 con)
ins_pipe(ialu_imm);
%}
+instruct loadAOTRCAddress(iRegPNoSp dst, immAOTRuntimeConstantsAddress con)
+%{
+ match(Set dst con);
+
+ ins_cost(INSN_COST);
+ format %{ "adr $dst, $con\t# AOT Runtime Constants Address" %}
+
+ ins_encode %{
+ __ load_aotrc_address($dst$$Register, (address)$con$$constant);
+ %}
+
+ ins_pipe(ialu_imm);
+%}
+
// Load Narrow Pointer Constant
instruct loadConN(iRegNNoSp dst, immN con)
diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad
index 78ef121bd29..19f03d97a72 100644
--- a/src/hotspot/cpu/aarch64/aarch64_vector.ad
+++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+// Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
// Copyright (c) 2020, 2025, Arm Limited. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
@@ -201,6 +201,8 @@ source %{
case Op_XorReductionV:
case Op_MinReductionV:
case Op_MaxReductionV:
+ case Op_UMinReductionV:
+ case Op_UMaxReductionV:
// Reductions with less than 8 bytes vector length are
// not supported.
if (length_in_bytes < 8) {
@@ -383,6 +385,8 @@ source %{
return !VM_Version::use_neon_for_vector(length_in_bytes);
case Op_MinReductionV:
case Op_MaxReductionV:
+ case Op_UMinReductionV:
+ case Op_UMaxReductionV:
// For BYTE/SHORT/INT/FLOAT/DOUBLE types, we prefer using NEON
// instructions rather than SVE predicated instructions for
// better performance.
@@ -4218,6 +4222,224 @@ instruct reduce_minD_masked(vRegD dst, vRegD dsrc, vReg vsrc, pRegGov pg) %{
ins_pipe(pipe_slow);
%}
+// -------------------- Vector reduction unsigned min/max ----------------------
+
+// reduction uminI
+
+instruct reduce_uminI_neon(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc,
+ vReg tmp, rFlagsReg cr) %{
+ predicate(VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) &&
+ (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 (UMinReductionV isrc vsrc));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_uminI_neon $dst, $isrc, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this, $vsrc);
+ uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc);
+ __ neon_reduce_minmax_integral(this->ideal_Opcode(), $dst$$Register, bt,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ length_in_bytes, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct reduce_uminI_sve(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc,
+ vRegD tmp, rFlagsReg cr) %{
+ predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) &&
+ (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 (UMinReductionV isrc vsrc));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_uminI_sve $dst, $isrc, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ assert(UseSVE > 0, "must be sve");
+ BasicType bt = Matcher::vector_element_basic_type(this, $vsrc);
+ uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc);
+ assert(length_in_bytes == MaxVectorSize, "invalid vector length");
+ __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ ptrue, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+// reduction uminL
+
+instruct reduce_uminL_neon(iRegLNoSp dst, iRegL isrc, vReg vsrc, rFlagsReg cr) %{
+ predicate(UseSVE == 0 && Matcher::vector_element_basic_type(n->in(2)) == T_LONG);
+ match(Set dst (UMinReductionV isrc vsrc));
+ effect(TEMP_DEF dst, KILL cr);
+ format %{ "reduce_uminL_neon $dst, $isrc, $vsrc\t# 2L. KILL cr" %}
+ ins_encode %{
+ __ neon_reduce_minmax_integral(this->ideal_Opcode(), $dst$$Register, T_LONG,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ /* vector_length_in_bytes */ 16, fnoreg);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct reduce_uminL_sve(iRegLNoSp dst, iRegL isrc, vReg vsrc,
+ vRegD tmp, rFlagsReg cr) %{
+ predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n->in(2)) == T_LONG);
+ match(Set dst (UMinReductionV isrc vsrc));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_uminL_sve $dst, $isrc, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc);
+ assert(length_in_bytes == MaxVectorSize, "invalid vector length");
+ __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, T_LONG,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ ptrue, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+// reduction umin - predicated
+
+instruct reduce_uminI_masked(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc, pRegGov pg,
+ vRegD tmp, rFlagsReg cr) %{
+ predicate(UseSVE > 0 &&
+ (Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_BYTE ||
+ Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_SHORT ||
+ Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_INT));
+ match(Set dst (UMinReductionV (Binary isrc vsrc) pg));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_uminI_masked $dst, $isrc, $pg, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this, $vsrc);
+ __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ $pg$$PRegister, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct reduce_uminL_masked(iRegLNoSp dst, iRegL isrc, vReg vsrc, pRegGov pg,
+ vRegD tmp, rFlagsReg cr) %{
+ predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_LONG);
+ match(Set dst (UMinReductionV (Binary isrc vsrc) pg));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_uminL_masked $dst, $isrc, $pg, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this, $vsrc);
+ __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ $pg$$PRegister, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+// reduction umaxI
+
+instruct reduce_umaxI_neon(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc,
+ vReg tmp, rFlagsReg cr) %{
+ predicate(VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) &&
+ (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 (UMaxReductionV isrc vsrc));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_umaxI_neon $dst, $isrc, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this, $vsrc);
+ uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc);
+ __ neon_reduce_minmax_integral(this->ideal_Opcode(), $dst$$Register, bt,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ length_in_bytes, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct reduce_umaxI_sve(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc,
+ vRegD tmp, rFlagsReg cr) %{
+ predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) &&
+ (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 (UMaxReductionV isrc vsrc));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_umaxI_sve $dst, $isrc, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ assert(UseSVE > 0, "must be sve");
+ BasicType bt = Matcher::vector_element_basic_type(this, $vsrc);
+ uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc);
+ assert(length_in_bytes == MaxVectorSize, "invalid vector length");
+ __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ ptrue, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+// reduction umaxL
+
+instruct reduce_umaxL_neon(iRegLNoSp dst, iRegL isrc, vReg vsrc, rFlagsReg cr) %{
+ predicate(UseSVE == 0 && Matcher::vector_element_basic_type(n->in(2)) == T_LONG);
+ match(Set dst (UMaxReductionV isrc vsrc));
+ effect(TEMP_DEF dst, KILL cr);
+ format %{ "reduce_umaxL_neon $dst, $isrc, $vsrc\t# 2L. KILL cr" %}
+ ins_encode %{
+ __ neon_reduce_minmax_integral(this->ideal_Opcode(), $dst$$Register, T_LONG,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ /* vector_length_in_bytes */ 16, fnoreg);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct reduce_umaxL_sve(iRegLNoSp dst, iRegL isrc, vReg vsrc,
+ vRegD tmp, rFlagsReg cr) %{
+ predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n->in(2)) == T_LONG);
+ match(Set dst (UMaxReductionV isrc vsrc));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_umaxL_sve $dst, $isrc, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc);
+ assert(length_in_bytes == MaxVectorSize, "invalid vector length");
+ __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, T_LONG,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ ptrue, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+// reduction umax - predicated
+
+instruct reduce_umaxI_masked(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc, pRegGov pg,
+ vRegD tmp, rFlagsReg cr) %{
+ predicate(UseSVE > 0 &&
+ (Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_BYTE ||
+ Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_SHORT ||
+ Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_INT));
+ match(Set dst (UMaxReductionV (Binary isrc vsrc) pg));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_umaxI_masked $dst, $isrc, $pg, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this, $vsrc);
+ __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ $pg$$PRegister, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct reduce_umaxL_masked(iRegLNoSp dst, iRegL isrc, vReg vsrc, pRegGov pg,
+ vRegD tmp, rFlagsReg cr) %{
+ predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_LONG);
+ match(Set dst (UMaxReductionV (Binary isrc vsrc) pg));
+ effect(TEMP_DEF dst, TEMP tmp, KILL cr);
+ format %{ "reduce_umaxL_masked $dst, $isrc, $pg, $vsrc\t# KILL $tmp, cr" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this, $vsrc);
+ __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt,
+ $isrc$$Register, $vsrc$$FloatRegister,
+ $pg$$PRegister, $tmp$$FloatRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
// ------------------------------ Vector reinterpret ---------------------------
instruct reinterpret_same_size(vReg dst_src) %{
diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4
index 66dc22c3758..48bffb3cf35 100644
--- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4
+++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+// Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
// Copyright (c) 2020, 2025, Arm Limited. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
@@ -191,6 +191,8 @@ source %{
case Op_XorReductionV:
case Op_MinReductionV:
case Op_MaxReductionV:
+ case Op_UMinReductionV:
+ case Op_UMaxReductionV:
// Reductions with less than 8 bytes vector length are
// not supported.
if (length_in_bytes < 8) {
@@ -373,6 +375,8 @@ source %{
return !VM_Version::use_neon_for_vector(length_in_bytes);
case Op_MinReductionV:
case Op_MaxReductionV:
+ case Op_UMinReductionV:
+ case Op_UMaxReductionV:
// For BYTE/SHORT/INT/FLOAT/DOUBLE types, we prefer using NEON
// instructions rather than SVE predicated instructions for
// better performance.
@@ -2505,6 +2509,32 @@ REDUCE_MAXMIN_INT_PREDICATE(min, L, iRegL, MinReductionV)
REDUCE_MAXMIN_FP_PREDICATE(min, F, fsrc, MinReductionV, sve_fminv, fmins)
REDUCE_MAXMIN_FP_PREDICATE(min, D, dsrc, MinReductionV, sve_fminv, fmind)
+// -------------------- Vector reduction unsigned min/max ----------------------
+
+// reduction uminI
+REDUCE_MAXMIN_I_NEON(umin, UMinReductionV)
+REDUCE_MAXMIN_I_SVE(umin, UMinReductionV)
+
+// reduction uminL
+REDUCE_MAXMIN_L_NEON(umin, UMinReductionV)
+REDUCE_MAXMIN_L_SVE(umin, UMinReductionV)
+
+// reduction umin - predicated
+REDUCE_MAXMIN_INT_PREDICATE(umin, I, iRegIorL2I, UMinReductionV)
+REDUCE_MAXMIN_INT_PREDICATE(umin, L, iRegL, UMinReductionV)
+
+// reduction umaxI
+REDUCE_MAXMIN_I_NEON(umax, UMaxReductionV)
+REDUCE_MAXMIN_I_SVE(umax, UMaxReductionV)
+
+// reduction umaxL
+REDUCE_MAXMIN_L_NEON(umax, UMaxReductionV)
+REDUCE_MAXMIN_L_SVE(umax, UMaxReductionV)
+
+// reduction umax - predicated
+REDUCE_MAXMIN_INT_PREDICATE(umax, I, iRegIorL2I, UMaxReductionV)
+REDUCE_MAXMIN_INT_PREDICATE(umax, L, iRegL, UMaxReductionV)
+
// ------------------------------ Vector reinterpret ---------------------------
instruct reinterpret_same_size(vReg dst_src) %{
diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
index d2e503ff606..5be7ec2e692 100644
--- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2024, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -2661,6 +2661,8 @@ template
INSN(uminv, 1, 0b011011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
INSN(smaxp, 0, 0b101001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
INSN(sminp, 0, 0b101011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
+ INSN(umaxp, 1, 0b101001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
+ INSN(uminp, 1, 0b101011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
INSN(sqdmulh,0, 0b101101, false); // accepted arrangements: T4H, T8H, T2S, T4S
INSN(shsubv, 0, 0b001001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S
@@ -3493,7 +3495,9 @@ public:
INSN(sve_sub, 0b00000100, 0b000001000); // vector sub
INSN(sve_uaddv, 0b00000100, 0b000001001); // unsigned add reduction to scalar
INSN(sve_umax, 0b00000100, 0b001001000); // unsigned maximum vectors
+ INSN(sve_umaxv, 0b00000100, 0b001001001); // unsigned maximum reduction to scalar
INSN(sve_umin, 0b00000100, 0b001011000); // unsigned minimum vectors
+ INSN(sve_uminv, 0b00000100, 0b001011001); // unsigned minimum reduction to scalar
#undef INSN
// SVE floating-point arithmetic - predicate
@@ -4328,6 +4332,7 @@ public:
#undef INSN
Assembler(CodeBuffer* code) : AbstractAssembler(code) {
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
}
// Stack overflow checking
diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
index 0fd2104f9d6..23805b75b8f 100644
--- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
@@ -33,6 +33,7 @@
#include "c1/c1_ValueStack.hpp"
#include "ci/ciArrayKlass.hpp"
#include "ci/ciInstance.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/compiledIC.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "gc/shared/gc_globals.hpp"
@@ -532,6 +533,15 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod
case T_LONG: {
assert(patch_code == lir_patch_none, "no patching handled here");
+#if INCLUDE_CDS
+ if (AOTCodeCache::is_on_for_dump()) {
+ address b = c->as_pointer();
+ if (AOTRuntimeConstants::contains(b)) {
+ __ load_aotrc_address(dest->as_register_lo(), b);
+ break;
+ }
+ }
+#endif
__ mov(dest->as_register_lo(), (intptr_t)c->as_jlong());
break;
}
diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
index 75897a16fe4..dc0b9eb9546 100644
--- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,9 @@
#include "opto/matcher.hpp"
#include "opto/output.hpp"
#include "opto/subnode.hpp"
+#include "runtime/objectMonitorTable.hpp"
#include "runtime/stubRoutines.hpp"
+#include "runtime/synchronizer.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/powerOfTwo.hpp"
@@ -221,37 +223,52 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1,
if (!UseObjectMonitorTable) {
assert(t1_monitor == t1_mark, "should be the same here");
} else {
+ const Register t1_hash = t1;
Label monitor_found;
- // Load cache address
- lea(t3_t, Address(rthread, JavaThread::om_cache_oops_offset()));
+ // Save the mark, we might need it to extract the hash.
+ mov(t3, t1_mark);
- const int num_unrolled = 2;
+ // Look for the monitor in the om_cache.
+
+ ByteSize cache_offset = JavaThread::om_cache_oops_offset();
+ ByteSize monitor_offset = OMCache::oop_to_monitor_difference();
+ const int num_unrolled = OMCache::CAPACITY;
for (int i = 0; i < num_unrolled; i++) {
- ldr(t1, Address(t3_t));
- cmp(obj, t1);
+ ldr(t1_monitor, Address(rthread, cache_offset + monitor_offset));
+ ldr(t2, Address(rthread, cache_offset));
+ cmp(obj, t2);
br(Assembler::EQ, monitor_found);
- increment(t3_t, in_bytes(OMCache::oop_to_oop_difference()));
+ cache_offset = cache_offset + OMCache::oop_to_oop_difference();
}
- Label loop;
+ // Look for the monitor in the table.
- // Search for obj in cache.
- bind(loop);
+ // Get the hash code.
+ ubfx(t1_hash, t3, markWord::hash_shift, markWord::hash_bits);
- // Check for match.
- ldr(t1, Address(t3_t));
- cmp(obj, t1);
- br(Assembler::EQ, monitor_found);
+ // Get the table and calculate the bucket's address
+ lea(t3, ExternalAddress(ObjectMonitorTable::current_table_address()));
+ ldr(t3, Address(t3));
+ ldr(t2, Address(t3, ObjectMonitorTable::table_capacity_mask_offset()));
+ ands(t1_hash, t1_hash, t2);
+ ldr(t3, Address(t3, ObjectMonitorTable::table_buckets_offset()));
- // Search until null encountered, guaranteed _null_sentinel at end.
- increment(t3_t, in_bytes(OMCache::oop_to_oop_difference()));
- cbnz(t1, loop);
- // Cache Miss, NE set from cmp above, cbnz does not set flags
- b(slow_path);
+ // Read the monitor from the bucket.
+ ldr(t1_monitor, Address(t3, t1_hash, Address::lsl(LogBytesPerWord)));
+
+ // Check if the monitor in the bucket is special (empty, tombstone or removed).
+ cmp(t1_monitor, (unsigned char)ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ br(Assembler::LO, slow_path);
+
+ // Check if object matches.
+ ldr(t3, Address(t1_monitor, ObjectMonitor::object_offset()));
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_resolve_weak_handle_in_c2(this, t3, t2, slow_path);
+ cmp(t3, obj);
+ br(Assembler::NE, slow_path);
bind(monitor_found);
- ldr(t1_monitor, Address(t3_t, OMCache::oop_to_monitor_difference()));
}
const Register t2_owner_addr = t2;
@@ -1960,50 +1977,76 @@ void C2_MacroAssembler::neon_reduce_logical(int opc, Register dst, BasicType bt,
BLOCK_COMMENT("} neon_reduce_logical");
}
-// Vector reduction min/max for integral type with ASIMD instructions.
+// Helper function to decode min/max reduction operation properties
+void C2_MacroAssembler::decode_minmax_reduction_opc(int opc, bool* is_min,
+ bool* is_unsigned,
+ Condition* cond) {
+ switch(opc) {
+ case Op_MinReductionV:
+ *is_min = true; *is_unsigned = false; *cond = LT; break;
+ case Op_MaxReductionV:
+ *is_min = false; *is_unsigned = false; *cond = GT; break;
+ case Op_UMinReductionV:
+ *is_min = true; *is_unsigned = true; *cond = LO; break;
+ case Op_UMaxReductionV:
+ *is_min = false; *is_unsigned = true; *cond = HI; break;
+ default:
+ ShouldNotReachHere();
+ }
+}
+
+// Vector reduction min/max/umin/umax for integral type with ASIMD instructions.
// Note: vtmp is not used and expected to be fnoreg for T_LONG case.
// Clobbers: rscratch1, rflags
void C2_MacroAssembler::neon_reduce_minmax_integral(int opc, Register dst, BasicType bt,
Register isrc, FloatRegister vsrc,
unsigned vector_length_in_bytes,
FloatRegister vtmp) {
- assert(opc == Op_MinReductionV || opc == Op_MaxReductionV, "unsupported");
+ assert(opc == Op_MinReductionV || opc == Op_MaxReductionV ||
+ opc == Op_UMinReductionV || opc == Op_UMaxReductionV, "unsupported");
assert(vector_length_in_bytes == 8 || vector_length_in_bytes == 16, "unsupported");
assert(bt == T_BYTE || bt == T_SHORT || bt == T_INT || bt == T_LONG, "unsupported");
assert_different_registers(dst, isrc);
bool isQ = vector_length_in_bytes == 16;
- bool is_min = opc == Op_MinReductionV;
-
+ bool is_min;
+ bool is_unsigned;
+ Condition cond;
+ decode_minmax_reduction_opc(opc, &is_min, &is_unsigned, &cond);
BLOCK_COMMENT("neon_reduce_minmax_integral {");
if (bt == T_LONG) {
assert(vtmp == fnoreg, "should be");
assert(isQ, "should be");
umov(rscratch1, vsrc, D, 0);
cmp(isrc, rscratch1);
- csel(dst, isrc, rscratch1, is_min ? LT : GT);
+ csel(dst, isrc, rscratch1, cond);
umov(rscratch1, vsrc, D, 1);
cmp(dst, rscratch1);
- csel(dst, dst, rscratch1, is_min ? LT : GT);
+ csel(dst, dst, rscratch1, cond);
} else {
SIMD_Arrangement size = esize2arrangement((unsigned)type2aelembytes(bt), isQ);
if (size == T2S) {
- is_min ? sminp(vtmp, size, vsrc, vsrc) : smaxp(vtmp, size, vsrc, vsrc);
+ // For T2S (2x32-bit elements), use pairwise instructions because
+ // uminv/umaxv/sminv/smaxv don't support arrangement 2S.
+ neon_minmaxp(is_unsigned, is_min, vtmp, size, vsrc, vsrc);
} else {
- is_min ? sminv(vtmp, size, vsrc) : smaxv(vtmp, size, vsrc);
+ // For other sizes, use reduction to scalar instructions.
+ neon_minmaxv(is_unsigned, is_min, vtmp, size, vsrc);
}
if (bt == T_INT) {
umov(dst, vtmp, S, 0);
+ } else if (is_unsigned) {
+ umov(dst, vtmp, elemType_to_regVariant(bt), 0);
} else {
smov(dst, vtmp, elemType_to_regVariant(bt), 0);
}
cmpw(dst, isrc);
- cselw(dst, dst, isrc, is_min ? LT : GT);
+ cselw(dst, dst, isrc, cond);
}
BLOCK_COMMENT("} neon_reduce_minmax_integral");
}
// Vector reduction for integral type with SVE instruction.
-// Supported operations are Add, And, Or, Xor, Max, Min.
+// Supported operations are Add, And, Or, Xor, Max, Min, UMax, UMin.
// rflags would be clobbered if opc is Op_MaxReductionV or Op_MinReductionV.
void C2_MacroAssembler::sve_reduce_integral(int opc, Register dst, BasicType bt, Register src1,
FloatRegister src2, PRegister pg, FloatRegister tmp) {
@@ -2075,35 +2118,27 @@ void C2_MacroAssembler::sve_reduce_integral(int opc, Register dst, BasicType bt,
}
break;
}
- case Op_MaxReductionV: {
- sve_smaxv(tmp, size, pg, src2);
- if (bt == T_INT || bt == T_LONG) {
+ case Op_MaxReductionV:
+ case Op_MinReductionV:
+ case Op_UMaxReductionV:
+ case Op_UMinReductionV: {
+ bool is_min;
+ bool is_unsigned;
+ Condition cond;
+ decode_minmax_reduction_opc(opc, &is_min, &is_unsigned, &cond);
+ sve_minmaxv(is_unsigned, is_min, tmp, size, pg, src2);
+ // Move result from vector to general register
+ if (is_unsigned || bt == T_INT || bt == T_LONG) {
umov(dst, tmp, size, 0);
} else {
smov(dst, tmp, size, 0);
}
if (bt == T_LONG) {
cmp(dst, src1);
- csel(dst, dst, src1, Assembler::GT);
+ csel(dst, dst, src1, cond);
} else {
cmpw(dst, src1);
- cselw(dst, dst, src1, Assembler::GT);
- }
- break;
- }
- case Op_MinReductionV: {
- sve_sminv(tmp, size, pg, src2);
- if (bt == T_INT || bt == T_LONG) {
- umov(dst, tmp, size, 0);
- } else {
- smov(dst, tmp, size, 0);
- }
- if (bt == T_LONG) {
- cmp(dst, src1);
- csel(dst, dst, src1, Assembler::LT);
- } else {
- cmpw(dst, src1);
- cselw(dst, dst, src1, Assembler::LT);
+ cselw(dst, dst, src1, cond);
}
break;
}
diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp
index 412f0f37e9e..4f3a41da402 100644
--- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,37 @@
void neon_reduce_logical_helper(int opc, bool sf, Register Rd, Register Rn, Register Rm,
enum shift_kind kind = Assembler::LSL, unsigned shift = 0);
+ // Helper functions for min/max reduction operations
+
+ void decode_minmax_reduction_opc(int opc, bool* is_min, bool* is_unsigned, Condition* cond);
+
+ void neon_minmaxp(bool is_unsigned, bool is_min, FloatRegister dst,
+ SIMD_Arrangement size, FloatRegister src1, FloatRegister src2) {
+ auto m = is_unsigned ? (is_min ? &Assembler::uminp : &Assembler::umaxp)
+ : (is_min ? &Assembler::sminp : &Assembler::smaxp);
+ (this->*m)(dst, size, src1, src2);
+ }
+
+ // Typedefs used to disambiguate overloaded member functions.
+ typedef void (Assembler::*neon_reduction2)
+ (FloatRegister, SIMD_Arrangement, FloatRegister);
+
+ void neon_minmaxv(bool is_unsigned, bool is_min, FloatRegister dst,
+ SIMD_Arrangement size, FloatRegister src) {
+ auto m = is_unsigned ? (is_min ? (neon_reduction2)&Assembler::uminv
+ : (neon_reduction2)&Assembler::umaxv)
+ : (is_min ? &Assembler::sminv
+ : &Assembler::smaxv);
+ (this->*m)(dst, size, src);
+ }
+
+ void sve_minmaxv(bool is_unsigned, bool is_min, FloatRegister dst,
+ SIMD_RegVariant size, PRegister pg, FloatRegister src) {
+ auto m = is_unsigned ? (is_min ? &Assembler::sve_uminv : &Assembler::sve_umaxv)
+ : (is_min ? &Assembler::sve_sminv : &Assembler::sve_smaxv);
+ (this->*m)(dst, size, pg, src);
+ }
+
void select_from_two_vectors_neon(FloatRegister dst, FloatRegister src1,
FloatRegister src2, FloatRegister index,
FloatRegister tmp, unsigned vector_length_in_bytes);
diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp
index d7884c27a2c..68291720208 100644
--- a/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/g1/g1BarrierSetAssembler_aarch64.cpp
@@ -23,6 +23,7 @@
*/
#include "asm/macroAssembler.inline.hpp"
+#include "code/aotCodeCache.hpp"
#include "gc/g1/g1BarrierSet.hpp"
#include "gc/g1/g1BarrierSetAssembler.hpp"
#include "gc/g1/g1BarrierSetRuntime.hpp"
@@ -243,9 +244,25 @@ static void generate_post_barrier(MacroAssembler* masm,
assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg, rscratch1);
// Does store cross heap regions?
- __ eor(tmp1, store_addr, new_val); // tmp1 := store address ^ new value
- __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
- __ cbz(tmp1, done);
+ #if INCLUDE_CDS
+ // AOT code needs to load the barrier grain shift from the aot
+ // runtime constants area in the code cache otherwise we can compile
+ // it as an immediate operand
+ if (AOTCodeCache::is_on_for_dump()) {
+ address grain_shift_address = (address)AOTRuntimeConstants::grain_shift_address();
+ __ eor(tmp1, store_addr, new_val);
+ __ lea(tmp2, ExternalAddress(grain_shift_address));
+ __ ldrb(tmp2, tmp2);
+ __ lsrv(tmp1, tmp1, tmp2);
+ __ cbz(tmp1, done);
+ } else
+#endif
+ {
+ __ eor(tmp1, store_addr, new_val); // tmp1 := store address ^ new value
+ __ lsr(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
+ __ cbz(tmp1, done);
+ }
+
// Crosses regions, storing null?
if (new_val_may_be_null) {
__ cbz(new_val, done);
diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
index 021af3e5698..2a78d688097 100644
--- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -441,6 +441,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na
return opto_reg;
}
+void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) {
+ // Load the oop from the weak handle.
+ __ ldr(obj, Address(obj));
+}
+
#undef __
#define __ _masm->
diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
index e69be999f00..c2581b2f962 100644
--- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -135,6 +135,7 @@ public:
OptoReg::Name opto_reg);
OptoReg::Name refine_register(const Node* node,
OptoReg::Name opto_reg);
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path);
#endif // COMPILER2
};
diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp
index 4d5ca01b6b4..3d5261c31d1 100644
--- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp
@@ -209,6 +209,10 @@ void BarrierSetNMethod::set_guard_value(nmethod* nm, int value, int bit_mask) {
bs_asm->increment_patching_epoch();
}
+ // Enable WXWrite: the function is called directly from nmethod_entry_barrier
+ // stub.
+ MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, Thread::current()));
+
NativeNMethodBarrier barrier(nm);
barrier.set_value(value, bit_mask);
}
diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp
index ad7bac4e067..2f7707227b4 100644
--- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -442,6 +443,30 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler
__ bind(done);
}
+#ifdef COMPILER2
+void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj,
+ Register tmp, Label& slow_path) {
+ assert_different_registers(obj, tmp);
+
+ Label done;
+
+ // Resolve weak handle using the standard implementation.
+ BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path);
+
+ // Check if the reference is null, and if it is, take the fast path.
+ __ cbz(obj, done);
+
+ Address gc_state(rthread, ShenandoahThreadLocalData::gc_state_offset());
+ __ lea(tmp, gc_state);
+ __ ldrb(tmp, __ legitimize_address(gc_state, 1, tmp));
+
+ // Check if the heap is under weak-reference/roots processing, in
+ // which case we need to take the slow path.
+ __ tbnz(tmp, ShenandoahHeap::WEAK_ROOTS_BITPOS, slow_path);
+ __ bind(done);
+}
+#endif
+
// Special Shenandoah CAS implementation that handles false negatives due
// to concurrent evacuation. The service is more complex than a
// traditional CAS operation because the CAS operation is intended to
diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp
index 362fcae1ccd..d5d5ce8950e 100644
--- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -79,6 +80,9 @@ public:
Address dst, Register val, Register tmp1, Register tmp2, Register tmp3);
virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
Register obj, Register tmp, Label& slowpath);
+#ifdef COMPILER2
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path);
+#endif
void cmpxchg_oop(MacroAssembler* masm, Register addr, Register expected, Register new_val,
bool acquire, bool release, bool is_cae, Register result);
};
diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp
index 07a2d6fbfa0..4f0977a414f 100644
--- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1326,6 +1326,23 @@ void ZStoreBarrierStubC2Aarch64::emit_code(MacroAssembler& masm) {
register_stub(this);
}
+#undef __
+#define __ masm->
+
+void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) {
+ // Resolve weak handle using the standard implementation.
+ BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path);
+
+ // Check if the oop is bad, in which case we need to take the slow path.
+ __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadBeforeMov);
+ __ movzw(tmp, barrier_Relocation::unpatched);
+ __ tst(obj, tmp);
+ __ br(Assembler::NE, slow_path);
+
+ // Oop is okay, so we uncolor it.
+ __ lsr(obj, obj, ZPointerLoadShift);
+}
+
#undef __
#endif // COMPILER2
diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp
index 487970ab0c5..fbbc5c1b517 100644
--- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -191,6 +191,7 @@ public:
ZLoadBarrierStubC2* stub) const;
void generate_c2_store_barrier_stub(MacroAssembler* masm,
ZStoreBarrierStubC2* stub) const;
+ void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path);
#endif // COMPILER2
void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error);
diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
index 8e520314c8b..a59e83c4b69 100644
--- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2019, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -39,7 +39,7 @@ define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap nulls
define_pd_global(bool, DelayCompilerStubsGeneration, COMPILER2_OR_JVMCI);
define_pd_global(size_t, CodeCacheSegmentSize, 64);
-define_pd_global(intx, CodeEntryAlignment, 64);
+define_pd_global(uint, CodeEntryAlignment, 64);
define_pd_global(intx, OptoLoopAlignment, 16);
#define DEFAULT_STACK_YELLOW_PAGES (2)
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
index 192938e2938..b0c57c13ed0 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2024, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -406,7 +406,6 @@ public:
offset <<= shift;
uint64_t target_page = ((uint64_t)insn_addr) + offset;
target_page &= ((uint64_t)-1) << shift;
- uint32_t insn2 = insn_at(insn_addr, 1);
target = address(target_page);
precond(inner != nullptr);
inner(insn_addr, target);
@@ -473,6 +472,7 @@ address MacroAssembler::target_addr_for_insn(address insn_addr) {
// Patch any kind of instruction; there may be several instructions.
// Return the total length (in bytes) of the instructions.
int MacroAssembler::pd_patch_instruction_size(address insn_addr, address target) {
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
return RelocActions::run(insn_addr, target);
}
@@ -481,6 +481,8 @@ int MacroAssembler::patch_oop(address insn_addr, address o) {
unsigned insn = *(unsigned*)insn_addr;
assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
+
// OOPs are either narrow (32 bits) or wide (48 bits). We encode
// narrow OOPs by setting the upper 16 bits in the first
// instruction.
@@ -510,18 +512,13 @@ int MacroAssembler::patch_narrow_klass(address insn_addr, narrowKlass n) {
assert(Instruction_aarch64::extract(insn->encoding(), 31, 21) == 0b11010010101 &&
nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
+
Instruction_aarch64::patch(insn_addr, 20, 5, n >> 16);
Instruction_aarch64::patch(insn_addr+4, 20, 5, n & 0xffff);
return 2 * NativeInstruction::instruction_size;
}
-address MacroAssembler::target_addr_for_insn_or_null(address insn_addr) {
- if (NativeInstruction::is_ldrw_to_zr(insn_addr)) {
- return nullptr;
- }
- return MacroAssembler::target_addr_for_insn(insn_addr);
-}
-
void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp) {
ldr(tmp, Address(rthread, JavaThread::polling_word_offset()));
if (at_return) {
@@ -1955,9 +1952,7 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass,
const Register
r_array_base = temp1,
- r_array_length = temp2,
- r_array_index = noreg, // unused
- r_bitmap = noreg; // unused
+ r_array_length = temp2;
BLOCK_COMMENT("verify_secondary_supers_table {");
@@ -3616,9 +3611,8 @@ extern "C" void findpc(intptr_t x);
void MacroAssembler::debug64(char* msg, int64_t pc, int64_t regs[])
{
// In order to get locks to work, we need to fake a in_VM state
- if (ShowMessageBoxOnError ) {
+ if (ShowMessageBoxOnError) {
JavaThread* thread = JavaThread::current();
- JavaThreadState saved_state = thread->thread_state();
thread->set_thread_state(_thread_in_vm);
#ifndef PRODUCT
if (CountBytecodes || TraceBytecodes || StopInterpreterAt) {
@@ -5743,7 +5737,6 @@ address MacroAssembler::read_polling_page(Register r, relocInfo::relocType rtype
}
void MacroAssembler::adrp(Register reg1, const Address &dest, uint64_t &byte_offset) {
- relocInfo::relocType rtype = dest.rspec().reloc()->type();
uint64_t low_page = (uint64_t)CodeCache::low_bound() >> 12;
uint64_t high_page = (uint64_t)(CodeCache::high_bound()-1) >> 12;
uint64_t dest_page = (uint64_t)dest.target() >> 12;
@@ -5771,6 +5764,14 @@ void MacroAssembler::adrp(Register reg1, const Address &dest, uint64_t &byte_off
}
void MacroAssembler::load_byte_map_base(Register reg) {
+#if INCLUDE_CDS
+ if (AOTCodeCache::is_on_for_dump()) {
+ address byte_map_base_adr = AOTRuntimeConstants::card_table_base_address();
+ lea(reg, ExternalAddress(byte_map_base_adr));
+ ldr(reg, Address(reg));
+ return;
+ }
+#endif
CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set();
// Strictly speaking the card table base isn't an address at all, and it might
@@ -5778,6 +5779,20 @@ void MacroAssembler::load_byte_map_base(Register reg) {
mov(reg, (uint64_t)ctbs->card_table_base_const());
}
+void MacroAssembler::load_aotrc_address(Register reg, address a) {
+#if INCLUDE_CDS
+ assert(AOTRuntimeConstants::contains(a), "address out of range for data area");
+ if (AOTCodeCache::is_on_for_dump()) {
+ // all aotrc field addresses should be registered in the AOTCodeCache address table
+ lea(reg, ExternalAddress(a));
+ } else {
+ mov(reg, (uint64_t)a);
+ }
+#else
+ ShouldNotReachHere();
+#endif
+}
+
void MacroAssembler::build_frame(int framesize) {
assert(framesize >= 2 * wordSize, "framesize must include space for FP/LR");
assert(framesize % (2*wordSize) == 0, "must preserve 2*wordSize alignment");
@@ -6119,7 +6134,6 @@ void MacroAssembler::string_equals(Register a1, Register a2,
Label SAME, DONE, SHORT, NEXT_WORD;
Register tmp1 = rscratch1;
Register tmp2 = rscratch2;
- Register cnt2 = tmp2; // cnt2 only used in array length compare
assert_different_registers(a1, a2, result, cnt1, rscratch1, rscratch2);
@@ -6429,10 +6443,14 @@ void MacroAssembler::fill_words(Register base, Register cnt, Register value)
// Intrinsic for
//
-// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray
-// return the number of characters copied.
-// - java/lang/StringUTF16.compress
-// return index of non-latin1 character if copy fails, otherwise 'len'.
+// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len)
+// Encodes char[] to byte[] in ISO-8859-1
+//
+// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len)
+// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1
+//
+// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len)
+// Encodes char[] to byte[] in ASCII
//
// This version always returns the number of characters copied, and does not
// clobber the 'len' register. A successful copy will complete with the post-
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
index e25fa86be9e..d3441a842df 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2024, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -697,7 +697,6 @@ public:
static bool uses_implicit_null_check(void* address);
static address target_addr_for_insn(address insn_addr);
- static address target_addr_for_insn_or_null(address insn_addr);
// Required platform-specific helpers for Label::patch_instructions.
// They _shadow_ the declarations in AbstractAssembler, which are undefined.
@@ -1501,6 +1500,9 @@ public:
// Load the base of the cardtable byte map into reg.
void load_byte_map_base(Register reg);
+ // Load a constant address in the AOT Runtime Constants area
+ void load_aotrc_address(Register reg, address a);
+
// Prolog generator routines to support switch between x86 code and
// generated ARM code
diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
index f2003dd9b55..8b76b96d345 100644
--- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -133,7 +133,6 @@ void NativeMovConstReg::verify() {
intptr_t NativeMovConstReg::data() const {
- // das(uint64_t(instruction_address()),2);
address addr = MacroAssembler::target_addr_for_insn(instruction_address());
if (maybe_cpool_ref(instruction_address())) {
return *(intptr_t*)addr;
@@ -144,6 +143,7 @@ intptr_t NativeMovConstReg::data() const {
void NativeMovConstReg::set_data(intptr_t x) {
if (maybe_cpool_ref(instruction_address())) {
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
address addr = MacroAssembler::target_addr_for_insn(instruction_address());
*(intptr_t*)addr = x;
} else {
@@ -192,7 +192,6 @@ int NativeMovRegMem::offset() const {
void NativeMovRegMem::set_offset(int x) {
address pc = instruction_address();
- unsigned insn = *(unsigned*)pc;
if (maybe_cpool_ref(pc)) {
address addr = MacroAssembler::target_addr_for_insn(pc);
*(int64_t*)addr = x;
@@ -204,7 +203,7 @@ void NativeMovRegMem::set_offset(int x) {
void NativeMovRegMem::verify() {
#ifdef ASSERT
- address dest = MacroAssembler::target_addr_for_insn_or_null(instruction_address());
+ MacroAssembler::target_addr_for_insn(instruction_address());
#endif
}
@@ -213,7 +212,7 @@ void NativeMovRegMem::verify() {
void NativeJump::verify() { ; }
address NativeJump::jump_destination() const {
- address dest = MacroAssembler::target_addr_for_insn_or_null(instruction_address());
+ address dest = MacroAssembler::target_addr_for_insn(instruction_address());
// We use jump to self as the unresolved address which the inline
// cache code (and relocs) know about
@@ -350,8 +349,6 @@ bool NativeInstruction::is_stop() {
//-------------------------------------------------------------------
-void NativeGeneralJump::verify() { }
-
// MT-safe patching of a long jump instruction.
void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) {
ShouldNotCallThis();
diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp
index c30cb911d96..fc7274714ad 100644
--- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2025, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -78,7 +78,6 @@ public:
inline bool is_nop() const;
bool is_jump();
bool is_general_jump();
- inline bool is_jump_or_nop();
inline bool is_cond_jump();
bool is_safepoint_poll();
bool is_movz();
@@ -90,16 +89,18 @@ protected:
s_char sbyte_at(int offset) const { return *(s_char*)addr_at(offset); }
u_char ubyte_at(int offset) const { return *(u_char*)addr_at(offset); }
- jint int_at(int offset) const { return *(jint*)addr_at(offset); }
- juint uint_at(int offset) const { return *(juint*)addr_at(offset); }
- address ptr_at(int offset) const { return *(address*)addr_at(offset); }
- oop oop_at(int offset) const { return *(oop*)addr_at(offset); }
+ jint int_at(int offset) const { return *(jint*)addr_at(offset); }
+ juint uint_at(int offset) const { return *(juint*)addr_at(offset); }
+ address ptr_at(int offset) const { return *(address*)addr_at(offset); }
+ oop oop_at(int offset) const { return *(oop*)addr_at(offset); }
- void set_char_at(int offset, char c) { *addr_at(offset) = (u_char)c; }
- void set_int_at(int offset, jint i) { *(jint*)addr_at(offset) = i; }
- void set_uint_at(int offset, jint i) { *(juint*)addr_at(offset) = i; }
- void set_ptr_at(int offset, address ptr) { *(address*)addr_at(offset) = ptr; }
- void set_oop_at(int offset, oop o) { *(oop*)addr_at(offset) = o; }
+#define MACOS_WX_WRITE MACOS_AARCH64_ONLY(os::thread_wx_enable_write())
+ void set_char_at(int offset, char c) { MACOS_WX_WRITE; *addr_at(offset) = (u_char)c; }
+ void set_int_at(int offset, jint i) { MACOS_WX_WRITE; *(jint*)addr_at(offset) = i; }
+ void set_uint_at(int offset, jint i) { MACOS_WX_WRITE; *(juint*)addr_at(offset) = i; }
+ void set_ptr_at(int offset, address ptr) { MACOS_WX_WRITE; *(address*)addr_at(offset) = ptr; }
+ void set_oop_at(int offset, oop o) { MACOS_WX_WRITE; *(oop*)addr_at(offset) = o; }
+#undef MACOS_WX_WRITE
void wrote(int offset);
@@ -380,7 +381,6 @@ public:
void set_jump_destination(address dest);
static void replace_mt_safe(address instr_addr, address code_buffer);
- static void verify();
};
inline NativeGeneralJump* nativeGeneralJump_at(address address) {
@@ -419,10 +419,6 @@ inline bool NativeInstruction::is_jump() {
return false;
}
-inline bool NativeInstruction::is_jump_or_nop() {
- return is_nop() || is_jump();
-}
-
// Call trampoline stubs.
class NativeCallTrampolineStub : public NativeInstruction {
public:
diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
index db653bcf236..a459a28b09e 100644
--- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
@@ -11742,7 +11742,9 @@ class StubGenerator: public StubCodeGenerator {
}
#endif
- StubRoutines::_unsafe_setmemory = generate_unsafe_setmemory();
+ if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_setMemory)) {
+ StubRoutines::_unsafe_setmemory = generate_unsafe_setmemory();
+ }
StubRoutines::aarch64::set_completed(); // Inidicate that arraycopy and zero_blocks stubs are generated
}
diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
index 5d4f7103a84..69769fb8441 100644
--- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
@@ -96,10 +96,6 @@ static inline Address aaddress(Register r) {
return iaddress(r);
}
-static inline Address at_rsp() {
- return Address(esp, 0);
-}
-
// At top of Java expression stack which may be different than esp(). It
// isn't for category 1 objects.
static inline Address at_tos () {
diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
index 659c231464a..9b85733ed08 100644
--- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2020, Red Hat Inc. All rights reserved.
* Copyright 2025 Arm Limited and/or its affiliates.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -201,16 +201,14 @@ void VM_Version::initialize() {
}
}
- // Cortex A53
- if (_cpu == CPU_ARM && model_is(0xd03)) {
+ if (_cpu == CPU_ARM && model_is(CPU_MODEL_ARM_CORTEX_A53)) {
set_feature(CPU_A53MAC);
if (FLAG_IS_DEFAULT(UseSIMDForArrayEquals)) {
FLAG_SET_DEFAULT(UseSIMDForArrayEquals, false);
}
}
- // Cortex A73
- if (_cpu == CPU_ARM && model_is(0xd09)) {
+ if (_cpu == CPU_ARM && model_is(CPU_MODEL_ARM_CORTEX_A73)) {
if (FLAG_IS_DEFAULT(SoftwarePrefetchHintDistance)) {
FLAG_SET_DEFAULT(SoftwarePrefetchHintDistance, -1);
}
@@ -220,16 +218,11 @@ void VM_Version::initialize() {
}
}
- // Neoverse
- // N1: 0xd0c
- // N2: 0xd49
- // N3: 0xd8e
- // V1: 0xd40
- // V2: 0xd4f
- // V3: 0xd84
- if (_cpu == CPU_ARM && (model_is(0xd0c) || model_is(0xd49) ||
- model_is(0xd40) || model_is(0xd4f) ||
- model_is(0xd8e) || model_is(0xd84))) {
+ if (_cpu == CPU_ARM &&
+ model_is_in({ CPU_MODEL_ARM_NEOVERSE_N1, CPU_MODEL_ARM_NEOVERSE_V1,
+ CPU_MODEL_ARM_NEOVERSE_N2, CPU_MODEL_ARM_NEOVERSE_V2,
+ CPU_MODEL_ARM_NEOVERSE_N3, CPU_MODEL_ARM_NEOVERSE_V3,
+ CPU_MODEL_ARM_NEOVERSE_V3AE })) {
if (FLAG_IS_DEFAULT(UseSIMDForMemoryOps)) {
FLAG_SET_DEFAULT(UseSIMDForMemoryOps, true);
}
@@ -261,12 +254,9 @@ void VM_Version::initialize() {
FLAG_SET_DEFAULT(UseCRC32, false);
}
- // Neoverse
- // V1: 0xd40
- // V2: 0xd4f
- // V3: 0xd84
if (_cpu == CPU_ARM &&
- (model_is(0xd40) || model_is(0xd4f) || model_is(0xd84))) {
+ model_is_in({ CPU_MODEL_ARM_NEOVERSE_V1, CPU_MODEL_ARM_NEOVERSE_V2,
+ CPU_MODEL_ARM_NEOVERSE_V3, CPU_MODEL_ARM_NEOVERSE_V3AE })) {
if (FLAG_IS_DEFAULT(UseCryptoPmullForCRC32)) {
FLAG_SET_DEFAULT(UseCryptoPmullForCRC32, true);
}
@@ -456,7 +446,9 @@ void VM_Version::initialize() {
FLAG_SET_DEFAULT(BlockZeroingLowLimit, 4 * VM_Version::zva_length());
}
} else if (UseBlockZeroing) {
- warning("DC ZVA is not available on this CPU");
+ if (!FLAG_IS_DEFAULT(UseBlockZeroing)) {
+ warning("DC ZVA is not available on this CPU");
+ }
FLAG_SET_DEFAULT(UseBlockZeroing, false);
}
@@ -632,6 +624,22 @@ void VM_Version::initialize() {
check_virtualizations();
+#ifdef __APPLE__
+ DefaultWXWriteMode = UseOldWX ? WXWrite : WXArmedForWrite;
+
+ if (TraceWXHealing) {
+ if (pthread_jit_write_protect_supported_np()) {
+ tty->print_cr("### TraceWXHealing is in use");
+ if (StressWXHealing) {
+ tty->print_cr("### StressWXHealing is in use");
+ }
+ } else {
+ tty->print_cr("WX Healing is not in use because MAP_JIT write protection "
+ "does not work on this system.");
+ }
+ }
+#endif
+
// Sync SVE related CPU features with flags
if (UseSVE < 2) {
clear_feature(CPU_SVE2);
@@ -658,16 +666,52 @@ void VM_Version::initialize() {
void VM_Version::insert_features_names(uint64_t features, stringStream& ss) {
int i = 0;
ss.join([&]() {
- while (i < MAX_CPU_FEATURES) {
- if (supports_feature((VM_Version::Feature_Flag)i)) {
- return _features_names[i++];
+ const char* str = nullptr;
+ while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
+ if (supports_feature(features, (VM_Version::Feature_Flag)i)) {
+ str = _features_names[i];
}
i += 1;
}
- return (const char*)nullptr;
+ return str;
}, ", ");
}
+void VM_Version::get_cpu_features_name(void* features_buffer, stringStream& ss) {
+ uint64_t features = *(uint64_t*)features_buffer;
+ insert_features_names(features, ss);
+}
+
+void VM_Version::get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss) {
+ uint64_t vm_features_set1 = *(uint64_t*)features_set1;
+ uint64_t vm_features_set2 = *(uint64_t*)features_set2;
+ int i = 0;
+ ss.join([&]() {
+ const char* str = nullptr;
+ while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
+ Feature_Flag flag = (Feature_Flag)i;
+ if (supports_feature(vm_features_set1, flag) && !supports_feature(vm_features_set2, flag)) {
+ str = _features_names[i];
+ }
+ i += 1;
+ }
+ return str;
+ }, ", ");
+}
+
+int VM_Version::cpu_features_size() {
+ return sizeof(_features);
+}
+
+void VM_Version::store_cpu_features(void* buf) {
+ *(uint64_t*)buf = _features;
+}
+
+bool VM_Version::supports_features(void* features_buffer) {
+ uint64_t features_to_test = *(uint64_t*)features_buffer;
+ return (_features & features_to_test) == features_to_test;
+}
+
#if defined(LINUX)
static bool check_info_file(const char* fpath,
const char* virt1, VirtualizationType vt1,
diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp
index 17087d243d3..0213872852b 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, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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.
*
@@ -30,6 +30,8 @@
#include "runtime/abstract_vm_version.hpp"
#include "utilities/sizes.hpp"
+#include
+
class stringStream;
#define BIT_MASK(flag) (1ULL<<(flag))
@@ -112,14 +114,26 @@ public:
CPU_APPLE = 'a',
};
-enum Ampere_CPU_Model {
+ enum Ampere_CPU_Model {
CPU_MODEL_EMAG = 0x0, /* CPU implementer is CPU_AMCC */
CPU_MODEL_ALTRA = 0xd0c, /* CPU implementer is CPU_ARM, Neoverse N1 */
CPU_MODEL_ALTRAMAX = 0xd0c, /* CPU implementer is CPU_ARM, Neoverse N1 */
CPU_MODEL_AMPERE_1 = 0xac3, /* CPU implementer is CPU_AMPERE */
CPU_MODEL_AMPERE_1A = 0xac4, /* CPU implementer is CPU_AMPERE */
CPU_MODEL_AMPERE_1B = 0xac5 /* AMPERE_1B core Implements ARMv8.7 with CSSC, MTE, SM3/SM4 extensions */
-};
+ };
+
+ enum ARM_CPU_Model {
+ CPU_MODEL_ARM_CORTEX_A53 = 0xd03,
+ CPU_MODEL_ARM_CORTEX_A73 = 0xd09,
+ CPU_MODEL_ARM_NEOVERSE_N1 = 0xd0c,
+ CPU_MODEL_ARM_NEOVERSE_V1 = 0xd40,
+ CPU_MODEL_ARM_NEOVERSE_N2 = 0xd49,
+ CPU_MODEL_ARM_NEOVERSE_V2 = 0xd4f,
+ CPU_MODEL_ARM_NEOVERSE_V3AE = 0xd83,
+ CPU_MODEL_ARM_NEOVERSE_V3 = 0xd84,
+ CPU_MODEL_ARM_NEOVERSE_N3 = 0xd8e,
+ };
#define CPU_FEATURE_FLAGS(decl) \
decl(FP, fp, 0) \
@@ -170,6 +184,9 @@ enum Ampere_CPU_Model {
static bool supports_feature(Feature_Flag flag) {
return (_features & BIT_MASK(flag)) != 0;
}
+ static bool supports_feature(uint64_t features, Feature_Flag flag) {
+ return (features & BIT_MASK(flag)) != 0;
+ }
static int cpu_family() { return _cpu; }
static int cpu_model() { return _model; }
@@ -181,7 +198,16 @@ enum Ampere_CPU_Model {
return _model == cpu_model || _model2 == cpu_model;
}
- static bool is_zva_enabled() { return 0 <= _zva_length; }
+ static bool model_is_in(std::initializer_list cpu_models) {
+ for (const int& cpu_model : cpu_models) {
+ if (_model == cpu_model || _model2 == cpu_model) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static bool is_zva_enabled() { return 0 < _zva_length; }
static int zva_length() {
assert(is_zva_enabled(), "ZVA not available");
return _zva_length;
@@ -221,6 +247,20 @@ enum Ampere_CPU_Model {
static bool use_neon_for_vector(int vector_length_in_bytes) {
return vector_length_in_bytes <= 16;
}
+
+ static void get_cpu_features_name(void* features_buffer, stringStream& ss);
+
+ // Returns names of features present in features_set1 but not in features_set2
+ static void get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss);
+
+ // Returns number of bytes required to store cpu features representation
+ static int cpu_features_size();
+
+ // Stores cpu features representation in the provided buffer. This representation is arch dependent.
+ // Size of the buffer must be same as returned by cpu_features_size()
+ static void store_cpu_features(void* buf);
+
+ static bool supports_features(void* features_to_test);
};
#endif // CPU_AARCH64_VM_VERSION_AARCH64_HPP
diff --git a/src/hotspot/cpu/arm/globals_arm.hpp b/src/hotspot/cpu/arm/globals_arm.hpp
index 363a9a2c25c..c568ea04122 100644
--- a/src/hotspot/cpu/arm/globals_arm.hpp
+++ b/src/hotspot/cpu/arm/globals_arm.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,7 +37,7 @@ define_pd_global(bool, TrapBasedNullChecks, false); // Not needed
define_pd_global(bool, DelayCompilerStubsGeneration, false); // No need - only few compiler's stubs
define_pd_global(size_t, CodeCacheSegmentSize, 64);
-define_pd_global(intx, CodeEntryAlignment, 16);
+define_pd_global(uint, CodeEntryAlignment, 16);
define_pd_global(intx, OptoLoopAlignment, 16);
#define DEFAULT_STACK_YELLOW_PAGES (2)
diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp
index 23775a3a52e..da2daffd579 100644
--- a/src/hotspot/cpu/ppc/assembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+ * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -599,6 +599,9 @@ class Assembler : public AbstractAssembler {
XVMAXSP_OPCODE = (60u << OPCODE_SHIFT | 192u << 3),
XVMAXDP_OPCODE = (60u << OPCODE_SHIFT | 224u << 3),
+ XSMINJDP_OPCODE = (60u << OPCODE_SHIFT | 152u << 3),
+ XSMAXJDP_OPCODE = (60u << OPCODE_SHIFT | 144u << 3),
+
// Deliver A Random Number (introduced with POWER9)
DARN_OPCODE = (31u << OPCODE_SHIFT | 755u << 1),
@@ -2455,6 +2458,9 @@ class Assembler : public AbstractAssembler {
inline void xvrdpim( VectorSRegister d, VectorSRegister b);
inline void xvrdpip( VectorSRegister d, VectorSRegister b);
+ inline void xsminjdp( VectorSRegister d, VectorSRegister a, VectorSRegister b); // Requires Power 9
+ inline void xsmaxjdp( VectorSRegister d, VectorSRegister a, VectorSRegister b); // Requires Power 9
+
// The following functions do not match exactly the Java.math semantics.
inline void xvminsp( VectorSRegister d, VectorSRegister a, VectorSRegister b);
inline void xvmindp( VectorSRegister d, VectorSRegister a, VectorSRegister b);
diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp
index 4cda782067e..bd6f3300606 100644
--- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp
+++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+ * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -908,6 +908,9 @@ inline void Assembler::xvrdpic( VectorSRegister d, VectorSRegister b)
inline void Assembler::xvrdpim( VectorSRegister d, VectorSRegister b) { emit_int32( XVRDPIM_OPCODE | vsrt(d) | vsrb(b)); }
inline void Assembler::xvrdpip( VectorSRegister d, VectorSRegister b) { emit_int32( XVRDPIP_OPCODE | vsrt(d) | vsrb(b)); }
+inline void Assembler::xsminjdp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XSMINJDP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); }
+inline void Assembler::xsmaxjdp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XSMAXJDP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); }
+
inline void Assembler::xvminsp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XVMINSP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); }
inline void Assembler::xvmindp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XVMINDP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); }
inline void Assembler::xvmaxsp(VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XVMAXSP_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); }
diff --git a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp
index 73b6b132895..8bbffc22c54 100644
--- a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -600,19 +601,21 @@ void C2_MacroAssembler::count_positives(Register src, Register cnt, Register res
orr(tmp0, tmp2, tmp0);
and_(tmp0, tmp0, tmp1);
- bne(CR0, Lslow); // Found negative byte.
+ bne(CR0, Lslow); // Found negative byte.
addi(result, result, 16);
bdnz(Lfastloop);
bind(Lslow); // Fallback to slow version.
subf(tmp0, src, result); // Bytes known positive.
- subf_(tmp0, tmp0, cnt); // Remaining Bytes.
+ clrldi(tmp1, cnt, 32); // Clear garbage from upper 32 bits.
+ subf_(tmp0, tmp0, tmp1); // Remaining Bytes.
beq(CR0, Ldone);
mtctr(tmp0);
+
bind(Lloop);
lbz(tmp0, 0, result);
andi_(tmp0, tmp0, 0x80);
- bne(CR0, Ldone); // Found negative byte.
+ bne(CR0, Ldone); // Found negative byte.
addi(result, result, 1);
bdnz(Lloop);
diff --git a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp
index d5a0ff10994..caef322d4a1 100644
--- a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp
+++ b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2019 SAP SE. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -44,7 +44,7 @@ define_pd_global(intx, CompileThreshold, 10000);
define_pd_global(intx, OnStackReplacePercentage, 140);
define_pd_global(intx, ConditionalMoveLimit, 3);
-define_pd_global(intx, FreqInlineSize, 175);
+define_pd_global(intx, FreqInlineSize, 325);
define_pd_global(intx, MinJumpTableSize, 10);
define_pd_global(intx, InteriorEntryAlignment, 16);
define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K));
diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
index 8712c75711d..82d06f6c685 100644
--- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -275,6 +275,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na
return opto_reg;
}
+void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) {
+ // Load the oop from the weak handle.
+ __ ld(obj, 0, obj);
+}
+
#undef __
#define __ _masm->
diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp
index 2bf26bd5010..d78071f2ee0 100644
--- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2022 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -81,6 +81,8 @@ public:
#ifdef COMPILER2
OptoReg::Name refine_register(const Node* node, OptoReg::Name opto_reg) const;
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj,
+ Register tmp, Label& slow_path);
#endif // COMPILER2
};
diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp
index c3bb1811031..e1f0416d65d 100644
--- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Red Hat, Inc. All rights reserved.
* Copyright (c) 2012, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -662,6 +663,33 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler
__ block_comment("} try_resolve_jobject_in_native (shenandoahgc)");
}
+#ifdef COMPILER2
+void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler *masm, Register obj,
+ Register tmp, Label &slow_path) {
+ __ block_comment("try_resolve_weak_handle_in_c2 (shenandoahgc) {");
+
+ assert_different_registers(obj, tmp);
+
+ Label done;
+
+ // Resolve weak handle using the standard implementation.
+ BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path);
+
+ // Check if the reference is null, and if it is, take the fast path.
+ __ cmpdi(CR0, obj, 0);
+ __ beq(CR0, done);
+
+ // Check if the heap is under weak-reference/roots processing, in
+ // which case we need to take the slow path.
+ __ lbz(tmp, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread);
+ __ andi_(tmp, tmp, ShenandoahHeap::WEAK_ROOTS);
+ __ bne(CR0, slow_path);
+ __ bind(done);
+
+ __ block_comment("} try_resolve_weak_handle_in_c2 (shenandoahgc)");
+}
+#endif
+
// Special shenandoah CAS implementation that handles false negatives due
// to concurrent evacuation. That is, the CAS operation is intended to succeed in
// the following scenarios (success criteria):
diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp
index 52615a740af..672f8122bcb 100644
--- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved.
* Copyright (c) 2012, 2022 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -121,6 +122,9 @@ public:
virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register dst, Register jni_env,
Register obj, Register tmp, Label& slowpath);
+#ifdef COMPILER2
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path);
+#endif
};
#endif // CPU_PPC_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_PPC_HPP
diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp
index 0aa5858c8e6..bfa3c87c179 100644
--- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -950,6 +950,19 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm,
__ b(*stub->continuation());
}
+void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) {
+ // Resolve weak handle using the standard implementation.
+ BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path);
+
+ // Check if the oop is bad, in which case we need to take the slow path.
+ __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatMarkBadMask);
+ __ andi_(R0, obj, barrier_Relocation::unpatched);
+ __ bne(CR0, slow_path);
+
+ // Oop is okay, so we uncolor it.
+ __ srdi(obj, obj, ZPointerLoadShift);
+}
+
#undef __
#endif // COMPILER2
diff --git a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp
index 27203e7b01c..e31817370d9 100644
--- a/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/gc/z/zBarrierSetAssembler_ppc.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -108,6 +108,8 @@ public:
void generate_c2_load_barrier_stub(MacroAssembler* masm, ZLoadBarrierStubC2* stub) const;
void generate_c2_store_barrier_stub(MacroAssembler* masm, ZStoreBarrierStubC2* stub) const;
+
+ void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path);
#endif // COMPILER2
void store_barrier_fast(MacroAssembler* masm,
diff --git a/src/hotspot/cpu/ppc/globals_ppc.hpp b/src/hotspot/cpu/ppc/globals_ppc.hpp
index 41a8e821ada..927a8cc2be3 100644
--- a/src/hotspot/cpu/ppc/globals_ppc.hpp
+++ b/src/hotspot/cpu/ppc/globals_ppc.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2024 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -60,7 +60,7 @@ define_pd_global(bool, VMContinuations, true);
// Use large code-entry alignment.
define_pd_global(size_t, CodeCacheSegmentSize, 128);
-define_pd_global(intx, CodeEntryAlignment, 64);
+define_pd_global(uint, CodeEntryAlignment, 64);
define_pd_global(intx, OptoLoopAlignment, 16);
define_pd_global(intx, InlineSmallCode, 1500);
diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
index 809285afddb..986dd335816 100644
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -42,6 +42,7 @@
#include "runtime/icache.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/objectMonitor.hpp"
+#include "runtime/objectMonitorTable.hpp"
#include "runtime/os.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/safepointMechanism.hpp"
@@ -2756,39 +2757,54 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register
addi(owner_addr, mark, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag);
mark = noreg;
} else {
+ const Register tmp3_bucket = tmp3;
+ const Register tmp2_hash = tmp2;
Label monitor_found;
- Register cache_addr = tmp2;
- // Load cache address
- addi(cache_addr, R16_thread, in_bytes(JavaThread::om_cache_oops_offset()));
+ // Save the mark, we might need it to extract the hash.
+ mr(tmp2_hash, mark);
- const int num_unrolled = 2;
+ // Look for the monitor in the om_cache.
+
+ ByteSize cache_offset = JavaThread::om_cache_oops_offset();
+ ByteSize monitor_offset = OMCache::oop_to_monitor_difference();
+ const int num_unrolled = OMCache::CAPACITY;
for (int i = 0; i < num_unrolled; i++) {
- ld(R0, 0, cache_addr);
+ ld(R0, in_bytes(cache_offset), R16_thread);
+ ld(monitor, in_bytes(cache_offset + monitor_offset), R16_thread);
cmpd(CR0, R0, obj);
beq(CR0, monitor_found);
- addi(cache_addr, cache_addr, in_bytes(OMCache::oop_to_oop_difference()));
+ cache_offset = cache_offset + OMCache::oop_to_oop_difference();
}
- Label loop;
+ // Look for the monitor in the table.
- // Search for obj in cache.
- bind(loop);
+ // Get the hash code.
+ srdi(tmp2_hash, tmp2_hash, markWord::hash_shift);
- // Check for match.
- ld(R0, 0, cache_addr);
- cmpd(CR0, R0, obj);
- beq(CR0, monitor_found);
+ // Get the table and calculate the bucket's address
+ int simm16_rest = load_const_optimized(tmp3, ObjectMonitorTable::current_table_address(), R0, true);
+ ld_ptr(tmp3, simm16_rest, tmp3);
+ ld(tmp1, in_bytes(ObjectMonitorTable::table_capacity_mask_offset()), tmp3);
+ andr(tmp2_hash, tmp2_hash, tmp1);
+ ld(tmp3_bucket, in_bytes(ObjectMonitorTable::table_buckets_offset()), tmp3);
- // Search until null encountered, guaranteed _null_sentinel at end.
- addi(cache_addr, cache_addr, in_bytes(OMCache::oop_to_oop_difference()));
- cmpdi(CR1, R0, 0);
- bne(CR1, loop);
- // Cache Miss, CR0.NE set from cmp above
- b(slow_path);
+ // Read the monitor from the bucket.
+ sldi(tmp2_hash, tmp2_hash, LogBytesPerWord);
+ ldx(monitor, tmp3_bucket, tmp2_hash);
+
+ // Check if the monitor in the bucket is special (empty, tombstone or removed).
+ cmpldi(CR0, monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ blt(CR0, slow_path);
+
+ // Check if object matches.
+ ld(tmp3, in_bytes(ObjectMonitor::object_offset()), monitor);
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_resolve_weak_handle_in_c2(this, tmp3, tmp2, slow_path);
+ cmpd(CR0, tmp3, obj);
+ bne(CR0, slow_path);
bind(monitor_found);
- ld(monitor, in_bytes(OMCache::oop_to_monitor_difference()), cache_addr);
// Compute owner address.
addi(owner_addr, monitor, in_bytes(ObjectMonitor::owner_offset()));
diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
index 875602cae58..58dec702d7a 100644
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+ * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/src/hotspot/cpu/ppc/matcher_ppc.hpp b/src/hotspot/cpu/ppc/matcher_ppc.hpp
index b50de6323de..2ddbec3e48c 100644
--- a/src/hotspot/cpu/ppc/matcher_ppc.hpp
+++ b/src/hotspot/cpu/ppc/matcher_ppc.hpp
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp
index 803bb6bfe69..45537e0ea96 100644
--- a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp
+++ b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,11 +49,6 @@
#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
-// Workaround for C++ overloading nastiness on '0' for RegisterOrConstant.
-inline static RegisterOrConstant constant(int value) {
- return RegisterOrConstant(value);
-}
-
void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg,
Register temp_reg, Register temp2_reg) {
if (VerifyMethodHandles) {
diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad
index d926fabd353..4cb9f8820a0 100644
--- a/src/hotspot/cpu/ppc/ppc.ad
+++ b/src/hotspot/cpu/ppc/ppc.ad
@@ -1,6 +1,6 @@
//
-// Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
-// Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+// Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved.
+// Copyright (c) 2012, 2026 SAP SE. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This code is free software; you can redistribute it and/or modify it
@@ -2234,6 +2234,12 @@ bool Matcher::match_rule_supported(int opcode) {
case Op_FmaVD:
return (SuperwordUseVSX && UseFMA);
+ case Op_MinF:
+ case Op_MaxF:
+ case Op_MinD:
+ case Op_MaxD:
+ return (PowerArchitecturePPC64 >= 9);
+
case Op_Digit:
return vmIntrinsics::is_intrinsic_available(vmIntrinsics::_isDigit);
case Op_LowerCase:
@@ -12313,6 +12319,58 @@ instruct maxI_reg_reg_isel(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsRegC
ins_pipe(pipe_class_default);
%}
+instruct minF(regF dst, regF src1, regF src2) %{
+ match(Set dst (MinF src1 src2));
+ predicate(PowerArchitecturePPC64 >= 9);
+ ins_cost(DEFAULT_COST);
+
+ format %{ "MinF $dst, $src1, $src2" %}
+ size(4);
+ ins_encode %{
+ __ xsminjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr());
+ %}
+ ins_pipe(pipe_class_default);
+%}
+
+instruct minD(regD dst, regD src1, regD src2) %{
+ match(Set dst (MinD src1 src2));
+ predicate(PowerArchitecturePPC64 >= 9);
+ ins_cost(DEFAULT_COST);
+
+ format %{ "MinD $dst, $src1, $src2" %}
+ size(4);
+ ins_encode %{
+ __ xsminjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr());
+ %}
+ ins_pipe(pipe_class_default);
+%}
+
+instruct maxF(regF dst, regF src1, regF src2) %{
+ match(Set dst (MaxF src1 src2));
+ predicate(PowerArchitecturePPC64 >= 9);
+ ins_cost(DEFAULT_COST);
+
+ format %{ "MaxF $dst, $src1, $src2" %}
+ size(4);
+ ins_encode %{
+ __ xsmaxjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr());
+ %}
+ ins_pipe(pipe_class_default);
+%}
+
+instruct maxD(regD dst, regD src1, regD src2) %{
+ match(Set dst (MaxD src1 src2));
+ predicate(PowerArchitecturePPC64 >= 9);
+ ins_cost(DEFAULT_COST);
+
+ format %{ "MaxD $dst, $src1, $src2" %}
+ size(4);
+ ins_encode %{
+ __ xsmaxjdp($dst$$FloatRegister->to_vsr(), $src1$$FloatRegister->to_vsr(), $src2$$FloatRegister->to_vsr());
+ %}
+ ins_pipe(pipe_class_default);
+%}
+
//---------- Population Count Instructions ------------------------------------
instruct popCountI(iRegIdst dst, iRegIsrc src) %{
diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
index 4eb2028f529..5260ed978ff 100644
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -775,7 +775,6 @@ int SharedRuntime::java_calling_convention(const BasicType *sig_bt,
return stk;
}
-#if defined(COMPILER1) || defined(COMPILER2)
// Calling convention for calling C code.
int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
VMRegPair *regs,
@@ -913,7 +912,6 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
return MAX2(arg, 8) * 2 + additional_frame_header_slots;
#endif
}
-#endif // COMPILER2
int SharedRuntime::vector_calling_convention(VMRegPair *regs,
uint num_bits,
@@ -2874,7 +2872,6 @@ void SharedRuntime::generate_deopt_blob() {
CodeBuffer buffer(name, 2048, 1024);
InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer);
Label exec_mode_initialized;
- int frame_size_in_words;
OopMap* map = nullptr;
OopMapSet *oop_maps = new OopMapSet();
@@ -2886,6 +2883,9 @@ void SharedRuntime::generate_deopt_blob() {
const Register exec_mode_reg = R21_tmp1;
const address start = __ pc();
+ int exception_offset = 0;
+ int exception_in_tls_offset = 0;
+ int reexecute_offset = 0;
#if defined(COMPILER1) || defined(COMPILER2)
// --------------------------------------------------------------------------
@@ -2925,7 +2925,7 @@ void SharedRuntime::generate_deopt_blob() {
// - R3_ARG1: exception oop
// - R4_ARG2: exception pc
- int exception_offset = __ pc() - start;
+ exception_offset = __ pc() - start;
BLOCK_COMMENT("Prolog for exception case");
@@ -2936,7 +2936,7 @@ void SharedRuntime::generate_deopt_blob() {
__ std(R4_ARG2, _abi0(lr), R1_SP);
// Vanilla deoptimization with an exception pending in exception_oop.
- int exception_in_tls_offset = __ pc() - start;
+ exception_in_tls_offset = __ pc() - start;
// Push the "unpack frame".
// Save everything in sight.
@@ -2949,8 +2949,6 @@ void SharedRuntime::generate_deopt_blob() {
__ li(exec_mode_reg, Deoptimization::Unpack_exception);
// fall through
-
- int reexecute_offset = 0;
#ifdef COMPILER1
__ b(exec_mode_initialized);
@@ -3068,11 +3066,12 @@ void SharedRuntime::generate_deopt_blob() {
// Return to the interpreter entry point.
__ blr();
- __ flush();
-#else // COMPILER2
+#else // !defined(COMPILER1) && !defined(COMPILER2)
__ unimplemented("deopt blob needed only with compiler");
- int exception_offset = __ pc() - start;
-#endif // COMPILER2
+#endif
+
+ // Make sure all code is generated
+ __ flush();
_deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset,
reexecute_offset, first_frame_size_in_bytes / wordSize);
diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
index e77a2067e89..63e2fd015d7 100644
--- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
@@ -1041,31 +1041,10 @@ void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) {
__ bind(*op->stub()->continuation());
}
-void LIR_Assembler::type_profile_helper(Register mdo, ciMethodData *md, ciProfileData *data,
- Register recv, Label* update_done) {
- for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) {
- Label next_test;
- // See if the receiver is receiver[n].
- __ ld(t1, Address(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i))));
- __ bne(recv, t1, next_test);
- Address data_addr(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i)));
- __ increment(data_addr, DataLayout::counter_increment);
- __ j(*update_done);
- __ bind(next_test);
- }
-
- // Didn't find receiver; find next empty slot and fill it in
- for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) {
- Label next_test;
- Address recv_addr(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i)));
- __ ld(t1, recv_addr);
- __ bnez(t1, next_test);
- __ sd(recv, recv_addr);
- __ mv(t1, DataLayout::counter_increment);
- __ sd(t1, Address(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i))));
- __ j(*update_done);
- __ bind(next_test);
- }
+void LIR_Assembler::type_profile_helper(Register mdo, ciMethodData *md,
+ ciProfileData *data, Register recv) {
+ int mdp_offset = md->byte_offset_of_slot(data, in_ByteSize(0));
+ __ profile_receiver_type(recv, mdo, mdp_offset);
}
void LIR_Assembler::data_check(LIR_OpTypeCheck *op, ciMethodData **md, ciProfileData **data) {
@@ -1139,14 +1118,9 @@ void LIR_Assembler::profile_object(ciMethodData* md, ciProfileData* data, Regist
__ j(*obj_is_null);
__ bind(not_null);
- Label update_done;
Register recv = k_RInfo;
__ load_klass(recv, obj);
- type_profile_helper(mdo, md, data, recv, &update_done);
- Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()));
- __ increment(counter_addr, DataLayout::counter_increment);
-
- __ bind(update_done);
+ type_profile_helper(mdo, md, data, recv);
}
void LIR_Assembler::typecheck_loaded(LIR_OpTypeCheck *op, ciKlass* k, Register k_RInfo) {
@@ -1554,11 +1528,8 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) {
// We know the type that will be seen at this call site; we can
// statically update the MethodData* rather than needing to do
// dynamic tests on the receiver type
- // NOTE: we should probably put a lock around this search to
- // avoid collisions by concurrent compilations
ciVirtualCallData* vc_data = (ciVirtualCallData*) data;
- uint i;
- for (i = 0; i < VirtualCallData::row_limit(); i++) {
+ for (uint i = 0; i < VirtualCallData::row_limit(); i++) {
ciKlass* receiver = vc_data->receiver(i);
if (known_klass->equals(receiver)) {
Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)));
@@ -1566,32 +1537,13 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) {
return;
}
}
-
- // Receiver type not found in profile data; select an empty slot
- // Note that this is less efficient than it should be because it
- // always does a write to the receiver part of the
- // VirtualCallData rather than just the first time
- for (i = 0; i < VirtualCallData::row_limit(); i++) {
- ciKlass* receiver = vc_data->receiver(i);
- if (receiver == nullptr) {
- Address recv_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)));
- __ mov_metadata(t1, known_klass->constant_encoding());
- __ sd(t1, recv_addr);
- Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)));
- __ increment(data_addr, DataLayout::counter_increment);
- return;
- }
- }
+ // Receiver type is not found in profile data.
+ // Fall back to runtime helper to handle the rest at runtime.
+ __ mov_metadata(recv, known_klass->constant_encoding());
} else {
__ load_klass(recv, recv);
- Label update_done;
- type_profile_helper(mdo, md, data, recv, &update_done);
- // Receiver did not match any saved receiver and there is no empty row for it.
- // Increment total counter to indicate polymorphic case.
- __ increment(counter_addr, DataLayout::counter_increment);
-
- __ bind(update_done);
}
+ type_profile_helper(mdo, md, data, recv);
} else {
// Static call
__ increment(counter_addr, DataLayout::counter_increment);
diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp
index 1e466e90d37..90b6b3ee4f4 100644
--- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp
@@ -54,9 +54,8 @@ private:
Address stack_slot_address(int index, uint shift, int adjust = 0);
// Record the type of the receiver in ReceiverTypeData
- void type_profile_helper(Register mdo,
- ciMethodData *md, ciProfileData *data,
- Register recv, Label* update_done);
+ void type_profile_helper(Register mdo, ciMethodData *md,
+ ciProfileData *data, Register recv);
void casw(Register addr, Register newval, Register cmpval);
void caswu(Register addr, Register newval, Register cmpval);
diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
index 824ea872935..72a90ddde1f 100644
--- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -30,7 +30,9 @@
#include "opto/intrinsicnode.hpp"
#include "opto/output.hpp"
#include "opto/subnode.hpp"
+#include "runtime/objectMonitorTable.hpp"
#include "runtime/stubRoutines.hpp"
+#include "runtime/synchronizer.hpp"
#include "utilities/globalDefinitions.hpp"
#ifdef PRODUCT
@@ -123,35 +125,52 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box,
if (!UseObjectMonitorTable) {
assert(tmp1_monitor == tmp1_mark, "should be the same here");
} else {
+ const Register tmp2_hash = tmp2;
+ const Register tmp3_bucket = tmp3;
Label monitor_found;
- // Load cache address
- la(tmp3_t, Address(xthread, JavaThread::om_cache_oops_offset()));
+ // Save the mark, we might need it to extract the hash.
+ mv(tmp2_hash, tmp1_mark);
- const int num_unrolled = 2;
+ // Look for the monitor in the om_cache.
+
+ ByteSize cache_offset = JavaThread::om_cache_oops_offset();
+ ByteSize monitor_offset = OMCache::oop_to_monitor_difference();
+ const int num_unrolled = OMCache::CAPACITY;
for (int i = 0; i < num_unrolled; i++) {
- ld(tmp1, Address(tmp3_t));
- beq(obj, tmp1, monitor_found);
- add(tmp3_t, tmp3_t, in_bytes(OMCache::oop_to_oop_difference()));
+ ld(tmp1_monitor, Address(xthread, cache_offset + monitor_offset));
+ ld(tmp4, Address(xthread, cache_offset));
+ beq(obj, tmp4, monitor_found);
+ cache_offset = cache_offset + OMCache::oop_to_oop_difference();
}
- Label loop;
+ // Look for the monitor in the table.
- // Search for obj in cache.
- bind(loop);
+ // Get the hash code.
+ srli(tmp2_hash, tmp2_hash, markWord::hash_shift);
- // Check for match.
- ld(tmp1, Address(tmp3_t));
- beq(obj, tmp1, monitor_found);
+ // Get the table and calculate the bucket's address.
+ la(tmp3_t, ExternalAddress(ObjectMonitorTable::current_table_address()));
+ ld(tmp3_t, Address(tmp3_t));
+ ld(tmp1, Address(tmp3_t, ObjectMonitorTable::table_capacity_mask_offset()));
+ andr(tmp2_hash, tmp2_hash, tmp1);
+ ld(tmp3_t, Address(tmp3_t, ObjectMonitorTable::table_buckets_offset()));
- // Search until null encountered, guaranteed _null_sentinel at end.
- add(tmp3_t, tmp3_t, in_bytes(OMCache::oop_to_oop_difference()));
- bnez(tmp1, loop);
- // Cache Miss. Take the slowpath.
- j(slow_path);
+ // Read the monitor from the bucket.
+ shadd(tmp3_bucket, tmp2_hash, tmp3_t, tmp4, LogBytesPerWord);
+ ld(tmp1_monitor, Address(tmp3_bucket));
+
+ // Check if the monitor in the bucket is special (empty, tombstone or removed).
+ mv(tmp2, ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ bltu(tmp1_monitor, tmp2, slow_path);
+
+ // Check if object matches.
+ ld(tmp3, Address(tmp1_monitor, ObjectMonitor::object_offset()));
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_resolve_weak_handle_in_c2(this, tmp3, tmp2, slow_path);
+ bne(tmp3, obj, slow_path);
bind(monitor_found);
- ld(tmp1_monitor, Address(tmp3_t, OMCache::oop_to_monitor_difference()));
}
const Register tmp2_owner_addr = tmp2;
@@ -2813,10 +2832,14 @@ void C2_MacroAssembler::char_array_compress_v(Register src, Register dst, Regist
// Intrinsic for
//
-// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray
-// return the number of characters copied.
-// - java/lang/StringUTF16.compress
-// return index of non-latin1 character if copy fails, otherwise 'len'.
+// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len)
+// Encodes char[] to byte[] in ISO-8859-1
+//
+// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len)
+// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1
+//
+// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len)
+// Encodes char[] to byte[] in ASCII
//
// This version always returns the number of characters copied. A successful
// copy will complete with the post-condition: 'res' == 'len', while an
diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
index f5916000890..aeb9df06de6 100644
--- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -369,6 +369,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na
return opto_reg;
}
+void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) {
+ // Load the oop from the weak handle.
+ __ ld(obj, Address(obj));
+}
+
#undef __
#define __ _masm->
diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp
index e50fa1dae36..bbb2a5af824 100644
--- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -110,6 +110,8 @@ public:
#ifdef COMPILER2
OptoReg::Name refine_register(const Node* node,
OptoReg::Name opto_reg);
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj,
+ Register tmp, Label& slow_path);
#endif // COMPILER2
};
diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
index 3cbbb783258..8d530d15ee5 100644
--- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved.
* Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -461,6 +462,30 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler
__ bind(done);
}
+#ifdef COMPILER2
+void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler *masm, Register obj,
+ Register tmp, Label& slow_path) {
+ assert_different_registers(obj, tmp);
+
+ Label done;
+
+ // Resolve weak handle using the standard implementation.
+ BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path);
+
+ // Check if the reference is null, and if it is, take the fast path.
+ __ beqz(obj, done);
+
+ Address gc_state(xthread, ShenandoahThreadLocalData::gc_state_offset());
+ __ lbu(tmp, gc_state);
+
+ // Check if the heap is under weak-reference/roots processing, in
+ // which case we need to take the slow path.
+ __ test_bit(tmp, tmp, ShenandoahHeap::WEAK_ROOTS_BITPOS);
+ __ bnez(tmp, slow_path);
+ __ bind(done);
+}
+#endif
+
// Special Shenandoah CAS implementation that handles false negatives due
// to concurrent evacuation. The service is more complex than a
// traditional CAS operation because the CAS operation is intended to
diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp
index 5085be26b2e..e35e09c93da 100644
--- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved.
* Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -84,7 +85,9 @@ public:
virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
Register obj, Register tmp, Label& slowpath);
-
+#ifdef COMPILER2
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path);
+#endif
void cmpxchg_oop(MacroAssembler* masm, Register addr, Register expected, Register new_val,
Assembler::Aqrl acquire, Assembler::Aqrl release, bool is_cae, Register result);
};
diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp
index 09dea62b6d1..163271a2f11 100644
--- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -602,6 +602,27 @@ void ZBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm,
BLOCK_COMMENT("} ZBarrierSetAssembler::try_resolve_jobject_in_native");
}
+#ifdef COMPILER2
+void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) {
+ BLOCK_COMMENT("ZBarrierSetAssembler::try_resolve_weak_handle_in_c2 {");
+
+ // Resolve weak handle using the standard implementation.
+ BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path);
+
+ // Check if the oop is bad, in which case we need to take the slow path.
+ __ relocate(barrier_Relocation::spec(), [&] {
+ __ li16u(tmp, barrier_Relocation::unpatched);
+ }, ZBarrierRelocationFormatMarkBadMask);
+ __ andr(tmp, obj, tmp);
+ __ bnez(tmp, slow_path);
+
+ // Oop is okay, so we uncolor it.
+ __ srli(obj, obj, ZPointerLoadShift);
+
+ BLOCK_COMMENT("} ZBarrierSetAssembler::try_resolve_weak_handle_in_c2");
+}
+#endif
+
static uint16_t patch_barrier_relocation_value(int format) {
switch (format) {
case ZBarrierRelocationFormatLoadBadMask:
diff --git a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.hpp
index 190d81acd0c..648cb3bf63d 100644
--- a/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/gc/z/zBarrierSetAssembler_riscv.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -170,6 +170,10 @@ public:
ZLoadBarrierStubC2* stub) const;
void generate_c2_store_barrier_stub(MacroAssembler* masm,
ZStoreBarrierStubC2* stub) const;
+ void try_resolve_weak_handle_in_c2(MacroAssembler* masm,
+ Register obj,
+ Register tmp,
+ Label& slow_path);
#endif // COMPILER2
void check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& error);
diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp
index 390ed2daeb9..21b119266e2 100644
--- a/src/hotspot/cpu/riscv/globals_riscv.hpp
+++ b/src/hotspot/cpu/riscv/globals_riscv.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -39,7 +39,7 @@ define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap nulls
define_pd_global(bool, DelayCompilerStubsGeneration, COMPILER2_OR_JVMCI);
define_pd_global(size_t, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment.
-define_pd_global(intx, CodeEntryAlignment, 64);
+define_pd_global(uint, CodeEntryAlignment, 64);
define_pd_global(intx, OptoLoopAlignment, 16);
#define DEFAULT_STACK_YELLOW_PAGES (2)
diff --git a/src/hotspot/cpu/riscv/icache_riscv.cpp b/src/hotspot/cpu/riscv/icache_riscv.cpp
index 258bc665770..20de2dbb2ad 100644
--- a/src/hotspot/cpu/riscv/icache_riscv.cpp
+++ b/src/hotspot/cpu/riscv/icache_riscv.cpp
@@ -39,7 +39,8 @@ static int icache_flush(address addr, int lines, int magic) {
// We need to make sure stores happens before the I/D cache synchronization.
__asm__ volatile("fence rw, rw" : : : "memory");
- RiscvFlushIcache::flush((uintptr_t)addr, ((uintptr_t)lines) << ICache::log2_line_size);
+ uintptr_t end = (uintptr_t)addr + ((uintptr_t)lines << ICache::log2_line_size);
+ RiscvFlushIcache::flush((uintptr_t)addr, end);
return magic;
}
diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
index 189c7c93d07..744590bec2b 100644
--- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
+++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
@@ -237,15 +237,14 @@ void InterpreterMacroAssembler::load_resolved_klass_at_offset(
// Rsub_klass: subklass
//
// Kills:
-// x12, x15
+// x12
void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass,
Label& ok_is_subtype) {
assert(Rsub_klass != x10, "x10 holds superklass");
assert(Rsub_klass != x12, "x12 holds 2ndary super array length");
- assert(Rsub_klass != x15, "x15 holds 2ndary super array scan ptr");
// Profile the not-null value's klass.
- profile_typecheck(x12, Rsub_klass, x15); // blows x12, reloads x15
+ profile_typecheck(x12, Rsub_klass); // blows x12
// Do the check.
check_klass_subtype(Rsub_klass, x10, x12, ok_is_subtype); // blows x12
@@ -1042,7 +1041,6 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) {
void InterpreterMacroAssembler::profile_virtual_call(Register receiver,
Register mdp,
- Register reg2,
bool receiver_can_be_null) {
if (ProfileInterpreter) {
Label profile_continue;
@@ -1060,7 +1058,7 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver,
}
// Record the receiver type.
- record_klass_in_profile(receiver, mdp, reg2);
+ profile_receiver_type(receiver, mdp, 0);
bind(skip_receiver_profile);
// The method data pointer needs to be updated to reflect the new target.
@@ -1072,153 +1070,6 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver,
}
}
-// This routine creates a state machine for updating the multi-row
-// type profile at a virtual call site (or other type-sensitive bytecode).
-// The machine visits each row (of receiver/count) until the receiver type
-// is found, or until it runs out of rows. At the same time, it remembers
-// the location of the first empty row. (An empty row records null for its
-// receiver, and can be allocated for a newly-observed receiver type.)
-// Because there are two degrees of freedom in the state, a simple linear
-// search will not work; it must be a decision tree. Hence this helper
-// function is recursive, to generate the required tree structured code.
-// It's the interpreter, so we are trading off code space for speed.
-// See below for example code.
-void InterpreterMacroAssembler::record_klass_in_profile_helper(
- Register receiver, Register mdp,
- Register reg2, Label& done) {
- if (TypeProfileWidth == 0) {
- increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
- } else {
- record_item_in_profile_helper(receiver, mdp, reg2, 0, done, TypeProfileWidth,
- &VirtualCallData::receiver_offset, &VirtualCallData::receiver_count_offset);
- }
-}
-
-void InterpreterMacroAssembler::record_item_in_profile_helper(Register item, Register mdp,
- Register reg2, int start_row, Label& done, int total_rows,
- OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn) {
- int last_row = total_rows - 1;
- assert(start_row <= last_row, "must be work left to do");
- // Test this row for both the item and for null.
- // Take any of three different outcomes:
- // 1. found item => increment count and goto done
- // 2. found null => keep looking for case 1, maybe allocate this cell
- // 3. found something else => keep looking for cases 1 and 2
- // Case 3 is handled by a recursive call.
- for (int row = start_row; row <= last_row; row++) {
- Label next_test;
- bool test_for_null_also = (row == start_row);
-
- // See if the item is item[n].
- int item_offset = in_bytes(item_offset_fn(row));
- test_mdp_data_at(mdp, item_offset, item,
- (test_for_null_also ? reg2 : noreg),
- next_test);
- // (Reg2 now contains the item from the CallData.)
-
- // The item is item[n]. Increment count[n].
- int count_offset = in_bytes(item_count_offset_fn(row));
- increment_mdp_data_at(mdp, count_offset);
- j(done);
- bind(next_test);
-
- if (test_for_null_also) {
- Label found_null;
- // Failed the equality check on item[n]... Test for null.
- if (start_row == last_row) {
- // The only thing left to do is handle the null case.
- beqz(reg2, found_null);
- // Item did not match any saved item and there is no empty row for it.
- // Increment total counter to indicate polymorphic case.
- increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
- j(done);
- bind(found_null);
- break;
- }
- // Since null is rare, make it be the branch-taken case.
- beqz(reg2, found_null);
-
- // Put all the "Case 3" tests here.
- record_item_in_profile_helper(item, mdp, reg2, start_row + 1, done, total_rows,
- item_offset_fn, item_count_offset_fn);
-
- // Found a null. Keep searching for a matching item,
- // but remember that this is an empty (unused) slot.
- bind(found_null);
- }
- }
-
- // In the fall-through case, we found no matching item, but we
- // observed the item[start_row] is null.
- // Fill in the item field and increment the count.
- int item_offset = in_bytes(item_offset_fn(start_row));
- set_mdp_data_at(mdp, item_offset, item);
- int count_offset = in_bytes(item_count_offset_fn(start_row));
- mv(reg2, DataLayout::counter_increment);
- set_mdp_data_at(mdp, count_offset, reg2);
- if (start_row > 0) {
- j(done);
- }
-}
-
-// Example state machine code for three profile rows:
-// # main copy of decision tree, rooted at row[1]
-// if (row[0].rec == rec) then [
-// row[0].incr()
-// goto done
-// ]
-// if (row[0].rec != nullptr) then [
-// # inner copy of decision tree, rooted at row[1]
-// if (row[1].rec == rec) then [
-// row[1].incr()
-// goto done
-// ]
-// if (row[1].rec != nullptr) then [
-// # degenerate decision tree, rooted at row[2]
-// if (row[2].rec == rec) then [
-// row[2].incr()
-// goto done
-// ]
-// if (row[2].rec != nullptr) then [
-// count.incr()
-// goto done
-// ] # overflow
-// row[2].init(rec)
-// goto done
-// ] else [
-// # remember row[1] is empty
-// if (row[2].rec == rec) then [
-// row[2].incr()
-// goto done
-// ]
-// row[1].init(rec)
-// goto done
-// ]
-// else [
-// # remember row[0] is empty
-// if (row[1].rec == rec) then [
-// row[1].incr()
-// goto done
-// ]
-// if (row[2].rec == rec) then [
-// row[2].incr()
-// goto done
-// ]
-// row[0].init(rec)
-// goto done
-// ]
-// done:
-
-void InterpreterMacroAssembler::record_klass_in_profile(Register receiver,
- Register mdp, Register reg2) {
- assert(ProfileInterpreter, "must be profiling");
- Label done;
-
- record_klass_in_profile_helper(receiver, mdp, reg2, done);
-
- bind(done);
-}
-
void InterpreterMacroAssembler::profile_ret(Register return_bci, Register mdp) {
if (ProfileInterpreter) {
Label profile_continue;
@@ -1274,7 +1125,7 @@ void InterpreterMacroAssembler::profile_null_seen(Register mdp) {
}
}
-void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, Register reg2) {
+void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass) {
if (ProfileInterpreter) {
Label profile_continue;
@@ -1287,7 +1138,7 @@ void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass,
mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
// Record the object type.
- record_klass_in_profile(klass, mdp, reg2);
+ profile_receiver_type(klass, mdp, 0);
}
update_mdp_by_constant(mdp, mdp_delta);
diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
index a9df09d656a..59cc76b022f 100644
--- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
+++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
@@ -262,14 +262,6 @@ class InterpreterMacroAssembler: public MacroAssembler {
Register test_value_out,
Label& not_equal_continue);
- void record_klass_in_profile(Register receiver, Register mdp,
- Register reg2);
- void record_klass_in_profile_helper(Register receiver, Register mdp,
- Register reg2, Label& done);
- void record_item_in_profile_helper(Register item, Register mdp,
- Register reg2, int start_row, Label& done, int total_rows,
- OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn);
-
void update_mdp_by_offset(Register mdp_in, int offset_of_offset);
void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp);
void update_mdp_by_constant(Register mdp_in, int constant);
@@ -283,11 +275,10 @@ class InterpreterMacroAssembler: public MacroAssembler {
void profile_call(Register mdp);
void profile_final_call(Register mdp);
void profile_virtual_call(Register receiver, Register mdp,
- Register t1,
bool receiver_can_be_null = false);
void profile_ret(Register return_bci, Register mdp);
void profile_null_seen(Register mdp);
- void profile_typecheck(Register mdp, Register klass, Register temp);
+ void profile_typecheck(Register mdp, Register klass);
void profile_typecheck_failed(Register mdp);
void profile_switch_default(Register mdp);
void profile_switch_case(Register index_in_scratch, Register mdp,
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
index fb30f64e9ed..4f5e7afc166 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
@@ -543,6 +543,160 @@ void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file,
BLOCK_COMMENT("} verify_oop");
}
+// Handle the receiver type profile update given the "recv" klass.
+//
+// Normally updates the ReceiverData (RD) that starts at "mdp" + "mdp_offset".
+// If there are no matching or claimable receiver entries in RD, updates
+// the polymorphic counter.
+//
+// This code expected to run by either the interpreter or JIT-ed code, without
+// extra synchronization. For safety, receiver cells are claimed atomically, which
+// avoids grossly misrepresenting the profiles under concurrent updates. For speed,
+// counter updates are not atomic.
+//
+void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_offset) {
+ assert_different_registers(recv, mdp, t0, t1);
+
+ int base_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(0));
+ int end_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(ReceiverTypeData::row_limit()));
+ int poly_count_offset = in_bytes(CounterData::count_offset());
+ int receiver_step = in_bytes(ReceiverTypeData::receiver_offset(1)) - base_receiver_offset;
+ int receiver_to_count_step = in_bytes(ReceiverTypeData::receiver_count_offset(0)) - base_receiver_offset;
+
+ // Adjust for MDP offsets. Slots are pointer-sized, so is the global offset.
+ base_receiver_offset += mdp_offset;
+ end_receiver_offset += mdp_offset;
+ poly_count_offset += mdp_offset;
+
+#ifdef ASSERT
+ // We are about to walk the MDO slots without asking for offsets.
+ // Check that our math hits all the right spots.
+ for (uint c = 0; c < ReceiverTypeData::row_limit(); c++) {
+ int real_recv_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_offset(c));
+ int real_count_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_count_offset(c));
+ int offset = base_receiver_offset + receiver_step*c;
+ int count_offset = offset + receiver_to_count_step;
+ assert(offset == real_recv_offset, "receiver slot math");
+ assert(count_offset == real_count_offset, "receiver count math");
+ }
+ int real_poly_count_offset = mdp_offset + in_bytes(CounterData::count_offset());
+ assert(poly_count_offset == real_poly_count_offset, "poly counter math");
+#endif
+
+ // Corner case: no profile table. Increment poly counter and exit.
+ if (ReceiverTypeData::row_limit() == 0) {
+ increment(Address(mdp, poly_count_offset), DataLayout::counter_increment);
+ return;
+ }
+
+ Register offset = t1;
+
+ Label L_loop_search_receiver, L_loop_search_empty;
+ Label L_restart, L_found_recv, L_found_empty, L_polymorphic, L_count_update;
+
+ // The code here recognizes three major cases:
+ // A. Fastest: receiver found in the table
+ // B. Fast: no receiver in the table, and the table is full
+ // C. Slow: no receiver in the table, free slots in the table
+ //
+ // The case A performance is most important, as perfectly-behaved code would end up
+ // there, especially with larger TypeProfileWidth. The case B performance is
+ // important as well, this is where bulk of code would land for normally megamorphic
+ // cases. The case C performance is not essential, its job is to deal with installation
+ // races, we optimize for code density instead. Case C needs to make sure that receiver
+ // rows are only claimed once. This makes sure we never overwrite a row for another
+ // receiver and never duplicate the receivers in the list, making profile type-accurate.
+ //
+ // It is very tempting to handle these cases in a single loop, and claim the first slot
+ // without checking the rest of the table. But, profiling code should tolerate free slots
+ // in the table, as class unloading can clear them. After such cleanup, the receiver
+ // we need might be _after_ the free slot. Therefore, we need to let at least full scan
+ // to complete, before trying to install new slots. Splitting the code in several tight
+ // loops also helpfully optimizes for cases A and B.
+ //
+ // This code is effectively:
+ //
+ // restart:
+ // // Fastest: receiver is already installed
+ // for (i = 0; i < receiver_count(); i++) {
+ // if (receiver(i) == recv) goto found_recv(i);
+ // }
+ //
+ // // Fast: no receiver, but profile is full
+ // for (i = 0; i < receiver_count(); i++) {
+ // if (receiver(i) == null) goto found_null(i);
+ // }
+ // goto polymorphic
+ //
+ // // Slow: try to install receiver
+ // found_null(i):
+ // CAS(&receiver(i), null, recv);
+ // goto restart
+ //
+ // polymorphic:
+ // count++;
+ // return
+ //
+ // found_recv(i):
+ // *receiver_count(i)++
+ //
+
+ bind(L_restart);
+
+ // Fastest: receiver is already installed
+ mv(offset, base_receiver_offset);
+ bind(L_loop_search_receiver);
+ add(t0, mdp, offset);
+ ld(t0, Address(t0));
+ beq(recv, t0, L_found_recv);
+ add(offset, offset, receiver_step);
+ sub(t0, offset, end_receiver_offset);
+ bnez(t0, L_loop_search_receiver);
+
+ // Fast: no receiver, but profile is full
+ mv(offset, base_receiver_offset);
+ bind(L_loop_search_empty);
+ add(t0, mdp, offset);
+ ld(t0, Address(t0));
+ beqz(t0, L_found_empty);
+ add(offset, offset, receiver_step);
+ sub(t0, offset, end_receiver_offset);
+ bnez(t0, L_loop_search_empty);
+ j(L_polymorphic);
+
+ // Slow: try to install receiver
+ bind(L_found_empty);
+
+ // Atomically swing receiver slot: null -> recv.
+ //
+ // The update uses CAS, which clobbers t0. Therefore, t1
+ // is used to hold the destination address. This is safe because the
+ // offset is no longer needed after the address is computed.
+ add(t1, mdp, offset);
+ weak_cmpxchg(/*addr*/ t1, /*expected*/ zr, /*new*/ recv, Assembler::int64,
+ /*acquire*/ Assembler::relaxed, /*release*/ Assembler::relaxed, /*result*/ t0);
+
+ // CAS success means the slot now has the receiver we want. CAS failure means
+ // something had claimed the slot concurrently: it can be the same receiver we want,
+ // or something else. Since this is a slow path, we can optimize for code density,
+ // and just restart the search from the beginning.
+ j(L_restart);
+
+ // Counter updates:
+ // Increment polymorphic counter instead of receiver slot.
+ bind(L_polymorphic);
+ mv(offset, poly_count_offset);
+ j(L_count_update);
+
+ // Found a receiver, convert its slot offset to corresponding count offset.
+ bind(L_found_recv);
+ add(offset, offset, receiver_to_count_step);
+
+ bind(L_count_update);
+ add(t1, mdp, offset);
+ increment(Address(t1), DataLayout::counter_increment);
+}
+
void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* file, int line) {
if (!VerifyOops) {
return;
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
index 3b021388fa5..f5e985c28a2 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
@@ -390,6 +390,8 @@ class MacroAssembler: public Assembler {
Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0);
+ void profile_receiver_type(Register recv, Register mdp, int mdp_offset);
+
// only if +VerifyOops
void _verify_oop(Register reg, const char* s, const char* file, int line);
void _verify_oop_addr(Address addr, const char* s, const char* file, int line);
diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad
index 730dd68dd88..ed6e8db0606 100644
--- a/src/hotspot/cpu/riscv/riscv.ad
+++ b/src/hotspot/cpu/riscv/riscv.ad
@@ -2274,7 +2274,7 @@ encode %{
} else if (rtype == relocInfo::metadata_type) {
__ mov_metadata(dst_reg, (Metadata*)con);
} else {
- assert(rtype == relocInfo::none, "unexpected reloc type");
+ assert(rtype == relocInfo::none || rtype == relocInfo::external_word_type, "unexpected reloc type");
__ mv(dst_reg, $src$$constant);
}
}
diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp
index 0fb529d1683..5cc725e3af4 100644
--- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp
+++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp
@@ -3279,7 +3279,7 @@ void TemplateTable::invokevirtual_helper(Register index,
__ load_klass(x10, recv);
// profile this call
- __ profile_virtual_call(x10, xlocals, x13);
+ __ profile_virtual_call(x10, xlocals);
// get target Method & entry point
__ lookup_virtual_method(x10, index, method);
@@ -3406,7 +3406,7 @@ void TemplateTable::invokeinterface(int byte_no) {
/*return_method=*/false);
// profile this call
- __ profile_virtual_call(x13, x30, x9);
+ __ profile_virtual_call(x13, x30);
// Get declaring interface class from method, and itable index
__ load_method_holder(x10, xmethod);
diff --git a/src/hotspot/cpu/s390/c2_globals_s390.hpp b/src/hotspot/cpu/s390/c2_globals_s390.hpp
index 431a36cda07..125b317588d 100644
--- a/src/hotspot/cpu/s390/c2_globals_s390.hpp
+++ b/src/hotspot/cpu/s390/c2_globals_s390.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -44,7 +44,7 @@ define_pd_global(intx, CompileThreshold, 10000);
define_pd_global(intx, OnStackReplacePercentage, 140);
define_pd_global(intx, ConditionalMoveLimit, 4);
-define_pd_global(intx, FreqInlineSize, 175);
+define_pd_global(intx, FreqInlineSize, 325);
define_pd_global(intx, InteriorEntryAlignment, 4);
define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K));
define_pd_global(intx, RegisterCostAreaRatio, 12000);
diff --git a/src/hotspot/cpu/s390/compiledIC_s390.cpp b/src/hotspot/cpu/s390/compiledIC_s390.cpp
index 8501a0cb346..43f5d80250e 100644
--- a/src/hotspot/cpu/s390/compiledIC_s390.cpp
+++ b/src/hotspot/cpu/s390/compiledIC_s390.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2019 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -29,9 +29,6 @@
#include "memory/resourceArea.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
-#ifdef COMPILER2
-#include "opto/matcher.hpp"
-#endif
// ----------------------------------------------------------------------------
@@ -39,7 +36,6 @@
#define __ masm->
address CompiledDirectCall::emit_to_interp_stub(MacroAssembler *masm, address mark/* = nullptr*/) {
-#ifdef COMPILER2
// Stub is fixed up when the corresponding call is converted from calling
// compiled code to calling interpreted code.
if (mark == nullptr) {
@@ -55,7 +51,7 @@ address CompiledDirectCall::emit_to_interp_stub(MacroAssembler *masm, address ma
__ relocate(static_stub_Relocation::spec(mark));
AddressLiteral meta = __ allocate_metadata_address(nullptr);
- bool success = __ load_const_from_toc(as_Register(Matcher::inline_cache_reg_encode()), meta);
+ bool success = __ load_const_from_toc(Z_inline_cache, meta);
__ set_inst_mark();
AddressLiteral a((address)-1);
@@ -67,10 +63,6 @@ address CompiledDirectCall::emit_to_interp_stub(MacroAssembler *masm, address ma
__ z_br(Z_R1);
__ end_a_stub(); // Update current stubs pointer and restore insts_end.
return stub;
-#else
- ShouldNotReachHere();
- return nullptr;
-#endif
}
#undef __
diff --git a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp
index 272136fc28c..617bc7cd00c 100644
--- a/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp
+++ b/src/hotspot/cpu/s390/gc/g1/g1BarrierSetAssembler_s390.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -129,6 +129,57 @@ void G1BarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* mas
}
}
+static void generate_post_barrier(MacroAssembler* masm,
+ const Register store_addr,
+ const Register new_val,
+ const Register thread,
+ const Register tmp1,
+ const Register tmp2,
+ Label& done,
+ bool new_val_may_be_null) {
+
+ __ block_comment("generate_post_barrier {");
+
+ assert(thread == Z_thread, "must be");
+ assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg);
+
+ // Does store cross heap regions?
+ if (VM_Version::has_DistinctOpnds()) {
+ __ z_xgrk(tmp1, store_addr, new_val); // tmp1 := store address ^ new value
+ } else {
+ __ z_lgr(tmp1, store_addr);
+ __ z_xgr(tmp1, new_val);
+ }
+ __ z_srag(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
+ __ branch_optimized(Assembler::bcondEqual, done);
+
+ // Crosses regions, storing null?
+ if (new_val_may_be_null) {
+ __ z_ltgr(new_val, new_val);
+ __ z_bre(done);
+ } else {
+#ifdef ASSERT
+ __ z_ltgr(new_val, new_val);
+ __ asm_assert(Assembler::bcondNotZero, "null oop not allowed (G1 post)", 0x322); // Checked by caller.
+#endif
+ }
+
+ __ z_srag(tmp1, store_addr, CardTable::card_shift());
+
+ Address card_table_addr(thread, in_bytes(G1ThreadLocalData::card_table_base_offset()));
+ __ z_alg(tmp1, card_table_addr); // tmp1 := card address
+
+ if(UseCondCardMark) {
+ __ z_cli(0, tmp1, G1CardTable::clean_card_val());
+ __ branch_optimized(Assembler::bcondNotEqual, done);
+ }
+
+ static_assert(G1CardTable::dirty_card_val() == 0, "must be to use z_mvi");
+ __ z_mvi(0, tmp1, G1CardTable::dirty_card_val()); // *(card address) := dirty_card_val
+
+ __ block_comment("} generate_post_barrier");
+}
+
#if defined(COMPILER2)
#undef __
@@ -204,57 +255,6 @@ void G1BarrierSetAssembler::generate_c2_pre_barrier_stub(MacroAssembler* masm,
BLOCK_COMMENT("} generate_c2_pre_barrier_stub");
}
-static void generate_post_barrier(MacroAssembler* masm,
- const Register store_addr,
- const Register new_val,
- const Register thread,
- const Register tmp1,
- const Register tmp2,
- Label& done,
- bool new_val_may_be_null) {
-
- __ block_comment("generate_post_barrier {");
-
- assert(thread == Z_thread, "must be");
- assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg);
-
- // Does store cross heap regions?
- if (VM_Version::has_DistinctOpnds()) {
- __ z_xgrk(tmp1, store_addr, new_val); // tmp1 := store address ^ new value
- } else {
- __ z_lgr(tmp1, store_addr);
- __ z_xgr(tmp1, new_val);
- }
- __ z_srag(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
- __ branch_optimized(Assembler::bcondEqual, done);
-
- // Crosses regions, storing null?
- if (new_val_may_be_null) {
- __ z_ltgr(new_val, new_val);
- __ z_bre(done);
- } else {
-#ifdef ASSERT
- __ z_ltgr(new_val, new_val);
- __ asm_assert(Assembler::bcondNotZero, "null oop not allowed (G1 post)", 0x322); // Checked by caller.
-#endif
- }
-
- __ z_srag(tmp1, store_addr, CardTable::card_shift());
-
- Address card_table_addr(thread, in_bytes(G1ThreadLocalData::card_table_base_offset()));
- __ z_alg(tmp1, card_table_addr); // tmp1 := card address
-
- if(UseCondCardMark) {
- __ z_cli(0, tmp1, G1CardTable::clean_card_val());
- __ branch_optimized(Assembler::bcondNotEqual, done);
- }
-
- static_assert(G1CardTable::dirty_card_val() == 0, "must be to use z_mvi");
- __ z_mvi(0, tmp1, G1CardTable::dirty_card_val()); // *(card address) := dirty_card_val
-
- __ block_comment("} generate_post_barrier");
-}
-
void G1BarrierSetAssembler::g1_write_barrier_post_c2(MacroAssembler* masm,
Register store_addr,
Register new_val,
diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp
index c6f5a4e119c..7617c7a49e8 100644
--- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp
+++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -206,6 +206,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na
return opto_reg;
}
+void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Register tmp, Label& slow_path) {
+ // Load the oop from the weak handle.
+ __ z_lg(obj, Address(obj));
+}
+
#undef __
#define __ _masm->
diff --git a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp
index 65db915b672..d5682450414 100644
--- a/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp
+++ b/src/hotspot/cpu/s390/gc/shared/barrierSetAssembler_s390.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -65,6 +65,8 @@ public:
#ifdef COMPILER2
OptoReg::Name refine_register(const Node* node,
OptoReg::Name opto_reg) const;
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj,
+ Register tmp, Label& slow_path);
#endif // COMPILER2
static const int OFFSET_TO_PATCHABLE_DATA_INSTRUCTION = 6 + 6 + 6; // iihf(6) + iilf(6) + lg(6)
diff --git a/src/hotspot/cpu/s390/globals_s390.hpp b/src/hotspot/cpu/s390/globals_s390.hpp
index 07987ea3469..d110443adf8 100644
--- a/src/hotspot/cpu/s390/globals_s390.hpp
+++ b/src/hotspot/cpu/s390/globals_s390.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -43,7 +43,7 @@ define_pd_global(size_t, CodeCacheSegmentSize, 256);
// Ideally, this is 256 (cache line size). This keeps code end data
// on separate lines. But we reduced it to 64 since 256 increased
// code size significantly by padding nops between IVC and second UEP.
-define_pd_global(intx, CodeEntryAlignment, 64);
+define_pd_global(uint, CodeEntryAlignment, 64);
define_pd_global(intx, OptoLoopAlignment, 2);
define_pd_global(intx, InlineSmallCode, 2000);
diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp
index f35e18c7398..78779a9098a 100644
--- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp
+++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2024 SAP SE. All rights reserved.
* Copyright 2024 IBM Corporation. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -44,6 +44,7 @@
#include "runtime/icache.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/objectMonitor.hpp"
+#include "runtime/objectMonitorTable.hpp"
#include "runtime/os.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/safepointMechanism.hpp"
@@ -6372,45 +6373,55 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis
if (!UseObjectMonitorTable) {
assert(tmp1_monitor == mark, "should be the same here");
} else {
+ const Register tmp1_bucket = tmp1;
+ const Register hash = Z_R0_scratch;
NearLabel monitor_found;
- // load cache address
- z_la(tmp1, Address(Z_thread, JavaThread::om_cache_oops_offset()));
+ // Save the mark, we might need it to extract the hash.
+ z_lgr(hash, mark);
- const int num_unrolled = 2;
+ // Look for the monitor in the om_cache.
+
+ ByteSize cache_offset = JavaThread::om_cache_oops_offset();
+ ByteSize monitor_offset = OMCache::oop_to_monitor_difference();
+ const int num_unrolled = OMCache::CAPACITY;
for (int i = 0; i < num_unrolled; i++) {
- z_cg(obj, Address(tmp1));
+ z_lg(tmp1_monitor, Address(Z_thread, cache_offset + monitor_offset));
+ z_cg(obj, Address(Z_thread, cache_offset));
z_bre(monitor_found);
- add2reg(tmp1, in_bytes(OMCache::oop_to_oop_difference()));
+ cache_offset = cache_offset + OMCache::oop_to_oop_difference();
}
- NearLabel loop;
- // Search for obj in cache
+ // Get the hash code.
+ z_srlg(hash, hash, markWord::hash_shift);
- bind(loop);
+ // Get the table and calculate the bucket's address.
+ load_const_optimized(tmp2, ObjectMonitorTable::current_table_address());
+ z_lg(tmp2, Address(tmp2));
+ z_ng(hash, Address(tmp2, ObjectMonitorTable::table_capacity_mask_offset()));
+ z_lg(tmp1_bucket, Address(tmp2, ObjectMonitorTable::table_buckets_offset()));
+ z_sllg(hash, hash, LogBytesPerWord);
+ z_agr(tmp1_bucket, hash);
- // check for match.
- z_cg(obj, Address(tmp1));
- z_bre(monitor_found);
+ // Read the monitor from the bucket.
+ z_lg(tmp1_monitor, Address(tmp1_bucket));
- // search until null encountered, guaranteed _null_sentinel at end.
- add2reg(tmp1, in_bytes(OMCache::oop_to_oop_difference()));
- z_cghsi(0, tmp1, 0);
- z_brne(loop); // if not EQ to 0, go for another loop
+ // Check if the monitor in the bucket is special (empty, tombstone or removed).
+ z_clgfi(tmp1_monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ z_brl(slow_path);
- // we reached to the end, cache miss
- z_ltgr(obj, obj); // set CC to NE
- z_bru(slow_path);
+ // Check if object matches.
+ z_lg(tmp2, Address(tmp1_monitor, ObjectMonitor::object_offset()));
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_resolve_weak_handle_in_c2(this, tmp2, Z_R0_scratch, slow_path);
+ z_cgr(obj, tmp2);
+ z_brne(slow_path);
- // cache hit
bind(monitor_found);
- z_lg(tmp1_monitor, Address(tmp1, OMCache::oop_to_monitor_difference()));
}
NearLabel monitor_locked;
// lock the monitor
- // mark contains the tagged ObjectMonitor*.
- const Register tagged_monitor = mark;
const Register zero = tmp2;
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value));
diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp
index cbc5c6988d4..3c8defe62d9 100644
--- a/src/hotspot/cpu/x86/assembler_x86.cpp
+++ b/src/hotspot/cpu/x86/assembler_x86.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -7320,6 +7320,25 @@ void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
emit_int16(0x2E, (0xC0 | encode));
}
+void Assembler::vucomxsd(XMMRegister dst, Address src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
+ emit_int8(0x2E);
+ emit_operand(dst, src, 0);
+}
+
+void Assembler::vucomxsd(XMMRegister dst, XMMRegister src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
+ emit_int16(0x2E, (0xC0 | encode));
+}
+
void Assembler::ucomiss(XMMRegister dst, Address src) {
InstructionMark im(this);
InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
@@ -7335,6 +7354,25 @@ void Assembler::ucomiss(XMMRegister dst, XMMRegister src) {
emit_int16(0x2E, (0xC0 | encode));
}
+void Assembler::vucomxss(XMMRegister dst, Address src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionMark im(this);
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
+ attributes.set_is_evex_instruction();
+ vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
+ emit_int8(0x2E);
+ emit_operand(dst, src, 0);
+}
+
+void Assembler::vucomxss(XMMRegister dst, XMMRegister src) {
+ assert(VM_Version::supports_avx10_2(), "");
+ InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_is_evex_instruction();
+ int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
+ emit_int16(0x2E, (0xC0 | encode));
+}
+
void Assembler::xabort(int8_t imm8) {
emit_int24((unsigned char)0xC6, (unsigned char)0xF8, (imm8 & 0xFF));
}
diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp
index be7fbde03a2..3aee0b84f0d 100644
--- a/src/hotspot/cpu/x86/assembler_x86.hpp
+++ b/src/hotspot/cpu/x86/assembler_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -2335,10 +2335,14 @@ private:
// Unordered Compare Scalar Double-Precision Floating-Point Values and set EFLAGS
void ucomisd(XMMRegister dst, Address src);
void ucomisd(XMMRegister dst, XMMRegister src);
+ void vucomxsd(XMMRegister dst, Address src);
+ void vucomxsd(XMMRegister dst, XMMRegister src);
// Unordered Compare Scalar Single-Precision Floating-Point Values and set EFLAGS
void ucomiss(XMMRegister dst, Address src);
void ucomiss(XMMRegister dst, XMMRegister src);
+ void vucomxss(XMMRegister dst, Address src);
+ void vucomxss(XMMRegister dst, XMMRegister src);
void xabort(int8_t imm8);
diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
index d104a59e5c4..320d30b8172 100644
--- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
@@ -32,6 +32,7 @@
#include "c1/c1_ValueStack.hpp"
#include "ci/ciArrayKlass.hpp"
#include "ci/ciInstance.hpp"
+#include "code/aotCodeCache.hpp"
#include "compiler/oopMap.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "gc/shared/gc_globals.hpp"
@@ -539,6 +540,15 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod
case T_LONG: {
assert(patch_code == lir_patch_none, "no patching handled here");
+#if INCLUDE_CDS
+ if (AOTCodeCache::is_on_for_dump()) {
+ address b = c->as_pointer();
+ if (AOTRuntimeConstants::contains(b)) {
+ __ load_aotrc_address(dest->as_register_lo(), b);
+ break;
+ }
+ }
+#endif
__ movptr(dest->as_register_lo(), (intptr_t)c->as_jlong());
break;
}
diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
index 8fc3d18abb1..a3ccc081b6b 100644
--- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,7 +34,9 @@
#include "opto/subnode.hpp"
#include "runtime/globals.hpp"
#include "runtime/objectMonitor.hpp"
+#include "runtime/objectMonitorTable.hpp"
#include "runtime/stubRoutines.hpp"
+#include "runtime/synchronizer.hpp"
#include "utilities/checkedCast.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/powerOfTwo.hpp"
@@ -217,7 +219,6 @@ inline Assembler::AvxVectorLen C2_MacroAssembler::vector_length_encoding(int vle
// In the case of failure, the node will branch directly to the
// FailureLabel
-
// obj: object to lock
// box: on-stack box address -- KILLED
// rax: tmp -- KILLED
@@ -286,7 +287,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg,
// After successful lock, push object on lock-stack.
movptr(Address(thread, top), obj);
addl(Address(thread, JavaThread::lock_stack_top_offset()), oopSize);
- jmpb(locked);
+ jmp(locked);
}
{ // Handle inflated monitor.
@@ -297,38 +298,49 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg,
if (!UseObjectMonitorTable) {
assert(mark == monitor, "should be the same here");
} else {
- // Uses ObjectMonitorTable. Look for the monitor in the om_cache.
- // Fetch ObjectMonitor* from the cache or take the slow-path.
+ const Register hash = t;
Label monitor_found;
- // Load cache address
- lea(t, Address(thread, JavaThread::om_cache_oops_offset()));
+ // Look for the monitor in the om_cache.
- const int num_unrolled = 2;
+ ByteSize cache_offset = JavaThread::om_cache_oops_offset();
+ ByteSize monitor_offset = OMCache::oop_to_monitor_difference();
+ const int num_unrolled = OMCache::CAPACITY;
for (int i = 0; i < num_unrolled; i++) {
- cmpptr(obj, Address(t));
+ movptr(monitor, Address(thread, cache_offset + monitor_offset));
+ cmpptr(obj, Address(thread, cache_offset));
jccb(Assembler::equal, monitor_found);
- increment(t, in_bytes(OMCache::oop_to_oop_difference()));
+ cache_offset = cache_offset + OMCache::oop_to_oop_difference();
}
- Label loop;
+ // Look for the monitor in the table.
- // Search for obj in cache.
- bind(loop);
+ // Get the hash code.
+ movptr(hash, Address(obj, oopDesc::mark_offset_in_bytes()));
+ shrq(hash, markWord::hash_shift);
+ andq(hash, markWord::hash_mask);
- // Check for match.
- cmpptr(obj, Address(t));
- jccb(Assembler::equal, monitor_found);
+ // Get the table and calculate the bucket's address.
+ lea(rax_reg, ExternalAddress(ObjectMonitorTable::current_table_address()));
+ movptr(rax_reg, Address(rax_reg));
+ andq(hash, Address(rax_reg, ObjectMonitorTable::table_capacity_mask_offset()));
+ movptr(rax_reg, Address(rax_reg, ObjectMonitorTable::table_buckets_offset()));
- // Search until null encountered, guaranteed _null_sentinel at end.
- cmpptr(Address(t), 1);
- jcc(Assembler::below, slow_path); // 0 check, but with ZF=0 when *t == 0
- increment(t, in_bytes(OMCache::oop_to_oop_difference()));
- jmpb(loop);
+ // Read the monitor from the bucket.
+ movptr(monitor, Address(rax_reg, hash, Address::times_ptr));
+
+ // Check if the monitor in the bucket is special (empty, tombstone or removed)
+ cmpptr(monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ jcc(Assembler::below, slow_path);
+
+ // Check if object matches.
+ movptr(rax_reg, Address(monitor, ObjectMonitor::object_offset()));
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_resolve_weak_handle_in_c2(this, rax_reg, slow_path);
+ cmpptr(rax_reg, obj);
+ jcc(Assembler::notEqual, slow_path);
- // Cache hit.
bind(monitor_found);
- movptr(monitor, Address(t, OMCache::oop_to_monitor_difference()));
}
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value));
const Address recursions_address(monitor, ObjectMonitor::recursions_offset() - monitor_tag);
@@ -487,14 +499,14 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
cmpl(top, in_bytes(JavaThread::lock_stack_base_offset()));
jcc(Assembler::below, check_done);
cmpptr(obj, Address(thread, top));
- jccb(Assembler::notEqual, inflated_check_lock_stack);
+ jcc(Assembler::notEqual, inflated_check_lock_stack);
stop("Fast Unlock lock on stack");
bind(check_done);
if (UseObjectMonitorTable) {
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
}
testptr(mark, markWord::monitor_value);
- jccb(Assembler::notZero, inflated);
+ jcc(Assembler::notZero, inflated);
stop("Fast Unlock not monitor");
#endif
@@ -519,7 +531,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
// Check if recursive.
cmpptr(recursions_address, 0);
- jccb(Assembler::notZero, recursive);
+ jcc(Assembler::notZero, recursive);
// Set owner to null.
// Release to satisfy the JMM
@@ -530,11 +542,11 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
// Check if the entry_list is empty.
cmpptr(entry_list_address, NULL_WORD);
- jccb(Assembler::zero, unlocked); // If so we are done.
+ jcc(Assembler::zero, unlocked); // If so we are done.
// Check if there is a successor.
cmpptr(succ_address, NULL_WORD);
- jccb(Assembler::notZero, unlocked); // If so we are done.
+ jcc(Assembler::notZero, unlocked); // If so we are done.
// Save the monitor pointer in the current thread, so we can try to
// reacquire the lock in SharedRuntime::monitor_exit_helper().
@@ -1046,17 +1058,28 @@ void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero,
Label DONE_LABEL;
+ // Handle special cases +0.0/-0.0 and NaN, if argument is +0.0/-0.0 or NaN, return argument
+ // If AVX10.2 (or newer) floating point comparison instructions used, SF=1 for equal and unordered cases
+ // If other floating point comparison instructions used, ZF=1 for equal and unordered cases
if (opcode == Op_SignumF) {
- ucomiss(dst, zero);
- jcc(Assembler::equal, DONE_LABEL); // handle special case +0.0/-0.0, if argument is +0.0/-0.0, return argument
- jcc(Assembler::parity, DONE_LABEL); // handle special case NaN, if argument NaN, return NaN
+ if (VM_Version::supports_avx10_2()) {
+ vucomxss(dst, zero);
+ jcc(Assembler::negative, DONE_LABEL);
+ } else {
+ ucomiss(dst, zero);
+ jcc(Assembler::equal, DONE_LABEL);
+ }
movflt(dst, one);
jcc(Assembler::above, DONE_LABEL);
xorps(dst, ExternalAddress(StubRoutines::x86::vector_float_sign_flip()), noreg);
} else if (opcode == Op_SignumD) {
- ucomisd(dst, zero);
- jcc(Assembler::equal, DONE_LABEL); // handle special case +0.0/-0.0, if argument is +0.0/-0.0, return argument
- jcc(Assembler::parity, DONE_LABEL); // handle special case NaN, if argument NaN, return NaN
+ if (VM_Version::supports_avx10_2()) {
+ vucomxsd(dst, zero);
+ jcc(Assembler::negative, DONE_LABEL);
+ } else {
+ ucomisd(dst, zero);
+ jcc(Assembler::equal, DONE_LABEL);
+ }
movdbl(dst, one);
jcc(Assembler::above, DONE_LABEL);
xorpd(dst, ExternalAddress(StubRoutines::x86::vector_double_sign_flip()), noreg);
diff --git a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp
index 34de9403ccf..b20d7b5cd07 100644
--- a/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/g1/g1BarrierSetAssembler_x86.cpp
@@ -23,6 +23,7 @@
*/
#include "asm/macroAssembler.inline.hpp"
+#include "code/aotCodeCache.hpp"
#include "gc/g1/g1BarrierSet.hpp"
#include "gc/g1/g1BarrierSetAssembler.hpp"
#include "gc/g1/g1BarrierSetRuntime.hpp"
@@ -268,6 +269,16 @@ void G1BarrierSetAssembler::g1_write_barrier_pre(MacroAssembler* masm,
__ bind(done);
}
+#if INCLUDE_CDS
+// return a register that differs from reg1, reg2, reg3 and reg4
+
+static Register pick_different_reg(Register reg1, Register reg2 = noreg, Register reg3= noreg, Register reg4 = noreg) {
+ RegSet available = (RegSet::of(rscratch1, rscratch2, rax, rbx) + rdx -
+ RegSet::of(reg1, reg2, reg3, reg4));
+ return *(available.begin());
+}
+#endif // INCLUDE_CDS
+
static void generate_post_barrier(MacroAssembler* masm,
const Register store_addr,
const Register new_val,
@@ -280,10 +291,32 @@ static void generate_post_barrier(MacroAssembler* masm,
Label L_done;
// Does store cross heap regions?
- __ movptr(tmp1, store_addr); // tmp1 := store address
- __ xorptr(tmp1, new_val); // tmp1 := store address ^ new value
- __ shrptr(tmp1, G1HeapRegion::LogOfHRGrainBytes); // ((store address ^ new value) >> LogOfHRGrainBytes) == 0?
- __ jccb(Assembler::equal, L_done);
+#if INCLUDE_CDS
+ // AOT code needs to load the barrier grain shift from the aot
+ // runtime constants area in the code cache otherwise we can compile
+ // it as an immediate operand
+
+ if (AOTCodeCache::is_on_for_dump()) {
+ address grain_shift_addr = AOTRuntimeConstants::grain_shift_address();
+ Register save = pick_different_reg(rcx, tmp1, new_val, store_addr);
+ __ push(save);
+ __ movptr(save, store_addr);
+ __ xorptr(save, new_val);
+ __ push(rcx);
+ __ lea(rcx, ExternalAddress(grain_shift_addr));
+ __ movl(rcx, Address(rcx, 0));
+ __ shrptr(save);
+ __ pop(rcx);
+ __ pop(save);
+ __ jcc(Assembler::equal, L_done);
+ } else
+#endif // INCLUDE_CDS
+ {
+ __ movptr(tmp1, store_addr); // tmp1 := store address
+ __ xorptr(tmp1, new_val); // tmp1 := store address ^ new value
+ __ shrptr(tmp1, G1HeapRegion::LogOfHRGrainBytes); // ((store address ^ new value) >> LogOfHRGrainBytes) == 0?
+ __ jccb(Assembler::equal, L_done);
+ }
// Crosses regions, storing null?
if (new_val_may_be_null) {
diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
index 09c5d93dbb3..215dc30f7fd 100644
--- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -395,6 +395,11 @@ OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Na
extern void vec_spill_helper(C2_MacroAssembler *masm, bool is_load,
int stack_offset, int reg, uint ireg, outputStream* st);
+void BarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slowpath) {
+ // Load the oop from the weak handle.
+ __ movptr(obj, Address(obj));
+}
+
#undef __
#define __ _masm->
diff --git a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp
index c5bf17c3b4e..6aff29850e3 100644
--- a/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/gc/shared/barrierSetAssembler_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -109,6 +109,8 @@ public:
#ifdef COMPILER2
OptoReg::Name refine_register(const Node* node,
OptoReg::Name opto_reg);
+
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slowpath);
#endif // COMPILER2
};
diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp
index 65e6b4e01fc..0ea769dd488 100644
--- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp
@@ -23,6 +23,7 @@
*/
#include "asm/macroAssembler.inline.hpp"
+#include "code/aotCodeCache.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/cardTable.hpp"
#include "gc/shared/cardTableBarrierSet.hpp"
@@ -111,7 +112,15 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl
__ shrptr(end, CardTable::card_shift());
__ subptr(end, addr); // end --> cards count
- __ mov64(tmp, (intptr_t)ctbs->card_table_base_const());
+#if INCLUDE_CDS
+ if (AOTCodeCache::is_on_for_dump()) {
+ __ lea(tmp, ExternalAddress(AOTRuntimeConstants::card_table_base_address()));
+ __ movq(tmp, Address(tmp, 0));
+ } else
+#endif
+ {
+ __ mov64(tmp, (intptr_t)ctbs->card_table_base_const());
+ }
__ addptr(addr, tmp);
__ BIND(L_loop);
__ movb(Address(addr, count, Address::times_1), 0);
@@ -121,7 +130,7 @@ __ BIND(L_loop);
__ BIND(L_done);
}
-void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst) {
+void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst, Register rscratch) {
// Does a store check for the oop in register obj. The content of
// register obj is destroyed afterwards.
CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set();
@@ -136,6 +145,13 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob
// never need to be relocated. On 64bit however the value may be too
// large for a 32bit displacement.
intptr_t byte_map_base = (intptr_t)ctbs->card_table_base_const();
+#if INCLUDE_CDS
+ if (AOTCodeCache::is_on_for_dump()) {
+ __ lea(rscratch, ExternalAddress(AOTRuntimeConstants::card_table_base_address()));
+ __ movq(rscratch, Address(rscratch, 0));
+ card_addr = Address(rscratch, obj, Address::times_1, 0);
+ } else
+#endif
if (__ is_simm32(byte_map_base)) {
card_addr = Address(noreg, obj, Address::times_1, byte_map_base);
} else {
@@ -174,10 +190,10 @@ void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorS
if (needs_post_barrier) {
// flatten object address if needed
if (!precise || (dst.index() == noreg && dst.disp() == 0)) {
- store_check(masm, dst.base(), dst);
+ store_check(masm, dst.base(), dst, tmp2);
} else {
__ lea(tmp1, dst);
- store_check(masm, tmp1, dst);
+ store_check(masm, tmp1, dst, tmp2);
}
}
}
diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp
index 0a36571c757..201c11062f2 100644
--- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.hpp
@@ -33,7 +33,7 @@ protected:
virtual void gen_write_ref_array_pre_barrier(MacroAssembler* masm, DecoratorSet decorators,
Register addr, Register count) {}
- void store_check(MacroAssembler* masm, Register obj, Address dst);
+ void store_check(MacroAssembler* masm, Register obj, Address dst, Register rscratch);
virtual void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register tmp);
diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp
index 97829a10a3b..67510fac58f 100644
--- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -618,6 +619,27 @@ void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler
__ bind(done);
}
+#ifdef COMPILER2
+void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slowpath) {
+ Label done;
+
+ // Resolve weak handle using the standard implementation.
+ BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, slowpath);
+
+ // Check if the reference is null, and if it is, take the fast path.
+ __ testptr(obj, obj);
+ __ jcc(Assembler::zero, done);
+
+ Address gc_state(r15_thread, ShenandoahThreadLocalData::gc_state_offset());
+
+ // Check if the heap is under weak-reference/roots processing, in
+ // which case we need to take the slow path.
+ __ testb(gc_state, ShenandoahHeap::WEAK_ROOTS);
+ __ jcc(Assembler::notZero, slowpath);
+ __ bind(done);
+}
+#endif // COMPILER2
+
// Special Shenandoah CAS implementation that handles false negatives
// due to concurrent evacuation.
void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm,
diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp
index b5cc5c8d834..79540aa19e1 100644
--- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -77,6 +78,9 @@ public:
Address dst, Register val, Register tmp1, Register tmp2, Register tmp3);
virtual void try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
Register obj, Register tmp, Label& slowpath);
+#ifdef COMPILER2
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slowpath);
+#endif // COMPILER2
};
#endif // CPU_X86_GC_SHENANDOAH_SHENANDOAHBARRIERSETASSEMBLER_X86_HPP
diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp
index ae93cca8c19..47a3dad54e7 100644
--- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1328,6 +1328,19 @@ void ZBarrierSetAssembler::generate_c2_store_barrier_stub(MacroAssembler* masm,
__ jmp(slow_continuation);
}
+void ZBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slow_path) {
+ // Resolve weak handle using the standard implementation.
+ BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, slow_path);
+
+ // Check if the oop is bad, in which case we need to take the slow path.
+ __ testptr(obj, Address(r15_thread, ZThreadLocalData::mark_bad_mask_offset()));
+ __ jcc(Assembler::notZero, slow_path);
+
+ // Oop is okay, so we uncolor it.
+ __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatLoadGoodBeforeShl);
+ __ shrq(obj, barrier_Relocation::unpatched);
+}
+
#undef __
#endif // COMPILER2
diff --git a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp
index 19902500f93..e91e2b9ea20 100644
--- a/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -167,6 +167,8 @@ public:
ZLoadBarrierStubC2* stub) const;
void generate_c2_store_barrier_stub(MacroAssembler* masm,
ZStoreBarrierStubC2* stub) const;
+
+ virtual void try_resolve_weak_handle_in_c2(MacroAssembler* masm, Register obj, Label& slow_path);
#endif // COMPILER2
void store_barrier_fast(MacroAssembler* masm,
diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp
index 103e22d0185..4f5b6d31e75 100644
--- a/src/hotspot/cpu/x86/globals_x86.hpp
+++ b/src/hotspot/cpu/x86/globals_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -46,9 +46,9 @@ define_pd_global(size_t, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRES
// the uep and the vep doesn't get real alignment but just slops on by
// only assured that the entry instruction meets the 5 byte size requirement.
#if COMPILER2_OR_JVMCI
-define_pd_global(intx, CodeEntryAlignment, 32);
+define_pd_global(uint, CodeEntryAlignment, 32);
#else
-define_pd_global(intx, CodeEntryAlignment, 16);
+define_pd_global(uint, CodeEntryAlignment, 16);
#endif // COMPILER2_OR_JVMCI
define_pd_global(intx, OptoLoopAlignment, 16);
define_pd_global(intx, InlineSmallCode, 1000);
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
index ce7d2b8b03a..6d902321ba8 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
@@ -765,7 +765,7 @@ void MacroAssembler::align32() {
void MacroAssembler::align(uint modulus) {
// 8273459: Ensure alignment is possible with current segment alignment
- assert(modulus <= (uintx)CodeEntryAlignment, "Alignment must be <= CodeEntryAlignment");
+ assert(modulus <= CodeEntryAlignment, "Alignment must be <= CodeEntryAlignment");
align(modulus, offset());
}
@@ -2656,6 +2656,17 @@ void MacroAssembler::ucomisd(XMMRegister dst, AddressLiteral src, Register rscra
}
}
+void MacroAssembler::vucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch) {
+ assert(rscratch != noreg || always_reachable(src), "missing");
+
+ if (reachable(src)) {
+ Assembler::vucomxsd(dst, as_Address(src));
+ } else {
+ lea(rscratch, src);
+ Assembler::vucomxsd(dst, Address(rscratch, 0));
+ }
+}
+
void MacroAssembler::ucomiss(XMMRegister dst, AddressLiteral src, Register rscratch) {
assert(rscratch != noreg || always_reachable(src), "missing");
@@ -2667,6 +2678,17 @@ void MacroAssembler::ucomiss(XMMRegister dst, AddressLiteral src, Register rscra
}
}
+void MacroAssembler::vucomxss(XMMRegister dst, AddressLiteral src, Register rscratch) {
+ assert(rscratch != noreg || always_reachable(src), "missing");
+
+ if (reachable(src)) {
+ Assembler::vucomxss(dst, as_Address(src));
+ } else {
+ lea(rscratch, src);
+ Assembler::vucomxss(dst, Address(rscratch, 0));
+ }
+}
+
void MacroAssembler::xorpd(XMMRegister dst, AddressLiteral src, Register rscratch) {
assert(rscratch != noreg || always_reachable(src), "missing");
@@ -6264,32 +6286,46 @@ void MacroAssembler::evpbroadcast(BasicType type, XMMRegister dst, Register src,
}
}
-// encode char[] to byte[] in ISO_8859_1 or ASCII
- //@IntrinsicCandidate
- //private static int implEncodeISOArray(byte[] sa, int sp,
- //byte[] da, int dp, int len) {
- // int i = 0;
- // for (; i < len; i++) {
- // char c = StringUTF16.getChar(sa, sp++);
- // if (c > '\u00FF')
- // break;
- // da[dp++] = (byte)c;
- // }
- // return i;
- //}
- //
- //@IntrinsicCandidate
- //private static int implEncodeAsciiArray(char[] sa, int sp,
- // byte[] da, int dp, int len) {
- // int i = 0;
- // for (; i < len; i++) {
- // char c = sa[sp++];
- // if (c >= '\u0080')
- // break;
- // da[dp++] = (byte)c;
- // }
- // return i;
- //}
+// Encode given char[]/byte[] to byte[] in ISO_8859_1 or ASCII
+//
+// @IntrinsicCandidate
+// int sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(
+// char[] sa, int sp, byte[] da, int dp, int len) {
+// int i = 0;
+// for (; i < len; i++) {
+// char c = sa[sp++];
+// if (c > '\u00FF')
+// break;
+// da[dp++] = (byte) c;
+// }
+// return i;
+// }
+//
+// @IntrinsicCandidate
+// int java.lang.StringCoding.encodeISOArray0(
+// byte[] sa, int sp, byte[] da, int dp, int len) {
+// int i = 0;
+// for (; i < len; i++) {
+// char c = StringUTF16.getChar(sa, sp++);
+// if (c > '\u00FF')
+// break;
+// da[dp++] = (byte) c;
+// }
+// return i;
+// }
+//
+// @IntrinsicCandidate
+// int java.lang.StringCoding.encodeAsciiArray0(
+// char[] sa, int sp, byte[] da, int dp, int len) {
+// int i = 0;
+// for (; i < len; i++) {
+// char c = sa[sp++];
+// if (c >= '\u0080')
+// break;
+// da[dp++] = (byte) c;
+// }
+// return i;
+// }
void MacroAssembler::encode_iso_array(Register src, Register dst, Register len,
XMMRegister tmp1Reg, XMMRegister tmp2Reg,
XMMRegister tmp3Reg, XMMRegister tmp4Reg,
@@ -10011,6 +10047,20 @@ void MacroAssembler::restore_legacy_gprs() {
addq(rsp, 16 * wordSize);
}
+void MacroAssembler::load_aotrc_address(Register reg, address a) {
+#if INCLUDE_CDS
+ assert(AOTRuntimeConstants::contains(a), "address out of range for data area");
+ if (AOTCodeCache::is_on_for_dump()) {
+ // all aotrc field addresses should be registered in the AOTCodeCache address table
+ lea(reg, ExternalAddress(a));
+ } else {
+ mov64(reg, (uint64_t)a);
+ }
+#else
+ ShouldNotReachHere();
+#endif
+}
+
void MacroAssembler::setcc(Assembler::Condition comparison, Register dst) {
if (VM_Version::supports_apx_f()) {
esetzucc(comparison, dst);
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
index 27da2ac53c6..5c193f8c298 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1318,10 +1318,18 @@ public:
void ucomiss(XMMRegister dst, Address src) { Assembler::ucomiss(dst, src); }
void ucomiss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg);
+ void vucomxss(XMMRegister dst, XMMRegister src) { Assembler::vucomxss(dst, src); }
+ void vucomxss(XMMRegister dst, Address src) { Assembler::vucomxss(dst, src); }
+ void vucomxss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg);
+
void ucomisd(XMMRegister dst, XMMRegister src) { Assembler::ucomisd(dst, src); }
void ucomisd(XMMRegister dst, Address src) { Assembler::ucomisd(dst, src); }
void ucomisd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg);
+ void vucomxsd(XMMRegister dst, XMMRegister src) { Assembler::vucomxsd(dst, src); }
+ void vucomxsd(XMMRegister dst, Address src) { Assembler::vucomxsd(dst, src); }
+ void vucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg);
+
// Bitwise Logical XOR of Packed Double-Precision Floating-Point Values
void xorpd(XMMRegister dst, XMMRegister src);
void xorpd(XMMRegister dst, Address src) { Assembler::xorpd(dst, src); }
@@ -2067,6 +2075,7 @@ public:
void save_legacy_gprs();
void restore_legacy_gprs();
+ void load_aotrc_address(Register reg, address a);
void setcc(Assembler::Condition comparison, Register dst);
};
diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp
index 74df41f8682..b352de77d6f 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.cpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.cpp
@@ -48,7 +48,7 @@ int VM_Version::_stepping;
bool VM_Version::_has_intel_jcc_erratum;
VM_Version::CpuidInfo VM_Version::_cpuid_info = { 0, };
-#define DECLARE_CPU_FEATURE_NAME(id, name, bit) name,
+#define DECLARE_CPU_FEATURE_NAME(id, name, bit) XSTR(name),
const char* VM_Version::_features_names[] = { CPU_FEATURE_FLAGS(DECLARE_CPU_FEATURE_NAME)};
#undef DECLARE_CPU_FEATURE_NAME
@@ -958,9 +958,17 @@ void VM_Version::get_processor_features() {
if (UseSSE < 1)
_features.clear_feature(CPU_SSE);
- //since AVX instructions is slower than SSE in some ZX cpus, force USEAVX=0.
- if (is_zx() && ((cpu_family() == 6) || (cpu_family() == 7))) {
- UseAVX = 0;
+ // ZX cpus specific settings
+ if (is_zx() && FLAG_IS_DEFAULT(UseAVX)) {
+ if (cpu_family() == 7) {
+ if (extended_cpu_model() == 0x5B || extended_cpu_model() == 0x6B) {
+ UseAVX = 1;
+ } else if (extended_cpu_model() == 0x1B || extended_cpu_model() == 0x3B) {
+ UseAVX = 0;
+ }
+ } else if (cpu_family() == 6) {
+ UseAVX = 0;
+ }
}
// UseSSE is set to the smaller of what hardware supports and what
@@ -1152,6 +1160,10 @@ void VM_Version::get_processor_features() {
warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled.");
}
FLAG_SET_DEFAULT(UseAESIntrinsics, false);
+ if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) {
+ warning("AES_CTR intrinsics require UseAES flag to be enabled. AES_CTR intrinsics will be disabled.");
+ }
+ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false);
} else {
if (UseSSE > 2) {
if (FLAG_IS_DEFAULT(UseAESIntrinsics)) {
@@ -1170,8 +1182,8 @@ void VM_Version::get_processor_features() {
if (!UseAESIntrinsics) {
if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) {
warning("AES-CTR intrinsics require UseAESIntrinsics flag to be enabled. Intrinsics will be disabled.");
- FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false);
}
+ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false);
} else {
if (supports_sse4_1()) {
if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) {
@@ -1191,16 +1203,16 @@ void VM_Version::get_processor_features() {
} else if (UseAES || UseAESIntrinsics || UseAESCTRIntrinsics) {
if (UseAES && !FLAG_IS_DEFAULT(UseAES)) {
warning("AES instructions are not available on this CPU");
- FLAG_SET_DEFAULT(UseAES, false);
}
+ FLAG_SET_DEFAULT(UseAES, false);
if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) {
warning("AES intrinsics are not available on this CPU");
- FLAG_SET_DEFAULT(UseAESIntrinsics, false);
}
+ FLAG_SET_DEFAULT(UseAESIntrinsics, false);
if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) {
warning("AES-CTR intrinsics are not available on this CPU");
- FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false);
}
+ FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false);
}
// Use CLMUL instructions if available.
@@ -1355,16 +1367,16 @@ void VM_Version::get_processor_features() {
FLAG_SET_DEFAULT(UseSHA512Intrinsics, false);
}
- if (supports_evex() && supports_avx512bw()) {
- if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) {
- UseSHA3Intrinsics = true;
- }
+ if (UseSHA && supports_evex() && supports_avx512bw()) {
+ if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) {
+ FLAG_SET_DEFAULT(UseSHA3Intrinsics, true);
+ }
} 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);
+ 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);
}
- if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) {
+ if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics || UseSHA3Intrinsics)) {
FLAG_SET_DEFAULT(UseSHA, false);
}
@@ -1655,41 +1667,40 @@ void VM_Version::get_processor_features() {
if (FLAG_IS_DEFAULT(AllocatePrefetchInstr) && supports_3dnow_prefetch()) {
FLAG_SET_DEFAULT(AllocatePrefetchInstr, 3);
}
-#ifdef COMPILER2
- if (UseAVX > 2) {
- if (FLAG_IS_DEFAULT(ArrayOperationPartialInlineSize) ||
- (!FLAG_IS_DEFAULT(ArrayOperationPartialInlineSize) &&
- ArrayOperationPartialInlineSize != 0 &&
- ArrayOperationPartialInlineSize != 16 &&
- ArrayOperationPartialInlineSize != 32 &&
- ArrayOperationPartialInlineSize != 64)) {
- int inline_size = 0;
- if (MaxVectorSize >= 64 && AVX3Threshold == 0) {
- inline_size = 64;
- } else if (MaxVectorSize >= 32) {
- inline_size = 32;
- } else if (MaxVectorSize >= 16) {
- inline_size = 16;
- }
- if(!FLAG_IS_DEFAULT(ArrayOperationPartialInlineSize)) {
- warning("Setting ArrayOperationPartialInlineSize as %d", inline_size);
- }
- ArrayOperationPartialInlineSize = inline_size;
- }
-
- if (ArrayOperationPartialInlineSize > MaxVectorSize) {
- ArrayOperationPartialInlineSize = MaxVectorSize >= 16 ? MaxVectorSize : 0;
- if (ArrayOperationPartialInlineSize) {
- warning("Setting ArrayOperationPartialInlineSize as MaxVectorSize=%zd", MaxVectorSize);
- } else {
- warning("Setting ArrayOperationPartialInlineSize as %zd", ArrayOperationPartialInlineSize);
- }
- }
- }
-#endif
}
#ifdef COMPILER2
+ if (UseAVX > 2) {
+ if (FLAG_IS_DEFAULT(ArrayOperationPartialInlineSize) ||
+ (!FLAG_IS_DEFAULT(ArrayOperationPartialInlineSize) &&
+ ArrayOperationPartialInlineSize != 0 &&
+ ArrayOperationPartialInlineSize != 16 &&
+ ArrayOperationPartialInlineSize != 32 &&
+ ArrayOperationPartialInlineSize != 64)) {
+ int inline_size = 0;
+ if (MaxVectorSize >= 64 && AVX3Threshold == 0) {
+ inline_size = 64;
+ } else if (MaxVectorSize >= 32) {
+ inline_size = 32;
+ } else if (MaxVectorSize >= 16) {
+ inline_size = 16;
+ }
+ if(!FLAG_IS_DEFAULT(ArrayOperationPartialInlineSize)) {
+ warning("Setting ArrayOperationPartialInlineSize as %d", inline_size);
+ }
+ ArrayOperationPartialInlineSize = inline_size;
+ }
+
+ if (ArrayOperationPartialInlineSize > MaxVectorSize) {
+ ArrayOperationPartialInlineSize = MaxVectorSize >= 16 ? MaxVectorSize : 0;
+ if (ArrayOperationPartialInlineSize) {
+ warning("Setting ArrayOperationPartialInlineSize as MaxVectorSize=%zd", MaxVectorSize);
+ } else {
+ warning("Setting ArrayOperationPartialInlineSize as %zd", ArrayOperationPartialInlineSize);
+ }
+ }
+ }
+
if (FLAG_IS_DEFAULT(OptimizeFill)) {
if (MaxVectorSize < 32 || (!EnableX86ECoreOpts && !VM_Version::supports_avx512vlbw())) {
OptimizeFill = false;
@@ -2620,6 +2631,23 @@ const char* VM_Version::cpu_family_description(void) {
return _family_id_intel[cpu_family_id];
}
}
+ if (is_zx()) {
+ int cpu_model_id = extended_cpu_model();
+ if (cpu_family_id == 7) {
+ switch (cpu_model_id) {
+ case 0x1B:
+ return "wudaokou";
+ case 0x3B:
+ return "lujiazui";
+ case 0x5B:
+ return "yongfeng";
+ case 0x6B:
+ return "shijidadao";
+ }
+ } else if (cpu_family_id == 6) {
+ return "zhangjiang";
+ }
+ }
if (is_hygon()) {
return "Dhyana";
}
@@ -2639,6 +2667,9 @@ int VM_Version::cpu_type_description(char* const buf, size_t buf_len) {
} else if (is_amd()) {
cpu_type = "AMD";
x64 = cpu_is_em64t() ? " AMD64" : "";
+ } else if (is_zx()) {
+ cpu_type = "Zhaoxin";
+ x64 = cpu_is_em64t() ? " x86_64" : "";
} else if (is_hygon()) {
cpu_type = "Hygon";
x64 = cpu_is_em64t() ? " AMD64" : "";
@@ -3256,6 +3287,12 @@ int VM_Version::allocate_prefetch_distance(bool use_watermark_prefetch) {
} else {
return 128; // Athlon
}
+ } else if (is_zx()) {
+ if (supports_sse2()) {
+ return 256;
+ } else {
+ return 128;
+ }
} else { // Intel
if (supports_sse3() && is_intel_server_family()) {
if (supports_sse4_2() && supports_ht()) { // Nehalem based cpus
@@ -3294,12 +3331,50 @@ bool VM_Version::is_intrinsic_supported(vmIntrinsicID id) {
void VM_Version::insert_features_names(VM_Version::VM_Features features, stringStream& ss) {
int i = 0;
ss.join([&]() {
- while (i < MAX_CPU_FEATURES) {
- if (_features.supports_feature((VM_Version::Feature_Flag)i)) {
- return _features_names[i++];
+ const char* str = nullptr;
+ while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
+ if (features.supports_feature((VM_Version::Feature_Flag)i)) {
+ str = _features_names[i];
}
i += 1;
}
- return (const char*)nullptr;
+ return str;
}, ", ");
}
+
+void VM_Version::get_cpu_features_name(void* features_buffer, stringStream& ss) {
+ VM_Features* features = (VM_Features*)features_buffer;
+ insert_features_names(*features, ss);
+}
+
+void VM_Version::get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss) {
+ VM_Features* vm_features_set1 = (VM_Features*)features_set1;
+ VM_Features* vm_features_set2 = (VM_Features*)features_set2;
+ int i = 0;
+ ss.join([&]() {
+ const char* str = nullptr;
+ while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
+ Feature_Flag flag = (Feature_Flag)i;
+ if (vm_features_set1->supports_feature(flag) && !vm_features_set2->supports_feature(flag)) {
+ str = _features_names[i];
+ }
+ i += 1;
+ }
+ return str;
+ }, ", ");
+}
+
+int VM_Version::cpu_features_size() {
+ return sizeof(VM_Features);
+}
+
+void VM_Version::store_cpu_features(void* buf) {
+ VM_Features copy = _features;
+ copy.clear_feature(CPU_HT); // HT does not result in incompatibility of aot code cache
+ memcpy(buf, ©, sizeof(VM_Features));
+}
+
+bool VM_Version::supports_features(void* features_buffer) {
+ VM_Features* features_to_test = (VM_Features*)features_buffer;
+ return _features.supports_features(features_to_test);
+}
diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp
index a3f2a801198..e0a895737b7 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.hpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -377,84 +377,84 @@ protected:
*/
enum Feature_Flag {
#define CPU_FEATURE_FLAGS(decl) \
- decl(CX8, "cx8", 0) /* next bits are from cpuid 1 (EDX) */ \
- decl(CMOV, "cmov", 1) \
- decl(FXSR, "fxsr", 2) \
- decl(HT, "ht", 3) \
+ decl(CX8, cx8, 0) /* next bits are from cpuid 1 (EDX) */ \
+ decl(CMOV, cmov, 1) \
+ decl(FXSR, fxsr, 2) \
+ decl(HT, ht, 3) \
\
- decl(MMX, "mmx", 4) \
- decl(3DNOW_PREFETCH, "3dnowpref", 5) /* Processor supports 3dnow prefetch and prefetchw instructions */ \
+ decl(MMX, mmx, 4) \
+ decl(3DNOW_PREFETCH, 3dnowpref, 5) /* Processor supports 3dnow prefetch and prefetchw instructions */ \
/* may not necessarily support other 3dnow instructions */ \
- decl(SSE, "sse", 6) \
- decl(SSE2, "sse2", 7) \
+ decl(SSE, sse, 6) \
+ decl(SSE2, sse2, 7) \
\
- decl(SSE3, "sse3", 8 ) /* SSE3 comes from cpuid 1 (ECX) */ \
- decl(SSSE3, "ssse3", 9 ) \
- decl(SSE4A, "sse4a", 10) \
- decl(SSE4_1, "sse4.1", 11) \
+ decl(SSE3, sse3, 8 ) /* SSE3 comes from cpuid 1 (ECX) */ \
+ decl(SSSE3, ssse3, 9 ) \
+ decl(SSE4A, sse4a, 10) \
+ decl(SSE4_1, sse4.1, 11) \
\
- decl(SSE4_2, "sse4.2", 12) \
- decl(POPCNT, "popcnt", 13) \
- decl(LZCNT, "lzcnt", 14) \
- decl(TSC, "tsc", 15) \
+ decl(SSE4_2, sse4.2, 12) \
+ decl(POPCNT, popcnt, 13) \
+ decl(LZCNT, lzcnt, 14) \
+ decl(TSC, tsc, 15) \
\
- decl(TSCINV_BIT, "tscinvbit", 16) \
- decl(TSCINV, "tscinv", 17) \
- decl(AVX, "avx", 18) \
- decl(AVX2, "avx2", 19) \
+ decl(TSCINV_BIT, tscinvbit, 16) \
+ decl(TSCINV, tscinv, 17) \
+ decl(AVX, avx, 18) \
+ decl(AVX2, avx2, 19) \
\
- decl(AES, "aes", 20) \
- decl(ERMS, "erms", 21) /* enhanced 'rep movsb/stosb' instructions */ \
- decl(CLMUL, "clmul", 22) /* carryless multiply for CRC */ \
- decl(BMI1, "bmi1", 23) \
+ decl(AES, aes, 20) \
+ decl(ERMS, erms, 21) /* enhanced 'rep movsb/stosb' instructions */ \
+ decl(CLMUL, clmul, 22) /* carryless multiply for CRC */ \
+ decl(BMI1, bmi1, 23) \
\
- decl(BMI2, "bmi2", 24) \
- decl(RTM, "rtm", 25) /* Restricted Transactional Memory instructions */ \
- decl(ADX, "adx", 26) \
- decl(AVX512F, "avx512f", 27) /* AVX 512bit foundation instructions */ \
+ decl(BMI2, bmi2, 24) \
+ decl(RTM, rtm, 25) /* Restricted Transactional Memory instructions */ \
+ decl(ADX, adx, 26) \
+ decl(AVX512F, avx512f, 27) /* AVX 512bit foundation instructions */ \
\
- decl(AVX512DQ, "avx512dq", 28) \
- decl(AVX512PF, "avx512pf", 29) \
- decl(AVX512ER, "avx512er", 30) \
- decl(AVX512CD, "avx512cd", 31) \
+ decl(AVX512DQ, avx512dq, 28) \
+ decl(AVX512PF, avx512pf, 29) \
+ decl(AVX512ER, avx512er, 30) \
+ decl(AVX512CD, avx512cd, 31) \
\
- decl(AVX512BW, "avx512bw", 32) /* Byte and word vector instructions */ \
- decl(AVX512VL, "avx512vl", 33) /* EVEX instructions with smaller vector length */ \
- decl(SHA, "sha", 34) /* SHA instructions */ \
- decl(FMA, "fma", 35) /* FMA instructions */ \
+ decl(AVX512BW, avx512bw, 32) /* Byte and word vector instructions */ \
+ decl(AVX512VL, avx512vl, 33) /* EVEX instructions with smaller vector length */ \
+ decl(SHA, sha, 34) /* SHA instructions */ \
+ decl(FMA, fma, 35) /* FMA instructions */ \
\
- decl(VZEROUPPER, "vzeroupper", 36) /* Vzeroupper instruction */ \
- decl(AVX512_VPOPCNTDQ, "avx512_vpopcntdq", 37) /* Vector popcount */ \
- decl(AVX512_VPCLMULQDQ, "avx512_vpclmulqdq", 38) /* Vector carryless multiplication */ \
- decl(AVX512_VAES, "avx512_vaes", 39) /* Vector AES instruction */ \
+ decl(VZEROUPPER, vzeroupper, 36) /* Vzeroupper instruction */ \
+ decl(AVX512_VPOPCNTDQ, avx512_vpopcntdq, 37) /* Vector popcount */ \
+ decl(AVX512_VPCLMULQDQ, avx512_vpclmulqdq, 38) /* Vector carryless multiplication */ \
+ decl(AVX512_VAES, avx512_vaes, 39) /* Vector AES instruction */ \
\
- decl(AVX512_VNNI, "avx512_vnni", 40) /* Vector Neural Network Instructions */ \
- decl(FLUSH, "clflush", 41) /* flush instruction */ \
- decl(FLUSHOPT, "clflushopt", 42) /* flusopth instruction */ \
- decl(CLWB, "clwb", 43) /* clwb instruction */ \
+ decl(AVX512_VNNI, avx512_vnni, 40) /* Vector Neural Network Instructions */ \
+ decl(FLUSH, clflush, 41) /* flush instruction */ \
+ decl(FLUSHOPT, clflushopt, 42) /* flusopth instruction */ \
+ decl(CLWB, clwb, 43) /* clwb instruction */ \
\
- decl(AVX512_VBMI2, "avx512_vbmi2", 44) /* VBMI2 shift left double instructions */ \
- decl(AVX512_VBMI, "avx512_vbmi", 45) /* Vector BMI instructions */ \
- decl(HV, "hv", 46) /* Hypervisor instructions */ \
- decl(SERIALIZE, "serialize", 47) /* CPU SERIALIZE */ \
- decl(RDTSCP, "rdtscp", 48) /* RDTSCP instruction */ \
- decl(RDPID, "rdpid", 49) /* RDPID instruction */ \
- decl(FSRM, "fsrm", 50) /* Fast Short REP MOV */ \
- decl(GFNI, "gfni", 51) /* Vector GFNI instructions */ \
- decl(AVX512_BITALG, "avx512_bitalg", 52) /* Vector sub-word popcount and bit gather instructions */\
- decl(F16C, "f16c", 53) /* Half-precision and single precision FP conversion instructions*/ \
- decl(PKU, "pku", 54) /* Protection keys for user-mode pages */ \
- decl(OSPKE, "ospke", 55) /* OS enables protection keys */ \
- decl(CET_IBT, "cet_ibt", 56) /* Control Flow Enforcement - Indirect Branch Tracking */ \
- decl(CET_SS, "cet_ss", 57) /* Control Flow Enforcement - Shadow Stack */ \
- decl(AVX512_IFMA, "avx512_ifma", 58) /* Integer Vector FMA instructions*/ \
- decl(AVX_IFMA, "avx_ifma", 59) /* 256-bit VEX-coded variant of AVX512-IFMA*/ \
- decl(APX_F, "apx_f", 60) /* Intel Advanced Performance Extensions*/ \
- decl(SHA512, "sha512", 61) /* SHA512 instructions*/ \
- decl(AVX512_FP16, "avx512_fp16", 62) /* AVX512 FP16 ISA support*/ \
- decl(AVX10_1, "avx10_1", 63) /* AVX10 512 bit vector ISA Version 1 support*/ \
- decl(AVX10_2, "avx10_2", 64) /* AVX10 512 bit vector ISA Version 2 support*/ \
- decl(HYBRID, "hybrid", 65) /* Hybrid architecture */
+ decl(AVX512_VBMI2, avx512_vbmi2, 44) /* VBMI2 shift left double instructions */ \
+ decl(AVX512_VBMI, avx512_vbmi, 45) /* Vector BMI instructions */ \
+ decl(HV, hv, 46) /* Hypervisor instructions */ \
+ decl(SERIALIZE, serialize, 47) /* CPU SERIALIZE */ \
+ decl(RDTSCP, rdtscp, 48) /* RDTSCP instruction */ \
+ decl(RDPID, rdpid, 49) /* RDPID instruction */ \
+ decl(FSRM, fsrm, 50) /* Fast Short REP MOV */ \
+ decl(GFNI, gfni, 51) /* Vector GFNI instructions */ \
+ decl(AVX512_BITALG, avx512_bitalg, 52) /* Vector sub-word popcount and bit gather instructions */\
+ decl(F16C, f16c, 53) /* Half-precision and single precision FP conversion instructions*/ \
+ decl(PKU, pku, 54) /* Protection keys for user-mode pages */ \
+ decl(OSPKE, ospke, 55) /* OS enables protection keys */ \
+ decl(CET_IBT, cet_ibt, 56) /* Control Flow Enforcement - Indirect Branch Tracking */ \
+ decl(CET_SS, cet_ss, 57) /* Control Flow Enforcement - Shadow Stack */ \
+ decl(AVX512_IFMA, avx512_ifma, 58) /* Integer Vector FMA instructions*/ \
+ decl(AVX_IFMA, avx_ifma, 59) /* 256-bit VEX-coded variant of AVX512-IFMA*/ \
+ decl(APX_F, apx_f, 60) /* Intel Advanced Performance Extensions*/ \
+ decl(SHA512, sha512, 61) /* SHA512 instructions*/ \
+ decl(AVX512_FP16, avx512_fp16, 62) /* AVX512 FP16 ISA support*/ \
+ decl(AVX10_1, avx10_1, 63) /* AVX10 512 bit vector ISA Version 1 support*/ \
+ decl(AVX10_2, avx10_2, 64) /* AVX10 512 bit vector ISA Version 2 support*/ \
+ decl(HYBRID, hybrid, 65) /* Hybrid architecture */
#define DECLARE_CPU_FEATURE_FLAG(id, name, bit) CPU_##id = (bit),
CPU_FEATURE_FLAGS(DECLARE_CPU_FEATURE_FLAG)
@@ -516,6 +516,15 @@ protected:
int idx = index(feature);
return (_features_bitmap[idx] & bit_mask(feature)) != 0;
}
+
+ bool supports_features(VM_Features* features_to_test) {
+ for (int i = 0; i < features_bitmap_element_count(); i++) {
+ if ((_features_bitmap[i] & features_to_test->_features_bitmap[i]) != features_to_test->_features_bitmap[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
};
// CPU feature flags vector, can be affected by VM settings.
@@ -1103,6 +1112,20 @@ public:
static bool supports_tscinv_ext(void);
static void initialize_cpu_information(void);
+
+ static void get_cpu_features_name(void* features_buffer, stringStream& ss);
+
+ // Returns names of features present in features_set1 but not in features_set2
+ static void get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss);
+
+ // Returns number of bytes required to store cpu features representation
+ static int cpu_features_size();
+
+ // Stores cpu features representation in the provided buffer. This representation is arch dependent.
+ // Size of the buffer must be same as returned by cpu_features_size()
+ static void store_cpu_features(void* buf);
+
+ static bool supports_features(void* features_to_test);
};
#endif // CPU_X86_VM_VERSION_X86_HPP
diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad
index 93b306c37d6..0ffa4c2031c 100644
--- a/src/hotspot/cpu/x86/x86.ad
+++ b/src/hotspot/cpu/x86/x86.ad
@@ -1699,9 +1699,10 @@ static void emit_cmpfp_fixup(MacroAssembler* masm) {
}
static void emit_cmpfp3(MacroAssembler* masm, Register dst) {
+ // If any floating point comparison instruction is used, unordered case always triggers jump
+ // for below condition, CF=1 is true when at least one input is NaN
Label done;
__ movl(dst, -1);
- __ jcc(Assembler::parity, done);
__ jcc(Assembler::below, done);
__ setcc(Assembler::notEqual, dst);
__ bind(done);
@@ -5186,6 +5187,18 @@ operand immL_65535()
interface(CONST_INTER);
%}
+// AOT Runtime Constants Address
+operand immAOTRuntimeConstantsAddress()
+%{
+ // Check if the address is in the range of AOT Runtime Constants
+ predicate(AOTRuntimeConstants::contains((address)(n->get_ptr())));
+ match(ConP);
+
+ op_cost(0);
+ format %{ %}
+ interface(CONST_INTER);
+%}
+
operand kReg()
%{
constraint(ALLOC_IN_RC(vectmask_reg));
@@ -5529,12 +5542,21 @@ operand rFlagsRegU()
operand rFlagsRegUCF() %{
constraint(ALLOC_IN_RC(int_flags));
match(RegFlags);
- predicate(false);
+ predicate(!UseAPX || !VM_Version::supports_avx10_2());
format %{ "RFLAGS_U_CF" %}
interface(REG_INTER);
%}
+operand rFlagsRegUCFE() %{
+ constraint(ALLOC_IN_RC(int_flags));
+ match(RegFlags);
+ predicate(UseAPX && VM_Version::supports_avx10_2());
+
+ format %{ "RFLAGS_U_CFE" %}
+ interface(REG_INTER);
+%}
+
// Float register operands
operand regF() %{
constraint(ALLOC_IN_RC(float_reg));
@@ -6027,10 +6049,10 @@ operand cmpOp()
interface(COND_INTER) %{
equal(0x4, "e");
not_equal(0x5, "ne");
- less(0xC, "l");
- greater_equal(0xD, "ge");
- less_equal(0xE, "le");
- greater(0xF, "g");
+ less(0xc, "l");
+ greater_equal(0xd, "ge");
+ less_equal(0xe, "le");
+ greater(0xf, "g");
overflow(0x0, "o");
no_overflow(0x1, "no");
%}
@@ -6062,11 +6084,12 @@ operand cmpOpU()
// don't need to use cmpOpUCF2 for eq/ne
operand cmpOpUCF() %{
match(Bool);
- predicate(n->as_Bool()->_test._test == BoolTest::lt ||
- n->as_Bool()->_test._test == BoolTest::ge ||
- n->as_Bool()->_test._test == BoolTest::le ||
- n->as_Bool()->_test._test == BoolTest::gt ||
- n->in(1)->in(1) == n->in(1)->in(2));
+ predicate((!UseAPX || !VM_Version::supports_avx10_2()) &&
+ (n->as_Bool()->_test._test == BoolTest::lt ||
+ n->as_Bool()->_test._test == BoolTest::ge ||
+ n->as_Bool()->_test._test == BoolTest::le ||
+ n->as_Bool()->_test._test == BoolTest::gt ||
+ n->in(1)->in(1) == n->in(1)->in(2)));
format %{ "" %}
interface(COND_INTER) %{
equal(0xb, "np");
@@ -6084,7 +6107,8 @@ operand cmpOpUCF() %{
// Floating comparisons that can be fixed up with extra conditional jumps
operand cmpOpUCF2() %{
match(Bool);
- predicate((n->as_Bool()->_test._test == BoolTest::ne ||
+ predicate((!UseAPX || !VM_Version::supports_avx10_2()) &&
+ (n->as_Bool()->_test._test == BoolTest::ne ||
n->as_Bool()->_test._test == BoolTest::eq) &&
n->in(1)->in(1) != n->in(1)->in(2));
format %{ "" %}
@@ -6100,6 +6124,37 @@ operand cmpOpUCF2() %{
%}
%}
+
+// Floating point comparisons that set condition flags to test more directly,
+// Unsigned tests are used for G (>) and GE (>=) conditions while signed tests
+// are used for L (<) and LE (<=) conditions. It's important to convert these
+// latter conditions to ones that use unsigned tests before passing into an
+// instruction because the preceding comparison might be based on a three way
+// comparison (CmpF3 or CmpD3) that also assigns unordered outcomes to -1.
+operand cmpOpUCFE()
+%{
+ match(Bool);
+ predicate((UseAPX && VM_Version::supports_avx10_2()) &&
+ (n->as_Bool()->_test._test == BoolTest::ne ||
+ n->as_Bool()->_test._test == BoolTest::eq ||
+ n->as_Bool()->_test._test == BoolTest::lt ||
+ n->as_Bool()->_test._test == BoolTest::ge ||
+ n->as_Bool()->_test._test == BoolTest::le ||
+ n->as_Bool()->_test._test == BoolTest::gt));
+
+ format %{ "" %}
+ interface(COND_INTER) %{
+ equal(0x4, "e");
+ not_equal(0x5, "ne");
+ less(0x2, "b");
+ greater_equal(0x3, "ae");
+ less_equal(0x6, "be");
+ greater(0x7, "a");
+ overflow(0x0, "o");
+ no_overflow(0x1, "no");
+ %}
+%}
+
// Operands for bound floating pointer register arguments
operand rxmm0() %{
constraint(ALLOC_IN_RC(xmm0_reg));
@@ -7289,6 +7344,19 @@ instruct loadD(regD dst, memory mem)
ins_pipe(pipe_slow); // XXX
%}
+instruct loadAOTRCAddress(rRegP dst, immAOTRuntimeConstantsAddress con)
+%{
+ match(Set dst con);
+
+ format %{ "leaq $dst, $con\t# AOT Runtime Constants Address" %}
+
+ ins_encode %{
+ __ load_aotrc_address($dst$$Register, (address)$con$$constant);
+ %}
+
+ ins_pipe(ialu_reg_fat);
+%}
+
// max = java.lang.Math.max(float a, float b)
instruct maxF_reg_avx10_2(regF dst, regF a, regF b) %{
predicate(VM_Version::supports_avx10_2());
@@ -9116,20 +9184,34 @@ instruct cmovI_imm_01UCF(rRegI dst, immI_1 src, rFlagsRegUCF cr, cmpOpUCF cop)
ins_pipe(ialu_reg);
%}
+instruct cmovI_imm_01UCFE(rRegI dst, immI_1 src, rFlagsRegUCFE cr, cmpOpUCFE cop)
+%{
+ predicate(n->in(2)->in(2)->is_Con() && n->in(2)->in(2)->get_int() == 0);
+ match(Set dst (CMoveI (Binary cop cr) (Binary src dst)));
+
+ ins_cost(100); // XXX
+ format %{ "setbn$cop $dst\t# signed, unsigned, int" %}
+ ins_encode %{
+ Assembler::Condition cond = (Assembler::Condition)($cop$$cmpcode);
+ __ setb(MacroAssembler::negate_condition(cond), $dst$$Register);
+ %}
+ ins_pipe(ialu_reg);
+%}
+
instruct cmovI_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegI dst, rRegI src) %{
- predicate(!UseAPX);
match(Set dst (CMoveI (Binary cop cr) (Binary dst src)));
+
ins_cost(200);
expand %{
cmovI_regU(cop, cr, dst, src);
%}
%}
-instruct cmovI_regUCF_ndd(rRegI dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegI src1, rRegI src2) %{
- predicate(UseAPX);
+instruct cmovI_regUCFE_ndd(rRegI dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegI src1, rRegI src2) %{
match(Set dst (CMoveI (Binary cop cr) (Binary src1 src2)));
+
ins_cost(200);
- format %{ "ecmovl$cop $dst, $src1, $src2\t# unsigned, int ndd" %}
+ format %{ "ecmovl$cop $dst, $src1, $src2\t# signed, unsigned, int ndd" %}
ins_encode %{
__ ecmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Register);
%}
@@ -9137,7 +9219,7 @@ instruct cmovI_regUCF_ndd(rRegI dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegI src1,
%}
instruct cmovI_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src) %{
- predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
+ predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
match(Set dst (CMoveI (Binary cop cr) (Binary dst src)));
ins_cost(200); // XXX
@@ -9150,25 +9232,10 @@ instruct cmovI_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src)
ins_pipe(pipe_cmov_reg);
%}
-instruct cmovI_regUCF2_ne_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src1, rRegI src2) %{
- predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
- match(Set dst (CMoveI (Binary cop cr) (Binary src1 src2)));
- effect(TEMP dst);
-
- ins_cost(200);
- format %{ "ecmovpl $dst, $src1, $src2\n\t"
- "cmovnel $dst, $src2" %}
- ins_encode %{
- __ ecmovl(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
- __ cmovl(Assembler::notEqual, $dst$$Register, $src2$$Register);
- %}
- ins_pipe(pipe_cmov_reg);
-%}
-
// Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
// inputs of the CMove
instruct cmovI_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src) %{
- predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
+ predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
match(Set dst (CMoveI (Binary cop cr) (Binary src dst)));
effect(TEMP dst);
@@ -9182,23 +9249,6 @@ instruct cmovI_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src)
ins_pipe(pipe_cmov_reg);
%}
-// We need this special handling for only eq / neq comparison since NaN == NaN is false,
-// and parity flag bit is set if any of the operand is a NaN.
-instruct cmovI_regUCF2_eq_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src1, rRegI src2) %{
- predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
- match(Set dst (CMoveI (Binary cop cr) (Binary src2 src1)));
- effect(TEMP dst);
-
- ins_cost(200);
- format %{ "ecmovpl $dst, $src1, $src2\n\t"
- "cmovnel $dst, $src2" %}
- ins_encode %{
- __ ecmovl(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
- __ cmovl(Assembler::notEqual, $dst$$Register, $src2$$Register);
- %}
- ins_pipe(pipe_cmov_reg);
-%}
-
// Conditional move
instruct cmovI_mem(cmpOp cop, rFlagsReg cr, rRegI dst, memory src) %{
predicate(!UseAPX);
@@ -9241,8 +9291,8 @@ instruct cmovI_memU(cmpOpU cop, rFlagsRegU cr, rRegI dst, memory src)
%}
instruct cmovI_memUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegI dst, memory src) %{
- predicate(!UseAPX);
match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src))));
+
ins_cost(250);
expand %{
cmovI_memU(cop, cr, dst, src);
@@ -9262,12 +9312,12 @@ instruct cmovI_rReg_rReg_memU_ndd(rRegI dst, cmpOpU cop, rFlagsRegU cr, rRegI sr
ins_pipe(pipe_cmov_mem);
%}
-instruct cmovI_rReg_rReg_memUCF_ndd(rRegI dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegI src1, memory src2)
+instruct cmovI_rReg_rReg_memUCFE_ndd(rRegI dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegI src1, memory src2)
%{
- predicate(UseAPX);
match(Set dst (CMoveI (Binary cop cr) (Binary src1 (LoadI src2))));
+
ins_cost(250);
- format %{ "ecmovl$cop $dst, $src1, $src2\t# unsigned, int ndd" %}
+ format %{ "ecmovl$cop $dst, $src1, $src2\t# signed, unsigned, int ndd" %}
ins_encode %{
__ ecmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address);
%}
@@ -9317,8 +9367,8 @@ instruct cmovN_regU(cmpOpU cop, rFlagsRegU cr, rRegN dst, rRegN src)
%}
instruct cmovN_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegN dst, rRegN src) %{
- predicate(!UseAPX);
match(Set dst (CMoveN (Binary cop cr) (Binary dst src)));
+
ins_cost(200);
expand %{
cmovN_regU(cop, cr, dst, src);
@@ -9339,11 +9389,11 @@ instruct cmovN_regU_ndd(rRegN dst, cmpOpU cop, rFlagsRegU cr, rRegN src1, rRegN
ins_pipe(pipe_cmov_reg);
%}
-instruct cmovN_regUCF_ndd(rRegN dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegN src1, rRegN src2) %{
- predicate(UseAPX);
+instruct cmovN_regUCFE_ndd(rRegN dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegN src1, rRegN src2) %{
match(Set dst (CMoveN (Binary cop cr) (Binary src1 src2)));
+
ins_cost(200);
- format %{ "ecmovl$cop $dst, $src1, $src2\t# unsigned, compressed ptr ndd" %}
+ format %{ "ecmovl$cop $dst, $src1, $src2\t# signed, unsigned, compressed ptr ndd" %}
ins_encode %{
__ ecmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Register);
%}
@@ -9437,19 +9487,19 @@ instruct cmovP_regU_ndd(rRegP dst, cmpOpU cop, rFlagsRegU cr, rRegP src1, rRegP
%}
instruct cmovP_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegP dst, rRegP src) %{
- predicate(!UseAPX);
match(Set dst (CMoveP (Binary cop cr) (Binary dst src)));
+
ins_cost(200);
expand %{
cmovP_regU(cop, cr, dst, src);
%}
%}
-instruct cmovP_regUCF_ndd(rRegP dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegP src1, rRegP src2) %{
- predicate(UseAPX);
+instruct cmovP_regUCFE_ndd(rRegP dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegP src1, rRegP src2) %{
match(Set dst (CMoveP (Binary cop cr) (Binary src1 src2)));
+
ins_cost(200);
- format %{ "ecmovq$cop $dst, $src1, $src2\t# unsigned, ptr ndd" %}
+ format %{ "ecmovq$cop $dst, $src1, $src2\t# signed, unsigned, ptr ndd" %}
ins_encode %{
__ ecmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Register);
%}
@@ -9457,7 +9507,7 @@ instruct cmovP_regUCF_ndd(rRegP dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegP src1,
%}
instruct cmovP_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src) %{
- predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
+ predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
match(Set dst (CMoveP (Binary cop cr) (Binary dst src)));
ins_cost(200); // XXX
@@ -9470,25 +9520,10 @@ instruct cmovP_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src)
ins_pipe(pipe_cmov_reg);
%}
-instruct cmovP_regUCF2_ne_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src1, rRegP src2) %{
- predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
- match(Set dst (CMoveP (Binary cop cr) (Binary src1 src2)));
- effect(TEMP dst);
-
- ins_cost(200);
- format %{ "ecmovpq $dst, $src1, $src2\n\t"
- "cmovneq $dst, $src2" %}
- ins_encode %{
- __ ecmovq(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
- __ cmovq(Assembler::notEqual, $dst$$Register, $src2$$Register);
- %}
- ins_pipe(pipe_cmov_reg);
-%}
-
// Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
// inputs of the CMove
instruct cmovP_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src) %{
- predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
+ predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
match(Set dst (CMoveP (Binary cop cr) (Binary src dst)));
ins_cost(200); // XXX
@@ -9501,21 +9536,6 @@ instruct cmovP_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src)
ins_pipe(pipe_cmov_reg);
%}
-instruct cmovP_regUCF2_eq_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src1, rRegP src2) %{
- predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
- match(Set dst (CMoveP (Binary cop cr) (Binary src2 src1)));
- effect(TEMP dst);
-
- ins_cost(200);
- format %{ "ecmovpq $dst, $src1, $src2\n\t"
- "cmovneq $dst, $src2" %}
- ins_encode %{
- __ ecmovq(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
- __ cmovq(Assembler::notEqual, $dst$$Register, $src2$$Register);
- %}
- ins_pipe(pipe_cmov_reg);
-%}
-
instruct cmovL_imm_01(rRegL dst, immL1 src, rFlagsReg cr, cmpOp cop)
%{
predicate(n->in(2)->in(2)->is_Con() && n->in(2)->in(2)->get_long() == 0);
@@ -9636,21 +9656,35 @@ instruct cmovL_imm_01UCF(rRegL dst, immL1 src, rFlagsRegUCF cr, cmpOpUCF cop)
ins_pipe(ialu_reg);
%}
+instruct cmovL_imm_01UCFE(rRegL dst, immL1 src, rFlagsRegUCFE cr, cmpOpUCFE cop)
+%{
+ predicate(n->in(2)->in(2)->is_Con() && n->in(2)->in(2)->get_long() == 0);
+ match(Set dst (CMoveL (Binary cop cr) (Binary src dst)));
+
+ ins_cost(100); // XXX
+ format %{ "setbn$cop $dst\t# signed, unsigned, long" %}
+ ins_encode %{
+ Assembler::Condition cond = (Assembler::Condition)($cop$$cmpcode);
+ __ setb(MacroAssembler::negate_condition(cond), $dst$$Register);
+ %}
+ ins_pipe(ialu_reg);
+%}
+
instruct cmovL_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{
- predicate(!UseAPX);
match(Set dst (CMoveL (Binary cop cr) (Binary dst src)));
+
ins_cost(200);
expand %{
cmovL_regU(cop, cr, dst, src);
%}
%}
-instruct cmovL_regUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1, rRegL src2)
+instruct cmovL_regUCFE_ndd(rRegL dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegL src1, rRegL src2)
%{
- predicate(UseAPX);
match(Set dst (CMoveL (Binary cop cr) (Binary src1 src2)));
+
ins_cost(200);
- format %{ "ecmovq$cop $dst, $src1, $src2\t# unsigned, long ndd" %}
+ format %{ "ecmovq$cop $dst, $src1, $src2\t# signed, unsigned, long ndd" %}
ins_encode %{
__ ecmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Register);
%}
@@ -9658,7 +9692,7 @@ instruct cmovL_regUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1,
%}
instruct cmovL_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{
- predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
+ predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
match(Set dst (CMoveL (Binary cop cr) (Binary dst src)));
ins_cost(200); // XXX
@@ -9671,25 +9705,10 @@ instruct cmovL_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src)
ins_pipe(pipe_cmov_reg);
%}
-instruct cmovL_regUCF2_ne_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src1, rRegL src2) %{
- predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
- match(Set dst (CMoveL (Binary cop cr) (Binary src1 src2)));
- effect(TEMP dst);
-
- ins_cost(200);
- format %{ "ecmovpq $dst, $src1, $src2\n\t"
- "cmovneq $dst, $src2" %}
- ins_encode %{
- __ ecmovq(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
- __ cmovq(Assembler::notEqual, $dst$$Register, $src2$$Register);
- %}
- ins_pipe(pipe_cmov_reg);
-%}
-
// Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
// inputs of the CMove
instruct cmovL_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{
- predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
+ predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
match(Set dst (CMoveL (Binary cop cr) (Binary src dst)));
ins_cost(200); // XXX
@@ -9702,21 +9721,6 @@ instruct cmovL_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src)
ins_pipe(pipe_cmov_reg);
%}
-instruct cmovL_regUCF2_eq_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src1, rRegL src2) %{
- predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
- match(Set dst (CMoveL (Binary cop cr) (Binary src2 src1)));
- effect(TEMP dst);
-
- ins_cost(200);
- format %{ "ecmovpq $dst, $src1, $src2\n\t"
- "cmovneq $dst, $src2" %}
- ins_encode %{
- __ ecmovq(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
- __ cmovq(Assembler::notEqual, $dst$$Register, $src2$$Register);
- %}
- ins_pipe(pipe_cmov_reg);
-%}
-
instruct cmovL_memU(cmpOpU cop, rFlagsRegU cr, rRegL dst, memory src)
%{
predicate(!UseAPX);
@@ -9731,8 +9735,8 @@ instruct cmovL_memU(cmpOpU cop, rFlagsRegU cr, rRegL dst, memory src)
%}
instruct cmovL_memUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegL dst, memory src) %{
- predicate(!UseAPX);
match(Set dst (CMoveL (Binary cop cr) (Binary dst (LoadL src))));
+
ins_cost(200);
expand %{
cmovL_memU(cop, cr, dst, src);
@@ -9752,12 +9756,12 @@ instruct cmovL_rReg_rReg_memU_ndd(rRegL dst, cmpOpU cop, rFlagsRegU cr, rRegL sr
ins_pipe(pipe_cmov_mem);
%}
-instruct cmovL_rReg_rReg_memUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1, memory src2)
+instruct cmovL_rReg_rReg_memUCFE_ndd(rRegL dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegL src1, memory src2)
%{
- predicate(UseAPX);
match(Set dst (CMoveL (Binary cop cr) (Binary src1 (LoadL src2))));
+
ins_cost(200);
- format %{ "ecmovq$cop $dst, $src1, $src2\t# unsigned, long ndd" %}
+ format %{ "ecmovq$cop $dst, $src1, $src2\t# signed, unsigned, long ndd" %}
ins_encode %{
__ ecmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address);
%}
@@ -9802,12 +9806,31 @@ instruct cmovF_regU(cmpOpU cop, rFlagsRegU cr, regF dst, regF src)
instruct cmovF_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, regF dst, regF src) %{
match(Set dst (CMoveF (Binary cop cr) (Binary dst src)));
+
ins_cost(200);
expand %{
cmovF_regU(cop, cr, dst, src);
%}
%}
+instruct cmovF_regUCFE(cmpOpUCFE cop, rFlagsRegUCFE cr, regF dst, regF src)
+%{
+ match(Set dst (CMoveF (Binary cop cr) (Binary dst src)));
+
+ ins_cost(200); // XXX
+ format %{ "jn$cop skip\t# signed, unsigned cmove float\n\t"
+ "movss $dst, $src\n"
+ "skip:" %}
+ ins_encode %{
+ Label Lskip;
+ // Invert sense of branch from sense of CMOV
+ __ jccb((Assembler::Condition)($cop$$cmpcode^1), Lskip);
+ __ movflt($dst$$XMMRegister, $src$$XMMRegister);
+ __ bind(Lskip);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct cmovD_reg(cmpOp cop, rFlagsReg cr, regD dst, regD src)
%{
match(Set dst (CMoveD (Binary cop cr) (Binary dst src)));
@@ -9846,12 +9869,31 @@ instruct cmovD_regU(cmpOpU cop, rFlagsRegU cr, regD dst, regD src)
instruct cmovD_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, regD dst, regD src) %{
match(Set dst (CMoveD (Binary cop cr) (Binary dst src)));
+
ins_cost(200);
expand %{
cmovD_regU(cop, cr, dst, src);
%}
%}
+instruct cmovD_regUCFE(cmpOpUCFE cop, rFlagsRegUCFE cr, regD dst, regD src)
+%{
+ match(Set dst (CMoveD (Binary cop cr) (Binary dst src)));
+
+ ins_cost(200); // XXX
+ format %{ "jn$cop skip\t# signed, unsigned cmove double\n\t"
+ "movsd $dst, $src\n"
+ "skip:" %}
+ ins_encode %{
+ Label Lskip;
+ // Invert sense of branch from sense of CMOV
+ __ jccb((Assembler::Condition)($cop$$cmpcode^1), Lskip);
+ __ movdbl($dst$$XMMRegister, $src$$XMMRegister);
+ __ bind(Lskip);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
//----------Arithmetic Instructions--------------------------------------------
//----------Addition Instructions----------------------------------------------
@@ -14319,7 +14361,7 @@ instruct cmpF_cc_reg(rFlagsRegU cr, regF src1, regF src2)
ins_pipe(pipe_slow);
%}
-instruct cmpF_cc_reg_CF(rFlagsRegUCF cr, regF src1, regF src2) %{
+instruct cmpF_cc_regCF(rFlagsRegUCF cr, regF src1, regF src2) %{
match(Set cr (CmpF src1 src2));
ins_cost(100);
@@ -14330,6 +14372,17 @@ instruct cmpF_cc_reg_CF(rFlagsRegUCF cr, regF src1, regF src2) %{
ins_pipe(pipe_slow);
%}
+instruct cmpF_cc_regCFE(rFlagsRegUCFE cr, regF src1, regF src2) %{
+ match(Set cr (CmpF src1 src2));
+
+ ins_cost(100);
+ format %{ "vucomxss $src1, $src2" %}
+ ins_encode %{
+ __ vucomxss($src1$$XMMRegister, $src2$$XMMRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct cmpF_cc_memCF(rFlagsRegUCF cr, regF src1, memory src2) %{
match(Set cr (CmpF src1 (LoadF src2)));
@@ -14341,8 +14394,20 @@ instruct cmpF_cc_memCF(rFlagsRegUCF cr, regF src1, memory src2) %{
ins_pipe(pipe_slow);
%}
+instruct cmpF_cc_memCFE(rFlagsRegUCFE cr, regF src1, memory src2) %{
+ match(Set cr (CmpF src1 (LoadF src2)));
+
+ ins_cost(100);
+ format %{ "vucomxss $src1, $src2" %}
+ ins_encode %{
+ __ vucomxss($src1$$XMMRegister, $src2$$Address);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct cmpF_cc_immCF(rFlagsRegUCF cr, regF src, immF con) %{
match(Set cr (CmpF src con));
+
ins_cost(100);
format %{ "ucomiss $src, [$constantaddress]\t# load from constant table: float=$con" %}
ins_encode %{
@@ -14351,6 +14416,17 @@ instruct cmpF_cc_immCF(rFlagsRegUCF cr, regF src, immF con) %{
ins_pipe(pipe_slow);
%}
+instruct cmpF_cc_immCFE(rFlagsRegUCFE cr, regF src, immF con) %{
+ match(Set cr (CmpF src con));
+
+ ins_cost(100);
+ format %{ "vucomxss $src, [$constantaddress]\t# load from constant table: float=$con" %}
+ ins_encode %{
+ __ vucomxss($src$$XMMRegister, $constantaddress($con));
+ %}
+ ins_pipe(pipe_slow);
+%}
+
// Really expensive, avoid
instruct cmpD_cc_reg(rFlagsRegU cr, regD src1, regD src2)
%{
@@ -14370,7 +14446,7 @@ instruct cmpD_cc_reg(rFlagsRegU cr, regD src1, regD src2)
ins_pipe(pipe_slow);
%}
-instruct cmpD_cc_reg_CF(rFlagsRegUCF cr, regD src1, regD src2) %{
+instruct cmpD_cc_regCF(rFlagsRegUCF cr, regD src1, regD src2) %{
match(Set cr (CmpD src1 src2));
ins_cost(100);
@@ -14381,6 +14457,17 @@ instruct cmpD_cc_reg_CF(rFlagsRegUCF cr, regD src1, regD src2) %{
ins_pipe(pipe_slow);
%}
+instruct cmpD_cc_regCFE(rFlagsRegUCFE cr, regD src1, regD src2) %{
+ match(Set cr (CmpD src1 src2));
+
+ ins_cost(100);
+ format %{ "vucomxsd $src1, $src2 test" %}
+ ins_encode %{
+ __ vucomxsd($src1$$XMMRegister, $src2$$XMMRegister);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct cmpD_cc_memCF(rFlagsRegUCF cr, regD src1, memory src2) %{
match(Set cr (CmpD src1 (LoadD src2)));
@@ -14392,6 +14479,17 @@ instruct cmpD_cc_memCF(rFlagsRegUCF cr, regD src1, memory src2) %{
ins_pipe(pipe_slow);
%}
+instruct cmpD_cc_memCFE(rFlagsRegUCFE cr, regD src1, memory src2) %{
+ match(Set cr (CmpD src1 (LoadD src2)));
+
+ ins_cost(100);
+ format %{ "vucomxsd $src1, $src2" %}
+ ins_encode %{
+ __ vucomxsd($src1$$XMMRegister, $src2$$Address);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct cmpD_cc_immCF(rFlagsRegUCF cr, regD src, immD con) %{
match(Set cr (CmpD src con));
ins_cost(100);
@@ -14402,6 +14500,17 @@ instruct cmpD_cc_immCF(rFlagsRegUCF cr, regD src, immD con) %{
ins_pipe(pipe_slow);
%}
+instruct cmpD_cc_immCFE(rFlagsRegUCFE cr, regD src, immD con) %{
+ match(Set cr (CmpD src con));
+
+ ins_cost(100);
+ format %{ "vucomxsd $src, [$constantaddress]\t# load from constant table: double=$con" %}
+ ins_encode %{
+ __ vucomxsd($src$$XMMRegister, $constantaddress($con));
+ %}
+ ins_pipe(pipe_slow);
+%}
+
// Compare into -1,0,1
instruct cmpF_reg(rRegI dst, regF src1, regF src2, rFlagsReg cr)
%{
@@ -16808,6 +16917,21 @@ instruct jmpConUCF2(cmpOpUCF2 cop, rFlagsRegUCF cmp, label labl) %{
ins_pipe(pipe_jcc);
%}
+// Jump Direct Conditional - using signed and unsigned comparison
+instruct jmpConUCFE(cmpOpUCFE cop, rFlagsRegUCFE cmp, label labl) %{
+ match(If cop cmp);
+ effect(USE labl);
+
+ ins_cost(200);
+ format %{ "j$cop,su $labl" %}
+ size(6);
+ ins_encode %{
+ Label* L = $labl$$label;
+ __ jcc((Assembler::Condition)($cop$$cmpcode), *L, false); // Always long jump
+ %}
+ ins_pipe(pipe_jcc);
+%}
+
// ============================================================================
// The 2nd slow-half of a subtype check. Scan the subklass's 2ndary
// superklass array for an instance of the superklass. Set a hidden
@@ -17026,6 +17150,22 @@ instruct jmpConUCF2_short(cmpOpUCF2 cop, rFlagsRegUCF cmp, label labl) %{
ins_short_branch(1);
%}
+// Jump Direct Conditional - using signed and unsigned comparison
+instruct jmpConUCFE_short(cmpOpUCFE cop, rFlagsRegUCFE cmp, label labl) %{
+ match(If cop cmp);
+ effect(USE labl);
+
+ ins_cost(300);
+ format %{ "j$cop,sus $labl" %}
+ size(2);
+ ins_encode %{
+ Label* L = $labl$$label;
+ __ jccb((Assembler::Condition)($cop$$cmpcode), *L);
+ %}
+ ins_pipe(pipe_jcc);
+ ins_short_branch(1);
+%}
+
// ============================================================================
// inlined locking and unlocking
diff --git a/src/hotspot/cpu/zero/bytecodeInterpreter_zero.inline.hpp b/src/hotspot/cpu/zero/bytecodeInterpreter_zero.inline.hpp
index 4d813cd53c6..4c73368b673 100644
--- a/src/hotspot/cpu/zero/bytecodeInterpreter_zero.inline.hpp
+++ b/src/hotspot/cpu/zero/bytecodeInterpreter_zero.inline.hpp
@@ -26,6 +26,8 @@
#ifndef CPU_ZERO_BYTECODEINTERPRETER_ZERO_INLINE_HPP
#define CPU_ZERO_BYTECODEINTERPRETER_ZERO_INLINE_HPP
+#include "sanitizers/ub.hpp"
+
// Inline interpreter functions for zero
inline jfloat BytecodeInterpreter::VMfloatAdd(jfloat op1, jfloat op2) {
@@ -40,6 +42,7 @@ inline jfloat BytecodeInterpreter::VMfloatMul(jfloat op1, jfloat op2) {
return op1 * op2;
}
+ATTRIBUTE_NO_UBSAN // IEEE-754 division by zero is well-defined
inline jfloat BytecodeInterpreter::VMfloatDiv(jfloat op1, jfloat op2) {
return op1 / op2;
}
@@ -68,7 +71,7 @@ inline void BytecodeInterpreter::VMmemCopy64(uint32_t to[2],
}
inline jlong BytecodeInterpreter::VMlongAdd(jlong op1, jlong op2) {
- return op1 + op2;
+ return java_add(op1, op2);
}
inline jlong BytecodeInterpreter::VMlongAnd(jlong op1, jlong op2) {
@@ -82,7 +85,7 @@ inline jlong BytecodeInterpreter::VMlongDiv(jlong op1, jlong op2) {
}
inline jlong BytecodeInterpreter::VMlongMul(jlong op1, jlong op2) {
- return op1 * op2;
+ return java_multiply(op1, op2);
}
inline jlong BytecodeInterpreter::VMlongOr(jlong op1, jlong op2) {
@@ -90,7 +93,7 @@ inline jlong BytecodeInterpreter::VMlongOr(jlong op1, jlong op2) {
}
inline jlong BytecodeInterpreter::VMlongSub(jlong op1, jlong op2) {
- return op1 - op2;
+ return java_subtract(op1, op2);
}
inline jlong BytecodeInterpreter::VMlongXor(jlong op1, jlong op2) {
@@ -104,19 +107,19 @@ inline jlong BytecodeInterpreter::VMlongRem(jlong op1, jlong op2) {
}
inline jlong BytecodeInterpreter::VMlongUshr(jlong op1, jint op2) {
- return ((unsigned long long) op1) >> (op2 & 0x3F);
+ return java_shift_right_unsigned(op1, op2);
}
inline jlong BytecodeInterpreter::VMlongShr(jlong op1, jint op2) {
- return op1 >> (op2 & 0x3F);
+ return java_shift_right(op1, op2);
}
inline jlong BytecodeInterpreter::VMlongShl(jlong op1, jint op2) {
- return op1 << (op2 & 0x3F);
+ return java_shift_left(op1, op2);
}
inline jlong BytecodeInterpreter::VMlongNeg(jlong op) {
- return -op;
+ return java_negate(op);
}
inline jlong BytecodeInterpreter::VMlongNot(jlong op) {
@@ -183,8 +186,8 @@ inline jdouble BytecodeInterpreter::VMdoubleAdd(jdouble op1, jdouble op2) {
return op1 + op2;
}
+ATTRIBUTE_NO_UBSAN // IEEE-754 division by zero is well-defined
inline jdouble BytecodeInterpreter::VMdoubleDiv(jdouble op1, jdouble op2) {
- // Divide by zero... QQQ
return op1 / op2;
}
@@ -228,7 +231,7 @@ inline jdouble BytecodeInterpreter::VMfloat2Double(jfloat op) {
// Integer Arithmetic
inline jint BytecodeInterpreter::VMintAdd(jint op1, jint op2) {
- return op1 + op2;
+ return java_add(op1, op2);
}
inline jint BytecodeInterpreter::VMintAnd(jint op1, jint op2) {
@@ -242,11 +245,11 @@ inline jint BytecodeInterpreter::VMintDiv(jint op1, jint op2) {
}
inline jint BytecodeInterpreter::VMintMul(jint op1, jint op2) {
- return op1 * op2;
+ return java_multiply(op1, op2);
}
inline jint BytecodeInterpreter::VMintNeg(jint op) {
- return -op;
+ return java_negate(op);
}
inline jint BytecodeInterpreter::VMintOr(jint op1, jint op2) {
@@ -260,19 +263,19 @@ inline jint BytecodeInterpreter::VMintRem(jint op1, jint op2) {
}
inline jint BytecodeInterpreter::VMintShl(jint op1, jint op2) {
- return op1 << (op2 & 0x1F);
+ return java_shift_left(op1, op2);
}
inline jint BytecodeInterpreter::VMintShr(jint op1, jint op2) {
- return op1 >> (op2 & 0x1F);
+ return java_shift_right(op1, op2);
}
inline jint BytecodeInterpreter::VMintSub(jint op1, jint op2) {
- return op1 - op2;
+ return java_subtract(op1, op2);
}
inline juint BytecodeInterpreter::VMintUshr(jint op1, jint op2) {
- return ((juint) op1) >> (op2 & 0x1F);
+ return java_shift_right_unsigned(op1, op2);
}
inline jint BytecodeInterpreter::VMintXor(jint op1, jint op2) {
diff --git a/src/hotspot/cpu/zero/globals_zero.hpp b/src/hotspot/cpu/zero/globals_zero.hpp
index 6b6c6ea983c..6dc7d81275c 100644
--- a/src/hotspot/cpu/zero/globals_zero.hpp
+++ b/src/hotspot/cpu/zero/globals_zero.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -39,7 +39,7 @@ define_pd_global(bool, UncommonNullCast, true);
define_pd_global(bool, DelayCompilerStubsGeneration, false); // Don't have compiler's stubs
define_pd_global(size_t, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment.
-define_pd_global(intx, CodeEntryAlignment, 32);
+define_pd_global(uint, CodeEntryAlignment, 32);
define_pd_global(intx, OptoLoopAlignment, 16);
define_pd_global(intx, InlineSmallCode, 1000);
diff --git a/src/hotspot/cpu/zero/register_zero.hpp b/src/hotspot/cpu/zero/register_zero.hpp
index fd30f206762..846b649eebd 100644
--- a/src/hotspot/cpu/zero/register_zero.hpp
+++ b/src/hotspot/cpu/zero/register_zero.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright 2007 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -48,7 +48,6 @@ class RegisterImpl : public AbstractRegisterImpl {
};
// construction
- inline friend Register as_Register(int encoding);
VMReg as_VMReg();
// derived registers, offsets, and addresses
@@ -113,7 +112,6 @@ class ConcreteRegisterImpl : public AbstractRegisterImpl {
static const int max_fpr;
};
-CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1));
-#define noreg ((Register)(noreg_RegisterEnumValue))
+const Register noreg = as_Register(-1);
#endif // CPU_ZERO_REGISTER_ZERO_HPP
diff --git a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp
index 28c2364315e..1b20761f6e4 100644
--- a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp
+++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp
@@ -368,12 +368,15 @@ int ZeroInterpreter::native_entry(Method* method, intptr_t UNUSED, TRAPS) {
goto unlock_unwind_and_return;
void **arguments;
- void *mirror; {
+ // These locals must remain on stack until call completes
+ void *mirror;
+ void *env;
+ {
arguments =
(void **) stack->alloc(handler->argument_count() * sizeof(void **));
void **dst = arguments;
- void *env = thread->jni_environment();
+ env = thread->jni_environment();
*(dst++) = &env;
if (method->is_static()) {
diff --git a/src/hotspot/os/aix/decoder_aix.hpp b/src/hotspot/os/aix/decoder_aix.hpp
index 2ba3e1c5a3a..632355ccf4e 100644
--- a/src/hotspot/os/aix/decoder_aix.hpp
+++ b/src/hotspot/os/aix/decoder_aix.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -38,7 +38,7 @@ class AIXDecoder: public AbstractDecoder {
virtual bool demangle(const char* symbol, char* buf, int buflen) { return false; } // use AixSymbols::get_function_name to demangle
virtual bool decode(address addr, char* buf, int buflen, int* offset, const char* modulepath, bool demangle) {
- return AixSymbols::get_function_name(addr, buf, buflen, offset, 0, demangle);
+ return AixSymbols::get_function_name(addr, buf, buflen, offset, nullptr, demangle);
}
virtual bool decode(address addr, char *buf, int buflen, int* offset, const void *base) {
ShouldNotReachHere();
diff --git a/src/hotspot/os/aix/globals_aix.hpp b/src/hotspot/os/aix/globals_aix.hpp
index 14b956235e8..473d7759063 100644
--- a/src/hotspot/os/aix/globals_aix.hpp
+++ b/src/hotspot/os/aix/globals_aix.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2024 SAP SE. All rights reserved.
+ * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -61,10 +61,6 @@
product(bool, OptimizePollingPageLocation, true, DIAGNOSTIC, \
"Optimize the location of the polling page used for Safepoints") \
\
- /* Use 64K pages for virtual memory (shmat). */ \
- product(bool, Use64KPages, true, DIAGNOSTIC, \
- "Use 64K pages if available.") \
- \
/* Normally AIX commits memory on touch, but sometimes it is helpful to have */ \
/* explicit commit behaviour. This flag, if true, causes the VM to touch */ \
/* memory on os::commit_memory() (which normally is a noop). */ \
@@ -79,7 +75,6 @@
//
// UseLargePages means nothing, for now, on AIX.
-// Use Use64KPages or Use16MPages instead.
define_pd_global(size_t, PreTouchParallelChunkSize, 1 * G);
define_pd_global(bool, UseLargePages, false);
define_pd_global(bool, UseLargePagesIndividualAllocation, false);
diff --git a/src/hotspot/os/aix/libodm_aix.cpp b/src/hotspot/os/aix/libodm_aix.cpp
index 38e8067181a..57eee47c098 100644
--- a/src/hotspot/os/aix/libodm_aix.cpp
+++ b/src/hotspot/os/aix/libodm_aix.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2015, 2019 SAP SE. All rights reserved.
+ * Copyright (c) 2015, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -63,13 +63,12 @@ dynamicOdm::~dynamicOdm() {
void odmWrapper::clean_data() { if (_data) { permit_forbidden_function::free(_data); _data = nullptr; } }
-int odmWrapper::class_offset(const char *field, bool is_aix_5)
+int odmWrapper::class_offset(const char *field)
{
assert(has_class(), "initialization");
for (int i = 0; i < odm_class()->nelem; i++) {
if (strcmp(odm_class()->elem[i].elemname, field) == 0) {
int offset = odm_class()->elem[i].offset;
- if (is_aix_5) { offset += LINK_VAL_OFFSET; }
return offset;
}
}
@@ -88,11 +87,10 @@ void odmWrapper::determine_os_kernel_version(uint32_t* p_ver) {
return;
}
int voff, roff, moff, foff;
- bool is_aix_5 = (major_aix_version == 5);
- voff = odm.class_offset("ver", is_aix_5);
- roff = odm.class_offset("rel", is_aix_5);
- moff = odm.class_offset("mod", is_aix_5);
- foff = odm.class_offset("fix", is_aix_5);
+ voff = odm.class_offset("ver");
+ roff = odm.class_offset("rel");
+ moff = odm.class_offset("mod");
+ foff = odm.class_offset("fix");
if (voff == -1 || roff == -1 || moff == -1 || foff == -1) {
trcVerbose("try_determine_os_kernel_version: could not get offsets");
return;
diff --git a/src/hotspot/os/aix/libodm_aix.hpp b/src/hotspot/os/aix/libodm_aix.hpp
index 924ccaf8c51..11e67a4f5ae 100644
--- a/src/hotspot/os/aix/libodm_aix.hpp
+++ b/src/hotspot/os/aix/libodm_aix.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2015, 2024 SAP SE. All rights reserved.
+ * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -82,7 +82,7 @@ class odmWrapper : private dynamicOdm {
CLASS_SYMBOL odm_class() { return _odm_class; }
bool has_class() { return odm_class() != (CLASS_SYMBOL)-1; }
- int class_offset(const char *field, bool is_aix_5);
+ int class_offset(const char *field);
char* data() { return _data; }
char* retrieve_obj(const char* name = nullptr) {
diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp
index d7c1911a914..af743dc7484 100644
--- a/src/hotspot/os/aix/os_aix.cpp
+++ b/src/hotspot/os/aix/os_aix.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -122,12 +122,6 @@
extern "C"
int mread_real_time(timebasestruct_t *t, size_t size_of_timebasestruct_t);
-#if !defined(_AIXVERSION_610)
-extern "C" int getthrds64(pid_t, struct thrdentry64*, int, tid64_t*, int);
-extern "C" int getprocs64(procentry64*, int, fdsinfo*, int, pid_t*, int);
-extern "C" int getargs(procsinfo*, int, char*, int);
-#endif
-
#define MAX_PATH (2 * K)
// for multipage initialization error analysis (in 'g_multipage_error')
@@ -216,7 +210,7 @@ static address g_brk_at_startup = nullptr;
// shmctl(). Different shared memory regions can have different page
// sizes.
//
-// More information can be found at AIBM info center:
+// More information can be found at IBM info center:
// http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=/com.ibm.aix.prftungd/doc/prftungd/multiple_page_size_app_support.htm
//
static struct {
@@ -703,7 +697,7 @@ static void *thread_native_entry(Thread *thread) {
log_info(os, thread)("Thread finished (tid: %zu, kernel thread id: %zu).",
os::current_thread_id(), (uintx) kernel_thread_id);
- return 0;
+ return nullptr;
}
bool os::create_thread(Thread* thread, ThreadType thr_type,
@@ -1753,10 +1747,9 @@ bool os::pd_create_stack_guard_pages(char* addr, size_t size) {
return true;
}
-bool os::remove_stack_guard_pages(char* addr, size_t size) {
+void os::remove_stack_guard_pages(char* addr, size_t size) {
// Do not call this; no need to commit stack pages on AIX.
ShouldNotReachHere();
- return true;
}
void os::pd_realign_memory(char *addr, size_t bytes, size_t alignment_hint) {
@@ -1957,11 +1950,6 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_
return nullptr;
}
-bool os::pd_release_memory_special(char* base, size_t bytes) {
- fatal("os::release_memory_special should not be called on AIX.");
- return false;
-}
-
size_t os::large_page_size() {
return _large_page_size;
}
@@ -2143,46 +2131,12 @@ void os::init(void) {
// 64k no --- AIX 5.2 ? ---
// 64k yes 64k new systems and standard java loader (we set datapsize=64k when linking)
- // We explicitly leave no option to change page size, because only upgrading would work,
- // not downgrading (if stack page size is 64k you cannot pretend its 4k).
-
- if (g_multipage_support.datapsize == 4*K) {
- // datapsize = 4K. Data segment, thread stacks are 4K paged.
- if (g_multipage_support.can_use_64K_pages || g_multipage_support.can_use_64K_mmap_pages) {
- // .. but we are able to use 64K pages dynamically.
- // This would be typical for java launchers which are not linked
- // with datapsize=64K (like, any other launcher but our own).
- //
- // In this case it would be smart to allocate the java heap with 64K
- // to get the performance benefit, and to fake 64k pages for the
- // data segment (when dealing with thread stacks).
- //
- // However, leave a possibility to downgrade to 4K, using
- // -XX:-Use64KPages.
- if (Use64KPages) {
- trcVerbose("64K page mode (faked for data segment)");
- set_page_size(64*K);
- } else {
- trcVerbose("4K page mode (Use64KPages=off)");
- set_page_size(4*K);
- }
- } else {
- // .. and not able to allocate 64k pages dynamically. Here, just
- // fall back to 4K paged mode and use mmap for everything.
- trcVerbose("4K page mode");
- set_page_size(4*K);
- FLAG_SET_ERGO(Use64KPages, false);
- }
- } else {
- // datapsize = 64k. Data segment, thread stacks are 64k paged.
- // This normally means that we can allocate 64k pages dynamically.
- // (There is one special case where this may be false: EXTSHM=on.
- // but we decided to not support that mode).
- assert0(g_multipage_support.can_use_64K_pages || g_multipage_support.can_use_64K_mmap_pages);
- set_page_size(64*K);
- trcVerbose("64K page mode");
- FLAG_SET_ERGO(Use64KPages, true);
- }
+ // datapsize = 64k. Data segment, thread stacks are 64k paged.
+ // This normally means that we can allocate 64k pages dynamically.
+ // (There is one special case where this may be false: EXTSHM=on.
+ // but we decided to not support that mode).
+ assert0(g_multipage_support.can_use_64K_pages || g_multipage_support.can_use_64K_mmap_pages);
+ set_page_size(64*K);
// For now UseLargePages is just ignored.
FLAG_SET_ERGO(UseLargePages, false);
@@ -2565,23 +2519,18 @@ void os::Aix::initialize_os_info() {
assert(minor > 0, "invalid OS release");
_os_version = (major << 24) | (minor << 16);
char ver_str[20] = {0};
- const char* name_str = "unknown OS";
- if (strcmp(uts.sysname, "AIX") == 0) {
- // We run on AIX. We do not support versions older than AIX 7.1.
- // Determine detailed AIX version: Version, Release, Modification, Fix Level.
- odmWrapper::determine_os_kernel_version(&_os_version);
- if (os_version_short() < 0x0701) {
- log_warning(os)("AIX releases older than AIX 7.1 are not supported.");
- assert(false, "AIX release too old.");
- }
- name_str = "AIX";
- jio_snprintf(ver_str, sizeof(ver_str), "%u.%u.%u.%u",
- major, minor, (_os_version >> 8) & 0xFF, _os_version & 0xFF);
- } else {
- assert(false, "%s", name_str);
+ // We do not support versions older than AIX 7.2 TL 5.
+ // Determine detailed AIX version: Version, Release, Modification, Fix Level.
+ odmWrapper::determine_os_kernel_version(&_os_version);
+ if (_os_version < 0x07020500) {
+ log_warning(os)("AIX releases older than AIX 7.2 TL 5 are not supported.");
+ assert(false, "AIX release too old.");
}
- log_info(os)("We run on %s %s", name_str, ver_str);
+
+ jio_snprintf(ver_str, sizeof(ver_str), "%u.%u.%u.%u",
+ major, minor, (_os_version >> 8) & 0xFF, _os_version & 0xFF);
+ log_info(os)("We run on AIX %s", ver_str);
}
guarantee(_os_version, "Could not determine AIX release");
diff --git a/src/hotspot/os/aix/os_perf_aix.cpp b/src/hotspot/os/aix/os_perf_aix.cpp
index aa8819d035f..cbf78083483 100644
--- a/src/hotspot/os/aix/os_perf_aix.cpp
+++ b/src/hotspot/os/aix/os_perf_aix.cpp
@@ -143,12 +143,6 @@ static OSReturn get_jvm_load(double* jvm_uload, double* jvm_sload) {
return OS_OK;
}
-static void update_prev_time(jvm_time_store_t* from, jvm_time_store_t* to) {
- if (from && to) {
- memcpy(to, from, sizeof(jvm_time_store_t));
- }
-}
-
static void update_prev_ticks(cpu_tick_store_t* from, cpu_tick_store_t* to) {
if (from && to) {
memcpy(to, from, sizeof(cpu_tick_store_t));
diff --git a/src/hotspot/os/aix/porting_aix.cpp b/src/hotspot/os/aix/porting_aix.cpp
index 7311afc197b..b3f878fbfdd 100644
--- a/src/hotspot/os/aix/porting_aix.cpp
+++ b/src/hotspot/os/aix/porting_aix.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012, 2024 SAP SE. All rights reserved.
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -78,7 +78,7 @@ class fixed_strings {
public:
- fixed_strings() : first(0) {}
+ fixed_strings() : first(nullptr) {}
~fixed_strings() {
node* n = first;
while (n) {
@@ -113,7 +113,7 @@ bool AixSymbols::get_function_name (
// information (null if not available)
bool demangle // [in] whether to demangle the name
) {
- struct tbtable* tb = 0;
+ struct tbtable* tb = nullptr;
unsigned int searchcount = 0;
// initialize output parameters
@@ -653,10 +653,10 @@ void AixNativeCallstack::print_callstack_for_context(outputStream* st, const uco
// To print the first frame, use the current value of iar:
// current entry indicated by iar (the current pc)
- codeptr_t cur_iar = 0;
- stackptr_t cur_sp = 0;
- codeptr_t cur_rtoc = 0;
- codeptr_t cur_lr = 0;
+ codeptr_t cur_iar = nullptr;
+ stackptr_t cur_sp = nullptr;
+ codeptr_t cur_rtoc = nullptr;
+ codeptr_t cur_lr = nullptr;
const ucontext_t* uc = (const ucontext_t*) context;
@@ -926,7 +926,7 @@ static struct handletableentry* p_handletable = nullptr;
static const char* rtv_linkedin_libpath() {
constexpr int bufsize = 4096;
static char buffer[bufsize];
- static const char* libpath = 0;
+ static const char* libpath = nullptr;
// we only try to retrieve the libpath once. After that try we
// let libpath point to buffer, which then contains a valid libpath
diff --git a/src/hotspot/os/bsd/globals_bsd.hpp b/src/hotspot/os/bsd/globals_bsd.hpp
index 850d491a11f..22f587ed789 100644
--- a/src/hotspot/os/bsd/globals_bsd.hpp
+++ b/src/hotspot/os/bsd/globals_bsd.hpp
@@ -28,6 +28,7 @@
//
// Declare Bsd specific flags. They are not available on other platforms.
//
+#ifdef AARCH64
#define RUNTIME_OS_FLAGS(develop, \
develop_pd, \
product, \
@@ -35,9 +36,21 @@
range, \
constraint) \
\
- AARCH64_ONLY(develop(bool, AssertWXAtThreadSync, true, \
- "Conservatively check W^X thread state at possible safepoint" \
- "or handshake"))
+ develop(bool, TraceWXHealing, false, \
+ "track occurrences of W^X mode healing") \
+ develop(bool, UseOldWX, false, \
+ "Choose old W^X implementation.") \
+ product(bool, StressWXHealing, false, DIAGNOSTIC, \
+ "Stress W xor X healing on MacOS")
+
+#else
+#define RUNTIME_OS_FLAGS(develop, \
+ develop_pd, \
+ product, \
+ product_pd, \
+ range, \
+ constraint)
+#endif
// end of RUNTIME_OS_FLAGS
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index 0e21c2d1785..29ebe65e0db 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -841,6 +841,7 @@ jlong os::javaTimeNanos() {
// We might also condition (c) on the magnitude of the delta between obsv and now.
// Avoiding excessive CAS operations to hot RW locations is critical.
// See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
+ // https://web.archive.org/web/20131214182431/https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
return (prev == obsv) ? now : obsv;
}
@@ -1781,10 +1782,8 @@ bool os::pd_create_stack_guard_pages(char* addr, size_t size) {
return os::commit_memory(addr, size, !ExecMem);
}
-// If this is a growable mapping, remove the guard pages entirely by
-// munmap()ping them. If not, just call uncommit_memory().
-bool os::remove_stack_guard_pages(char* addr, size_t size) {
- return os::uncommit_memory(addr, size);
+void os::remove_stack_guard_pages(char* addr, size_t size) {
+ os::uncommit_memory(addr, size);
}
// 'requested_addr' is only treated as a hint, the return value may or
@@ -1886,11 +1885,6 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_
return nullptr;
}
-bool os::pd_release_memory_special(char* base, size_t bytes) {
- fatal("os::release_memory_special should not be called on BSD.");
- return false;
-}
-
size_t os::large_page_size() {
return _large_page_size;
}
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index 7190845a8ba..9c2fbab7535 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -3523,6 +3523,9 @@ bool os::pd_uncommit_memory(char* addr, size_t size, bool exec) {
log_trace(os, map)("mmap failed: " RANGEFMT " errno=(%s)",
RANGEFMTARGS(addr, size),
os::strerror(ep.saved_errno()));
+ if (ep.saved_errno() == ENOMEM) {
+ fatal("Failed to uncommit " RANGEFMT ". It is possible that the process's maximum number of mappings would have been exceeded. Try increasing the limit.", RANGEFMTARGS(addr, size));
+ }
return false;
}
return true;
@@ -3633,14 +3636,16 @@ bool os::pd_create_stack_guard_pages(char* addr, size_t size) {
// It's safe to always unmap guard pages for primordial thread because we
// always place it right after end of the mapped region.
-bool os::remove_stack_guard_pages(char* addr, size_t size) {
- uintptr_t stack_extent, stack_base;
+void os::remove_stack_guard_pages(char* addr, size_t size) {
if (os::is_primordial_thread()) {
- return ::munmap(addr, size) == 0;
+ if (::munmap(addr, size) != 0) {
+ fatal("Failed to munmap " RANGEFMT, RANGEFMTARGS(addr, size));
+ }
+ return;
}
- return os::uncommit_memory(addr, size);
+ os::uncommit_memory(addr, size);
}
// 'requested_addr' is only treated as a hint, the return value may or
@@ -4203,12 +4208,6 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_
return addr;
}
-bool os::pd_release_memory_special(char* base, size_t bytes) {
- assert(UseLargePages, "only for large pages");
- // Plain munmap is sufficient
- return pd_release_memory(base, bytes);
-}
-
size_t os::large_page_size() {
return _large_page_size;
}
diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp
index 5412e2bc92d..f147ed4be93 100644
--- a/src/hotspot/os/posix/os_posix.cpp
+++ b/src/hotspot/os/posix/os_posix.cpp
@@ -906,8 +906,25 @@ FILE* os::fdopen(int fd, const char* mode) {
ssize_t os::pd_write(int fd, const void *buf, size_t nBytes) {
ssize_t res;
+#ifdef __APPLE__
+ // macOS fails for individual write operations > 2GB.
+ // See https://gitlab.haskell.org/ghc/ghc/-/issues/17414
+ ssize_t total = 0;
+ while (nBytes > 0) {
+ size_t bytes_to_write = MIN2(nBytes, (size_t)INT_MAX);
+ RESTARTABLE(::write(fd, buf, bytes_to_write), res);
+ if (res == OS_ERR) {
+ return OS_ERR;
+ }
+ buf = (const char*)buf + res;
+ nBytes -= res;
+ total += res;
+ }
+ return total;
+#else
RESTARTABLE(::write(fd, buf, nBytes), res);
return res;
+#endif
}
ssize_t os::read_at(int fd, void *buf, unsigned int nBytes, jlong offset) {
diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp
index ce9c2a4f031..d9bde6fa825 100644
--- a/src/hotspot/os/posix/perfMemory_posix.cpp
+++ b/src/hotspot/os/posix/perfMemory_posix.cpp
@@ -494,6 +494,7 @@ static char* get_user_name(uid_t uid) {
return user_name;
}
+#ifndef __APPLE__
// return the name of the user that owns the process identified by vmid.
//
// This method uses a slow directory search algorithm to find the backing
@@ -657,6 +658,7 @@ static char* get_user_name(int vmid, int *nspid, TRAPS) {
#endif
return result;
}
+#endif
// return the file name of the backing store file for the named
// shared memory region for the given user name and vmid.
diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index b0b7ae18106..76f47640e5a 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -3281,11 +3281,10 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi
// Do manual alignment
aligned_base = align_up(extra_base, alignment);
- bool rc = (file_desc != -1) ? os::unmap_memory(extra_base, extra_size) :
- os::release_memory(extra_base, extra_size);
- assert(rc, "release failed");
- if (!rc) {
- return nullptr;
+ if (file_desc != -1) {
+ os::unmap_memory(extra_base, extra_size);
+ } else {
+ os::release_memory(extra_base, extra_size);
}
// Attempt to map, into the just vacated space, the slightly smaller aligned area.
@@ -3518,11 +3517,6 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_
return reserve_large_pages(bytes, addr, exec);
}
-bool os::pd_release_memory_special(char* base, size_t bytes) {
- assert(base != nullptr, "Sanity check");
- return pd_release_memory(base, bytes);
-}
-
static void warn_fail_commit_memory(char* addr, size_t bytes, bool exec) {
int err = os::get_last_error();
char buf[256];
@@ -3681,8 +3675,8 @@ bool os::pd_create_stack_guard_pages(char* addr, size_t size) {
return os::commit_memory(addr, size, !ExecMem);
}
-bool os::remove_stack_guard_pages(char* addr, size_t size) {
- return os::uncommit_memory(addr, size);
+void os::remove_stack_guard_pages(char* addr, size_t size) {
+ os::uncommit_memory(addr, size);
}
static bool protect_pages_individually(char* addr, size_t bytes, unsigned int p, DWORD *old_status) {
diff --git a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp
index 62dba218b2f..36599594842 100644
--- a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp
+++ b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp
@@ -54,8 +54,11 @@
#include "signals_posix.hpp"
#include "utilities/align.hpp"
#include "utilities/debug.hpp"
+#include "utilities/decoder.hpp"
#include "utilities/events.hpp"
+#include "utilities/nativeStackPrinter.hpp"
#include "utilities/vmError.hpp"
+#include "compiler/disassembler.hpp"
// put OS-includes here
# include
@@ -85,6 +88,8 @@
#define SPELL_REG_SP "sp"
#ifdef __APPLE__
+WXMode DefaultWXWriteMode;
+
// see darwin-xnu/osfmk/mach/arm/_structs.h
// 10.5 UNIX03 member name prefixes
@@ -233,19 +238,56 @@ NOINLINE frame os::current_frame() {
bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
ucontext_t* uc, JavaThread* thread) {
- // Enable WXWrite: this function is called by the signal handler at arbitrary
- // point of execution.
- ThreadWXEnable wx(WXWrite, thread);
-
// decide if this trap can be handled by a stub
address stub = nullptr;
-
- address pc = nullptr;
+ address pc = nullptr;
//%note os_trap_1
if (info != nullptr && uc != nullptr && thread != nullptr) {
pc = (address) os::Posix::ucontext_get_pc(uc);
+#ifdef MACOS_AARCH64
+ // If we got a SIGBUS because we tried to write into the code
+ // cache, try enabling WXWrite mode.
+ if (sig == SIGBUS
+ && pc != info->si_addr
+ && CodeCache::contains(info->si_addr)
+ && os::address_is_in_vm(pc)) {
+ WXMode *entry_mode = thread->_cur_wx_mode;
+ if (entry_mode != nullptr && *entry_mode == WXArmedForWrite) {
+ if (TraceWXHealing) {
+ static const char *mode_names[3] = {"WXWrite", "WXExec", "WXArmedForWrite"};
+ tty->print("Healing WXMode %s at %p to WXWrite",
+ mode_names[*entry_mode], entry_mode);
+ char name[128];
+ int offset = 0;
+ if (os::dll_address_to_function_name(pc, name, sizeof name, &offset)) {
+ tty->print_cr(" (%s+0x%x)", name, offset);
+ } else {
+ tty->cr();
+ }
+ if (Verbose) {
+ char buf[O_BUFLEN];
+ NativeStackPrinter nsp(thread);
+ nsp.print_stack(tty, buf, sizeof(buf), pc,
+ true /* print_source_info */, -1 /* max stack */);
+ }
+ }
+#ifndef PRODUCT
+ guarantee(StressWXHealing,
+ "We should not reach here unless StressWXHealing");
+#endif
+ *(thread->_cur_wx_mode) = WXWrite;
+ return thread->wx_enable_write();
+ }
+ }
+
+ // There may be cases where code after this point that we call
+ // from the signal handler changes WX state, so we protect against
+ // that by saving and restoring the state.
+ ThreadWXEnable wx(thread->get_wx_state(), thread);
+#endif
+
// Handle ALL stack overflow variations here
if (sig == SIGSEGV || sig == SIGBUS) {
address addr = (address) info->si_addr;
@@ -515,11 +557,42 @@ int os::extra_bang_size_in_bytes() {
return 0;
}
-#ifdef __APPLE__
+#ifdef MACOS_AARCH64
+THREAD_LOCAL bool os::_jit_exec_enabled;
+
+// This is a wrapper around the standard library function
+// pthread_jit_write_protect_np(3). We keep track of the state of
+// per-thread write protection on the MAP_JIT region in the
+// thread-local variable os::_jit_exec_enabled
void os::current_thread_enable_wx(WXMode mode) {
- pthread_jit_write_protect_np(mode == WXExec);
+ bool exec_enabled = mode != WXWrite;
+ if (exec_enabled != _jit_exec_enabled NOT_PRODUCT( || DefaultWXWriteMode == WXWrite)) {
+ permit_forbidden_function::pthread_jit_write_protect_np(exec_enabled);
+ _jit_exec_enabled = exec_enabled;
+ }
}
-#endif
+
+// If the current thread is in the WX state WXArmedForWrite, change
+// the state to WXWrite.
+bool Thread::wx_enable_write() {
+ if (_wx_state == WXArmedForWrite) {
+ _wx_state = WXWrite;
+ os::current_thread_enable_wx(WXWrite);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// A wrapper around wx_enable_write() for when the current thread is
+// not known.
+void os::thread_wx_enable_write_impl() {
+ if (!StressWXHealing) {
+ Thread::current()->wx_enable_write();
+ }
+}
+
+#endif // MACOS_AARCH64
static inline void atomic_copy64(const volatile void *src, volatile void *dst) {
*(jlong *) dst = *(const jlong *) src;
diff --git a/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp
index d82b9d90417..2b96e978980 100644
--- a/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp
+++ b/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp
@@ -42,6 +42,16 @@ frame JavaThread::pd_last_frame() {
void JavaThread::cache_global_variables() {
BarrierSet* bs = BarrierSet::barrier_set();
+#if INCLUDE_G1GC
+ if (bs->is_a(BarrierSet::G1BarrierSet)) {
+ _card_table_base = nullptr;
+ } else
+#endif
+#if INCLUDE_SHENANDOAHGC
+ if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) {
+ _card_table_base = nullptr;
+ } else
+#endif
if (bs->is_a(BarrierSet::CardTableBarrierSet)) {
CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set();
_card_table_base = (address)ctbs->card_table_base_const();
diff --git a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp
index a20feadcba4..93beb549366 100644
--- a/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp
+++ b/src/hotspot/os_cpu/windows_aarch64/vm_version_windows_aarch64.cpp
@@ -27,22 +27,30 @@
#include "runtime/vm_version.hpp"
int VM_Version::get_current_sve_vector_length() {
- assert(_features & CPU_SVE, "should not call this");
+ assert(VM_Version::supports_sve(), "should not call this");
ShouldNotReachHere();
return 0;
}
int VM_Version::set_and_get_current_sve_vector_length(int length) {
- assert(_features & CPU_SVE, "should not call this");
+ assert(VM_Version::supports_sve(), "should not call this");
ShouldNotReachHere();
return 0;
}
void VM_Version::get_os_cpu_info() {
- if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) _features |= CPU_CRC32;
- if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) _features |= CPU_AES | CPU_SHA1 | CPU_SHA2;
- if (IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE)) _features |= CPU_ASIMD;
+ if (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) {
+ set_feature(CPU_CRC32);
+ }
+ if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) {
+ set_feature(CPU_AES);
+ set_feature(CPU_SHA1);
+ set_feature(CPU_SHA2);
+ }
+ if (IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE)) {
+ set_feature(CPU_ASIMD);
+ }
// No check for CPU_PMULL, CPU_SVE, CPU_SVE2
__int64 dczid_el0 = _ReadStatusReg(0x5807 /* ARM64_DCZID_EL0 */);
diff --git a/src/hotspot/share/adlc/adlArena.cpp b/src/hotspot/share/adlc/adlArena.cpp
index ebd1f74911d..e3ae60e91a9 100644
--- a/src/hotspot/share/adlc/adlArena.cpp
+++ b/src/hotspot/share/adlc/adlArena.cpp
@@ -136,9 +136,9 @@ void *AdlArena::Acalloc( size_t items, size_t x ) {
}
//------------------------------realloc----------------------------------------
-static size_t pointer_delta(const void *left, const void *right) {
- assert(left >= right, "pointer delta underflow");
- return (uintptr_t)left - (uintptr_t)right;
+static size_t pointer_delta(const void* high, const void* low) {
+ assert(high >= low, "pointer delta underflow");
+ return (uintptr_t)high - (uintptr_t)low;
}
// Reallocate storage in AdlArena.
diff --git a/src/hotspot/share/adlc/main.cpp b/src/hotspot/share/adlc/main.cpp
index 4e8a96617e8..8e6ea5bbec9 100644
--- a/src/hotspot/share/adlc/main.cpp
+++ b/src/hotspot/share/adlc/main.cpp
@@ -213,6 +213,7 @@ int main(int argc, char *argv[])
AD.addInclude(AD._CPP_file, "adfiles", get_basename(AD._VM_file._name));
AD.addInclude(AD._CPP_file, "adfiles", get_basename(AD._HPP_file._name));
AD.addInclude(AD._CPP_file, "memory/allocation.inline.hpp");
+ AD.addInclude(AD._CPP_file, "code/aotCodeCache.hpp");
AD.addInclude(AD._CPP_file, "code/codeCache.hpp");
AD.addInclude(AD._CPP_file, "code/compiledIC.hpp");
AD.addInclude(AD._CPP_file, "code/nativeInst.hpp");
@@ -257,6 +258,7 @@ int main(int argc, char *argv[])
AD.addInclude(AD._CPP_PEEPHOLE_file, "adfiles", get_basename(AD._HPP_file._name));
AD.addInclude(AD._CPP_PIPELINE_file, "adfiles", get_basename(AD._HPP_file._name));
AD.addInclude(AD._DFA_file, "adfiles", get_basename(AD._HPP_file._name));
+ AD.addInclude(AD._DFA_file, "code/aotCodeCache.hpp");
AD.addInclude(AD._DFA_file, "oops/compressedOops.hpp");
AD.addInclude(AD._DFA_file, "opto/cfgnode.hpp"); // Use PROB_MAX in predicate.
AD.addInclude(AD._DFA_file, "opto/intrinsicnode.hpp");
diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp
index 7871134e923..ba525588f32 100644
--- a/src/hotspot/share/asm/codeBuffer.cpp
+++ b/src/hotspot/share/asm/codeBuffer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -98,6 +98,8 @@ CodeBuffer::CodeBuffer(const CodeBlob* blob) DEBUG_ONLY(: Scrubber(this, sizeof(
}
void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) {
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
+
// Always allow for empty slop around each section.
int slop = (int) CodeSection::end_slop();
@@ -466,9 +468,7 @@ void CodeBuffer::compute_final_layout(CodeBuffer* dest) const {
assert(!_finalize_stubs, "non-finalized stubs");
{
- // not sure why this is here, but why not...
- int alignSize = MAX2((intx) sizeof(jdouble), CodeEntryAlignment);
- assert( (dest->_total_start - _insts.start()) % alignSize == 0, "copy must preserve alignment");
+ assert( (dest->_total_start - _insts.start()) % CodeEntryAlignment == 0, "copy must preserve alignment");
}
const CodeSection* prev_cs = nullptr;
diff --git a/src/hotspot/share/asm/register.hpp b/src/hotspot/share/asm/register.hpp
index f406995b8ac..95c7c7922cf 100644
--- a/src/hotspot/share/asm/register.hpp
+++ b/src/hotspot/share/asm/register.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,15 +49,7 @@ class AbstractRegisterImpl {
// Macros to help define all kinds of registers
-#ifndef USE_POINTERS_TO_REGISTER_IMPL_ARRAY
-
-#define AS_REGISTER(type,name) ((type)name##_##type##EnumValue)
-
-#define CONSTANT_REGISTER_DECLARATION(type, name, value) \
-const type name = ((type)value); \
-enum { name##_##type##EnumValue = (value) }
-
-#else // USE_POINTERS_TO_REGISTER_IMPL_ARRAY
+#ifdef USE_POINTERS_TO_REGISTER_IMPL_ARRAY
#define REGISTER_IMPL_DECLARATION(type, impl_type, reg_count) \
inline constexpr type as_ ## type(int encoding) { \
@@ -69,16 +61,8 @@ inline constexpr type impl_type::first() { return all_ ## type ## s + 1; }
#define REGISTER_IMPL_DEFINITION(type, impl_type, reg_count) \
impl_type all_ ## type ## s[reg_count + 1];
-#define CONSTANT_REGISTER_DECLARATION(type, name, value) \
-constexpr type name = as_ ## type(value);
-
#endif // USE_POINTERS_TO_REGISTER_IMPL_ARRAY
-
-#define REGISTER_DECLARATION(type, name, value) \
-const type name = ((type)value)
-
-
// For definitions of RegisterImpl* instances. To be redefined in an
// OS-specific way.
#ifdef __GNUC__
diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp
index a4c956ff5be..63764dd113a 100644
--- a/src/hotspot/share/c1/c1_Runtime1.cpp
+++ b/src/hotspot/share/c1/c1_Runtime1.cpp
@@ -541,6 +541,7 @@ extern void vm_exit(int code);
// unpack_with_exception entry instead. This makes life for the exception blob easier
// because making that same check and diverting is painful from assembly language.
JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* current, oopDesc* ex, address pc, nmethod*& nm))
+ MACOS_AARCH64_ONLY(current->wx_enable_write());
Handle exception(current, ex);
// This function is called when we are about to throw an exception. Therefore,
diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp
index f77aac3540c..9275b914ef9 100644
--- a/src/hotspot/share/cds/aotClassLocation.cpp
+++ b/src/hotspot/share/cds/aotClassLocation.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -426,7 +426,8 @@ bool AOTClassLocation::check(const char* runtime_path, bool has_aot_linked_class
bool size_differs = _filesize != st.st_size;
bool time_differs = _check_time && (_timestamp != st.st_mtime);
if (size_differs || time_differs) {
- aot_log_warning(aot)("This file is not the one used while building the shared archive file: '%s'%s%s",
+ aot_log_warning(aot)("This file is not the one used while building the %s: '%s'%s%s",
+ CDSConfig::type_of_archive_being_loaded(),
runtime_path,
time_differs ? ", timestamp has changed" : "",
size_differs ? ", size has changed" : "");
@@ -448,6 +449,13 @@ void AOTClassLocationConfig::dumptime_init(JavaThread* current) {
java_lang_Throwable::print(current->pending_exception(), tty);
vm_exit_during_initialization("AOTClassLocationConfig::dumptime_init_helper() failed unexpectedly");
}
+
+ if (CDSConfig::is_dumping_final_static_archive()) {
+ // The _max_used_index is usually updated by ClassLoader::record_result(). However,
+ // when dumping the final archive, the classes are loaded from their images in
+ // the AOT config file, so we don't go through ClassLoader::record_result().
+ dumptime_update_max_used_index(runtime()->_max_used_index); // Same value as recorded in the training run.
+ }
}
void AOTClassLocationConfig::dumptime_init_helper(TRAPS) {
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CallTypeDataInterface.java b/src/hotspot/share/cds/aotCompressedPointers.cpp
similarity index 75%
rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CallTypeDataInterface.java
rename to src/hotspot/share/cds/aotCompressedPointers.cpp
index 0a8bf4721e4..c3efa7a7185 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/CallTypeDataInterface.java
+++ b/src/hotspot/share/cds/aotCompressedPointers.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,14 +22,9 @@
*
*/
-package sun.jvm.hotspot.oops;
+#include "cds/aotCompressedPointers.hpp"
+#include "cds/archiveBuilder.hpp"
-public interface CallTypeDataInterface {
- int numberOfArguments();
- boolean hasArguments();
- K argumentType(int i);
- boolean hasReturn();
- K returnType();
- int argumentTypeIndex(int i);
- int returnTypeIndex();
+size_t AOTCompressedPointers::compute_byte_offset(address p) {
+ return ArchiveBuilder::current()->any_to_offset(p);
}
diff --git a/src/hotspot/share/cds/aotCompressedPointers.hpp b/src/hotspot/share/cds/aotCompressedPointers.hpp
new file mode 100644
index 00000000000..a3919fb95a9
--- /dev/null
+++ b/src/hotspot/share/cds/aotCompressedPointers.hpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_CDS_AOTCOMPRESSEDPOINTERS_HPP
+#define SHARE_CDS_AOTCOMPRESSEDPOINTERS_HPP
+
+#include "cds/cds_globals.hpp"
+#include "memory/allStatic.hpp"
+#include "memory/metaspace.hpp"
+#include "metaprogramming/enableIf.hpp"
+#include "utilities/align.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/macros.hpp"
+
+class AOTCompressedPointers: public AllStatic {
+public:
+ // For space saving, we can encode the location of metadata objects in the "rw" and "ro"
+ // regions using a 32-bit offset from the bottom of the mapped AOT metaspace. Since metadata
+ // objects are 8-byte aligned, we store scaled offset units (offset_bytes >> 3) to address
+ // up to ~32GB on 64-bit platforms. We currently limit the MaxMetadataOffsetBytes to about
+ // 3.5 GB to be compatible with +CompactObjectHeaders.
+ enum class narrowPtr : u4;
+ static constexpr size_t MetadataOffsetShift = LP64_ONLY(3) NOT_LP64(0);
+ static constexpr size_t MaxMetadataOffsetBytes = LP64_ONLY(3584ULL * M) NOT_LP64(0x7FFFFFFF);
+
+ // Convert the encoded narrowPtr to a byte offset by applying the shift.
+ inline static size_t get_byte_offset(narrowPtr narrowp) {
+ return ((size_t)checked_cast(narrowp)) << MetadataOffsetShift;
+ }
+
+ inline static narrowPtr null() {
+ return static_cast(0);
+ }
+
+ // Encoding ------
+
+ // ptr can point to one of the following
+ // - an object in the ArchiveBuilder's buffer.
+ // - an object in the currently mapped AOT cache rw/ro regions.
+ // - an object that has been copied into the ArchiveBuilder's buffer.
+ template
+ static narrowPtr encode_not_null(T ptr) {
+ address p = reinterpret_cast(ptr);
+ return encode_byte_offset(compute_byte_offset(p));
+ }
+
+ template
+ static narrowPtr encode(T ptr) { // may be null
+ if (ptr == nullptr) {
+ return null();
+ } else {
+ return encode_not_null(ptr);
+ }
+ }
+
+ // ptr must be in the currently mapped AOT cache rw/ro regions.
+ template
+ static narrowPtr encode_address_in_cache(T ptr) {
+ assert(Metaspace::in_aot_cache(ptr), "must be");
+ address p = reinterpret_cast(ptr);
+ address base = reinterpret_cast(SharedBaseAddress);
+ return encode_byte_offset(pointer_delta(p, base, 1));
+ }
+
+ template
+ static narrowPtr encode_address_in_cache_or_null(T ptr) {
+ if (ptr == nullptr) {
+ return null();
+ } else {
+ return encode_address_in_cache(ptr);
+ }
+ }
+
+ // Decoding -----
+
+ // If base_address is null, decode an address within the mapped aot cache range.
+ template
+ static T decode_not_null(narrowPtr narrowp, address base_address = nullptr) {
+ assert(narrowp != null(), "sanity");
+ if (base_address == nullptr) {
+ T p = reinterpret_cast(reinterpret_cast(SharedBaseAddress) + get_byte_offset(narrowp));
+ assert(Metaspace::in_aot_cache(p), "must be");
+ return p;
+ } else {
+ // This is usually called before the cache is fully mapped.
+ return reinterpret_cast(base_address + get_byte_offset(narrowp));
+ }
+ }
+
+ template
+ static T decode(narrowPtr narrowp, address base_address = nullptr) { // may be null
+ if (narrowp == null()) {
+ return nullptr;
+ } else {
+ return decode_not_null(narrowp, base_address);
+ }
+ }
+
+private:
+ static size_t compute_byte_offset(address p);
+
+ static narrowPtr encode_byte_offset(size_t offset) {
+ assert(offset != 0, "offset 0 is in protection zone");
+ precond(offset <= MaxMetadataOffsetBytes);
+ assert(is_aligned(offset, (size_t)1 << MetadataOffsetShift), "offset not aligned");
+ return checked_cast(offset >> MetadataOffsetShift);
+ }
+};
+
+// Type casts -- declared as global functions to save a few keystrokes
+
+// A simple type cast. No change in numerical value.
+inline AOTCompressedPointers::narrowPtr cast_from_u4(u4 narrowp) {
+ return checked_cast(narrowp);
+}
+
+// A simple type cast. No change in numerical value.
+// !!!DO NOT CALL THIS if you want a byte offset!!!
+inline u4 cast_to_u4(AOTCompressedPointers::narrowPtr narrowp) {
+ return checked_cast(narrowp);
+}
+
+#endif // SHARE_CDS_AOTCOMPRESSEDPOINTERS_HPP
diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp
index 5e4e0956824..fa769aee1bf 100644
--- a/src/hotspot/share/cds/aotMapLogger.cpp
+++ b/src/hotspot/share/cds/aotMapLogger.cpp
@@ -88,7 +88,7 @@ void AOTMapLogger::ergo_initialize() {
}
void AOTMapLogger::dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo,
- ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info,
+ AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info,
char* bitmap, size_t bitmap_size_in_bytes) {
_is_runtime_logging = false;
_buffer_to_requested_delta = ArchiveBuilder::current()->buffer_to_requested_delta();
@@ -823,7 +823,7 @@ public:
}
}; // AOTMapLogger::ArchivedFieldPrinter
-void AOTMapLogger::dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* heap_info) {
+void AOTMapLogger::dumptime_log_mapped_heap_region(AOTMappedHeapInfo* heap_info) {
MemRegion r = heap_info->buffer_region();
address buffer_start = address(r.start()); // start of the current oop inside the buffer
address buffer_end = address(r.end());
@@ -835,7 +835,7 @@ void AOTMapLogger::dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* heap_i
log_archived_objects(AOTMappedHeapWriter::oop_iterator(heap_info));
}
-void AOTMapLogger::dumptime_log_streamed_heap_region(ArchiveStreamedHeapInfo* heap_info) {
+void AOTMapLogger::dumptime_log_streamed_heap_region(AOTStreamedHeapInfo* heap_info) {
MemRegion r = heap_info->buffer_region();
address buffer_start = address(r.start()); // start of the current oop inside the buffer
address buffer_end = address(r.end());
diff --git a/src/hotspot/share/cds/aotMapLogger.hpp b/src/hotspot/share/cds/aotMapLogger.hpp
index bf7ce0028b9..f495ed97f40 100644
--- a/src/hotspot/share/cds/aotMapLogger.hpp
+++ b/src/hotspot/share/cds/aotMapLogger.hpp
@@ -33,8 +33,8 @@
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
-class ArchiveMappedHeapInfo;
-class ArchiveStreamedHeapInfo;
+class AOTMappedHeapInfo;
+class AOTStreamedHeapInfo;
class CompileTrainingData;
class DumpRegion;
class FileMapInfo;
@@ -157,8 +157,8 @@ private:
#if INCLUDE_CDS_JAVA_HEAP
- static void dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* mapped_heap_info);
- static void dumptime_log_streamed_heap_region(ArchiveStreamedHeapInfo* streamed_heap_info);
+ static void dumptime_log_mapped_heap_region(AOTMappedHeapInfo* mapped_heap_info);
+ static void dumptime_log_streamed_heap_region(AOTStreamedHeapInfo* streamed_heap_info);
static void runtime_log_heap_region(FileMapInfo* mapinfo);
static void print_oop_info_cr(outputStream* st, FakeOop fake_oop, bool print_location = true);
@@ -173,7 +173,7 @@ public:
static bool is_logging_at_bootstrap() { return _is_logging_at_bootstrap; }
static void dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo,
- ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info,
+ AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info,
char* bitmap, size_t bitmap_size_in_bytes);
static void runtime_log(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo);
};
diff --git a/src/hotspot/share/cds/aotMappedHeap.cpp b/src/hotspot/share/cds/aotMappedHeap.cpp
new file mode 100644
index 00000000000..ba24c43eea1
--- /dev/null
+++ b/src/hotspot/share/cds/aotMappedHeap.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "cds/aotMappedHeap.hpp"
+
+// Anything that goes in the header must be thoroughly purged from uninitialized memory
+// as it will be written to disk. Therefore, the constructors memset the memory to 0.
+// This is not the prettiest thing, but we need to know every byte is initialized,
+// including potential padding between fields.
+
+AOTMappedHeapHeader::AOTMappedHeapHeader(size_t ptrmap_start_pos,
+ size_t oopmap_start_pos,
+ HeapRootSegments root_segments) {
+ memset((char*)this, 0, sizeof(*this));
+ _ptrmap_start_pos = ptrmap_start_pos;
+ _oopmap_start_pos = oopmap_start_pos;
+ _root_segments = root_segments;
+}
+
+AOTMappedHeapHeader::AOTMappedHeapHeader() {
+ memset((char*)this, 0, sizeof(*this));
+}
+
+AOTMappedHeapHeader AOTMappedHeapInfo::create_header() {
+ return AOTMappedHeapHeader{_ptrmap_start_pos,
+ _oopmap_start_pos,
+ _root_segments};
+}
diff --git a/src/hotspot/share/cds/aotMappedHeap.hpp b/src/hotspot/share/cds/aotMappedHeap.hpp
new file mode 100644
index 00000000000..307451b24d4
--- /dev/null
+++ b/src/hotspot/share/cds/aotMappedHeap.hpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_CDS_AOTMAPPEDHEAP_HPP
+#define SHARE_CDS_AOTMAPPEDHEAP_HPP
+
+#include "cds/aotMapLogger.hpp"
+#include "utilities/growableArray.hpp"
+#include "utilities/macros.hpp"
+
+class AOTMappedHeapHeader {
+ size_t _ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the heap.
+ size_t _oopmap_start_pos; // The first bit in the oopmap corresponds to this position in the heap.
+ HeapRootSegments _root_segments; // Heap root segments info
+
+public:
+ AOTMappedHeapHeader();
+ AOTMappedHeapHeader(size_t ptrmap_start_pos,
+ size_t oopmap_start_pos,
+ HeapRootSegments root_segments);
+
+ size_t ptrmap_start_pos() const { return _ptrmap_start_pos; }
+ size_t oopmap_start_pos() const { return _oopmap_start_pos; }
+ HeapRootSegments root_segments() const { return _root_segments; }
+
+ // This class is trivially copyable and assignable.
+ AOTMappedHeapHeader(const AOTMappedHeapHeader&) = default;
+ AOTMappedHeapHeader& operator=(const AOTMappedHeapHeader&) = default;
+};
+
+class AOTMappedHeapInfo {
+ MemRegion _buffer_region; // Contains the archived objects to be written into the CDS archive.
+ CHeapBitMap _oopmap;
+ CHeapBitMap _ptrmap;
+ HeapRootSegments _root_segments;
+ size_t _oopmap_start_pos; // How many zeros were removed from the beginning of the bit map?
+ size_t _ptrmap_start_pos; // How many zeros were removed from the beginning of the bit map?
+
+public:
+ AOTMappedHeapInfo() :
+ _buffer_region(),
+ _oopmap(128, mtClassShared),
+ _ptrmap(128, mtClassShared),
+ _root_segments(),
+ _oopmap_start_pos(),
+ _ptrmap_start_pos() {}
+ bool is_used() { return !_buffer_region.is_empty(); }
+
+ MemRegion buffer_region() { return _buffer_region; }
+ void set_buffer_region(MemRegion r) { _buffer_region = r; }
+
+ char* buffer_start() { return (char*)_buffer_region.start(); }
+ size_t buffer_byte_size() { return _buffer_region.byte_size(); }
+
+ CHeapBitMap* oopmap() { return &_oopmap; }
+ CHeapBitMap* ptrmap() { return &_ptrmap; }
+
+ void set_oopmap_start_pos(size_t start_pos) { _oopmap_start_pos = start_pos; }
+ void set_ptrmap_start_pos(size_t start_pos) { _ptrmap_start_pos = start_pos; }
+
+ void set_root_segments(HeapRootSegments segments) { _root_segments = segments; };
+ HeapRootSegments root_segments() { return _root_segments; }
+
+ AOTMappedHeapHeader create_header();
+};
+
+#if INCLUDE_CDS_JAVA_HEAP
+class AOTMappedHeapOopIterator : public AOTMapLogger::OopDataIterator {
+protected:
+ address _current;
+ address _next;
+
+ address _buffer_start;
+ address _buffer_end;
+ uint64_t _buffer_start_narrow_oop;
+ intptr_t _buffer_to_requested_delta;
+ int _requested_shift;
+
+ size_t _num_root_segments;
+ size_t _num_obj_arrays_logged;
+
+public:
+ AOTMappedHeapOopIterator(address buffer_start,
+ address buffer_end,
+ address requested_base,
+ address requested_start,
+ int requested_shift,
+ size_t num_root_segments)
+ : _current(nullptr),
+ _next(buffer_start),
+ _buffer_start(buffer_start),
+ _buffer_end(buffer_end),
+ _requested_shift(requested_shift),
+ _num_root_segments(num_root_segments),
+ _num_obj_arrays_logged(0) {
+ _buffer_to_requested_delta = requested_start - buffer_start;
+ _buffer_start_narrow_oop = 0xdeadbeed;
+ if (UseCompressedOops) {
+ _buffer_start_narrow_oop = (uint64_t)(pointer_delta(requested_start, requested_base, 1)) >> requested_shift;
+ assert(_buffer_start_narrow_oop < 0xffffffff, "sanity");
+ }
+ }
+
+ virtual AOTMapLogger::OopData capture(address buffered_addr) = 0;
+
+ bool has_next() override {
+ return _next < _buffer_end;
+ }
+
+ AOTMapLogger::OopData next() override {
+ _current = _next;
+ AOTMapLogger::OopData result = capture(_current);
+ if (result._klass->is_objArray_klass()) {
+ result._is_root_segment = _num_obj_arrays_logged++ < _num_root_segments;
+ }
+ _next = _current + result._size * BytesPerWord;
+ return result;
+ }
+
+ AOTMapLogger::OopData obj_at(narrowOop* addr) override {
+ uint64_t n = (uint64_t)(*addr);
+ if (n == 0) {
+ return null_data();
+ } else {
+ precond(n >= _buffer_start_narrow_oop);
+ address buffer_addr = _buffer_start + ((n - _buffer_start_narrow_oop) << _requested_shift);
+ return capture(buffer_addr);
+ }
+ }
+
+ AOTMapLogger::OopData obj_at(oop* addr) override {
+ address requested_value = cast_from_oop(*addr);
+ if (requested_value == nullptr) {
+ return null_data();
+ } else {
+ address buffer_addr = requested_value - _buffer_to_requested_delta;
+ return capture(buffer_addr);
+ }
+ }
+
+ GrowableArrayCHeap* roots() override {
+ return new GrowableArrayCHeap();
+ }
+};
+#endif // INCLUDE_CDS_JAVA_HEAP
+
+#endif // SHARE_CDS_AOTMAPPEDHEAP_HPP
diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.cpp b/src/hotspot/share/cds/aotMappedHeapLoader.cpp
index 210867be70c..7a201d8297f 100644
--- a/src/hotspot/share/cds/aotMappedHeapLoader.cpp
+++ b/src/hotspot/share/cds/aotMappedHeapLoader.cpp
@@ -23,6 +23,7 @@
*/
#include "cds/aotLogging.hpp"
+#include "cds/aotMappedHeap.hpp"
#include "cds/aotMappedHeapLoader.inline.hpp"
#include "cds/aotMappedHeapWriter.hpp"
#include "cds/aotMetaspace.hpp"
@@ -221,7 +222,7 @@ void AOTMappedHeapLoader::patch_embedded_pointers(FileMapInfo* info,
// the heap object may be loaded at a different address at run time. This structure is used
// to translate the dump time addresses for all objects in FileMapInfo::space_at(region_index)
// to their runtime addresses.
-struct LoadedArchiveHeapRegion {
+struct AOTMappedHeapRegion {
int _region_index; // index for FileMapInfo::space_at(index)
size_t _region_size; // number of bytes in this region
uintptr_t _dumptime_base; // The dump-time (decoded) address of the first object in this region
@@ -232,7 +233,7 @@ struct LoadedArchiveHeapRegion {
}
};
-void AOTMappedHeapLoader::init_loaded_heap_relocation(LoadedArchiveHeapRegion* loaded_region) {
+void AOTMappedHeapLoader::init_loaded_heap_relocation(AOTMappedHeapRegion* loaded_region) {
_dumptime_base = loaded_region->_dumptime_base;
_dumptime_top = loaded_region->top();
_runtime_offset = loaded_region->_runtime_offset;
@@ -249,7 +250,7 @@ class AOTMappedHeapLoader::PatchLoadedRegionPointers: public BitMapClosure {
uintptr_t _top;
public:
- PatchLoadedRegionPointers(narrowOop* start, LoadedArchiveHeapRegion* loaded_region)
+ PatchLoadedRegionPointers(narrowOop* start, AOTMappedHeapRegion* loaded_region)
: _start(start),
_offset(loaded_region->_runtime_offset),
_base(loaded_region->_dumptime_base),
@@ -270,7 +271,7 @@ class AOTMappedHeapLoader::PatchLoadedRegionPointers: public BitMapClosure {
}
};
-bool AOTMappedHeapLoader::init_loaded_region(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_region,
+bool AOTMappedHeapLoader::init_loaded_region(FileMapInfo* mapinfo, AOTMappedHeapRegion* loaded_region,
MemRegion& archive_space) {
size_t total_bytes = 0;
FileMapRegion* r = mapinfo->region_at(AOTMetaspace::hp);
@@ -301,7 +302,7 @@ bool AOTMappedHeapLoader::init_loaded_region(FileMapInfo* mapinfo, LoadedArchive
return true;
}
-bool AOTMappedHeapLoader::load_heap_region_impl(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_region,
+bool AOTMappedHeapLoader::load_heap_region_impl(FileMapInfo* mapinfo, AOTMappedHeapRegion* loaded_region,
uintptr_t load_address) {
uintptr_t bitmap_base = (uintptr_t)mapinfo->map_bitmap_region();
if (bitmap_base == 0) {
@@ -340,7 +341,7 @@ bool AOTMappedHeapLoader::load_heap_region(FileMapInfo* mapinfo) {
assert(can_load(), "loaded heap for must be supported");
init_narrow_oop_decoding(mapinfo->narrow_oop_base(), mapinfo->narrow_oop_shift());
- LoadedArchiveHeapRegion loaded_region;
+ AOTMappedHeapRegion loaded_region;
memset(&loaded_region, 0, sizeof(loaded_region));
MemRegion archive_space;
@@ -733,40 +734,22 @@ void AOTMappedHeapLoader::dealloc_heap_region(FileMapInfo* info) {
}
AOTMapLogger::OopDataIterator* AOTMappedHeapLoader::oop_iterator(FileMapInfo* info, address buffer_start, address buffer_end) {
- class MappedLoaderOopIterator : public AOTMapLogger::OopDataIterator {
- private:
- address _current;
- address _next;
-
- address _buffer_start;
- address _buffer_end;
- uint64_t _buffer_start_narrow_oop;
- intptr_t _buffer_to_requested_delta;
- int _requested_shift;
-
- size_t _num_root_segments;
- size_t _num_obj_arrays_logged;
-
+ class MappedLoaderOopIterator : public AOTMappedHeapOopIterator {
public:
MappedLoaderOopIterator(address buffer_start,
address buffer_end,
- uint64_t buffer_start_narrow_oop,
- intptr_t buffer_to_requested_delta,
+ address requested_base,
+ address requested_start,
int requested_shift,
- size_t num_root_segments)
- : _current(nullptr),
- _next(buffer_start),
- _buffer_start(buffer_start),
- _buffer_end(buffer_end),
- _buffer_start_narrow_oop(buffer_start_narrow_oop),
- _buffer_to_requested_delta(buffer_to_requested_delta),
- _requested_shift(requested_shift),
- _num_root_segments(num_root_segments),
- _num_obj_arrays_logged(0) {
- }
+ size_t num_root_segments) :
+ AOTMappedHeapOopIterator(buffer_start,
+ buffer_end,
+ requested_base,
+ requested_start,
+ requested_shift,
+ num_root_segments) {}
-
- AOTMapLogger::OopData capture(address buffered_addr) {
+ AOTMapLogger::OopData capture(address buffered_addr) override {
oopDesc* raw_oop = (oopDesc*)buffered_addr;
size_t size = raw_oop->size();
address requested_addr = buffered_addr + _buffer_to_requested_delta;
@@ -784,62 +767,17 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapLoader::oop_iterator(FileMapInfo* in
size,
false };
}
-
- bool has_next() override {
- return _next < _buffer_end;
- }
-
- AOTMapLogger::OopData next() override {
- _current = _next;
- AOTMapLogger::OopData result = capture(_current);
- if (result._klass->is_objArray_klass()) {
- result._is_root_segment = _num_obj_arrays_logged++ < _num_root_segments;
- }
- _next = _current + result._size * BytesPerWord;
- return result;
- }
-
- AOTMapLogger::OopData obj_at(narrowOop* addr) override {
- uint64_t n = (uint64_t)(*addr);
- if (n == 0) {
- return null_data();
- } else {
- precond(n >= _buffer_start_narrow_oop);
- address buffer_addr = _buffer_start + ((n - _buffer_start_narrow_oop) << _requested_shift);
- return capture(buffer_addr);
- }
- }
-
- AOTMapLogger::OopData obj_at(oop* addr) override {
- address requested_value = cast_from_oop(*addr);
- if (requested_value == nullptr) {
- return null_data();
- } else {
- address buffer_addr = requested_value - _buffer_to_requested_delta;
- return capture(buffer_addr);
- }
- }
-
- GrowableArrayCHeap* roots() override {
- return new GrowableArrayCHeap();
- }
};
FileMapRegion* r = info->region_at(AOTMetaspace::hp);
address requested_base = UseCompressedOops ? (address)info->narrow_oop_base() : heap_region_requested_address(info);
address requested_start = requested_base + r->mapping_offset();
int requested_shift = info->narrow_oop_shift();
- intptr_t buffer_to_requested_delta = requested_start - buffer_start;
- uint64_t buffer_start_narrow_oop = 0xdeadbeed;
- if (UseCompressedOops) {
- buffer_start_narrow_oop = (uint64_t)(pointer_delta(requested_start, requested_base, 1)) >> requested_shift;
- assert(buffer_start_narrow_oop < 0xffffffff, "sanity");
- }
return new MappedLoaderOopIterator(buffer_start,
buffer_end,
- buffer_start_narrow_oop,
- buffer_to_requested_delta,
+ requested_base,
+ requested_start,
requested_shift,
info->mapped_heap()->root_segments().count());
}
diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.hpp b/src/hotspot/share/cds/aotMappedHeapLoader.hpp
index d344d7b0b0a..7c5ca1b1f9e 100644
--- a/src/hotspot/share/cds/aotMappedHeapLoader.hpp
+++ b/src/hotspot/share/cds/aotMappedHeapLoader.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,8 +37,8 @@
#include "utilities/growableArray.hpp"
#include "utilities/macros.hpp"
+struct AOTMappedHeapRegion;
class FileMapInfo;
-struct LoadedArchiveHeapRegion;
class AOTMappedHeapLoader : AllStatic {
friend class AOTMapLogger;
@@ -93,7 +93,7 @@ public:
// function instead.
inline static oop decode_from_archive(narrowOop v) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
- // More efficient version, but works only when ArchiveHeap is mapped.
+ // More efficient version, but works only when is_mapped()
inline static oop decode_from_mapped_archive(narrowOop v) NOT_CDS_JAVA_HEAP_RETURN_(nullptr);
static void patch_compressed_embedded_pointers(BitMapView bm,
@@ -113,7 +113,7 @@ private:
static bool _is_loaded;
// Support for loaded archived heap. These are cached values from
- // LoadedArchiveHeapRegion's.
+ // AOTMappedHeapRegion's.
static uintptr_t _dumptime_base;
static uintptr_t _dumptime_top;
static intx _runtime_offset;
@@ -141,10 +141,10 @@ private:
static bool _heap_pointers_need_patching;
static void init_narrow_oop_decoding(address base, int shift);
- static bool init_loaded_region(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_region,
+ static bool init_loaded_region(FileMapInfo* mapinfo, AOTMappedHeapRegion* loaded_region,
MemRegion& archive_space);
- static bool load_heap_region_impl(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_region, uintptr_t buffer);
- static void init_loaded_heap_relocation(LoadedArchiveHeapRegion* reloc_info);
+ static bool load_heap_region_impl(FileMapInfo* mapinfo, AOTMappedHeapRegion* loaded_region, uintptr_t buffer);
+ static void init_loaded_heap_relocation(AOTMappedHeapRegion* reloc_info);
static void patch_native_pointers();
static void finish_loaded_heap();
static void verify_loaded_heap();
diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp
index e73b980614a..64c0e3c40e8 100644
--- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp
+++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp
@@ -22,7 +22,7 @@
*
*/
-#include "cds/aotMappedHeapLoader.hpp"
+#include "cds/aotMappedHeap.hpp"
#include "cds/aotMappedHeapWriter.hpp"
#include "cds/aotReferenceObjSupport.hpp"
#include "cds/cdsConfig.hpp"
@@ -151,7 +151,7 @@ void AOTMappedHeapWriter::add_source_obj(oop src_obj) {
}
void AOTMappedHeapWriter::write(GrowableArrayCHeap* roots,
- ArchiveMappedHeapInfo* heap_info) {
+ AOTMappedHeapInfo* heap_info) {
assert(CDSConfig::is_dumping_heap(), "sanity");
allocate_buffer();
copy_source_objs_to_buffer(roots);
@@ -598,7 +598,7 @@ size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) {
//
// So we just hard code it to NOCOOPS_REQUESTED_BASE.
//
-void AOTMappedHeapWriter::set_requested_address_range(ArchiveMappedHeapInfo* info) {
+void AOTMappedHeapWriter::set_requested_address_range(AOTMappedHeapInfo* info) {
assert(!info->is_used(), "only set once");
size_t heap_region_byte_size = _buffer_used;
@@ -792,7 +792,7 @@ static void log_bitmap_usage(const char* which, BitMap* bitmap, size_t total_bit
// Update all oop fields embedded in the buffered objects
void AOTMappedHeapWriter::relocate_embedded_oops(GrowableArrayCHeap* roots,
- ArchiveMappedHeapInfo* heap_info) {
+ AOTMappedHeapInfo* heap_info) {
size_t oopmap_unit = (UseCompressedOops ? sizeof(narrowOop) : sizeof(oop));
size_t heap_region_byte_size = _buffer_used;
heap_info->oopmap()->resize(heap_region_byte_size / oopmap_unit);
@@ -862,7 +862,7 @@ void AOTMappedHeapWriter::mark_native_pointers(oop orig_obj) {
});
}
-void AOTMappedHeapWriter::compute_ptrmap(ArchiveMappedHeapInfo* heap_info) {
+void AOTMappedHeapWriter::compute_ptrmap(AOTMappedHeapInfo* heap_info) {
int num_non_null_ptrs = 0;
Metadata** bottom = (Metadata**) _requested_bottom;
Metadata** top = (Metadata**) _requested_top; // exclusive
@@ -909,40 +909,23 @@ void AOTMappedHeapWriter::compute_ptrmap(ArchiveMappedHeapInfo* heap_info) {
num_non_null_ptrs, size_t(heap_info->ptrmap()->size()));
}
-AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHeapInfo* heap_info) {
- class MappedWriterOopIterator : public AOTMapLogger::OopDataIterator {
- private:
- address _current;
- address _next;
-
- address _buffer_start;
- address _buffer_end;
- uint64_t _buffer_start_narrow_oop;
- intptr_t _buffer_to_requested_delta;
- int _requested_shift;
-
- size_t _num_root_segments;
- size_t _num_obj_arrays_logged;
-
+AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(AOTMappedHeapInfo* heap_info) {
+ class MappedWriterOopIterator : public AOTMappedHeapOopIterator {
public:
MappedWriterOopIterator(address buffer_start,
address buffer_end,
- uint64_t buffer_start_narrow_oop,
- intptr_t buffer_to_requested_delta,
+ address requested_base,
+ address requested_start,
int requested_shift,
- size_t num_root_segments)
- : _current(nullptr),
- _next(buffer_start),
- _buffer_start(buffer_start),
- _buffer_end(buffer_end),
- _buffer_start_narrow_oop(buffer_start_narrow_oop),
- _buffer_to_requested_delta(buffer_to_requested_delta),
- _requested_shift(requested_shift),
- _num_root_segments(num_root_segments),
- _num_obj_arrays_logged(0) {
- }
+ size_t num_root_segments) :
+ AOTMappedHeapOopIterator(buffer_start,
+ buffer_end,
+ requested_base,
+ requested_start,
+ requested_shift,
+ num_root_segments) {}
- AOTMapLogger::OopData capture(address buffered_addr) {
+ AOTMapLogger::OopData capture(address buffered_addr) override {
oopDesc* raw_oop = (oopDesc*)buffered_addr;
size_t size = size_of_buffered_oop(buffered_addr);
address requested_addr = buffered_addr_to_requested_addr(buffered_addr);
@@ -960,45 +943,6 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHe
size,
false };
}
-
- bool has_next() override {
- return _next < _buffer_end;
- }
-
- AOTMapLogger::OopData next() override {
- _current = _next;
- AOTMapLogger::OopData result = capture(_current);
- if (result._klass->is_objArray_klass()) {
- result._is_root_segment = _num_obj_arrays_logged++ < _num_root_segments;
- }
- _next = _current + result._size * BytesPerWord;
- return result;
- }
-
- AOTMapLogger::OopData obj_at(narrowOop* addr) override {
- uint64_t n = (uint64_t)(*addr);
- if (n == 0) {
- return null_data();
- } else {
- precond(n >= _buffer_start_narrow_oop);
- address buffer_addr = _buffer_start + ((n - _buffer_start_narrow_oop) << _requested_shift);
- return capture(buffer_addr);
- }
- }
-
- AOTMapLogger::OopData obj_at(oop* addr) override {
- address requested_value = cast_from_oop(*addr);
- if (requested_value == nullptr) {
- return null_data();
- } else {
- address buffer_addr = requested_value - _buffer_to_requested_delta;
- return capture(buffer_addr);
- }
- }
-
- GrowableArrayCHeap* roots() override {
- return new GrowableArrayCHeap();
- }
};
MemRegion r = heap_info->buffer_region();
@@ -1008,17 +952,11 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHe
address requested_base = UseCompressedOops ? AOTMappedHeapWriter::narrow_oop_base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
address requested_start = UseCompressedOops ? AOTMappedHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base;
int requested_shift = AOTMappedHeapWriter::narrow_oop_shift();
- intptr_t buffer_to_requested_delta = requested_start - buffer_start;
- uint64_t buffer_start_narrow_oop = 0xdeadbeed;
- if (UseCompressedOops) {
- buffer_start_narrow_oop = (uint64_t)(pointer_delta(requested_start, requested_base, 1)) >> requested_shift;
- assert(buffer_start_narrow_oop < 0xffffffff, "sanity");
- }
return new MappedWriterOopIterator(buffer_start,
buffer_end,
- buffer_start_narrow_oop,
- buffer_to_requested_delta,
+ requested_base,
+ requested_start,
requested_shift,
heap_info->root_segments().count());
}
diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.hpp b/src/hotspot/share/cds/aotMappedHeapWriter.hpp
index eafd38ac8bb..7481e7922a0 100644
--- a/src/hotspot/share/cds/aotMappedHeapWriter.hpp
+++ b/src/hotspot/share/cds/aotMappedHeapWriter.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -196,10 +196,10 @@ private:
static int filler_array_length(size_t fill_bytes);
static HeapWord* init_filler_array_at_buffer_top(int array_length, size_t fill_bytes);
- static void set_requested_address_range(ArchiveMappedHeapInfo* info);
+ static void set_requested_address_range(AOTMappedHeapInfo* info);
static void mark_native_pointers(oop orig_obj);
- static void relocate_embedded_oops(GrowableArrayCHeap* roots, ArchiveMappedHeapInfo* info);
- static void compute_ptrmap(ArchiveMappedHeapInfo *info);
+ static void relocate_embedded_oops(GrowableArrayCHeap* roots, AOTMappedHeapInfo* info);
+ static void compute_ptrmap(AOTMappedHeapInfo *info);
static bool is_in_requested_range(oop o);
static oop requested_obj_from_buffer_offset(size_t offset);
@@ -229,7 +229,7 @@ public:
static bool is_string_too_large_to_archive(oop string);
static bool is_dumped_interned_string(oop o);
static void add_to_dumped_interned_strings(oop string);
- static void write(GrowableArrayCHeap*, ArchiveMappedHeapInfo* heap_info);
+ static void write(GrowableArrayCHeap*, AOTMappedHeapInfo* heap_info);
static address requested_address(); // requested address of the lowest achived heap object
static size_t get_filler_size_at(address buffered_addr);
@@ -240,7 +240,7 @@ public:
static Klass* real_klass_of_buffered_oop(address buffered_addr);
static size_t size_of_buffered_oop(address buffered_addr);
- static AOTMapLogger::OopDataIterator* oop_iterator(ArchiveMappedHeapInfo* heap_info);
+ static AOTMapLogger::OopDataIterator* oop_iterator(AOTMappedHeapInfo* heap_info);
};
#endif // INCLUDE_CDS_JAVA_HEAP
#endif // SHARE_CDS_AOTMAPPEDHEAPWRITER_HPP
diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp
index 894a35183ca..b75d7628aa9 100644
--- a/src/hotspot/share/cds/aotMetaspace.cpp
+++ b/src/hotspot/share/cds/aotMetaspace.cpp
@@ -661,8 +661,8 @@ void AOTMetaspace::rewrite_bytecodes_and_calculate_fingerprints(Thread* thread,
class VM_PopulateDumpSharedSpace : public VM_Operation {
private:
- ArchiveMappedHeapInfo _mapped_heap_info;
- ArchiveStreamedHeapInfo _streamed_heap_info;
+ AOTMappedHeapInfo _mapped_heap_info;
+ AOTStreamedHeapInfo _streamed_heap_info;
FileMapInfo* _map_info;
StaticArchiveBuilder& _builder;
@@ -682,8 +682,8 @@ public:
bool skip_operation() const { return false; }
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
- ArchiveMappedHeapInfo* mapped_heap_info() { return &_mapped_heap_info; }
- ArchiveStreamedHeapInfo* streamed_heap_info() { return &_streamed_heap_info; }
+ AOTMappedHeapInfo* mapped_heap_info() { return &_mapped_heap_info; }
+ AOTStreamedHeapInfo* streamed_heap_info() { return &_streamed_heap_info; }
void doit(); // outline because gdb sucks
bool allow_nested_vm_operations() const { return true; }
}; // class VM_PopulateDumpSharedSpace
@@ -1148,7 +1148,7 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
if (CDSConfig::is_dumping_full_module_graph()) {
ClassLoaderDataShared::ensure_module_entry_tables_exist();
ClassLoaderDataShared::build_tables(CHECK);
- HeapShared::reset_archived_object_states(CHECK);
+ HeapShared::prepare_for_archiving(CHECK);
}
AOTReferenceObjSupport::initialize(CHECK);
@@ -1212,8 +1212,8 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
bool AOTMetaspace::write_static_archive(ArchiveBuilder* builder,
FileMapInfo* map_info,
- ArchiveMappedHeapInfo* mapped_heap_info,
- ArchiveStreamedHeapInfo* streamed_heap_info) {
+ AOTMappedHeapInfo* mapped_heap_info,
+ AOTStreamedHeapInfo* streamed_heap_info) {
// relocate the data so that it can be mapped to AOTMetaspace::requested_base_address()
// without runtime relocation.
builder->relocate_to_requested();
@@ -2106,7 +2106,7 @@ MapArchiveResult AOTMetaspace::map_archive(FileMapInfo* mapinfo, char* mapped_ba
// Currently, only static archive uses early serialized data.
char* buffer = mapinfo->early_serialized_data();
intptr_t* array = (intptr_t*)buffer;
- ReadClosure rc(&array, (intptr_t)mapped_base_address);
+ ReadClosure rc(&array, (address)mapped_base_address);
early_serialize(&rc);
}
@@ -2152,7 +2152,7 @@ void AOTMetaspace::initialize_shared_spaces() {
// shared string/symbol tables.
char* buffer = static_mapinfo->serialized_data();
intptr_t* array = (intptr_t*)buffer;
- ReadClosure rc(&array, (intptr_t)SharedBaseAddress);
+ ReadClosure rc(&array, (address)SharedBaseAddress);
serialize(&rc);
// Finish initializing the heap dump mode used in the archive
@@ -2164,7 +2164,7 @@ void AOTMetaspace::initialize_shared_spaces() {
if (dynamic_mapinfo != nullptr) {
intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data();
- ReadClosure rc(&buffer, (intptr_t)SharedBaseAddress);
+ ReadClosure rc(&buffer, (address)SharedBaseAddress);
DynamicArchive::serialize(&rc);
}
diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp
index ab78787288f..4607a936abe 100644
--- a/src/hotspot/share/cds/aotMetaspace.hpp
+++ b/src/hotspot/share/cds/aotMetaspace.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,8 +33,8 @@
#include "utilities/macros.hpp"
class ArchiveBuilder;
-class ArchiveMappedHeapInfo;
-class ArchiveStreamedHeapInfo;
+class AOTMappedHeapInfo;
+class AOTStreamedHeapInfo;
class FileMapInfo;
class Method;
class outputStream;
@@ -192,8 +192,8 @@ private:
static void open_output_mapinfo();
static bool write_static_archive(ArchiveBuilder* builder,
FileMapInfo* map_info,
- ArchiveMappedHeapInfo* mapped_heap_info,
- ArchiveStreamedHeapInfo* streamed_heap_info);
+ AOTMappedHeapInfo* mapped_heap_info,
+ AOTStreamedHeapInfo* streamed_heap_info);
static FileMapInfo* open_static_archive();
static FileMapInfo* open_dynamic_archive();
// use_requested_addr: If true (default), attempt to map at the address the
diff --git a/src/hotspot/share/cds/aotStreamedHeap.cpp b/src/hotspot/share/cds/aotStreamedHeap.cpp
new file mode 100644
index 00000000000..3378924bf32
--- /dev/null
+++ b/src/hotspot/share/cds/aotStreamedHeap.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "cds/aotStreamedHeap.hpp"
+
+// Anything that goes in the header must be thoroughly purged from uninitialized memory
+// as it will be written to disk. Therefore, the constructors memset the memory to 0.
+// This is not the prettiest thing, but we need to know every byte is initialized,
+// including potential padding between fields.
+
+AOTStreamedHeapHeader::AOTStreamedHeapHeader(size_t forwarding_offset,
+ size_t roots_offset,
+ size_t num_roots,
+ size_t root_highest_object_index_table_offset,
+ size_t num_archived_objects) {
+ memset((char*)this, 0, sizeof(*this));
+ _forwarding_offset = forwarding_offset;
+ _roots_offset = roots_offset;
+ _num_roots = num_roots;
+ _root_highest_object_index_table_offset = root_highest_object_index_table_offset;
+ _num_archived_objects = num_archived_objects;
+}
+
+AOTStreamedHeapHeader::AOTStreamedHeapHeader() {
+ memset((char*)this, 0, sizeof(*this));
+}
+
+AOTStreamedHeapHeader AOTStreamedHeapInfo::create_header() {
+ return AOTStreamedHeapHeader{_forwarding_offset,
+ _roots_offset,
+ _num_roots,
+ _root_highest_object_index_table_offset,
+ _num_archived_objects};
+}
diff --git a/src/hotspot/share/cds/aotStreamedHeap.hpp b/src/hotspot/share/cds/aotStreamedHeap.hpp
new file mode 100644
index 00000000000..f06b1bcb4c6
--- /dev/null
+++ b/src/hotspot/share/cds/aotStreamedHeap.hpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_CDS_AOTSTREAMEDHEAP_HPP
+#define SHARE_CDS_AOTSTREAMEDHEAP_HPP
+
+#include "cds/aotMapLogger.hpp"
+#include "utilities/growableArray.hpp"
+#include "utilities/macros.hpp"
+
+class AOTStreamedHeapHeader {
+ size_t _forwarding_offset; // Offset of forwarding information in the heap region.
+ size_t _roots_offset; // Start position for the roots
+ size_t _root_highest_object_index_table_offset; // Offset of root dfs depth information
+ size_t _num_roots; // Number of embedded roots
+ size_t _num_archived_objects; // The number of archived heap objects
+
+public:
+ AOTStreamedHeapHeader();
+ AOTStreamedHeapHeader(size_t forwarding_offset,
+ size_t roots_offset,
+ size_t num_roots,
+ size_t root_highest_object_index_table_offset,
+ size_t num_archived_objects);
+
+ size_t forwarding_offset() const { return _forwarding_offset; }
+ size_t roots_offset() const { return _roots_offset; }
+ size_t num_roots() const { return _num_roots; }
+ size_t root_highest_object_index_table_offset() const { return _root_highest_object_index_table_offset; }
+ size_t num_archived_objects() const { return _num_archived_objects; }
+
+ // This class is trivially copyable and assignable.
+ AOTStreamedHeapHeader(const AOTStreamedHeapHeader&) = default;
+ AOTStreamedHeapHeader& operator=(const AOTStreamedHeapHeader&) = default;
+};
+
+class AOTStreamedHeapInfo {
+ MemRegion _buffer_region; // Contains the archived objects to be written into the CDS archive.
+ CHeapBitMap _oopmap;
+ size_t _roots_offset; // Offset of the HeapShared::roots() object, from the bottom
+ // of the archived heap objects, in bytes.
+ size_t _num_roots;
+
+ size_t _forwarding_offset; // Offset of forwarding information from the bottom
+ size_t _root_highest_object_index_table_offset; // Offset to root dfs depth information
+ size_t _num_archived_objects; // The number of archived objects written into the CDS archive.
+
+public:
+ AOTStreamedHeapInfo()
+ : _buffer_region(),
+ _oopmap(128, mtClassShared),
+ _roots_offset(),
+ _forwarding_offset(),
+ _root_highest_object_index_table_offset(),
+ _num_archived_objects() {}
+
+ bool is_used() { return !_buffer_region.is_empty(); }
+
+ void set_buffer_region(MemRegion r) { _buffer_region = r; }
+ MemRegion buffer_region() { return _buffer_region; }
+ char* buffer_start() { return (char*)_buffer_region.start(); }
+ size_t buffer_byte_size() { return _buffer_region.byte_size(); }
+
+ CHeapBitMap* oopmap() { return &_oopmap; }
+ void set_roots_offset(size_t n) { _roots_offset = n; }
+ size_t roots_offset() { return _roots_offset; }
+ void set_num_roots(size_t n) { _num_roots = n; }
+ size_t num_roots() { return _num_roots; }
+ void set_forwarding_offset(size_t n) { _forwarding_offset = n; }
+ void set_root_highest_object_index_table_offset(size_t n) { _root_highest_object_index_table_offset = n; }
+ void set_num_archived_objects(size_t n) { _num_archived_objects = n; }
+ size_t num_archived_objects() { return _num_archived_objects; }
+
+ AOTStreamedHeapHeader create_header();
+};
+
+#if INCLUDE_CDS_JAVA_HEAP
+class AOTStreamedHeapOopIterator : public AOTMapLogger::OopDataIterator {
+protected:
+ int _current;
+ int _next;
+ address _buffer_start;
+ int _num_archived_objects;
+
+public:
+ AOTStreamedHeapOopIterator(address buffer_start,
+ int num_archived_objects)
+ : _current(0),
+ _next(1),
+ _buffer_start(buffer_start),
+ _num_archived_objects(num_archived_objects) {}
+
+ virtual AOTMapLogger::OopData capture(int dfs_index) = 0;
+
+ bool has_next() override {
+ return _next <= _num_archived_objects;
+ }
+
+ AOTMapLogger::OopData next() override {
+ _current = _next;
+ AOTMapLogger::OopData result = capture(_current);
+ _next = _current + 1;
+ return result;
+ }
+
+ AOTMapLogger::OopData obj_at(narrowOop* addr) override {
+ int dfs_index = (int)(*addr);
+ if (dfs_index == 0) {
+ return null_data();
+ } else {
+ return capture(dfs_index);
+ }
+ }
+
+ AOTMapLogger::OopData obj_at(oop* addr) override {
+ int dfs_index = (int)cast_from_oop(*addr);
+ if (dfs_index == 0) {
+ return null_data();
+ } else {
+ return capture(dfs_index);
+ }
+ }
+};
+#endif // INCLUDE_CDS_JAVA_HEAP
+
+#endif // SHARE_CDS_AOTSTREAMEDHEAP_HPP
diff --git a/src/hotspot/share/cds/aotStreamedHeapLoader.cpp b/src/hotspot/share/cds/aotStreamedHeapLoader.cpp
index 6719f9bf898..39f735543cd 100644
--- a/src/hotspot/share/cds/aotStreamedHeapLoader.cpp
+++ b/src/hotspot/share/cds/aotStreamedHeapLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1102,25 +1102,13 @@ void AOTStreamedHeapLoader::finish_initialization(FileMapInfo* static_mapinfo) {
}
AOTMapLogger::OopDataIterator* AOTStreamedHeapLoader::oop_iterator(FileMapInfo* info, address buffer_start, address buffer_end) {
- class StreamedLoaderOopIterator : public AOTMapLogger::OopDataIterator {
- private:
- int _current;
- int _next;
-
- address _buffer_start;
-
- int _num_archived_objects;
-
+ class StreamedLoaderOopIterator : public AOTStreamedHeapOopIterator {
public:
StreamedLoaderOopIterator(address buffer_start,
int num_archived_objects)
- : _current(0),
- _next(1),
- _buffer_start(buffer_start),
- _num_archived_objects(num_archived_objects) {
- }
+ : AOTStreamedHeapOopIterator(buffer_start, num_archived_objects) {}
- AOTMapLogger::OopData capture(int dfs_index) {
+ AOTMapLogger::OopData capture(int dfs_index) override {
size_t buffered_offset = buffer_offset_for_object_index(dfs_index);
address buffered_addr = _buffer_start + buffered_offset;
oopDesc* raw_oop = (oopDesc*)buffered_addr;
@@ -1142,35 +1130,6 @@ AOTMapLogger::OopDataIterator* AOTStreamedHeapLoader::oop_iterator(FileMapInfo*
false };
}
- bool has_next() override {
- return _next <= _num_archived_objects;
- }
-
- AOTMapLogger::OopData next() override {
- _current = _next;
- AOTMapLogger::OopData result = capture(_current);
- _next = _current + 1;
- return result;
- }
-
- AOTMapLogger::OopData obj_at(narrowOop* addr) override {
- int dfs_index = (int)(*addr);
- if (dfs_index == 0) {
- return null_data();
- } else {
- return capture(dfs_index);
- }
- }
-
- AOTMapLogger::OopData obj_at(oop* addr) override {
- int dfs_index = (int)cast_from_oop(*addr);
- if (dfs_index == 0) {
- return null_data();
- } else {
- return capture(dfs_index);
- }
- }
-
GrowableArrayCHeap* roots() override {
GrowableArrayCHeap* result = new GrowableArrayCHeap();
diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp
index 16acebc7d8d..f52532b2f2a 100644
--- a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp
+++ b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -163,7 +163,7 @@ void AOTStreamedHeapWriter::order_source_objs(GrowableArrayCHeap* roots,
- ArchiveStreamedHeapInfo* heap_info) {
+ AOTStreamedHeapInfo* heap_info) {
assert(CDSConfig::is_dumping_heap(), "sanity");
allocate_buffer();
order_source_objs(roots);
@@ -453,7 +453,7 @@ static void log_bitmap_usage(const char* which, BitMap* bitmap, size_t total_bit
}
// Update all oop fields embedded in the buffered objects
-void AOTStreamedHeapWriter::map_embedded_oops(ArchiveStreamedHeapInfo* heap_info) {
+void AOTStreamedHeapWriter::map_embedded_oops(AOTStreamedHeapInfo* heap_info) {
size_t oopmap_unit = (UseCompressedOops ? sizeof(narrowOop) : sizeof(oop));
size_t heap_region_byte_size = _buffer_used;
heap_info->oopmap()->resize(heap_region_byte_size / oopmap_unit);
@@ -497,7 +497,7 @@ oop AOTStreamedHeapWriter::buffered_addr_to_source_obj(address buffered_addr) {
return buffered_offset_to_source_obj(buffered_address_to_offset(buffered_addr));
}
-void AOTStreamedHeapWriter::populate_archive_heap_info(ArchiveStreamedHeapInfo* info) {
+void AOTStreamedHeapWriter::populate_archive_heap_info(AOTStreamedHeapInfo* info) {
assert(!info->is_used(), "only set once");
size_t heap_region_byte_size = _buffer_used;
@@ -512,15 +512,9 @@ void AOTStreamedHeapWriter::populate_archive_heap_info(ArchiveStreamedHeapInfo*
info->set_num_archived_objects((size_t)_source_objs->length());
}
-AOTMapLogger::OopDataIterator* AOTStreamedHeapWriter::oop_iterator(ArchiveStreamedHeapInfo* heap_info) {
- class StreamedWriterOopIterator : public AOTMapLogger::OopDataIterator {
+AOTMapLogger::OopDataIterator* AOTStreamedHeapWriter::oop_iterator(AOTStreamedHeapInfo* heap_info) {
+ class StreamedWriterOopIterator : public AOTStreamedHeapOopIterator {
private:
- int _current;
- int _next;
-
- address _buffer_start;
-
- int _num_archived_objects;
int _num_archived_roots;
int* _roots;
@@ -529,15 +523,11 @@ AOTMapLogger::OopDataIterator* AOTStreamedHeapWriter::oop_iterator(ArchiveStream
int num_archived_objects,
int num_archived_roots,
int* roots)
- : _current(0),
- _next(1),
- _buffer_start(buffer_start),
- _num_archived_objects(num_archived_objects),
+ : AOTStreamedHeapOopIterator(buffer_start, num_archived_objects),
_num_archived_roots(num_archived_roots),
- _roots(roots) {
- }
+ _roots(roots) {}
- AOTMapLogger::OopData capture(int dfs_index) {
+ AOTMapLogger::OopData capture(int dfs_index) override {
size_t buffered_offset = _dfs_to_archive_object_table[dfs_index];
address buffered_addr = _buffer_start + buffered_offset;
oop src_obj = AOTStreamedHeapWriter::buffered_offset_to_source_obj(buffered_offset);
@@ -561,35 +551,6 @@ AOTMapLogger::OopDataIterator* AOTStreamedHeapWriter::oop_iterator(ArchiveStream
false };
}
- bool has_next() override {
- return _next <= _num_archived_objects;
- }
-
- AOTMapLogger::OopData next() override {
- _current = _next;
- AOTMapLogger::OopData result = capture(_current);
- _next = _current + 1;
- return result;
- }
-
- AOTMapLogger::OopData obj_at(narrowOop* addr) override {
- int dfs_index = (int)(*addr);
- if (dfs_index == 0) {
- return null_data();
- } else {
- return capture(dfs_index);
- }
- }
-
- AOTMapLogger::OopData obj_at(oop* addr) override {
- int dfs_index = (int)cast_from_oop(*addr);
- if (dfs_index == 0) {
- return null_data();
- } else {
- return capture(dfs_index);
- }
- }
-
GrowableArrayCHeap* roots() override {
GrowableArrayCHeap* result = new GrowableArrayCHeap();
diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.hpp b/src/hotspot/share/cds/aotStreamedHeapWriter.hpp
index bde82f8ce29..ab5aec0327b 100644
--- a/src/hotspot/share/cds/aotStreamedHeapWriter.hpp
+++ b/src/hotspot/share/cds/aotStreamedHeapWriter.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -117,7 +117,7 @@ class AOTStreamedHeapWriter : AllStatic {
static void copy_forwarding_to_buffer();
static void copy_roots_max_dfs_to_buffer(int roots_length);
- static void map_embedded_oops(ArchiveStreamedHeapInfo* info);
+ static void map_embedded_oops(AOTStreamedHeapInfo* info);
static bool is_in_requested_range(oop o);
static oop requested_obj_from_buffer_offset(size_t offset);
@@ -131,14 +131,14 @@ class AOTStreamedHeapWriter : AllStatic {
static void update_header_for_buffered_addr(address buffered_addr, oop src_obj, Klass* src_klass);
- static void populate_archive_heap_info(ArchiveStreamedHeapInfo* info);
+ static void populate_archive_heap_info(AOTStreamedHeapInfo* info);
public:
static void init() NOT_CDS_JAVA_HEAP_RETURN;
static void delete_tables_with_raw_oops();
static void add_source_obj(oop src_obj);
- static void write(GrowableArrayCHeap*, ArchiveStreamedHeapInfo* heap_info);
+ static void write(GrowableArrayCHeap*, AOTStreamedHeapInfo* heap_info);
static address buffered_heap_roots_addr() {
return offset_to_buffered_address(_roots_offset);
}
@@ -156,7 +156,7 @@ public:
static oop buffered_offset_to_source_obj(size_t buffered_offset);
static oop buffered_addr_to_source_obj(address buffered_addr);
- static AOTMapLogger::OopDataIterator* oop_iterator(ArchiveStreamedHeapInfo* heap_info);
+ static AOTMapLogger::OopDataIterator* oop_iterator(AOTStreamedHeapInfo* heap_info);
};
#endif // INCLUDE_CDS_JAVA_HEAP
#endif // SHARE_CDS_AOTSTREAMEDHEAPWRITER_HPP
diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp
index 9161980c4be..0ea5d6c6ecb 100644
--- a/src/hotspot/share/cds/archiveBuilder.cpp
+++ b/src/hotspot/share/cds/archiveBuilder.cpp
@@ -24,6 +24,7 @@
#include "cds/aotArtifactFinder.hpp"
#include "cds/aotClassLinker.hpp"
+#include "cds/aotCompressedPointers.hpp"
#include "cds/aotLogging.hpp"
#include "cds/aotMapLogger.hpp"
#include "cds/aotMetaspace.hpp"
@@ -175,10 +176,10 @@ ArchiveBuilder::ArchiveBuilder() :
_mapped_static_archive_bottom(nullptr),
_mapped_static_archive_top(nullptr),
_buffer_to_requested_delta(0),
- _pz_region("pz", MAX_SHARED_DELTA), // protection zone -- used only during dumping; does NOT exist in cds archive.
- _rw_region("rw", MAX_SHARED_DELTA),
- _ro_region("ro", MAX_SHARED_DELTA),
- _ac_region("ac", MAX_SHARED_DELTA),
+ _pz_region("pz"), // protection zone -- used only during dumping; does NOT exist in cds archive.
+ _rw_region("rw"),
+ _ro_region("ro"),
+ _ac_region("ac"),
_ptrmap(mtClassShared),
_rw_ptrmap(mtClassShared),
_ro_ptrmap(mtClassShared),
@@ -320,8 +321,10 @@ void ArchiveBuilder::sort_klasses() {
}
address ArchiveBuilder::reserve_buffer() {
- // AOTCodeCache::max_aot_code_size() accounts for aot code region.
- size_t buffer_size = LP64_ONLY(CompressedClassSpaceSize) NOT_LP64(256 * M) + AOTCodeCache::max_aot_code_size();
+ // On 64-bit: reserve address space for archives up to the max encoded offset limit.
+ // On 32-bit: use 256MB + AOT code size due to limited virtual address space.
+ size_t buffer_size = LP64_ONLY(AOTCompressedPointers::MaxMetadataOffsetBytes)
+ NOT_LP64(256 * M + AOTCodeCache::max_aot_code_size());
ReservedSpace rs = MemoryReserver::reserve(buffer_size,
AOTMetaspace::core_region_alignment(),
os::vm_page_size(),
@@ -990,16 +993,15 @@ void ArchiveBuilder::make_training_data_shareable() {
_src_obj_table.iterate_all(clean_td);
}
-uintx ArchiveBuilder::buffer_to_offset(address p) const {
+size_t ArchiveBuilder::buffer_to_offset(address p) const {
address requested_p = to_requested(p);
- assert(requested_p >= _requested_static_archive_bottom, "must be");
- return requested_p - _requested_static_archive_bottom;
+ return pointer_delta(requested_p, _requested_static_archive_bottom, 1);
}
-uintx ArchiveBuilder::any_to_offset(address p) const {
+size_t ArchiveBuilder::any_to_offset(address p) const {
if (is_in_mapped_static_archive(p)) {
assert(CDSConfig::is_dumping_dynamic_archive(), "must be");
- return p - _mapped_static_archive_bottom;
+ return pointer_delta(p, _mapped_static_archive_bottom, 1);
}
if (!is_in_buffer_space(p)) {
// p must be a "source" address
@@ -1008,7 +1010,7 @@ uintx ArchiveBuilder::any_to_offset(address p) const {
return buffer_to_offset(p);
}
-address ArchiveBuilder::offset_to_buffered_address(u4 offset) const {
+address ArchiveBuilder::offset_to_buffered_address(size_t offset) const {
address requested_addr = _requested_static_archive_bottom + offset;
address buffered_addr = requested_addr - _buffer_to_requested_delta;
assert(is_in_buffer_space(buffered_addr), "bad offset");
@@ -1154,7 +1156,7 @@ void ArchiveBuilder::print_stats() {
_alloc_stats.print_stats(int(_ro_region.used()), int(_rw_region.used()));
}
-void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info) {
+void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info) {
// Make sure NUM_CDS_REGIONS (exported in cds.h) agrees with
// AOTMetaspace::n_regions (internal to hotspot).
assert(NUM_CDS_REGIONS == AOTMetaspace::n_regions, "sanity");
@@ -1213,8 +1215,8 @@ void ArchiveBuilder::count_relocated_pointer(bool tagged, bool nulled) {
}
void ArchiveBuilder::print_region_stats(FileMapInfo *mapinfo,
- ArchiveMappedHeapInfo* mapped_heap_info,
- ArchiveStreamedHeapInfo* streamed_heap_info) {
+ AOTMappedHeapInfo* mapped_heap_info,
+ AOTStreamedHeapInfo* streamed_heap_info) {
// Print statistics of all the regions
const size_t bitmap_used = mapinfo->region_at(AOTMetaspace::bm)->used();
const size_t bitmap_reserved = mapinfo->region_at(AOTMetaspace::bm)->used_aligned();
diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp
index 9de6c02edc5..b3667ea11b4 100644
--- a/src/hotspot/share/cds/archiveBuilder.hpp
+++ b/src/hotspot/share/cds/archiveBuilder.hpp
@@ -39,8 +39,8 @@
#include "utilities/hashTable.hpp"
#include "utilities/resizableHashTable.hpp"
-class ArchiveMappedHeapInfo;
-class ArchiveStreamedHeapInfo;
+class AOTMappedHeapInfo;
+class AOTStreamedHeapInfo;
class CHeapBitMap;
class FileMapInfo;
class Klass;
@@ -247,8 +247,8 @@ private:
} _relocated_ptr_info;
void print_region_stats(FileMapInfo *map_info,
- ArchiveMappedHeapInfo* mapped_heap_info,
- ArchiveStreamedHeapInfo* streamed_heap_info);
+ AOTMappedHeapInfo* mapped_heap_info,
+ AOTStreamedHeapInfo* streamed_heap_info);
void print_bitmap_region_stats(size_t size, size_t total_size);
void print_heap_region_stats(char* start, size_t size, size_t total_size);
@@ -329,49 +329,22 @@ public:
return current()->buffer_to_requested_delta();
}
- inline static u4 to_offset_u4(uintx offset) {
- guarantee(offset <= MAX_SHARED_DELTA, "must be 32-bit offset " INTPTR_FORMAT, offset);
- return (u4)offset;
- }
-
public:
- static const uintx MAX_SHARED_DELTA = ArchiveUtils::MAX_SHARED_DELTA;;
-
// The address p points to an object inside the output buffer. When the archive is mapped
// at the requested address, what's the offset of this object from _requested_static_archive_bottom?
- uintx buffer_to_offset(address p) const;
+ size_t buffer_to_offset(address p) const;
- // Same as buffer_to_offset, except that the address p points to either (a) an object
- // inside the output buffer, or (b), an object in the currently mapped static archive.
- uintx any_to_offset(address p) const;
+ // Same as buffer_to_offset, except that the address p points to one of the following:
+ // - an object in the ArchiveBuilder's buffer.
+ // - an object in the currently mapped AOT cache rw/ro regions.
+ // - an object that has been copied into the ArchiveBuilder's buffer.
+ size_t any_to_offset(address p) const;
// The reverse of buffer_to_offset()
- address offset_to_buffered_address(u4 offset) const;
+ address offset_to_buffered_address(size_t offset) const;
template
- u4 buffer_to_offset_u4(T p) const {
- uintx offset = buffer_to_offset((address)p);
- return to_offset_u4(offset);
- }
-
- template
- u4 any_to_offset_u4(T p) const {
- assert(p != nullptr, "must not be null");
- uintx offset = any_to_offset((address)p);
- return to_offset_u4(offset);
- }
-
- template
- u4 any_or_null_to_offset_u4(T p) const {
- if (p == nullptr) {
- return 0;
- } else {
- return any_to_offset_u4(p);
- }
- }
-
- template
- T offset_to_buffered(u4 offset) const {
+ T offset_to_buffered(size_t offset) const {
return (T)offset_to_buffered_address(offset);
}
@@ -438,8 +411,8 @@ public:
void make_training_data_shareable();
void relocate_to_requested();
void write_archive(FileMapInfo* mapinfo,
- ArchiveMappedHeapInfo* mapped_heap_info,
- ArchiveStreamedHeapInfo* streamed_heap_info);
+ AOTMappedHeapInfo* mapped_heap_info,
+ AOTStreamedHeapInfo* streamed_heap_info);
void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region,
bool read_only, bool allow_exec);
diff --git a/src/hotspot/share/cds/archiveUtils.cpp b/src/hotspot/share/cds/archiveUtils.cpp
index 842668509cf..ea9bde8eb8d 100644
--- a/src/hotspot/share/cds/archiveUtils.cpp
+++ b/src/hotspot/share/cds/archiveUtils.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
*
*/
+#include "cds/aotCompressedPointers.hpp"
#include "cds/aotLogging.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp"
@@ -201,13 +202,13 @@ char* DumpRegion::expand_top_to(char* newtop) {
commit_to(newtop);
_top = newtop;
- if (_max_delta > 0) {
+ if (ArchiveBuilder::is_active() && ArchiveBuilder::current()->is_in_buffer_space(_base)) {
uintx delta = ArchiveBuilder::current()->buffer_to_offset((address)(newtop-1));
- if (delta > _max_delta) {
+ if (delta > AOTCompressedPointers::MaxMetadataOffsetBytes) {
// This is just a sanity check and should not appear in any real world usage. This
// happens only if you allocate more than 2GB of shared objects and would require
// millions of shared classes.
- aot_log_error(aot)("Out of memory in the CDS archive: Please reduce the number of shared classes.");
+ aot_log_error(aot)("Out of memory in the %s: Please reduce the number of shared classes.", CDSConfig::type_of_archive_being_written());
AOTMetaspace::unrecoverable_writing_error();
}
}
@@ -311,29 +312,15 @@ void DumpRegion::pack(DumpRegion* next) {
}
void WriteClosure::do_ptr(void** p) {
- // Write ptr into the archive; ptr can be:
- // (a) null -> written as 0
- // (b) a "buffered" address -> written as is
- // (c) a "source" address -> convert to "buffered" and write
- // The common case is (c). E.g., when writing the vmClasses into the archive.
- // We have (b) only when we don't have a corresponding source object. E.g.,
- // the archived c++ vtable entries.
address ptr = *(address*)p;
- if (ptr != nullptr && !ArchiveBuilder::current()->is_in_buffer_space(ptr)) {
- ptr = ArchiveBuilder::current()->get_buffered_addr(ptr);
- }
- // null pointers do not need to be converted to offsets
- if (ptr != nullptr) {
- ptr = (address)ArchiveBuilder::current()->buffer_to_offset(ptr);
- }
- _dump_region->append_intptr_t((intptr_t)ptr, false);
+ AOTCompressedPointers::narrowPtr narrowp = AOTCompressedPointers::encode(ptr);
+ _dump_region->append_intptr_t(checked_cast(narrowp), false);
}
void ReadClosure::do_ptr(void** p) {
assert(*p == nullptr, "initializing previous initialized pointer.");
- intptr_t obj = nextPtr();
- assert(obj >= 0, "sanity.");
- *p = (obj != 0) ? (void*)(_base_address + obj) : (void*)obj;
+ u4 narrowp = checked_cast(nextPtr());
+ *p = AOTCompressedPointers::decode(cast_from_u4(narrowp), _base_address);
}
void ReadClosure::do_u4(u4* p) {
diff --git a/src/hotspot/share/cds/archiveUtils.hpp b/src/hotspot/share/cds/archiveUtils.hpp
index 79d894f0144..e5d1efa5eab 100644
--- a/src/hotspot/share/cds/archiveUtils.hpp
+++ b/src/hotspot/share/cds/archiveUtils.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -153,7 +153,6 @@ private:
char* _base;
char* _top;
char* _end;
- uintx _max_delta;
bool _is_packed;
ReservedSpace* _rs;
VirtualSpace* _vs;
@@ -161,9 +160,9 @@ private:
void commit_to(char* newtop);
public:
- DumpRegion(const char* name, uintx max_delta = 0)
+ DumpRegion(const char* name)
: _name(name), _base(nullptr), _top(nullptr), _end(nullptr),
- _max_delta(max_delta), _is_packed(false),
+ _is_packed(false),
_rs(nullptr), _vs(nullptr) {}
char* expand_top_to(char* newtop);
@@ -237,13 +236,13 @@ public:
class ReadClosure : public SerializeClosure {
private:
intptr_t** _ptr_array;
- intptr_t _base_address;
+ address _base_address;
inline intptr_t nextPtr() {
return *(*_ptr_array)++;
}
public:
- ReadClosure(intptr_t** ptr_array, intptr_t base_address) :
+ ReadClosure(intptr_t** ptr_array, address base_address) :
_ptr_array(ptr_array), _base_address(base_address) {}
void do_ptr(void** p);
@@ -260,7 +259,6 @@ class ArchiveUtils {
template static Array* archive_ptr_array(GrowableArray* tmp_array);
public:
- static const uintx MAX_SHARED_DELTA = 0x7FFFFFFF;
static void log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) NOT_CDS_RETURN;
static bool has_aot_initialized_mirror(InstanceKlass* src_ik);
@@ -273,50 +271,6 @@ public:
static Array* archive_array(GrowableArray* tmp_array) {
return archive_ptr_array(tmp_array);
}
-
- // The following functions translate between a u4 offset and an address in the
- // the range of the mapped CDS archive (e.g., Metaspace::in_aot_cache()).
- // Since the first 16 bytes in this range are dummy data (see ArchiveBuilder::reserve_buffer()),
- // we know that offset 0 never represents a valid object. As a result, an offset of 0
- // is used to encode a nullptr.
- //
- // Use the "archived_address_or_null" variants if a nullptr may be encoded.
-
- // offset must represent an object of type T in the mapped shared space. Return
- // a direct pointer to this object.
- template T static offset_to_archived_address(u4 offset) {
- assert(offset != 0, "sanity");
- T p = (T)(SharedBaseAddress + offset);
- assert(Metaspace::in_aot_cache(p), "must be");
- return p;
- }
-
- template T static offset_to_archived_address_or_null(u4 offset) {
- if (offset == 0) {
- return nullptr;
- } else {
- return offset_to_archived_address(offset);
- }
- }
-
- // p must be an archived object. Get its offset from SharedBaseAddress
- template static u4 archived_address_to_offset(T p) {
- uintx pn = (uintx)p;
- uintx base = (uintx)SharedBaseAddress;
- assert(Metaspace::in_aot_cache(p), "must be");
- assert(pn > base, "sanity"); // No valid object is stored at 0 offset from SharedBaseAddress
- uintx offset = pn - base;
- assert(offset <= MAX_SHARED_DELTA, "range check");
- return static_cast(offset);
- }
-
- template static u4 archived_address_or_null_to_offset(T p) {
- if (p == nullptr) {
- return 0;
- } else {
- return archived_address_to_offset(p);
- }
- }
};
class HeapRootSegments {
diff --git a/src/hotspot/share/cds/cppVtables.cpp b/src/hotspot/share/cds/cppVtables.cpp
index da68fa70761..dc5a777d7b1 100644
--- a/src/hotspot/share/cds/cppVtables.cpp
+++ b/src/hotspot/share/cds/cppVtables.cpp
@@ -140,7 +140,7 @@ void CppVtableCloner::initialize(const char* name, CppVtableInfo* info) {
// We already checked (and, if necessary, adjusted n) when the vtables were allocated, so we are
// safe to do memcpy.
- log_debug(aot, vtables)("Copying %3d vtable entries for %s", n, name);
+ log_debug(aot, vtables)("Copying %3d vtable entries for %s to " INTPTR_FORMAT, n, name, p2i(dstvtable));
memcpy(dstvtable, srcvtable, sizeof(intptr_t) * n);
}
diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp
index d39cf3775e4..cd6890555d3 100644
--- a/src/hotspot/share/cds/dynamicArchive.cpp
+++ b/src/hotspot/share/cds/dynamicArchive.cpp
@@ -25,6 +25,7 @@
#include "cds/aotArtifactFinder.hpp"
#include "cds/aotClassLinker.hpp"
#include "cds/aotClassLocation.hpp"
+#include "cds/aotCompressedPointers.hpp"
#include "cds/aotLogging.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp"
@@ -75,13 +76,13 @@ public:
return 0;
}
- u4 a_offset = ArchiveBuilder::current()->any_to_offset_u4(a_name);
- u4 b_offset = ArchiveBuilder::current()->any_to_offset_u4(b_name);
+ u4 a_narrowp = cast_to_u4(AOTCompressedPointers::encode_not_null(a_name));
+ u4 b_narrowp = cast_to_u4(AOTCompressedPointers::encode_not_null(b_name));
- if (a_offset < b_offset) {
+ if (a_narrowp < b_narrowp) {
return -1;
} else {
- assert(a_offset > b_offset, "must be");
+ assert(a_narrowp > b_narrowp, "must be");
return 1;
}
}
diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
index 0eeb96bb269..91fbce701e5 100644
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -298,11 +298,11 @@ void FileMapHeader::print(outputStream* st) {
st->print_cr("- compressed_class_ptrs: %d", _compressed_class_ptrs);
st->print_cr("- narrow_klass_pointer_bits: %d", _narrow_klass_pointer_bits);
st->print_cr("- narrow_klass_shift: %d", _narrow_klass_shift);
- st->print_cr("- cloned_vtables_offset: 0x%zx", _cloned_vtables_offset);
- st->print_cr("- early_serialized_data_offset: 0x%zx", _early_serialized_data_offset);
- st->print_cr("- serialized_data_offset: 0x%zx", _serialized_data_offset);
+ st->print_cr("- cloned_vtables: %u", cast_to_u4(_cloned_vtables));
+ st->print_cr("- early_serialized_data: %u", cast_to_u4(_early_serialized_data));
+ st->print_cr("- serialized_data: %u", cast_to_u4(_serialized_data));
st->print_cr("- jvm_ident: %s", _jvm_ident);
- st->print_cr("- class_location_config_offset: 0x%zx", _class_location_config_offset);
+ st->print_cr("- class_location_config: %d", cast_to_u4(_class_location_config));
st->print_cr("- verify_local: %d", _verify_local);
st->print_cr("- verify_remote: %d", _verify_remote);
st->print_cr("- has_platform_or_app_classes: %d", _has_platform_or_app_classes);
@@ -717,8 +717,8 @@ bool FileMapInfo::init_from_file(int fd) {
}
void FileMapInfo::seek_to_position(size_t pos) {
- if (os::lseek(_fd, (long)pos, SEEK_SET) < 0) {
- aot_log_error(aot)("Unable to seek to position %zu", pos);
+ if (os::lseek(_fd, (jlong)pos, SEEK_SET) < 0) {
+ aot_log_error(aot)("Unable to seek to position %zu (errno=%d: %s)", pos, errno, os::strerror(errno));
AOTMetaspace::unrecoverable_loading_error();
}
}
@@ -974,8 +974,8 @@ size_t FileMapInfo::remove_bitmap_zeros(CHeapBitMap* map) {
char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap,
CHeapBitMap* ro_ptrmap,
- ArchiveMappedHeapInfo* mapped_heap_info,
- ArchiveStreamedHeapInfo* streamed_heap_info,
+ AOTMappedHeapInfo* mapped_heap_info,
+ AOTStreamedHeapInfo* streamed_heap_info,
size_t &size_in_bytes) {
size_t removed_rw_leading_zeros = remove_bitmap_zeros(rw_ptrmap);
size_t removed_ro_leading_zeros = remove_bitmap_zeros(ro_ptrmap);
@@ -1035,7 +1035,7 @@ char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap,
}
#if INCLUDE_CDS_JAVA_HEAP
-size_t FileMapInfo::write_mapped_heap_region(ArchiveMappedHeapInfo* heap_info) {
+size_t FileMapInfo::write_mapped_heap_region(AOTMappedHeapInfo* heap_info) {
char* buffer_start = heap_info->buffer_start();
size_t buffer_size = heap_info->buffer_byte_size();
write_region(AOTMetaspace::hp, buffer_start, buffer_size, false, false);
@@ -1043,7 +1043,7 @@ size_t FileMapInfo::write_mapped_heap_region(ArchiveMappedHeapInfo* heap_info) {
return buffer_size;
}
-size_t FileMapInfo::write_streamed_heap_region(ArchiveStreamedHeapInfo* heap_info) {
+size_t FileMapInfo::write_streamed_heap_region(AOTStreamedHeapInfo* heap_info) {
char* buffer_start = heap_info->buffer_start();
size_t buffer_size = heap_info->buffer_byte_size();
write_region(AOTMetaspace::hp, buffer_start, buffer_size, true, false);
@@ -1325,9 +1325,7 @@ char* FileMapInfo::map_auxiliary_region(int region_index, bool read_only) {
if (VerifySharedSpaces && !r->check_region_crc(mapped_base)) {
aot_log_error(aot)("region %d CRC error", region_index);
- if (!os::unmap_memory(mapped_base, r->used_aligned())) {
- fatal("os::unmap_memory of region %d failed", region_index);
- }
+ os::unmap_memory(mapped_base, r->used_aligned());
return nullptr;
}
@@ -1654,9 +1652,7 @@ void FileMapInfo::unmap_region(int i) {
// is released. Zero it so that we don't accidentally read its content.
aot_log_info(aot)("Region #%d (%s) is in a reserved space, it will be freed when the space is released", i, shared_region_name[i]);
} else {
- if (!os::unmap_memory(mapped_base, size)) {
- fatal("os::unmap_memory failed");
- }
+ os::unmap_memory(mapped_base, size);
}
}
r->set_mapped_base(nullptr);
@@ -1767,10 +1763,6 @@ void FileMapInfo::print(outputStream* st) const {
}
}
-void FileMapHeader::set_as_offset(char* p, size_t *offset) {
- *offset = ArchiveBuilder::current()->any_to_offset((address)p);
-}
-
int FileMapHeader::compute_crc() {
char* start = (char*)this;
// start computing from the field after _header_size to end of base archive name.
diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp
index fbd3c8e1681..56b88df378a 100644
--- a/src/hotspot/share/cds/filemap.hpp
+++ b/src/hotspot/share/cds/filemap.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,10 @@
#ifndef SHARE_CDS_FILEMAP_HPP
#define SHARE_CDS_FILEMAP_HPP
+#include "cds/aotCompressedPointers.hpp"
+#include "cds/aotMappedHeap.hpp"
#include "cds/aotMetaspace.hpp"
+#include "cds/aotStreamedHeap.hpp"
#include "cds/archiveUtils.hpp"
#include "cds/heapShared.hpp"
#include "include/cds.h"
@@ -102,7 +105,7 @@ public:
class FileMapHeader: private CDSFileMapHeaderBase {
friend class CDSConstants;
friend class VMStructs;
-
+ using narrowPtr = AOTCompressedPointers::narrowPtr;
private:
// The following fields record the states of the VM during dump time.
// They are compared with the runtime states to see if the archive
@@ -120,16 +123,16 @@ private:
bool _compressed_class_ptrs; // save the flag UseCompressedClassPointers
int _narrow_klass_pointer_bits; // save number of bits in narrowKlass
int _narrow_klass_shift; // save shift width used to pre-compute narrowKlass IDs in archived heap objects
- size_t _cloned_vtables_offset; // The address of the first cloned vtable
- size_t _early_serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize()
- size_t _serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize()
+ narrowPtr _cloned_vtables; // The address of the first cloned vtable
+ narrowPtr _early_serialized_data; // Data accessed using {ReadClosure,WriteClosure}::serialize()
+ narrowPtr _serialized_data; // Data accessed using {ReadClosure,WriteClosure}::serialize()
// The following fields are all sanity checks for whether this archive
// will function correctly with this JVM and the bootclasspath it's
// invoked with.
char _jvm_ident[JVM_IDENT_MAX]; // identifier string of the jvm that created this dump
- size_t _class_location_config_offset;
+ narrowPtr _class_location_config;
bool _verify_local; // BytecodeVerificationLocal setting
bool _verify_remote; // BytecodeVerificationRemote setting
@@ -144,8 +147,8 @@ private:
size_t _rw_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the rw region
size_t _ro_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the ro region
- ArchiveMappedHeapHeader _mapped_heap_header;
- ArchiveStreamedHeapHeader _streamed_heap_header;
+ AOTMappedHeapHeader _mapped_heap_header;
+ AOTStreamedHeapHeader _streamed_heap_header;
// The following are parameters that affect MethodData layout.
u1 _compiler_type;
@@ -158,12 +161,8 @@ private:
bool _type_profile_casts;
int _spec_trap_limit_extra_entries;
- template T from_mapped_offset(size_t offset) const {
- return (T)(mapped_base_address() + offset);
- }
- void set_as_offset(char* p, size_t *offset);
- template void set_as_offset(T p, size_t *offset) {
- set_as_offset((char*)p, offset);
+ template T decode(narrowPtr narrowp) const {
+ return AOTCompressedPointers::decode_not_null(narrowp, reinterpret_cast(mapped_base_address()));
}
public:
@@ -191,9 +190,9 @@ public:
bool compact_headers() const { return _compact_headers; }
uintx max_heap_size() const { return _max_heap_size; }
CompressedOops::Mode narrow_oop_mode() const { return _narrow_oop_mode; }
- char* cloned_vtables() const { return from_mapped_offset(_cloned_vtables_offset); }
- char* early_serialized_data() const { return from_mapped_offset(_early_serialized_data_offset); }
- char* serialized_data() const { return from_mapped_offset(_serialized_data_offset); }
+ char* cloned_vtables() const { return decode(_cloned_vtables); }
+ char* early_serialized_data() const { return decode(_early_serialized_data); }
+ char* serialized_data() const { return decode(_serialized_data); }
bool object_streaming_mode() const { return _object_streaming_mode; }
const char* jvm_ident() const { return _jvm_ident; }
char* requested_base_address() const { return _requested_base_address; }
@@ -209,16 +208,16 @@ public:
size_t ro_ptrmap_start_pos() const { return _ro_ptrmap_start_pos; }
// Heap archiving
- const ArchiveMappedHeapHeader* mapped_heap() const { return &_mapped_heap_header; }
- const ArchiveStreamedHeapHeader* streamed_heap() const { return &_streamed_heap_header; }
+ const AOTMappedHeapHeader* mapped_heap() const { return &_mapped_heap_header; }
+ const AOTStreamedHeapHeader* streamed_heap() const { return &_streamed_heap_header; }
- void set_streamed_heap_header(ArchiveStreamedHeapHeader header) { _streamed_heap_header = header; }
- void set_mapped_heap_header(ArchiveMappedHeapHeader header) { _mapped_heap_header = header; }
+ void set_streamed_heap_header(AOTStreamedHeapHeader header) { _streamed_heap_header = header; }
+ void set_mapped_heap_header(AOTMappedHeapHeader header) { _mapped_heap_header = header; }
void set_has_platform_or_app_classes(bool v) { _has_platform_or_app_classes = v; }
- void set_cloned_vtables(char* p) { set_as_offset(p, &_cloned_vtables_offset); }
- void set_early_serialized_data(char* p) { set_as_offset(p, &_early_serialized_data_offset); }
- void set_serialized_data(char* p) { set_as_offset(p, &_serialized_data_offset); }
+ void set_cloned_vtables(char* p) { _cloned_vtables = AOTCompressedPointers::encode_not_null(p); }
+ void set_early_serialized_data(char* p) { _early_serialized_data = AOTCompressedPointers::encode_not_null(p); }
+ void set_serialized_data(char* p) { _serialized_data = AOTCompressedPointers::encode_not_null(p); }
void set_mapped_base_address(char* p) { _mapped_base_address = p; }
void set_rw_ptrmap_start_pos(size_t n) { _rw_ptrmap_start_pos = n; }
void set_ro_ptrmap_start_pos(size_t n) { _ro_ptrmap_start_pos = n; }
@@ -226,11 +225,11 @@ public:
void copy_base_archive_name(const char* name);
void set_class_location_config(AOTClassLocationConfig* table) {
- set_as_offset(table, &_class_location_config_offset);
+ _class_location_config = AOTCompressedPointers::encode_not_null(table);
}
AOTClassLocationConfig* class_location_config() {
- return from_mapped_offset(_class_location_config_offset);
+ return decode(_class_location_config);
}
void set_requested_base(char* b) {
@@ -309,8 +308,8 @@ public:
uintx max_heap_size() const { return header()->max_heap_size(); }
size_t core_region_alignment() const { return header()->core_region_alignment(); }
- const ArchiveMappedHeapHeader* mapped_heap() const { return header()->mapped_heap(); }
- const ArchiveStreamedHeapHeader* streamed_heap() const { return header()->streamed_heap(); }
+ const AOTMappedHeapHeader* mapped_heap() const { return header()->mapped_heap(); }
+ const AOTStreamedHeapHeader* streamed_heap() const { return header()->streamed_heap(); }
bool object_streaming_mode() const { return header()->object_streaming_mode(); }
CompressedOops::Mode narrow_oop_mode() const { return header()->narrow_oop_mode(); }
@@ -372,11 +371,11 @@ public:
size_t remove_bitmap_zeros(CHeapBitMap* map);
char* write_bitmap_region(CHeapBitMap* rw_ptrmap,
CHeapBitMap* ro_ptrmap,
- ArchiveMappedHeapInfo* mapped_heap_info,
- ArchiveStreamedHeapInfo* streamed_heap_info,
+ AOTMappedHeapInfo* mapped_heap_info,
+ AOTStreamedHeapInfo* streamed_heap_info,
size_t &size_in_bytes);
- size_t write_mapped_heap_region(ArchiveMappedHeapInfo* heap_info) NOT_CDS_JAVA_HEAP_RETURN_(0);
- size_t write_streamed_heap_region(ArchiveStreamedHeapInfo* heap_info) NOT_CDS_JAVA_HEAP_RETURN_(0);
+ size_t write_mapped_heap_region(AOTMappedHeapInfo* heap_info) NOT_CDS_JAVA_HEAP_RETURN_(0);
+ size_t write_streamed_heap_region(AOTStreamedHeapInfo* heap_info) NOT_CDS_JAVA_HEAP_RETURN_(0);
void write_bytes(const void* buffer, size_t count);
void write_bytes_aligned(const void* buffer, size_t count);
size_t read_bytes(void* buffer, size_t count);
diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp
index 143f9147853..0c0f70eac0a 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/aotCompressedPointers.hpp"
#include "cds/aotLogging.hpp"
#include "cds/aotMappedHeapLoader.hpp"
#include "cds/aotMappedHeapWriter.hpp"
@@ -95,55 +96,6 @@ struct ArchivableStaticFieldInfo {
}
};
-// Anything that goes in the header must be thoroughly purged from uninitialized memory
-// as it will be written to disk. Therefore, the constructors memset the memory to 0.
-// This is not the prettiest thing, but we need to know every byte is initialized,
-// including potential padding between fields.
-
-ArchiveMappedHeapHeader::ArchiveMappedHeapHeader(size_t ptrmap_start_pos,
- size_t oopmap_start_pos,
- HeapRootSegments root_segments) {
- memset((char*)this, 0, sizeof(*this));
- _ptrmap_start_pos = ptrmap_start_pos;
- _oopmap_start_pos = oopmap_start_pos;
- _root_segments = root_segments;
-}
-
-ArchiveMappedHeapHeader::ArchiveMappedHeapHeader() {
- memset((char*)this, 0, sizeof(*this));
-}
-
-ArchiveMappedHeapHeader ArchiveMappedHeapInfo::create_header() {
- return ArchiveMappedHeapHeader{_ptrmap_start_pos,
- _oopmap_start_pos,
- _root_segments};
-}
-
-ArchiveStreamedHeapHeader::ArchiveStreamedHeapHeader(size_t forwarding_offset,
- size_t roots_offset,
- size_t num_roots,
- size_t root_highest_object_index_table_offset,
- size_t num_archived_objects) {
- memset((char*)this, 0, sizeof(*this));
- _forwarding_offset = forwarding_offset;
- _roots_offset = roots_offset;
- _num_roots = num_roots;
- _root_highest_object_index_table_offset = root_highest_object_index_table_offset;
- _num_archived_objects = num_archived_objects;
-}
-
-ArchiveStreamedHeapHeader::ArchiveStreamedHeapHeader() {
- memset((char*)this, 0, sizeof(*this));
-}
-
-ArchiveStreamedHeapHeader ArchiveStreamedHeapInfo::create_header() {
- return ArchiveStreamedHeapHeader{_forwarding_offset,
- _roots_offset,
- _num_roots,
- _root_highest_object_index_table_offset,
- _num_archived_objects};
-}
-
HeapArchiveMode HeapShared::_heap_load_mode = HeapArchiveMode::_uninitialized;
HeapArchiveMode HeapShared::_heap_write_mode = HeapArchiveMode::_uninitialized;
@@ -295,6 +247,28 @@ void HeapShared::reset_archived_object_states(TRAPS) {
reset_states(boot_loader(), CHECK);
}
+void HeapShared::ensure_determinism(TRAPS) {
+ TempNewSymbol class_name = SymbolTable::new_symbol("jdk/internal/util/WeakReferenceKey");
+ TempNewSymbol method_name = SymbolTable::new_symbol("ensureDeterministicAOTCache");
+
+ Klass* weak_ref_key_class = SystemDictionary::resolve_or_fail(class_name, true, CHECK);
+ precond(weak_ref_key_class != nullptr);
+
+ log_debug(aot)("Calling WeakReferenceKey::ensureDeterministicAOTCache(Object.class)");
+ JavaValue result(T_BOOLEAN);
+ JavaCalls::call_static(&result,
+ weak_ref_key_class,
+ method_name,
+ vmSymbols::void_boolean_signature(),
+ CHECK);
+ assert(result.get_jboolean() == false, "sanity");
+}
+
+void HeapShared::prepare_for_archiving(TRAPS) {
+ reset_archived_object_states(CHECK);
+ ensure_determinism(CHECK);
+}
+
HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = nullptr;
bool HeapShared::is_archived_heap_in_use() {
@@ -892,7 +866,7 @@ void HeapShared::end_scanning_for_oops() {
delete_seen_objects_table();
}
-void HeapShared::write_heap(ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info) {
+void HeapShared::write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info) {
{
NoSafepointVerifier nsv;
CDSHeapVerifier::verify();
@@ -1197,8 +1171,7 @@ public:
ArchivedKlassSubGraphInfoRecord* record = HeapShared::archive_subgraph_info(&info);
Klass* buffered_k = ArchiveBuilder::get_buffered_klass(klass);
unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary((address)buffered_k);
- u4 delta = ArchiveBuilder::current()->any_to_offset_u4(record);
- _writer->add(hash, delta);
+ _writer->add(hash, AOTCompressedPointers::encode_not_null(record));
}
return true; // keep on iterating
}
diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp
index 3c7068e96ab..2cb330160e4 100644
--- a/src/hotspot/share/cds/heapShared.hpp
+++ b/src/hotspot/share/cds/heapShared.hpp
@@ -142,129 +142,6 @@ enum class HeapArchiveMode {
_streaming
};
-class ArchiveMappedHeapHeader {
- size_t _ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the heap.
- size_t _oopmap_start_pos; // The first bit in the oopmap corresponds to this position in the heap.
- HeapRootSegments _root_segments; // Heap root segments info
-
-public:
- ArchiveMappedHeapHeader();
- ArchiveMappedHeapHeader(size_t ptrmap_start_pos,
- size_t oopmap_start_pos,
- HeapRootSegments root_segments);
-
- size_t ptrmap_start_pos() const { return _ptrmap_start_pos; }
- size_t oopmap_start_pos() const { return _oopmap_start_pos; }
- HeapRootSegments root_segments() const { return _root_segments; }
-
- // This class is trivially copyable and assignable.
- ArchiveMappedHeapHeader(const ArchiveMappedHeapHeader&) = default;
- ArchiveMappedHeapHeader& operator=(const ArchiveMappedHeapHeader&) = default;
-};
-
-
-class ArchiveStreamedHeapHeader {
- size_t _forwarding_offset; // Offset of forwarding information in the heap region.
- size_t _roots_offset; // Start position for the roots
- size_t _root_highest_object_index_table_offset; // Offset of root dfs depth information
- size_t _num_roots; // Number of embedded roots
- size_t _num_archived_objects; // The number of archived heap objects
-
-public:
- ArchiveStreamedHeapHeader();
- ArchiveStreamedHeapHeader(size_t forwarding_offset,
- size_t roots_offset,
- size_t num_roots,
- size_t root_highest_object_index_table_offset,
- size_t num_archived_objects);
-
- size_t forwarding_offset() const { return _forwarding_offset; }
- size_t roots_offset() const { return _roots_offset; }
- size_t num_roots() const { return _num_roots; }
- size_t root_highest_object_index_table_offset() const { return _root_highest_object_index_table_offset; }
- size_t num_archived_objects() const { return _num_archived_objects; }
-
- // This class is trivially copyable and assignable.
- ArchiveStreamedHeapHeader(const ArchiveStreamedHeapHeader&) = default;
- ArchiveStreamedHeapHeader& operator=(const ArchiveStreamedHeapHeader&) = default;
-};
-
-class ArchiveMappedHeapInfo {
- MemRegion _buffer_region; // Contains the archived objects to be written into the CDS archive.
- CHeapBitMap _oopmap;
- CHeapBitMap _ptrmap;
- HeapRootSegments _root_segments;
- size_t _oopmap_start_pos; // How many zeros were removed from the beginning of the bit map?
- size_t _ptrmap_start_pos; // How many zeros were removed from the beginning of the bit map?
-
-public:
- ArchiveMappedHeapInfo() :
- _buffer_region(),
- _oopmap(128, mtClassShared),
- _ptrmap(128, mtClassShared),
- _root_segments(),
- _oopmap_start_pos(),
- _ptrmap_start_pos() {}
- bool is_used() { return !_buffer_region.is_empty(); }
-
- MemRegion buffer_region() { return _buffer_region; }
- void set_buffer_region(MemRegion r) { _buffer_region = r; }
-
- char* buffer_start() { return (char*)_buffer_region.start(); }
- size_t buffer_byte_size() { return _buffer_region.byte_size(); }
-
- CHeapBitMap* oopmap() { return &_oopmap; }
- CHeapBitMap* ptrmap() { return &_ptrmap; }
-
- void set_oopmap_start_pos(size_t start_pos) { _oopmap_start_pos = start_pos; }
- void set_ptrmap_start_pos(size_t start_pos) { _ptrmap_start_pos = start_pos; }
-
- void set_root_segments(HeapRootSegments segments) { _root_segments = segments; };
- HeapRootSegments root_segments() { return _root_segments; }
-
- ArchiveMappedHeapHeader create_header();
-};
-
-class ArchiveStreamedHeapInfo {
- MemRegion _buffer_region; // Contains the archived objects to be written into the CDS archive.
- CHeapBitMap _oopmap;
- size_t _roots_offset; // Offset of the HeapShared::roots() object, from the bottom
- // of the archived heap objects, in bytes.
- size_t _num_roots;
-
- size_t _forwarding_offset; // Offset of forwarding information from the bottom
- size_t _root_highest_object_index_table_offset; // Offset to root dfs depth information
- size_t _num_archived_objects; // The number of archived objects written into the CDS archive.
-
-public:
- ArchiveStreamedHeapInfo()
- : _buffer_region(),
- _oopmap(128, mtClassShared),
- _roots_offset(),
- _forwarding_offset(),
- _root_highest_object_index_table_offset(),
- _num_archived_objects() {}
-
- bool is_used() { return !_buffer_region.is_empty(); }
-
- void set_buffer_region(MemRegion r) { _buffer_region = r; }
- MemRegion buffer_region() { return _buffer_region; }
- char* buffer_start() { return (char*)_buffer_region.start(); }
- size_t buffer_byte_size() { return _buffer_region.byte_size(); }
-
- CHeapBitMap* oopmap() { return &_oopmap; }
- void set_roots_offset(size_t n) { _roots_offset = n; }
- size_t roots_offset() { return _roots_offset; }
- void set_num_roots(size_t n) { _num_roots = n; }
- size_t num_roots() { return _num_roots; }
- void set_forwarding_offset(size_t n) { _forwarding_offset = n; }
- void set_root_highest_object_index_table_offset(size_t n) { _root_highest_object_index_table_offset = n; }
- void set_num_archived_objects(size_t n) { _num_archived_objects = n; }
- size_t num_archived_objects() { return _num_archived_objects; }
-
- ArchiveStreamedHeapHeader create_header();
-};
-
class HeapShared: AllStatic {
friend class VerifySharedOopClosure;
@@ -505,8 +382,10 @@ private:
static bool walk_one_object(PendingOopStack* stack, int level, KlassSubGraphInfo* subgraph_info,
oop orig_obj, oop referrer);
- public:
static void reset_archived_object_states(TRAPS);
+ static void ensure_determinism(TRAPS);
+ public:
+ static void prepare_for_archiving(TRAPS);
static void create_archived_object_cache() {
_archived_object_cache =
new (mtClass)ArchivedObjectCache(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE);
@@ -575,7 +454,7 @@ private:
public:
static void finish_materialize_objects() NOT_CDS_JAVA_HEAP_RETURN;
- static void write_heap(ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info) NOT_CDS_JAVA_HEAP_RETURN;
+ static void write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info) NOT_CDS_JAVA_HEAP_RETURN;
static objArrayOop scratch_resolved_references(ConstantPool* src);
static void add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) NOT_CDS_JAVA_HEAP_RETURN;
static void init_dumping() NOT_CDS_JAVA_HEAP_RETURN;
diff --git a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp
index 19dae28c5b5..3ff5705b79d 100644
--- a/src/hotspot/share/cds/lambdaFormInvokers.cpp
+++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,6 +23,7 @@
*/
#include "cds/aotClassFilter.hpp"
+#include "cds/aotCompressedPointers.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/cdsConfig.hpp"
@@ -52,7 +53,7 @@
#include "runtime/mutexLocker.hpp"
GrowableArrayCHeap* LambdaFormInvokers::_lambdaform_lines = nullptr;
-Array* LambdaFormInvokers::_static_archive_invokers = nullptr;
+Array* LambdaFormInvokers::_static_archive_invokers = nullptr;
static bool _stop_appending = false;
#define NUM_FILTER 4
@@ -252,7 +253,7 @@ void LambdaFormInvokers::dump_static_archive_invokers() {
}
}
if (count > 0) {
- _static_archive_invokers = ArchiveBuilder::new_ro_array(count);
+ _static_archive_invokers = ArchiveBuilder::new_ro_array(count);
int index = 0;
for (int i = 0; i < len; i++) {
char* str = _lambdaform_lines->at(i);
@@ -261,7 +262,7 @@ void LambdaFormInvokers::dump_static_archive_invokers() {
Array* line = ArchiveBuilder::new_ro_array((int)str_len);
strncpy(line->adr_at(0), str, str_len);
- _static_archive_invokers->at_put(index, ArchiveBuilder::current()->any_to_offset_u4(line));
+ _static_archive_invokers->at_put(index, AOTCompressedPointers::encode_not_null(line));
index++;
}
}
@@ -274,8 +275,8 @@ void LambdaFormInvokers::dump_static_archive_invokers() {
void LambdaFormInvokers::read_static_archive_invokers() {
if (_static_archive_invokers != nullptr) {
for (int i = 0; i < _static_archive_invokers->length(); i++) {
- u4 offset = _static_archive_invokers->at(i);
- Array* line = ArchiveUtils::offset_to_archived_address*>(offset);
+ narrowPtr encoded = _static_archive_invokers->at(i);
+ Array* line = AOTCompressedPointers::decode_not_null*>(encoded);
char* str = line->adr_at(0);
append(str);
}
diff --git a/src/hotspot/share/cds/lambdaFormInvokers.hpp b/src/hotspot/share/cds/lambdaFormInvokers.hpp
index 583a863a1c2..9b91850f5b1 100644
--- a/src/hotspot/share/cds/lambdaFormInvokers.hpp
+++ b/src/hotspot/share/cds/lambdaFormInvokers.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,8 @@
#ifndef SHARE_CDS_LAMBDAFORMINVOKERS_HPP
#define SHARE_CDS_LAMBDAFORMINVOKERS_HPP
+
+#include "cds/aotCompressedPointers.hpp"
#include "memory/allStatic.hpp"
#include "oops/oopHandle.hpp"
#include "runtime/handles.hpp"
@@ -35,10 +37,11 @@ class Array;
class SerializeClosure;
class LambdaFormInvokers : public AllStatic {
+ using narrowPtr = AOTCompressedPointers::narrowPtr;
private:
static GrowableArrayCHeap* _lambdaform_lines;
// For storing LF form lines (LF_RESOLVE only) in read only table.
- static Array* _static_archive_invokers;
+ static Array* _static_archive_invokers;
static void regenerate_class(char* name, ClassFileStream& st, TRAPS);
public:
static void append(char* line);
diff --git a/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp b/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp
index d091067c116..4d212dbf2c2 100644
--- a/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp
+++ b/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,6 +23,7 @@
*/
#include "cds/aotClassFilter.hpp"
+#include "cds/aotCompressedPointers.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/cdsProtectionDomain.hpp"
@@ -49,11 +50,11 @@ unsigned int LambdaProxyClassKey::hash() const {
}
unsigned int RunTimeLambdaProxyClassKey::hash() const {
- return primitive_hash(_caller_ik) +
- primitive_hash(_invoked_name) +
- primitive_hash(_invoked_type) +
- primitive_hash(_method_type) +
- primitive_hash(_instantiated_method_type);
+ return primitive_hash(cast_to_u4(_caller_ik)) +
+ primitive_hash(cast_to_u4(_invoked_name)) +
+ primitive_hash(cast_to_u4(_invoked_type)) +
+ primitive_hash(cast_to_u4(_method_type)) +
+ primitive_hash(cast_to_u4(_instantiated_method_type));
}
#ifndef PRODUCT
@@ -71,12 +72,12 @@ void LambdaProxyClassKey::print_on(outputStream* st) const {
void RunTimeLambdaProxyClassKey::print_on(outputStream* st) const {
ResourceMark rm;
st->print_cr("LambdaProxyClassKey : " INTPTR_FORMAT " hash: %0x08x", p2i(this), hash());
- st->print_cr("_caller_ik : %d", _caller_ik);
- st->print_cr("_instantiated_method_type : %d", _instantiated_method_type);
- st->print_cr("_invoked_name : %d", _invoked_name);
- st->print_cr("_invoked_type : %d", _invoked_type);
- st->print_cr("_member_method : %d", _member_method);
- st->print_cr("_method_type : %d", _method_type);
+ st->print_cr("_caller_ik : %d", cast_to_u4(_caller_ik));
+ st->print_cr("_instantiated_method_type : %d", cast_to_u4(_instantiated_method_type));
+ st->print_cr("_invoked_name : %d", cast_to_u4(_invoked_name));
+ st->print_cr("_invoked_type : %d", cast_to_u4(_invoked_type));
+ st->print_cr("_member_method : %d", cast_to_u4(_member_method));
+ st->print_cr("_method_type : %d", cast_to_u4(_method_type));
}
void RunTimeLambdaProxyClassInfo::print_on(outputStream* st) const {
@@ -418,8 +419,7 @@ public:
(RunTimeLambdaProxyClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size);
runtime_info->init(key, info);
unsigned int hash = runtime_info->hash();
- u4 delta = _builder->any_to_offset_u4((void*)runtime_info);
- _writer->add(hash, delta);
+ _writer->add(hash, AOTCompressedPointers::encode_not_null(runtime_info));
return true;
}
};
diff --git a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp
index 91e508bfdc5..dfb75532917 100644
--- a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp
+++ b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,9 @@
#ifndef SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP
#define SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP
+#include "cds/aotCompressedPointers.hpp"
#include "cds/aotMetaspace.hpp"
-#include "cds/archiveBuilder.hpp"
+#include "classfile/compactHashtable.hpp"
#include "classfile/javaClasses.hpp"
#include "memory/metaspaceClosure.hpp"
#include "utilities/growableArray.hpp"
@@ -132,19 +133,20 @@ public:
};
class RunTimeLambdaProxyClassKey {
- u4 _caller_ik;
- u4 _invoked_name;
- u4 _invoked_type;
- u4 _method_type;
- u4 _member_method;
- u4 _instantiated_method_type;
+ using narrowPtr = AOTCompressedPointers::narrowPtr;
+ narrowPtr _caller_ik;
+ narrowPtr _invoked_name;
+ narrowPtr _invoked_type;
+ narrowPtr _method_type;
+ narrowPtr _member_method;
+ narrowPtr _instantiated_method_type;
- RunTimeLambdaProxyClassKey(u4 caller_ik,
- u4 invoked_name,
- u4 invoked_type,
- u4 method_type,
- u4 member_method,
- u4 instantiated_method_type) :
+ RunTimeLambdaProxyClassKey(narrowPtr caller_ik,
+ narrowPtr invoked_name,
+ narrowPtr invoked_type,
+ narrowPtr method_type,
+ narrowPtr member_method,
+ narrowPtr instantiated_method_type) :
_caller_ik(caller_ik),
_invoked_name(invoked_name),
_invoked_type(invoked_type),
@@ -154,15 +156,12 @@ class RunTimeLambdaProxyClassKey {
public:
static RunTimeLambdaProxyClassKey init_for_dumptime(LambdaProxyClassKey& key) {
- assert(ArchiveBuilder::is_active(), "sanity");
- ArchiveBuilder* b = ArchiveBuilder::current();
-
- u4 caller_ik = b->any_to_offset_u4(key.caller_ik());
- u4 invoked_name = b->any_to_offset_u4(key.invoked_name());
- u4 invoked_type = b->any_to_offset_u4(key.invoked_type());
- u4 method_type = b->any_to_offset_u4(key.method_type());
- u4 member_method = b->any_or_null_to_offset_u4(key.member_method()); // could be null
- u4 instantiated_method_type = b->any_to_offset_u4(key.instantiated_method_type());
+ narrowPtr caller_ik = AOTCompressedPointers::encode_not_null(key.caller_ik());
+ narrowPtr invoked_name = AOTCompressedPointers::encode_not_null(key.invoked_name());
+ narrowPtr invoked_type = AOTCompressedPointers::encode_not_null(key.invoked_type());
+ narrowPtr method_type = AOTCompressedPointers::encode_not_null(key.method_type());
+ narrowPtr member_method = AOTCompressedPointers::encode(key.member_method()); // could be null
+ narrowPtr instantiated_method_type = AOTCompressedPointers::encode_not_null(key.instantiated_method_type());
return RunTimeLambdaProxyClassKey(caller_ik, invoked_name, invoked_type, method_type,
member_method, instantiated_method_type);
@@ -176,12 +175,12 @@ public:
Symbol* instantiated_method_type) {
// All parameters must be in shared space, or else you'd get an assert in
// ArchiveUtils::to_offset().
- return RunTimeLambdaProxyClassKey(ArchiveUtils::archived_address_to_offset(caller_ik),
- ArchiveUtils::archived_address_to_offset(invoked_name),
- ArchiveUtils::archived_address_to_offset(invoked_type),
- ArchiveUtils::archived_address_to_offset(method_type),
- ArchiveUtils::archived_address_or_null_to_offset(member_method), // could be null
- ArchiveUtils::archived_address_to_offset(instantiated_method_type));
+ return RunTimeLambdaProxyClassKey(AOTCompressedPointers::encode_address_in_cache(caller_ik),
+ AOTCompressedPointers::encode_address_in_cache(invoked_name),
+ AOTCompressedPointers::encode_address_in_cache(invoked_type),
+ AOTCompressedPointers::encode_address_in_cache(method_type),
+ AOTCompressedPointers::encode_address_in_cache_or_null(member_method), // could be null
+ AOTCompressedPointers::encode_address_in_cache(instantiated_method_type));
}
unsigned int hash() const;
diff --git a/src/hotspot/share/cds/runTimeClassInfo.cpp b/src/hotspot/share/cds/runTimeClassInfo.cpp
index fe940ca6c18..a1f50ab4ffa 100644
--- a/src/hotspot/share/cds/runTimeClassInfo.cpp
+++ b/src/hotspot/share/cds/runTimeClassInfo.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,15 +22,15 @@
*
*/
+#include "cds/aotCompressedPointers.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/dumpTimeClassInfo.hpp"
#include "cds/runTimeClassInfo.hpp"
#include "classfile/systemDictionaryShared.hpp"
void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
- ArchiveBuilder* builder = ArchiveBuilder::current();
InstanceKlass* k = info._klass;
- _klass_offset = builder->any_to_offset_u4(k);
+ _klass = AOTCompressedPointers::encode_not_null(k);
if (!SystemDictionaryShared::is_builtin(k)) {
CrcInfo* c = crc();
@@ -50,8 +50,8 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
RTVerifierConstraint* vf_constraints = verifier_constraints();
char* flags = verifier_constraint_flags();
for (i = 0; i < _num_verifier_constraints; i++) {
- vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i).name());
- vf_constraints[i]._from_name = builder->any_or_null_to_offset_u4(info._verifier_constraints->at(i).from_name());
+ vf_constraints[i]._name = AOTCompressedPointers::encode_not_null(info._verifier_constraints->at(i).name());
+ vf_constraints[i]._from_name = AOTCompressedPointers::encode(info._verifier_constraints->at(i).from_name());
}
for (i = 0; i < _num_verifier_constraints; i++) {
flags[i] = info._verifier_constraint_flags->at(i);
@@ -61,14 +61,14 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
if (_num_loader_constraints > 0) {
RTLoaderConstraint* ld_constraints = loader_constraints();
for (i = 0; i < _num_loader_constraints; i++) {
- ld_constraints[i]._name = builder->any_to_offset_u4(info._loader_constraints->at(i).name());
+ ld_constraints[i]._name = AOTCompressedPointers::encode_not_null(info._loader_constraints->at(i).name());
ld_constraints[i]._loader_type1 = info._loader_constraints->at(i).loader_type1();
ld_constraints[i]._loader_type2 = info._loader_constraints->at(i).loader_type2();
}
}
if (k->is_hidden() && info.nest_host() != nullptr) {
- _nest_host_offset = builder->any_to_offset_u4(info.nest_host());
+ _nest_host = AOTCompressedPointers::encode_not_null(info.nest_host());
}
if (k->has_archived_enum_objs()) {
int num = info.num_enum_klass_static_fields();
@@ -83,11 +83,12 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
InstanceKlass* RunTimeClassInfo::klass() const {
if (AOTMetaspace::in_aot_cache(this)) {
// is inside a mmaped CDS archive.
- return ArchiveUtils::offset_to_archived_address(_klass_offset);
+ return AOTCompressedPointers::decode_not_null(_klass);
} else {
// is a temporary copy of a RunTimeClassInfo that's being initialized
// by the ArchiveBuilder.
- return ArchiveBuilder::current()->offset_to_buffered(_klass_offset);
+ size_t byte_offset = AOTCompressedPointers::get_byte_offset(_klass);
+ return ArchiveBuilder::current()->offset_to_buffered(byte_offset);
}
}
diff --git a/src/hotspot/share/cds/runTimeClassInfo.hpp b/src/hotspot/share/cds/runTimeClassInfo.hpp
index 371924f9065..d63a04698bb 100644
--- a/src/hotspot/share/cds/runTimeClassInfo.hpp
+++ b/src/hotspot/share/cds/runTimeClassInfo.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
#ifndef SHARE_CDS_RUNTIMECLASSINFO_HPP
#define SHARE_CDS_RUNTIMECLASSINFO_HPP
+#include "cds/aotCompressedPointers.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp"
@@ -41,8 +42,10 @@ class Method;
class Symbol;
class RunTimeClassInfo {
- public:
- enum : char {
+ using narrowPtr = AOTCompressedPointers::narrowPtr;
+
+public:
+ enum : char {
FROM_FIELD_IS_PROTECTED = 1 << 0,
FROM_IS_ARRAY = 1 << 1,
FROM_IS_OBJECT = 1 << 2
@@ -56,19 +59,19 @@ class RunTimeClassInfo {
// This is different than DumpTimeClassInfo::DTVerifierConstraint. We use
// u4 instead of Symbol* to save space on 64-bit CPU.
struct RTVerifierConstraint {
- u4 _name;
- u4 _from_name;
- Symbol* name() { return ArchiveUtils::offset_to_archived_address(_name); }
+ narrowPtr _name;
+ narrowPtr _from_name;
+ Symbol* name() { return AOTCompressedPointers::decode_not_null(_name); }
Symbol* from_name() {
- return (_from_name == 0) ? nullptr : ArchiveUtils::offset_to_archived_address(_from_name);
+ return AOTCompressedPointers::decode(_from_name);
}
};
struct RTLoaderConstraint {
- u4 _name;
+ narrowPtr _name;
char _loader_type1;
char _loader_type2;
- Symbol* constraint_name() { return ArchiveUtils::offset_to_archived_address(_name); }
+ Symbol* constraint_name() { return AOTCompressedPointers::decode_not_null(_name); }
};
struct RTEnumKlassStaticFields {
int _num;
@@ -76,8 +79,8 @@ class RunTimeClassInfo {
};
private:
- u4 _klass_offset;
- u4 _nest_host_offset;
+ narrowPtr _klass;
+ narrowPtr _nest_host;
int _num_verifier_constraints;
int _num_loader_constraints;
@@ -185,7 +188,7 @@ public:
InstanceKlass* nest_host() {
assert(!ArchiveBuilder::is_active(), "not called when dumping archive");
- return ArchiveUtils::offset_to_archived_address_or_null(_nest_host_offset);
+ return AOTCompressedPointers::decode(_nest_host); // may be null
}
RTLoaderConstraint* loader_constraints() {
diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp
index e0c818f02fc..946fea5346f 100644
--- a/src/hotspot/share/ci/ciField.cpp
+++ b/src/hotspot/share/ci/ciField.cpp
@@ -213,7 +213,7 @@ ciField::ciField(fieldDescriptor *fd) :
"bootstrap classes must not create & cache unshared fields");
}
-static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
+static bool trust_final_nonstatic_fields(ciInstanceKlass* holder) {
if (holder == nullptr)
return false;
if (holder->trust_final_fields()) {
@@ -259,7 +259,7 @@ void ciField::initialize_from(fieldDescriptor* fd) {
// An instance field can be constant if it's a final static field or if
// it's a final non-static field of a trusted class (classes in
// java.lang.invoke and sun.invoke packages and subpackages).
- _is_constant = is_stable_field || trust_final_non_static_fields(_holder);
+ _is_constant = is_stable_field || trust_final_nonstatic_fields(_holder);
}
} else {
// For CallSite objects treat the target field as a compile time constant.
diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp
index 33bcabc4566..6243258acd9 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.cpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.cpp
@@ -392,7 +392,7 @@ bool ciInstanceKlass::contains_field_offset(int offset) {
return get_instanceKlass()->contains_field_offset(offset);
}
-ciField* ciInstanceKlass::get_non_static_field_by_offset(const int field_offset) {
+ciField* ciInstanceKlass::get_nonstatic_field_by_offset(const int field_offset) {
for (int i = 0, len = nof_nonstatic_fields(); i < len; i++) {
ciField* field = _nonstatic_fields->at(i);
int field_off = field->offset_in_bytes();
@@ -406,7 +406,7 @@ ciField* ciInstanceKlass::get_non_static_field_by_offset(const int field_offset)
// ciInstanceKlass::get_field_by_offset
ciField* ciInstanceKlass::get_field_by_offset(int field_offset, bool is_static) {
if (!is_static) {
- return get_non_static_field_by_offset(field_offset);
+ return get_nonstatic_field_by_offset(field_offset);
}
VM_ENTRY_MARK;
InstanceKlass* k = get_instanceKlass();
@@ -437,7 +437,7 @@ ciField* ciInstanceKlass::get_field_by_name(ciSymbol* name, ciSymbol* signature,
// except this does not require allocating memory for a new ciField
BasicType ciInstanceKlass::get_field_type_by_offset(const int field_offset, const bool is_static) {
if (!is_static) {
- ciField* field = get_non_static_field_by_offset(field_offset);
+ ciField* field = get_nonstatic_field_by_offset(field_offset);
return field != nullptr ? field->layout_type() : T_ILLEGAL;
}
diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp
index 8ccf1fadfb7..a84c63981c9 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.hpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.hpp
@@ -83,7 +83,7 @@ private:
bool compute_injected_fields_helper();
void compute_transitive_interfaces();
- ciField* get_non_static_field_by_offset(int field_offset);
+ ciField* get_nonstatic_field_by_offset(int field_offset);
protected:
ciInstanceKlass(Klass* k);
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index 817d0c64d11..c1f00cbe536 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1017,7 +1017,8 @@ public:
};
-static int skip_annotation_value(const u1*, int, int); // fwd decl
+static int skip_annotation_value(const u1* buffer, int limit, int index, int recursion_depth); // fwd decl
+static const int max_recursion_depth = 5;
// Safely increment index by val if does not pass limit
#define SAFE_ADD(index, limit, val) \
@@ -1025,23 +1026,29 @@ if (index >= limit - val) return limit; \
index += val;
// Skip an annotation. Return >=limit if there is any problem.
-static int skip_annotation(const u1* buffer, int limit, int index) {
+static int skip_annotation(const u1* buffer, int limit, int index, int recursion_depth = 0) {
assert(buffer != nullptr, "invariant");
+ if (recursion_depth > max_recursion_depth) {
+ return limit;
+ }
// annotation := atype:u2 do(nmem:u2) {member:u2 value}
// value := switch (tag:u1) { ... }
SAFE_ADD(index, limit, 4); // skip atype and read nmem
int nmem = Bytes::get_Java_u2((address)buffer + index - 2);
while (--nmem >= 0 && index < limit) {
SAFE_ADD(index, limit, 2); // skip member
- index = skip_annotation_value(buffer, limit, index);
+ index = skip_annotation_value(buffer, limit, index, recursion_depth + 1);
}
return index;
}
// Skip an annotation value. Return >=limit if there is any problem.
-static int skip_annotation_value(const u1* buffer, int limit, int index) {
+static int skip_annotation_value(const u1* buffer, int limit, int index, int recursion_depth) {
assert(buffer != nullptr, "invariant");
+ if (recursion_depth > max_recursion_depth) {
+ return limit;
+ }
// value := switch (tag:u1) {
// case B, C, I, S, Z, D, F, J, c: con:u2;
// case e: e_class:u2 e_name:u2;
@@ -1073,12 +1080,12 @@ static int skip_annotation_value(const u1* buffer, int limit, int index) {
SAFE_ADD(index, limit, 2); // read nval
int nval = Bytes::get_Java_u2((address)buffer + index - 2);
while (--nval >= 0 && index < limit) {
- index = skip_annotation_value(buffer, limit, index);
+ index = skip_annotation_value(buffer, limit, index, recursion_depth + 1);
}
}
break;
case '@':
- index = skip_annotation(buffer, limit, index);
+ index = skip_annotation(buffer, limit, index, recursion_depth + 1);
break;
default:
return limit; // bad tag byte
diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp
index f631bfaa102..eced83577cb 100644
--- a/src/hotspot/share/classfile/classLoader.cpp
+++ b/src/hotspot/share/classfile/classLoader.cpp
@@ -127,6 +127,7 @@ PerfCounter* ClassLoader::_perf_ik_link_methods_count = nullptr;
PerfCounter* ClassLoader::_perf_method_adapters_count = nullptr;
PerfCounter* ClassLoader::_unsafe_defineClassCallCounter = nullptr;
PerfCounter* ClassLoader::_perf_secondary_hash_time = nullptr;
+PerfCounter* ClassLoader::_perf_change_wx_time = nullptr;
PerfCounter* ClassLoader::_perf_resolve_indy_time = nullptr;
PerfCounter* ClassLoader::_perf_resolve_invokehandle_time = nullptr;
@@ -1370,6 +1371,7 @@ void ClassLoader::initialize(TRAPS) {
NEWPERFBYTECOUNTER(_perf_sys_classfile_bytes_read, SUN_CLS, "sysClassBytes");
NEWPERFEVENTCOUNTER(_unsafe_defineClassCallCounter, SUN_CLS, "unsafeDefineClassCalls");
NEWPERFTICKCOUNTER(_perf_secondary_hash_time, SUN_CLS, "secondarySuperHashTime");
+ NEWPERFTICKCOUNTER(_perf_change_wx_time, SUN_CLS, "changeWXTime");
if (log_is_enabled(Info, perf, class, link)) {
NEWPERFTICKCOUNTER(_perf_ik_link_methods_time, SUN_CLS, "linkMethodsTime");
diff --git a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp
index afb0a581dcc..a935d3027ac 100644
--- a/src/hotspot/share/classfile/classLoader.hpp
+++ b/src/hotspot/share/classfile/classLoader.hpp
@@ -184,6 +184,7 @@ class ClassLoader: AllStatic {
// Count the time taken to hash the scondary superclass arrays.
static PerfCounter* _perf_secondary_hash_time;
+ static PerfCounter* _perf_change_wx_time;
// The boot class path consists of 3 ordered pieces:
// 1. the module/path pairs specified to --patch-module
@@ -268,6 +269,9 @@ class ClassLoader: AllStatic {
static PerfCounter* perf_secondary_hash_time() {
return _perf_secondary_hash_time;
}
+ static PerfCounter* perf_change_wx_time() {
+ return _perf_change_wx_time;
+ }
static PerfCounter* perf_sys_classload_time() { return _perf_sys_classload_time; }
static PerfCounter* perf_app_classload_time() { return _perf_app_classload_time; }
static PerfCounter* perf_app_classload_selftime() { return _perf_app_classload_selftime; }
diff --git a/src/hotspot/share/classfile/compactHashtable.cpp b/src/hotspot/share/classfile/compactHashtable.cpp
index 6808ae7bb8f..de67971c403 100644
--- a/src/hotspot/share/classfile/compactHashtable.cpp
+++ b/src/hotspot/share/classfile/compactHashtable.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -96,14 +96,16 @@ void CompactHashtableWriter::allocate_table() {
"Too many entries.");
}
- _compact_buckets = ArchiveBuilder::new_ro_array(_num_buckets + 1);
- _compact_entries = ArchiveBuilder::new_ro_array(entries_space);
+ _num_compact_buckets = checked_cast(_num_buckets + 1); // extra slot for TABLEEND_BUCKET_TYPE
+ _num_compact_entries = checked_cast(entries_space);
+ _compact_buckets = (u4*)ArchiveBuilder::ro_region_alloc(_num_compact_buckets * sizeof(u4));
+ _compact_entries = (u4*)ArchiveBuilder::ro_region_alloc(_num_compact_entries * sizeof(u4));
_stats->bucket_count = _num_buckets;
- _stats->bucket_bytes = align_up(_compact_buckets->size() * BytesPerWord,
+ _stats->bucket_bytes = align_up(checked_cast(_num_compact_buckets * sizeof(u4)),
SharedSpaceObjectAlignment);
_stats->hashentry_count = _num_entries_written;
- _stats->hashentry_bytes = align_up(_compact_entries->size() * BytesPerWord,
+ _stats->hashentry_bytes = align_up(checked_cast(_num_compact_entries * sizeof(u4)),
SharedSpaceObjectAlignment);
}
@@ -114,21 +116,21 @@ void CompactHashtableWriter::dump_table(NumberSeq* summary) {
GrowableArray* bucket = _buckets[index];
int bucket_size = bucket->length();
if (bucket_size == 1) {
- _compact_buckets->at_put(index, BUCKET_INFO(offset, VALUE_ONLY_BUCKET_TYPE));
+ compact_buckets_set(index, BUCKET_INFO(offset, VALUE_ONLY_BUCKET_TYPE));
Entry ent = bucket->at(0);
// bucket with one entry is value_only and only has the encoded_value
- _compact_entries->at_put(offset++, ent.encoded_value());
+ compact_entries_set(offset++, ent.encoded_value());
_num_value_only_buckets++;
} else {
// regular bucket, it could contain zero or more than one entry,
// each entry is a pair
- _compact_buckets->at_put(index, BUCKET_INFO(offset, REGULAR_BUCKET_TYPE));
+ compact_buckets_set(index, BUCKET_INFO(offset, REGULAR_BUCKET_TYPE));
for (int i=0; iat(i);
- _compact_entries->at_put(offset++, u4(ent.hash())); // write entry hash
- _compact_entries->at_put(offset++, ent.encoded_value()); // write entry encoded_value
+ compact_entries_set(offset++, u4(ent.hash())); // write entry hash
+ compact_entries_set(offset++, ent.encoded_value()); // write entry encoded_value
}
if (bucket_size == 0) {
_num_empty_buckets++;
@@ -140,10 +142,19 @@ void CompactHashtableWriter::dump_table(NumberSeq* summary) {
}
// Mark the end of the buckets
- _compact_buckets->at_put(_num_buckets, BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE));
- assert(offset == (u4)_compact_entries->length(), "sanity");
+ compact_buckets_set(_num_buckets, BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE));
+ assert(offset == checked_cast(_num_compact_entries), "sanity");
}
+void CompactHashtableWriter::compact_buckets_set(u4 index, u4 value) {
+ precond(index < _num_compact_buckets);
+ _compact_buckets[index] = value;
+}
+
+void CompactHashtableWriter::compact_entries_set(u4 index, u4 value) {
+ precond(index < _num_compact_entries);
+ _compact_entries[index] = value;
+}
// Write the compact table
void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table_name) {
@@ -154,7 +165,7 @@ void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table
int table_bytes = _stats->bucket_bytes + _stats->hashentry_bytes;
address base_address = address(SharedBaseAddress);
cht->init(base_address, _num_entries_written, _num_buckets,
- _compact_buckets->data(), _compact_entries->data());
+ _compact_buckets, _compact_entries);
LogMessage(aot, hashtables) msg;
if (msg.is_info()) {
diff --git a/src/hotspot/share/classfile/compactHashtable.hpp b/src/hotspot/share/classfile/compactHashtable.hpp
index 944fb876521..81f2951289d 100644
--- a/src/hotspot/share/classfile/compactHashtable.hpp
+++ b/src/hotspot/share/classfile/compactHashtable.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
#ifndef SHARE_CLASSFILE_COMPACTHASHTABLE_HPP
#define SHARE_CLASSFILE_COMPACTHASHTABLE_HPP
+#include "cds/aotCompressedPointers.hpp"
#include "cds/cds_globals.hpp"
#include "oops/array.hpp"
#include "oops/symbol.hpp"
@@ -114,8 +115,13 @@ private:
int _num_other_buckets;
GrowableArray** _buckets;
CompactHashtableStats* _stats;
- Array* _compact_buckets;
- Array* _compact_entries;
+ u4* _compact_buckets;
+ size_t _num_compact_buckets;
+ u4* _compact_entries;
+ size_t _num_compact_entries;
+
+ void compact_buckets_set(u4 index, u4 value);
+ void compact_entries_set(u4 index, u4 value);
public:
// This is called at dump-time only
@@ -123,6 +129,9 @@ public:
~CompactHashtableWriter();
void add(unsigned int hash, u4 encoded_value);
+ void add(unsigned int hash, AOTCompressedPointers::narrowPtr encoded_value) {
+ add(hash, cast_to_u4(encoded_value));
+ }
void dump(SimpleCompactHashtable *cht, const char* table_name);
private:
@@ -371,11 +380,11 @@ public:
//
// OffsetCompactHashtable -- This is used to store many types of objects
// in the CDS archive. On 64-bit platforms, we save space by using a 32-bit
-// offset from the CDS base address.
+// narrowPtr from the CDS base address.
template
-inline V read_value_from_compact_hashtable(address base_address, u4 offset) {
- return (V)(base_address + offset);
+inline V read_value_from_compact_hashtable(address base_address, u4 narrowp) {
+ return AOTCompressedPointers::decode_not_null(cast_from_u4(narrowp), base_address);
}
template <
diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp
index b650bf8cfb8..ef1eeec14dd 100644
--- a/src/hotspot/share/classfile/javaClasses.cpp
+++ b/src/hotspot/share/classfile/javaClasses.cpp
@@ -1908,16 +1908,16 @@ oop java_lang_Thread::park_blocker(oop java_thread) {
return java_thread->obj_field_access(_park_blocker_offset);
}
-// Obtain stack trace for platform or mounted virtual thread.
-// If jthread is a virtual thread and it has been unmounted (or remounted to different carrier) the method returns null.
-// The caller (java.lang.VirtualThread) handles returned nulls via retry.
+// Obtain stack trace for a platform or virtual thread.
oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) {
ThreadsListHandle tlh(THREAD);
JavaThread* java_thread = nullptr;
- oop thread_oop;
+ oop thread_oop = nullptr;
bool has_java_thread = tlh.cv_internal_thread_to_JavaThread(jthread, &java_thread, &thread_oop);
- if (!has_java_thread) {
+ assert(thread_oop != nullptr, "Missing Thread oop");
+ bool is_virtual = java_lang_VirtualThread::is_instance(thread_oop);
+ if (!has_java_thread && !is_virtual) {
return nullptr;
}
@@ -1925,61 +1925,33 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) {
public:
const Handle _thread_h;
int _depth;
- bool _retry_handshake;
- GrowableArray* _methods;
- GrowableArray* _bcis;
+ enum InitLength { len = 64 }; // Minimum length that covers most cases
+ GrowableArrayCHeap _methods;
+ GrowableArrayCHeap _bcis;
GetStackTraceHandshakeClosure(Handle thread_h) :
- HandshakeClosure("GetStackTraceHandshakeClosure"), _thread_h(thread_h), _depth(0), _retry_handshake(false),
- _methods(nullptr), _bcis(nullptr) {
- }
- ~GetStackTraceHandshakeClosure() {
- delete _methods;
- delete _bcis;
- }
-
- bool read_reset_retry() {
- bool ret = _retry_handshake;
- // If we re-execute the handshake this method need to return false
- // when the handshake cannot be performed. (E.g. thread terminating)
- _retry_handshake = false;
- return ret;
+ HandshakeClosure("GetStackTraceHandshakeClosure"), _thread_h(thread_h), _depth(0),
+ _methods(InitLength::len), _bcis(InitLength::len) {
}
void do_thread(Thread* th) {
- if (!Thread::current()->is_Java_thread()) {
- _retry_handshake = true;
+ JavaThread* java_thread = th != nullptr ? JavaThread::cast(th) : nullptr;
+ if (java_thread != nullptr && !java_thread->has_last_Java_frame()) {
+ // stack trace is empty
return;
}
- JavaThread* java_thread = JavaThread::cast(th);
-
- if (!java_thread->has_last_Java_frame()) {
- return;
- }
-
- bool carrier = false;
- if (java_lang_VirtualThread::is_instance(_thread_h())) {
- // Ensure _thread_h is still mounted to java_thread.
- const ContinuationEntry* ce = java_thread->vthread_continuation();
- if (ce == nullptr || ce->cont_oop(java_thread) != java_lang_VirtualThread::continuation(_thread_h())) {
- // Target thread has been unmounted.
- return;
- }
- } else {
- carrier = (java_thread->vthread_continuation() != nullptr);
- }
+ bool is_virtual = java_lang_VirtualThread::is_instance(_thread_h());
+ bool vthread_carrier = !is_virtual && (java_thread->vthread_continuation() != nullptr);
const int max_depth = MaxJavaStackTraceDepth;
const bool skip_hidden = !ShowHiddenFrames;
- // Pick minimum length that will cover most cases
- int init_length = 64;
- _methods = new (mtInternal) GrowableArray(init_length, mtInternal);
- _bcis = new (mtInternal) GrowableArray(init_length, mtInternal);
-
int total_count = 0;
- for (vframeStream vfst(java_thread, false, false, carrier); // we don't process frames as we don't care about oops
+ vframeStream vfst(java_thread != nullptr
+ ? vframeStream(java_thread, false, false, vthread_carrier) // we don't process frames as we don't care about oops
+ : vframeStream(java_lang_VirtualThread::continuation(_thread_h())));
+ for (;
!vfst.at_end() && (max_depth == 0 || max_depth != total_count);
vfst.next()) {
@@ -1988,8 +1960,8 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) {
continue;
}
- _methods->push(vfst.method());
- _bcis->push(vfst.bci());
+ _methods.push(vfst.method());
+ _bcis.push(vfst.bci());
total_count++;
}
@@ -2001,9 +1973,11 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) {
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
GetStackTraceHandshakeClosure gsthc(Handle(THREAD, thread_oop));
- do {
- Handshake::execute(&gsthc, &tlh, java_thread);
- } while (gsthc.read_reset_retry());
+ if (is_virtual) {
+ Handshake::execute(&gsthc, thread_oop);
+ } else {
+ Handshake::execute(&gsthc, &tlh, java_thread);
+ }
// Stop if no stack trace is found.
if (gsthc._depth == 0) {
@@ -2019,9 +1993,9 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) {
objArrayHandle trace = oopFactory::new_objArray_handle(k, gsthc._depth, CHECK_NULL);
for (int i = 0; i < gsthc._depth; i++) {
- methodHandle method(THREAD, gsthc._methods->at(i));
+ methodHandle method(THREAD, gsthc._methods.at(i));
oop element = java_lang_StackTraceElement::create(method,
- gsthc._bcis->at(i),
+ gsthc._bcis.at(i),
CHECK_NULL);
trace->obj_at_put(i, element);
}
@@ -2200,7 +2174,7 @@ void java_lang_VirtualThread::set_timeout(oop vthread, jlong value) {
JavaThreadStatus java_lang_VirtualThread::map_state_to_thread_status(int state) {
JavaThreadStatus status = JavaThreadStatus::NEW;
- switch (state & ~SUSPENDED) {
+ switch (state) {
case NEW:
status = JavaThreadStatus::NEW;
break;
diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp
index a8562a345c8..3276d398faf 100644
--- a/src/hotspot/share/classfile/javaClasses.hpp
+++ b/src/hotspot/share/classfile/javaClasses.hpp
@@ -592,9 +592,6 @@ class java_lang_VirtualThread : AllStatic {
TIMED_WAITING = 17,
TIMED_WAIT = 18, // waiting in timed-Object.wait
TERMINATED = 99,
-
- // additional state bits
- SUSPENDED = 1 << 8, // suspended when unmounted
};
static void compute_offsets();
diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp
index c49aa10fa0d..20aa7f0776d 100644
--- a/src/hotspot/share/classfile/symbolTable.cpp
+++ b/src/hotspot/share/classfile/symbolTable.cpp
@@ -22,6 +22,7 @@
*
*/
+#include "cds/aotCompressedPointers.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/dynamicArchive.hpp"
@@ -690,7 +691,7 @@ void SymbolTable::copy_shared_symbol_table(GrowableArray* symbols,
assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length(), false),
"must not rehash during dumping");
sym->set_permanent();
- writer->add(fixed_hash, builder->buffer_to_offset_u4((address)sym));
+ writer->add(fixed_hash, AOTCompressedPointers::encode_not_null(sym));
}
}
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
index afc190c36cf..cfb20412ab8 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
#include "cds/aotClassFilter.hpp"
#include "cds/aotClassLocation.hpp"
+#include "cds/aotCompressedPointers.hpp"
#include "cds/aotLogging.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp"
@@ -1282,11 +1283,10 @@ unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) {
class CopySharedClassInfoToArchive : StackObj {
CompactHashtableWriter* _writer;
bool _is_builtin;
- ArchiveBuilder *_builder;
public:
CopySharedClassInfoToArchive(CompactHashtableWriter* writer,
bool is_builtin)
- : _writer(writer), _is_builtin(is_builtin), _builder(ArchiveBuilder::current()) {}
+ : _writer(writer), _is_builtin(is_builtin) {}
void do_entry(InstanceKlass* k, DumpTimeClassInfo& info) {
if (!info.is_excluded() && info.is_builtin() == _is_builtin) {
@@ -1299,11 +1299,10 @@ public:
Symbol* name = info._klass->name();
name = ArchiveBuilder::current()->get_buffered_addr(name);
hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name);
- u4 delta = _builder->buffer_to_offset_u4((address)record);
if (_is_builtin && info._klass->is_hidden()) {
// skip
} else {
- _writer->add(hash, delta);
+ _writer->add(hash, AOTCompressedPointers::encode_not_null(record));
}
if (log_is_enabled(Trace, aot, hashtables)) {
ResourceMark rm;
diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp
index 38dba1d3d5f..76d09161fdd 100644
--- a/src/hotspot/share/classfile/verifier.cpp
+++ b/src/hotspot/share/classfile/verifier.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -190,9 +190,8 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) {
// effect (sic!) for external_name(), but instead of doing that, we opt to
// explicitly push the hashcode in here. This is signify the following block
// is IMPORTANT:
- if (klass->java_mirror() != nullptr) {
- klass->java_mirror()->identity_hash();
- }
+ assert(klass->java_mirror() != nullptr, "must be");
+ klass->java_mirror()->identity_hash();
if (!is_eligible_for_verification(klass, should_verify_class)) {
return true;
@@ -621,9 +620,6 @@ TypeOrigin ClassVerifier::ref_ctx(const char* sig) {
void ClassVerifier::verify_class(TRAPS) {
log_info(verification)("Verifying class %s with new format", _klass->external_name());
- // Either verifying both local and remote classes or just remote classes.
- assert(BytecodeVerificationRemote, "Should not be here");
-
Array* methods = _klass->methods();
int num_methods = methods->length();
diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp
index 07fa294e8e1..67817682ced 100644
--- a/src/hotspot/share/classfile/vmIntrinsics.hpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -257,6 +257,7 @@ class methodHandle;
do_intrinsic(_bitCount_i, java_lang_Integer, bitCount_name, int_int_signature, F_S) \
do_intrinsic(_bitCount_l, java_lang_Long, bitCount_name, long_int_signature, F_S) \
do_intrinsic(_compress_i, java_lang_Integer, compress_name, int2_int_signature, F_S) \
+ do_name( compress_name, "compress") \
do_intrinsic(_compress_l, java_lang_Long, compress_name, long2_long_signature, F_S) \
do_intrinsic(_expand_i, java_lang_Integer, expand_name, int2_int_signature, F_S) \
do_intrinsic(_expand_l, java_lang_Long, expand_name, long2_long_signature, F_S) \
@@ -358,11 +359,11 @@ class methodHandle;
do_name( vectorizedHashCode_name, "vectorizedHashCode") \
do_signature(vectorizedHashCode_signature, "(Ljava/lang/Object;IIII)I") \
\
- do_intrinsic(_compressStringC, java_lang_StringUTF16, compress_name, encodeISOArray_signature, F_S) \
- do_name( compress_name, "compress") \
- do_intrinsic(_compressStringB, java_lang_StringUTF16, compress_name, indexOfI_signature, F_S) \
+ do_intrinsic(_compressStringC, java_lang_StringUTF16, compressString_name, encodeISOArray_signature, F_S) \
+ do_name( compressString_name, "compress0") \
+ do_intrinsic(_compressStringB, java_lang_StringUTF16, compressString_name, indexOfI_signature, F_S) \
do_intrinsic(_inflateStringC, java_lang_StringLatin1, inflate_name, inflateC_signature, F_S) \
- do_name( inflate_name, "inflate") \
+ do_name( inflate_name, "inflate0") \
do_signature(inflateC_signature, "([BI[CII)V") \
do_intrinsic(_inflateStringB, java_lang_StringLatin1, inflate_name, inflateB_signature, F_S) \
do_signature(inflateB_signature, "([BI[BII)V") \
@@ -381,9 +382,9 @@ class methodHandle;
do_intrinsic(_compareToLU, java_lang_StringLatin1,compareToLU_name, compareTo_indexOf_signature, F_S) \
do_intrinsic(_compareToUL, java_lang_StringUTF16, compareToUL_name, compareTo_indexOf_signature, F_S) \
do_signature(compareTo_indexOf_signature, "([B[B)I") \
- do_name( compareTo_name, "compareTo") \
- do_name( compareToLU_name, "compareToUTF16") \
- do_name( compareToUL_name, "compareToLatin1") \
+ do_name( compareTo_name, "compareTo0") \
+ do_name( compareToLU_name, "compareToUTF16_0") \
+ do_name( compareToUL_name, "compareToLatin1_0") \
do_intrinsic(_indexOfL, java_lang_StringLatin1,indexOf_name, compareTo_indexOf_signature, F_S) \
do_intrinsic(_indexOfU, java_lang_StringUTF16, indexOf_name, compareTo_indexOf_signature, F_S) \
do_intrinsic(_indexOfUL, java_lang_StringUTF16, indexOfUL_name, compareTo_indexOf_signature, F_S) \
@@ -392,12 +393,13 @@ class methodHandle;
do_intrinsic(_indexOfIUL, java_lang_StringUTF16, indexOfUL_name, indexOfI_signature, F_S) \
do_intrinsic(_indexOfU_char, java_lang_StringUTF16, indexOfChar_name, indexOfChar_signature, F_S) \
do_intrinsic(_indexOfL_char, java_lang_StringLatin1,indexOfChar_name, indexOfChar_signature, F_S) \
- do_name( indexOf_name, "indexOf") \
- do_name( indexOfChar_name, "indexOfChar") \
- do_name( indexOfUL_name, "indexOfLatin1") \
+ do_name( indexOf_name, "indexOf0") \
+ do_name( indexOfChar_name, "indexOfChar0") \
+ do_name( indexOfUL_name, "indexOfLatin1_0") \
do_signature(indexOfI_signature, "([BI[BII)I") \
do_signature(indexOfChar_signature, "([BIII)I") \
- do_intrinsic(_equalsL, java_lang_StringLatin1,equals_name, equalsB_signature, F_S) \
+ do_intrinsic(_equalsL, java_lang_StringLatin1,equalsString_name, equalsB_signature, F_S) \
+ do_name( equalsString_name, "equals0") \
\
do_intrinsic(_isDigit, java_lang_CharacterDataLatin1, isDigit_name, int_bool_signature, F_R) \
do_name( isDigit_name, "isDigit") \
@@ -415,18 +417,18 @@ class methodHandle;
\
do_class(java_lang_StringCoding, "java/lang/StringCoding") \
do_intrinsic(_countPositives, java_lang_StringCoding, countPositives_name, countPositives_signature, F_S) \
- do_name( countPositives_name, "countPositives") \
+ do_name( countPositives_name, "countPositives0") \
do_signature(countPositives_signature, "([BII)I") \
\
do_class(sun_nio_cs_iso8859_1_Encoder, "sun/nio/cs/ISO_8859_1$Encoder") \
do_intrinsic(_encodeISOArray, sun_nio_cs_iso8859_1_Encoder, encodeISOArray_name, encodeISOArray_signature, F_S) \
- do_name( encodeISOArray_name, "implEncodeISOArray") \
+ do_name( encodeISOArray_name, "encodeISOArray0") \
do_signature(encodeISOArray_signature, "([CI[BII)I") \
\
do_intrinsic(_encodeByteISOArray, java_lang_StringCoding, encodeISOArray_name, indexOfI_signature, F_S) \
\
do_intrinsic(_encodeAsciiArray, java_lang_StringCoding, encodeAsciiArray_name, encodeISOArray_signature, F_S) \
- do_name( encodeAsciiArray_name, "implEncodeAsciiArray") \
+ do_name( encodeAsciiArray_name, "encodeAsciiArray0") \
\
do_class(java_math_BigInteger, "java/math/BigInteger") \
do_intrinsic(_multiplyToLen, java_math_BigInteger, multiplyToLen_name, multiplyToLen_signature, F_S) \
@@ -1003,7 +1005,7 @@ class methodHandle;
do_signature(vector_unary_op_sig, "(I" \
"Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \
@@ -1015,7 +1017,7 @@ class methodHandle;
do_signature(vector_binary_op_sig, "(I" \
"Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \
@@ -1027,7 +1029,7 @@ class methodHandle;
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" \
"I" \
"Ljava/lang/String;" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
@@ -1038,7 +1040,7 @@ class methodHandle;
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" \
"I" \
"Ljava/lang/String;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \
@@ -1051,7 +1053,7 @@ class methodHandle;
do_signature(vector_ternary_op_sig, "(I" \
"Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
@@ -1063,7 +1065,7 @@ class methodHandle;
\
do_intrinsic(_VectorSelectFromTwoVectorOp, jdk_internal_vm_vector_VectorSupport, vector_select_from_op_name, vector_select_from_op_sig, F_S) \
do_signature(vector_select_from_op_sig, "(Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
@@ -1074,7 +1076,7 @@ class methodHandle;
\
do_intrinsic(_VectorFromBitsCoerced, jdk_internal_vm_vector_VectorSupport, vector_frombits_coerced_name, vector_frombits_coerced_sig, F_S) \
do_signature(vector_frombits_coerced_sig, "(Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"J" \
"I" \
@@ -1085,7 +1087,7 @@ class methodHandle;
\
do_intrinsic(_VectorLoadOp, jdk_internal_vm_vector_VectorSupport, vector_load_op_name, vector_load_op_sig, F_S) \
do_signature(vector_load_op_sig, "(Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljava/lang/Object;" \
"J" \
@@ -1100,7 +1102,7 @@ class methodHandle;
do_intrinsic(_VectorLoadMaskedOp, jdk_internal_vm_vector_VectorSupport, vector_load_masked_op_name, vector_load_masked_op_sig, F_S) \
do_signature(vector_load_masked_op_sig, "(Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljava/lang/Object;" \
"J" \
@@ -1116,7 +1118,7 @@ class methodHandle;
\
do_intrinsic(_VectorStoreOp, jdk_internal_vm_vector_VectorSupport, vector_store_op_name, vector_store_op_sig, F_S) \
do_signature(vector_store_op_sig, "(Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljava/lang/Object;" \
"J" \
@@ -1131,7 +1133,7 @@ class methodHandle;
do_intrinsic(_VectorStoreMaskedOp, jdk_internal_vm_vector_VectorSupport, vector_store_masked_op_name, vector_store_masked_op_sig, F_S) \
do_signature(vector_store_masked_op_sig, "(Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljava/lang/Object;" \
"J" \
@@ -1148,7 +1150,7 @@ class methodHandle;
do_signature(vector_reduction_coerced_sig, "(I" \
"Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \
@@ -1159,7 +1161,7 @@ class methodHandle;
do_intrinsic(_VectorTest, jdk_internal_vm_vector_VectorSupport, vector_test_name, vector_test_sig, F_S) \
do_signature(vector_test_sig, "(I" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \
@@ -1170,7 +1172,7 @@ class methodHandle;
do_intrinsic(_VectorBlend, jdk_internal_vm_vector_VectorSupport, vector_blend_name, vector_blend_sig, F_S) \
do_signature(vector_blend_sig, "(Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
@@ -1182,7 +1184,8 @@ class methodHandle;
do_intrinsic(_VectorCompare, jdk_internal_vm_vector_VectorSupport, vector_compare_name, vector_compare_sig, F_S) \
do_signature(vector_compare_sig, "(I" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;Ljava/lang/Class;" \
+ "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
@@ -1195,7 +1198,7 @@ class methodHandle;
do_signature(vector_rearrange_sig, "(Ljava/lang/Class;" \
"Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorShuffle;" \
@@ -1207,18 +1210,18 @@ class methodHandle;
do_intrinsic(_VectorSelectFrom, jdk_internal_vm_vector_VectorSupport, vector_select_from_name, vector_select_from_sig, F_S) \
do_signature(vector_select_from_sig, "(Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorSelectFromOp;)" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;") \
- do_name(vector_select_from_name, "selectFromOp") \
+ do_name(vector_select_from_name, "selectFromOp") \
\
do_intrinsic(_VectorExtract, jdk_internal_vm_vector_VectorSupport, vector_extract_name, vector_extract_sig, F_S) \
do_signature(vector_extract_sig, "(Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \
"I" \
@@ -1228,7 +1231,7 @@ class methodHandle;
\
do_intrinsic(_VectorInsert, jdk_internal_vm_vector_VectorSupport, vector_insert_name, vector_insert_sig, F_S) \
do_signature(vector_insert_sig, "(Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"IJ" \
@@ -1240,7 +1243,7 @@ class methodHandle;
do_signature(vector_broadcast_int_sig, "(I" \
"Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"I" \
@@ -1252,10 +1255,10 @@ class methodHandle;
do_intrinsic(_VectorConvert, jdk_internal_vm_vector_VectorSupport, vector_convert_name, vector_convert_sig, F_S) \
do_signature(vector_convert_sig, "(I" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorSpecies;" \
@@ -1266,7 +1269,7 @@ class methodHandle;
do_intrinsic(_VectorGatherOp, jdk_internal_vm_vector_VectorSupport, vector_gather_name, vector_gather_sig, F_S) \
do_signature(vector_gather_sig, "(Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljava/lang/Class;" \
"I" \
@@ -1287,7 +1290,7 @@ class methodHandle;
do_intrinsic(_VectorScatterOp, jdk_internal_vm_vector_VectorSupport, vector_scatter_name, vector_scatter_sig, F_S) \
do_signature(vector_scatter_sig, "(Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljava/lang/Class;" \
"I" \
@@ -1309,7 +1312,7 @@ class methodHandle;
do_intrinsic(_VectorMaskOp, jdk_internal_vm_vector_VectorSupport, vector_mask_oper_name, vector_mask_oper_sig, F_S) \
do_signature(vector_mask_oper_sig, "(I" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorMaskOp;)" \
@@ -1320,7 +1323,7 @@ class methodHandle;
do_signature(vector_compress_expand_op_sig, "(I" \
"Ljava/lang/Class;" \
"Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \
@@ -1330,7 +1333,7 @@ class methodHandle;
\
do_intrinsic(_IndexVector, jdk_internal_vm_vector_VectorSupport, index_vector_op_name, index_vector_op_sig, F_S) \
do_signature(index_vector_op_sig, "(Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"Ljdk/internal/vm/vector/VectorSupport$Vector;" \
"I" \
@@ -1341,7 +1344,7 @@ class methodHandle;
\
do_intrinsic(_IndexPartiallyInUpperRange, jdk_internal_vm_vector_VectorSupport, index_partially_in_upper_range_name, index_partially_in_upper_range_sig, F_S)\
do_signature(index_partially_in_upper_range_sig, "(Ljava/lang/Class;" \
- "Ljava/lang/Class;" \
+ "I" \
"I" \
"J" \
"J" \
diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index 79646f24d0e..2ae42bebcfd 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -98,6 +98,7 @@ class SerializeClosure;
template(jdk_internal_vm_vector_VectorMask, "jdk/internal/vm/vector/VectorSupport$VectorMask") \
template(jdk_internal_vm_vector_VectorShuffle, "jdk/internal/vm/vector/VectorSupport$VectorShuffle") \
template(payload_name, "payload") \
+ template(CTYPE_name, "CTYPE") \
template(ETYPE_name, "ETYPE") \
template(VLENGTH_name, "VLENGTH") \
\
@@ -748,8 +749,6 @@ class SerializeClosure;
template(jdk_internal_vm_ThreadLock, "jdk/internal/vm/ThreadSnapshot$ThreadLock") \
template(jdk_internal_vm_ThreadLock_signature, "Ljdk/internal/vm/ThreadSnapshot$ThreadLock;") \
template(jdk_internal_vm_ThreadLock_array, "[Ljdk/internal/vm/ThreadSnapshot$ThreadLock;") \
- template(java_lang_StackTraceElement_of_name, "of") \
- template(java_lang_StackTraceElement_of_signature, "([Ljava/lang/StackTraceElement;)[Ljava/lang/StackTraceElement;") \
\
/* jcmd Thread.vthread_scheduler and Thread.vthread_pollers */ \
template(jdk_internal_vm_JcmdVThreadCommands, "jdk/internal/vm/JcmdVThreadCommands") \
diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp
index f51c068f1e7..e5f68afc51d 100644
--- a/src/hotspot/share/code/aotCodeCache.cpp
+++ b/src/hotspot/share/code/aotCodeCache.cpp
@@ -29,9 +29,11 @@
#include "cds/cds_globals.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/heapShared.hpp"
+#include "ci/ciUtilities.hpp"
#include "classfile/javaAssertions.hpp"
#include "code/aotCodeCache.hpp"
#include "code/codeCache.hpp"
+#include "gc/shared/cardTableBarrierSet.hpp"
#include "gc/shared/gcConfig.hpp"
#include "logging/logStream.hpp"
#include "memory/memoryReserver.hpp"
@@ -53,6 +55,7 @@
#endif
#if INCLUDE_G1GC
#include "gc/g1/g1BarrierSetRuntime.hpp"
+#include "gc/g1/g1HeapRegion.hpp"
#endif
#if INCLUDE_SHENANDOAHGC
#include "gc/shenandoah/shenandoahRuntime.hpp"
@@ -258,6 +261,9 @@ void AOTCodeCache::init2() {
return;
}
+ // initialize aot runtime constants as appropriate to this runtime
+ AOTRuntimeConstants::initialize_from_runtime();
+
// initialize the table of external routines so we can save
// generated code blobs that reference them
AOTCodeAddressTable* table = opened_cache->_table;
@@ -399,7 +405,7 @@ AOTCodeCache::~AOTCodeCache() {
}
}
-void AOTCodeCache::Config::record() {
+void AOTCodeCache::Config::record(uint cpu_features_offset) {
_flags = 0;
#ifdef ASSERT
_flags |= debugVM;
@@ -430,9 +436,50 @@ void AOTCodeCache::Config::record() {
_compressedKlassShift = CompressedKlassPointers::shift();
_contendedPaddingWidth = ContendedPaddingWidth;
_gc = (uint)Universe::heap()->kind();
+ _cpu_features_offset = cpu_features_offset;
}
-bool AOTCodeCache::Config::verify() const {
+bool AOTCodeCache::Config::verify_cpu_features(AOTCodeCache* cache) const {
+ LogStreamHandle(Debug, aot, codecache, init) log;
+ uint offset = _cpu_features_offset;
+ uint cpu_features_size = *(uint *)cache->addr(offset);
+ assert(cpu_features_size == (uint)VM_Version::cpu_features_size(), "must be");
+ offset += sizeof(uint);
+
+ void* cached_cpu_features_buffer = (void *)cache->addr(offset);
+ if (log.is_enabled()) {
+ ResourceMark rm; // required for stringStream::as_string()
+ stringStream ss;
+ VM_Version::get_cpu_features_name(cached_cpu_features_buffer, ss);
+ log.print_cr("CPU features recorded in AOTCodeCache: %s", ss.as_string());
+ }
+
+ if (VM_Version::supports_features(cached_cpu_features_buffer)) {
+ if (log.is_enabled()) {
+ ResourceMark rm; // required for stringStream::as_string()
+ stringStream ss;
+ char* runtime_cpu_features = NEW_RESOURCE_ARRAY(char, VM_Version::cpu_features_size());
+ VM_Version::store_cpu_features(runtime_cpu_features);
+ VM_Version::get_missing_features_name(runtime_cpu_features, cached_cpu_features_buffer, ss);
+ if (!ss.is_empty()) {
+ log.print_cr("Additional runtime CPU features: %s", ss.as_string());
+ }
+ }
+ } else {
+ if (log.is_enabled()) {
+ ResourceMark rm; // required for stringStream::as_string()
+ stringStream ss;
+ char* runtime_cpu_features = NEW_RESOURCE_ARRAY(char, VM_Version::cpu_features_size());
+ VM_Version::store_cpu_features(runtime_cpu_features);
+ VM_Version::get_missing_features_name(cached_cpu_features_buffer, runtime_cpu_features, ss);
+ log.print_cr("AOT Code Cache disabled: required cpu features are missing: %s", ss.as_string());
+ }
+ return false;
+ }
+ return true;
+}
+
+bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
// First checks affect all cached AOT code
#ifdef ASSERT
if ((_flags & debugVM) == 0) {
@@ -478,6 +525,9 @@ bool AOTCodeCache::Config::verify() const {
AOTStubCaching = false;
}
+ if (!verify_cpu_features(cache)) {
+ return false;
+ }
return true;
}
@@ -679,6 +729,17 @@ extern "C" {
}
}
+void AOTCodeCache::store_cpu_features(char*& buffer, uint buffer_size) {
+ uint* size_ptr = (uint *)buffer;
+ *size_ptr = buffer_size;
+ buffer += sizeof(uint);
+
+ VM_Version::store_cpu_features(buffer);
+ log_debug(aot, codecache, exit)("CPU features recorded in AOTCodeCache: %s", VM_Version::features_string());
+ buffer += buffer_size;
+ buffer = align_up(buffer, DATA_ALIGNMENT);
+}
+
bool AOTCodeCache::finish_write() {
if (!align_write()) {
return false;
@@ -698,23 +759,32 @@ bool AOTCodeCache::finish_write() {
uint store_count = _store_entries_cnt;
if (store_count > 0) {
- uint header_size = (uint)align_up(sizeof(AOTCodeCache::Header), DATA_ALIGNMENT);
+ uint header_size = (uint)align_up(sizeof(AOTCodeCache::Header), DATA_ALIGNMENT);
uint code_count = store_count;
uint search_count = code_count * 2;
uint search_size = search_count * sizeof(uint);
uint entries_size = (uint)align_up(code_count * sizeof(AOTCodeEntry), DATA_ALIGNMENT); // In bytes
// _write_position includes size of code and strings
uint code_alignment = code_count * DATA_ALIGNMENT; // We align_up code size when storing it.
- uint total_size = header_size + _write_position + code_alignment + search_size + entries_size;
+ uint cpu_features_size = VM_Version::cpu_features_size();
+ uint total_cpu_features_size = sizeof(uint) + cpu_features_size; // sizeof(uint) to store cpu_features_size
+ uint total_size = header_size + _write_position + code_alignment + search_size + entries_size +
+ align_up(total_cpu_features_size, DATA_ALIGNMENT);
assert(total_size < max_aot_code_size(), "AOT Code size (" UINT32_FORMAT " bytes) is greater than AOTCodeMaxSize(" UINT32_FORMAT " bytes).", total_size, max_aot_code_size());
- // Create ordered search table for entries [id, index];
- uint* search = NEW_C_HEAP_ARRAY(uint, search_count, mtCode);
// Allocate in AOT Cache buffer
char* buffer = (char *)AOTCacheAccess::allocate_aot_code_region(total_size + DATA_ALIGNMENT);
char* start = align_up(buffer, DATA_ALIGNMENT);
char* current = start + header_size; // Skip header
+ uint cpu_features_offset = current - start;
+ store_cpu_features(current, cpu_features_size);
+ assert(is_aligned(current, DATA_ALIGNMENT), "sanity check");
+ assert(current < start + total_size, "sanity check");
+
+ // Create ordered search table for entries [id, index];
+ uint* search = NEW_C_HEAP_ARRAY(uint, search_count, mtCode);
+
AOTCodeEntry* entries_address = _store_entries; // Pointer to latest entry
uint adapters_count = 0;
uint shared_blobs_count = 0;
@@ -790,7 +860,7 @@ bool AOTCodeCache::finish_write() {
header->init(size, (uint)strings_count, strings_offset,
entries_count, new_entries_offset,
adapters_count, shared_blobs_count,
- C1_blobs_count, C2_blobs_count);
+ C1_blobs_count, C2_blobs_count, cpu_features_offset);
log_info(aot, codecache, exit)("Wrote %d AOT code entries to AOT Code Cache", entries_count);
}
@@ -1383,6 +1453,12 @@ void AOTCodeAddressTable::init_extrs() {
#endif
#endif // ZERO
+ // addresses of fields in AOT runtime constants area
+ address* p = AOTRuntimeConstants::field_addresses_list();
+ while (*p != nullptr) {
+ SET_ADDRESS(_extrs, *p++);
+ }
+
_extrs_complete = true;
log_debug(aot, codecache, init)("External addresses recorded");
}
@@ -1665,6 +1741,11 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB
if (addr == (address)-1) { // Static call stub has jump to itself
return id;
}
+ // Check card_table_base address first since it can point to any address
+ BarrierSet* bs = BarrierSet::barrier_set();
+ bool is_const_card_table_base = !UseG1GC && !UseShenandoahGC && bs->is_a(BarrierSet::CardTableBarrierSet);
+ guarantee(!is_const_card_table_base || addr != ci_card_table_address_const(), "sanity");
+
// Seach for C string
id = id_for_C_string(addr);
if (id >= 0) {
@@ -1734,6 +1815,44 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB
return id;
}
+AOTRuntimeConstants AOTRuntimeConstants::_aot_runtime_constants;
+
+void AOTRuntimeConstants::initialize_from_runtime() {
+ BarrierSet* bs = BarrierSet::barrier_set();
+ address card_table_base = nullptr;
+ uint grain_shift = 0;
+#if INCLUDE_G1GC
+ if (bs->is_a(BarrierSet::G1BarrierSet)) {
+ grain_shift = G1HeapRegion::LogOfHRGrainBytes;
+ } else
+#endif
+#if INCLUDE_SHENANDOAHGC
+ if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) {
+ grain_shift = 0;
+ } else
+#endif
+ if (bs->is_a(BarrierSet::CardTableBarrierSet)) {
+ CardTable::CardValue* base = ci_card_table_address_const();
+ assert(base != nullptr, "unexpected byte_map_base");
+ card_table_base = base;
+ CardTableBarrierSet* ctbs = barrier_set_cast(bs);
+ grain_shift = ctbs->grain_shift();
+ }
+ _aot_runtime_constants._card_table_base = card_table_base;
+ _aot_runtime_constants._grain_shift = grain_shift;
+}
+
+address AOTRuntimeConstants::_field_addresses_list[] = {
+ ((address)&_aot_runtime_constants._card_table_base),
+ ((address)&_aot_runtime_constants._grain_shift),
+ nullptr
+};
+
+address AOTRuntimeConstants::card_table_base_address() {
+ assert(UseSerialGC || UseParallelGC, "Only these GCs have constant card table base");
+ return (address)&_aot_runtime_constants._card_table_base;
+}
+
// This is called after initialize() but before init2()
// and _cache is not set yet.
void AOTCodeCache::print_on(outputStream* st) {
diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp
index 778ad34e448..85f8b47920f 100644
--- a/src/hotspot/share/code/aotCodeCache.hpp
+++ b/src/hotspot/share/code/aotCodeCache.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
#ifndef SHARE_CODE_AOTCODECACHE_HPP
#define SHARE_CODE_AOTCODECACHE_HPP
+#include "gc/shared/gc_globals.hpp"
#include "runtime/stubInfo.hpp"
/*
@@ -185,10 +186,12 @@ protected:
restrictContendedPadding = 128
};
uint _flags;
+ uint _cpu_features_offset; // offset in the cache where cpu features are stored
public:
- void record();
- bool verify() const;
+ void record(uint cpu_features_offset);
+ bool verify_cpu_features(AOTCodeCache* cache) const;
+ bool verify(AOTCodeCache* cache) const;
};
class Header : public CHeapObj {
@@ -206,14 +209,15 @@ protected:
uint _shared_blobs_count;
uint _C1_blobs_count;
uint _C2_blobs_count;
- Config _config;
+ Config _config; // must be the last element as there is trailing data stored immediately after Config
public:
void init(uint cache_size,
uint strings_count, uint strings_offset,
uint entries_count, uint entries_offset,
uint adapters_count, uint shared_blobs_count,
- uint C1_blobs_count, uint C2_blobs_count) {
+ uint C1_blobs_count, uint C2_blobs_count,
+ uint cpu_features_offset) {
_version = AOT_CODE_VERSION;
_cache_size = cache_size;
_strings_count = strings_count;
@@ -224,7 +228,7 @@ protected:
_shared_blobs_count = shared_blobs_count;
_C1_blobs_count = C1_blobs_count;
_C2_blobs_count = C2_blobs_count;
- _config.record();
+ _config.record(cpu_features_offset);
}
@@ -239,8 +243,8 @@ protected:
uint C2_blobs_count() const { return _C2_blobs_count; }
bool verify(uint load_size) const;
- bool verify_config() const { // Called after Universe initialized
- return _config.verify();
+ bool verify_config(AOTCodeCache* cache) const { // Called after Universe initialized
+ return _config.verify(cache);
}
};
@@ -320,6 +324,8 @@ public:
AOTCodeEntry* find_entry(AOTCodeEntry::Kind kind, uint id);
+ void store_cpu_features(char*& buffer, uint buffer_size);
+
bool finish_write();
bool write_relocations(CodeBlob& code_blob);
@@ -361,7 +367,7 @@ private:
static bool open_cache(bool is_dumping, bool is_using);
bool verify_config() {
if (for_use()) {
- return _load_header->verify_config();
+ return _load_header->verify_config(this);
}
return true;
}
@@ -417,4 +423,36 @@ public:
#endif // PRODUCT
};
+// code cache internal runtime constants area used by AOT code
+class AOTRuntimeConstants {
+ friend class AOTCodeCache;
+ private:
+ address _card_table_base;
+ uint _grain_shift;
+ static address _field_addresses_list[];
+ static AOTRuntimeConstants _aot_runtime_constants;
+ // private constructor for unique singleton
+ AOTRuntimeConstants() { }
+ // private for use by friend class AOTCodeCache
+ static void initialize_from_runtime();
+ public:
+#if INCLUDE_CDS
+ static bool contains(address adr) {
+ address base = (address)&_aot_runtime_constants;
+ address hi = base + sizeof(AOTRuntimeConstants);
+ return (base <= adr && adr < hi);
+ }
+ static address card_table_base_address();
+ static address grain_shift_address() { return (address)&_aot_runtime_constants._grain_shift; }
+ static address* field_addresses_list() {
+ return _field_addresses_list;
+ }
+#else
+ static bool contains(address adr) { return false; }
+ static address card_table_base_address() { return nullptr; }
+ static address grain_shift_address() { return nullptr; }
+ static address* field_addresses_list() { return nullptr; }
+#endif
+};
+
#endif // SHARE_CODE_AOTCODECACHE_HPP
diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp
index a0a34ec23fa..fcc0b42a461 100644
--- a/src/hotspot/share/code/codeBlob.cpp
+++ b/src/hotspot/share/code/codeBlob.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -336,6 +336,7 @@ RuntimeBlob::RuntimeBlob(
void RuntimeBlob::free(RuntimeBlob* blob) {
assert(blob != nullptr, "caller must check for nullptr");
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock
blob->purge();
{
@@ -520,6 +521,8 @@ VtableBlob* VtableBlob::create(const char* name, int buffer_size) {
// eventually.
return nullptr;
}
+
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
blob = new (size) VtableBlob(name, size);
CodeCache_lock->unlock();
}
diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp
index 95a2fb908de..2a0256cc316 100644
--- a/src/hotspot/share/code/codeCache.cpp
+++ b/src/hotspot/share/code/codeCache.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -248,13 +248,61 @@ void CodeCache::initialize_heaps() {
set_size_of_unset_code_heap(&non_nmethod, cache_size, profiled.size + non_profiled.size, non_nmethod_min_size);
}
- size_t total = non_nmethod.size + profiled.size + non_profiled.size;
- if (total != cache_size && !cache_size_set) {
- log_info(codecache)("ReservedCodeCache size %zuK changed to total segments size NonNMethod "
- "%zuK NonProfiled %zuK Profiled %zuK = %zuK",
- cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, total/K);
- // Adjust ReservedCodeCacheSize as necessary because it was not set explicitly
- cache_size = total;
+ // Note: if large page support is enabled, min_size is at least the large
+ // page size. This ensures that the code cache is covered by large pages.
+ non_nmethod.size = align_up(non_nmethod.size, min_size);
+ profiled.size = align_up(profiled.size, min_size);
+ non_profiled.size = align_up(non_profiled.size, min_size);
+
+ size_t aligned_total = non_nmethod.size + profiled.size + non_profiled.size;
+ if (!cache_size_set) {
+ // If ReservedCodeCacheSize is explicitly set and exceeds CODE_CACHE_SIZE_LIMIT,
+ // it is rejected by flag validation elsewhere. Here we only handle the case
+ // where ReservedCodeCacheSize is not set explicitly, but the computed segmented
+ // sizes (after alignment) exceed the platform limit.
+ if (aligned_total > CODE_CACHE_SIZE_LIMIT) {
+ err_msg message("ReservedCodeCacheSize (%zuK), Max (%zuK)."
+ "Segments: NonNMethod (%zuK), NonProfiled (%zuK), Profiled (%zuK).",
+ aligned_total/K, CODE_CACHE_SIZE_LIMIT/K,
+ non_nmethod.size/K, non_profiled.size/K, profiled.size/K);
+ vm_exit_during_initialization("Code cache size exceeds platform limit", message);
+ }
+ if (aligned_total != cache_size) {
+ log_info(codecache)("ReservedCodeCache size %zuK changed to total segments size NonNMethod "
+ "%zuK NonProfiled %zuK Profiled %zuK = %zuK",
+ cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, aligned_total/K);
+ // Adjust ReservedCodeCacheSize as necessary because it was not set explicitly
+ cache_size = aligned_total;
+ }
+ } else {
+ check_min_size("reserved code cache", cache_size, min_cache_size);
+ // ReservedCodeCacheSize was set explicitly, so treat it as a hard cap.
+ // If alignment causes the total to exceed the cap, shrink unset heaps
+ // in min_size steps, never below their minimum sizes.
+ //
+ // A total smaller than cache_size typically happens when all segment sizes
+ // are explicitly set. In that case there is nothing to adjust, so we
+ // only validate the sizes.
+ if (aligned_total > cache_size) {
+ size_t delta = (aligned_total - cache_size) / min_size;
+ while (delta > 0) {
+ size_t start_delta = delta;
+ // Do not shrink the non-nmethod heap here: running out of non-nmethod space
+ // is more critical and may lead to unrecoverable VM errors.
+ if (non_profiled.enabled && !non_profiled.set && non_profiled.size > min_size) {
+ non_profiled.size -= min_size;
+ if (--delta == 0) break;
+ }
+ if (profiled.enabled && !profiled.set && profiled.size > min_size) {
+ profiled.size -= min_size;
+ delta--;
+ }
+ if (delta == start_delta) {
+ break;
+ }
+ }
+ aligned_total = non_nmethod.size + profiled.size + non_profiled.size;
+ }
}
log_debug(codecache)("Initializing code heaps ReservedCodeCache %zuK NonNMethod %zuK"
@@ -270,12 +318,9 @@ void CodeCache::initialize_heaps() {
if (non_profiled.enabled) { // non_profiled.enabled is always ON for segmented code heap, leave it checked for clarity
check_min_size("non-profiled code heap", non_profiled.size, min_size);
}
- if (cache_size_set) {
- check_min_size("reserved code cache", cache_size, min_cache_size);
- }
// ReservedCodeCacheSize was set explicitly, so report an error and abort if it doesn't match the segment sizes
- if (total != cache_size && cache_size_set) {
+ if (aligned_total != cache_size && cache_size_set) {
err_msg message("NonNMethodCodeHeapSize (%zuK)", non_nmethod.size/K);
if (profiled.enabled) {
message.append(" + ProfiledCodeHeapSize (%zuK)", profiled.size/K);
@@ -283,8 +328,8 @@ void CodeCache::initialize_heaps() {
if (non_profiled.enabled) {
message.append(" + NonProfiledCodeHeapSize (%zuK)", non_profiled.size/K);
}
- message.append(" = %zuK", total/K);
- message.append((total > cache_size) ? " is greater than " : " is less than ");
+ message.append(" = %zuK", aligned_total/K);
+ message.append((aligned_total > cache_size) ? " is greater than " : " is less than ");
message.append("ReservedCodeCacheSize (%zuK).", cache_size/K);
vm_exit_during_initialization("Invalid code heap sizes", message);
@@ -300,13 +345,6 @@ void CodeCache::initialize_heaps() {
}
}
- // Note: if large page support is enabled, min_size is at least the large
- // page size. This ensures that the code cache is covered by large pages.
- non_nmethod.size = align_up(non_nmethod.size, min_size);
- profiled.size = align_up(profiled.size, min_size);
- non_profiled.size = align_up(non_profiled.size, min_size);
- cache_size = non_nmethod.size + profiled.size + non_profiled.size;
-
FLAG_SET_ERGO(NonNMethodCodeHeapSize, non_nmethod.size);
FLAG_SET_ERGO(ProfiledCodeHeapSize, profiled.size);
FLAG_SET_ERGO(NonProfiledCodeHeapSize, non_profiled.size);
@@ -1101,7 +1139,7 @@ size_t CodeCache::freelists_length() {
void icache_init();
void CodeCache::initialize() {
- assert(CodeCacheSegmentSize >= (size_t)CodeEntryAlignment, "CodeCacheSegmentSize must be large enough to align entry points");
+ assert(CodeCacheSegmentSize >= CodeEntryAlignment, "CodeCacheSegmentSize must be large enough to align entry points");
#ifdef COMPILER2
assert(CodeCacheSegmentSize >= (size_t)OptoLoopAlignment, "CodeCacheSegmentSize must be large enough to align inner loops");
#endif
diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp
index edfca5c98ee..4c2f9157b99 100644
--- a/src/hotspot/share/code/nmethod.cpp
+++ b/src/hotspot/share/code/nmethod.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -938,7 +938,8 @@ address nmethod::continuation_for_implicit_exception(address pc, bool for_div0_c
stringStream ss;
ss.print_cr("implicit exception happened at " INTPTR_FORMAT, p2i(pc));
print_on(&ss);
- method()->print_codes_on(&ss);
+ // Buffering to a stringStream, disable internal buffering so it's not done twice.
+ method()->print_codes_on(&ss, 0, false);
print_code_on(&ss);
print_pcs_on(&ss);
tty->print("%s", ss.as_string()); // print all at once
@@ -2137,6 +2138,9 @@ void nmethod::make_deoptimized() {
ResourceMark rm;
RelocIterator iter(this, oops_reloc_begin());
+ // Assume there will be some calls to make deoptimized.
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
+
while (iter.next()) {
switch (iter.type()) {
@@ -2213,6 +2217,7 @@ void nmethod::verify_clean_inline_caches() {
}
void nmethod::mark_as_maybe_on_stack() {
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
AtomicAccess::store(&_gc_epoch, CodeCache::gc_epoch());
}
@@ -2305,6 +2310,8 @@ bool nmethod::make_not_entrant(InvalidationReason invalidation_reason) {
return false;
}
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
+
{
// Enter critical section. Does not block for safepoint.
ConditionalMutexLocker ml(NMethodState_lock, !NMethodState_lock->owned_by_self(), Mutex::_no_safepoint_check_flag);
@@ -2740,6 +2747,8 @@ bool nmethod::is_unloading() {
state_is_unloading = IsUnloadingBehaviour::is_unloading(this);
uint8_t new_state = IsUnloadingState::create(state_is_unloading, state_unloading_cycle);
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
+
// Note that if an nmethod has dead oops, everyone will agree that the
// nmethod is_unloading. However, the is_cold heuristics can yield
// different outcomes, so we guard the computed result with a CAS
diff --git a/src/hotspot/share/code/vtableStubs.cpp b/src/hotspot/share/code/vtableStubs.cpp
index b926888595d..35b226a8798 100644
--- a/src/hotspot/share/code/vtableStubs.cpp
+++ b/src/hotspot/share/code/vtableStubs.cpp
@@ -51,6 +51,9 @@ VMReg VtableStub::_receiver_location = VMRegImpl::Bad();
void* VtableStub::operator new(size_t size, int code_size) throw() {
assert_lock_strong(VtableStubs_lock);
assert(size == sizeof(VtableStub), "mismatched size");
+
+ MACOS_AARCH64_ONLY(os::thread_wx_enable_write());
+
// compute real VtableStub size (rounded to nearest word)
const int real_size = align_up(code_size + (int)sizeof(VtableStub), wordSize);
// malloc them in chunks to minimize header overhead
diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp
index aed1edc0db5..9bd6e893bcd 100644
--- a/src/hotspot/share/compiler/compilerDefinitions.cpp
+++ b/src/hotspot/share/compiler/compilerDefinitions.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -404,14 +404,12 @@ void CompilerConfig::set_compilation_policy_flags() {
#endif
if (CompilerConfig::is_tiered() && CompilerConfig::is_c2_enabled()) {
-#ifdef COMPILER2
- // Some inlining tuning
-#if defined(X86) || defined(AARCH64) || defined(RISCV64)
+#if defined(COMPILER2) && defined(_LP64)
+ // LP64 specific inlining tuning for C2
if (FLAG_IS_DEFAULT(InlineSmallCode)) {
FLAG_SET_DEFAULT(InlineSmallCode, 2500);
}
#endif
-#endif // COMPILER2
}
}
diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp
index 59ab69b2427..e32daa3d79e 100644
--- a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp
+++ b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2022, Red Hat, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -34,7 +34,6 @@
#include "memory/metaspaceUtils.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
-#include "runtime/atomicAccess.hpp"
#include "runtime/globals.hpp"
#include "utilities/ostream.hpp"
@@ -75,6 +74,10 @@ jint EpsilonHeap::initialize() {
void EpsilonHeap::initialize_serviceability() {
_pool = new EpsilonMemoryPool(this);
_memory_manager.add_pool(_pool);
+}
+
+void EpsilonHeap::post_initialize() {
+ CollectedHeap::post_initialize();
_monitoring_support->mark_ready();
}
diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp
index 8d7aa7960fd..ef75f7c1bb9 100644
--- a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp
+++ b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp
@@ -69,6 +69,7 @@ public:
jint initialize() override;
void initialize_serviceability() override;
+ void post_initialize() override;
GrowableArray memory_managers() override;
GrowableArray memory_pools() override;
diff --git a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp
index 61402301eb1..34d31702e80 100644
--- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp
+++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -351,7 +351,6 @@ Node* G1BarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) co
Node* G1BarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access, Node* expected_val,
Node* new_val, const Type* value_type) const {
- GraphKit* kit = access.kit();
if (!access.is_oop()) {
return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type);
}
@@ -361,7 +360,6 @@ Node* G1BarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access
Node* G1BarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAccess& access, Node* expected_val,
Node* new_val, const Type* value_type) const {
- GraphKit* kit = access.kit();
if (!access.is_oop()) {
return BarrierSetC2::atomic_cmpxchg_bool_at_resolved(access, expected_val, new_val, value_type);
}
@@ -370,7 +368,6 @@ Node* G1BarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAccess& acces
}
Node* G1BarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* new_val, const Type* value_type) const {
- GraphKit* kit = access.kit();
if (!access.is_oop()) {
return BarrierSetC2::atomic_xchg_at_resolved(access, new_val, value_type);
}
diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp
index ffb06a7d822..8bec6e7e86f 100644
--- a/src/hotspot/share/gc/g1/g1Arguments.cpp
+++ b/src/hotspot/share/gc/g1/g1Arguments.cpp
@@ -242,10 +242,6 @@ void G1Arguments::initialize() {
FLAG_SET_DEFAULT(GCPauseIntervalMillis, MaxGCPauseMillis + 1);
}
- if (FLAG_IS_DEFAULT(ParallelRefProcEnabled) && ParallelGCThreads > 1) {
- FLAG_SET_DEFAULT(ParallelRefProcEnabled, true);
- }
-
#ifdef COMPILER2
// Enable loop strip mining to offer better pause time guarantees
if (FLAG_IS_DEFAULT(UseCountedLoopSafepoints)) {
diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.cpp b/src/hotspot/share/gc/g1/g1BarrierSet.cpp
index 622651ce0d8..dee50500e07 100644
--- a/src/hotspot/share/gc/g1/g1BarrierSet.cpp
+++ b/src/hotspot/share/gc/g1/g1BarrierSet.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -64,13 +64,13 @@ G1BarrierSet::G1BarrierSet(G1CardTable* card_table,
{}
G1BarrierSet::~G1BarrierSet() {
- delete _refinement_table;
+ delete refinement_table();
}
void G1BarrierSet::swap_global_card_table() {
- G1CardTable* temp = static_cast(_card_table);
- _card_table = _refinement_table;
- _refinement_table = temp;
+ G1CardTable* temp = static_cast(card_table());
+ _card_table.store_relaxed(refinement_table());
+ _refinement_table.store_relaxed(temp);
}
void G1BarrierSet::update_card_table_base(Thread* thread) {
@@ -80,7 +80,7 @@ void G1BarrierSet::update_card_table_base(Thread* thread) {
assert(thread->is_Java_thread(), "may only update card table base of JavaThreads, not %s", thread->name());
}
#endif
- G1ThreadLocalData::set_byte_map_base(thread, _card_table->byte_map_base());
+ G1ThreadLocalData::set_byte_map_base(thread, card_table()->byte_map_base());
}
template void
@@ -135,10 +135,10 @@ void G1BarrierSet::write_region(MemRegion mr) {
// marks next time.
// If we write to the old card table (after the switching, then the refinement
// table) the oncoming handshake will do the memory synchronization.
- CardTable* card_table = AtomicAccess::load(&_card_table);
+ CardTable* local_card_table = card_table();
- volatile CardValue* byte = card_table->byte_for(mr.start());
- CardValue* last_byte = card_table->byte_for(mr.last());
+ volatile CardValue* byte = local_card_table->byte_for(mr.start());
+ CardValue* last_byte = local_card_table->byte_for(mr.last());
// Dirty cards only if necessary.
for (; byte <= last_byte; byte++) {
@@ -190,6 +190,6 @@ void G1BarrierSet::on_thread_detach(Thread* thread) {
}
void G1BarrierSet::print_on(outputStream* st) const {
- _card_table->print_on(st, "Card");
- _refinement_table->print_on(st, "Refinement");
+ card_table()->print_on(st, "Card");
+ refinement_table()->print_on(st, "Refinement");
}
diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp
index bf595973a32..c5c7058471c 100644
--- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp
+++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,10 +25,12 @@
#ifndef SHARE_GC_G1_G1BARRIERSET_HPP
#define SHARE_GC_G1_G1BARRIERSET_HPP
+#include "gc/g1/g1HeapRegion.hpp"
#include "gc/g1/g1SATBMarkQueueSet.hpp"
#include "gc/shared/bufferNode.hpp"
#include "gc/shared/cardTable.hpp"
#include "gc/shared/cardTableBarrierSet.hpp"
+#include "runtime/atomic.hpp"
class G1CardTable;
class Thread;
@@ -66,7 +68,7 @@ class G1BarrierSet: public CardTableBarrierSet {
BufferNode::Allocator _satb_mark_queue_buffer_allocator;
G1SATBMarkQueueSet _satb_mark_queue_set;
- G1CardTable* _refinement_table;
+ Atomic _refinement_table;
public:
G1BarrierSet(G1CardTable* card_table, G1CardTable* refinement_table);
@@ -76,7 +78,7 @@ class G1BarrierSet: public CardTableBarrierSet {
return barrier_set_cast(BarrierSet::barrier_set());
}
- G1CardTable* refinement_table() const { return _refinement_table; }
+ G1CardTable* refinement_table() const { return _refinement_table.load_relaxed(); }
// Swap the global card table references, without synchronization.
void swap_global_card_table();
@@ -115,6 +117,8 @@ class G1BarrierSet: public CardTableBarrierSet {
virtual void print_on(outputStream* st) const;
+ virtual uint grain_shift() { return G1HeapRegion::LogOfHRGrainBytes; }
+
// Callbacks for runtime accesses.
template
class AccessBarrier: public CardTableBarrierSet::AccessBarrier {
diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp
index 794e5db0634..54892c9191d 100644
--- a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -73,8 +73,8 @@ inline void G1BarrierSet::write_ref_field_post(T* field) {
// Make sure that the card table reference is read only once. Otherwise the compiler
// might reload that value in the two accesses below, that could cause writes to
// the wrong card table.
- CardTable* card_table = AtomicAccess::load(&_card_table);
- CardValue* byte = card_table->byte_for(field);
+ CardTable* local_card_table = card_table();
+ CardValue* byte = local_card_table->byte_for(field);
if (*byte == G1CardTable::clean_card_val()) {
*byte = G1CardTable::dirty_card_val();
}
diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp
index 4653f96980d..fd70796251d 100644
--- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp
+++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,10 +24,9 @@
#include "gc/g1/g1BlockOffsetTable.inline.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
-#include "gc/g1/g1HeapRegion.inline.hpp"
+#include "gc/g1/g1RegionToSpaceMapper.hpp"
+#include "gc/shared/memset_with_concurrent_readers.hpp"
#include "logging/log.hpp"
-#include "oops/oop.inline.hpp"
-#include "runtime/java.hpp"
#include "runtime/os.hpp"
size_t G1BlockOffsetTable::compute_size(size_t mem_region_words) {
@@ -40,26 +39,32 @@ G1BlockOffsetTable::G1BlockOffsetTable(MemRegion heap, G1RegionToSpaceMapper* st
MemRegion bot_reserved = storage->reserved();
- _offset_base = ((uint8_t*)bot_reserved.start() - (uintptr_t(_reserved.start()) >> CardTable::card_shift()));
+ _offset_base = ((Atomic*)bot_reserved.start() - (uintptr_t(_reserved.start()) >> CardTable::card_shift()));
log_trace(gc, bot)("G1BlockOffsetTable::G1BlockOffsetTable: ");
log_trace(gc, bot)(" rs.base(): " PTR_FORMAT " rs.size(): %zu rs end(): " PTR_FORMAT,
p2i(bot_reserved.start()), bot_reserved.byte_size(), p2i(bot_reserved.end()));
}
-void G1BlockOffsetTable::set_offset_array(uint8_t* addr, uint8_t offset) {
+void G1BlockOffsetTable::set_offset_array(Atomic* addr, uint8_t offset) {
check_address(addr, "Block offset table address out of range");
- AtomicAccess::store(addr, offset);
+ addr->store_relaxed(offset);
}
-void G1BlockOffsetTable::set_offset_array(uint8_t* addr, HeapWord* high, HeapWord* low) {
+static void check_offset(size_t offset, const char* msg) {
+ assert(offset < CardTable::card_size_in_words(),
+ "%s - offset: %zu, N_words: %u",
+ msg, offset, CardTable::card_size_in_words());
+}
+
+void G1BlockOffsetTable::set_offset_array(Atomic* addr, HeapWord* high, HeapWord* low) {
assert(high >= low, "addresses out of order");
size_t offset = pointer_delta(high, low);
check_offset(offset, "offset too large");
set_offset_array(addr, (uint8_t)offset);
}
-void G1BlockOffsetTable::set_offset_array(uint8_t* left, uint8_t* right, uint8_t offset) {
+void G1BlockOffsetTable::set_offset_array(Atomic* left, Atomic* right, uint8_t offset) {
check_address(right, "Right block offset table address out of range");
assert(left <= right, "indexes out of order");
size_t num_cards = right - left + 1;
@@ -67,9 +72,9 @@ void G1BlockOffsetTable::set_offset_array(uint8_t* left, uint8_t* right, uint8_t
}
#ifdef ASSERT
-void G1BlockOffsetTable::check_address(uint8_t* addr, const char* msg) const {
- uint8_t* start_addr = const_cast(_offset_base + (uintptr_t(_reserved.start()) >> CardTable::card_shift()));
- uint8_t* end_addr = const_cast(_offset_base + (uintptr_t(_reserved.end()) >> CardTable::card_shift()));
+void G1BlockOffsetTable::check_address(Atomic* addr, const char* msg) const {
+ Atomic* start_addr = const_cast*>(_offset_base + (uintptr_t(_reserved.start()) >> CardTable::card_shift()));
+ Atomic* end_addr = const_cast*>(_offset_base + (uintptr_t(_reserved.end()) >> CardTable::card_shift()));
assert(addr >= start_addr && addr <= end_addr,
"%s - offset address: " PTR_FORMAT ", start address: " PTR_FORMAT ", end address: " PTR_FORMAT,
msg, (p2i(addr)), (p2i(start_addr)), (p2i(end_addr)));
@@ -113,17 +118,17 @@ void G1BlockOffsetTable::check_address(uint8_t* addr, const char* msg) const {
// Move back N (e.g., 8) entries and repeat with the
// value of the new entry
//
-void G1BlockOffsetTable::set_remainder_to_point_to_start_incl(uint8_t* start_card, uint8_t* end_card) {
+void G1BlockOffsetTable::set_remainder_to_point_to_start_incl(Atomic* start_card, Atomic* end_card) {
assert(start_card <= end_card, "precondition");
assert(offset_array(start_card-1) < CardTable::card_size_in_words(),
"Offset card has an unexpected value");
- uint8_t* start_card_for_region = start_card;
+ Atomic* start_card_for_region = start_card;
uint8_t offset = UINT8_MAX;
for (uint i = 0; i < BOTConstants::N_powers; i++) {
// -1 so that the card with the actual offset is counted. Another -1
// so that the reach ends in this region and not at the start
// of the next.
- uint8_t* reach = start_card - 1 + (BOTConstants::power_to_cards_back(i+1) - 1);
+ Atomic* reach = start_card - 1 + (BOTConstants::power_to_cards_back(i+1) - 1);
offset = CardTable::card_size_in_words() + i;
if (reach >= end_card) {
set_offset_array(start_card_for_region, end_card, offset);
@@ -141,12 +146,12 @@ void G1BlockOffsetTable::set_remainder_to_point_to_start_incl(uint8_t* start_car
// The card-interval [start_card, end_card] is a closed interval; this
// is an expensive check -- use with care and only under protection of
// suitable flag.
-void G1BlockOffsetTable::check_all_cards(uint8_t* start_card, uint8_t* end_card) const {
+void G1BlockOffsetTable::check_all_cards(Atomic* start_card, Atomic* end_card) const {
if (end_card < start_card) {
return;
}
guarantee(offset_array(start_card) == CardTable::card_size_in_words(), "Wrong value in second card");
- for (uint8_t* c = start_card + 1; c <= end_card; c++ /* yeah! */) {
+ for (Atomic* c = start_card + 1; c <= end_card; c++ /* yeah! */) {
uint8_t entry = offset_array(c);
if ((unsigned)(c - start_card) > BOTConstants::power_to_cards_back(1)) {
guarantee(entry > CardTable::card_size_in_words(),
@@ -157,7 +162,7 @@ void G1BlockOffsetTable::check_all_cards(uint8_t* start_card, uint8_t* end_card)
(uint)entry, (uint)offset_array(c), CardTable::card_size_in_words());
}
size_t backskip = BOTConstants::entry_to_cards_back(entry);
- uint8_t* landing_card = c - backskip;
+ Atomic* landing_card = c - backskip;
guarantee(landing_card >= (start_card - 1), "Inv");
if (landing_card >= start_card) {
guarantee(offset_array(landing_card) <= entry,
@@ -188,7 +193,7 @@ void G1BlockOffsetTable::check_all_cards(uint8_t* start_card, uint8_t* end_card)
//
void G1BlockOffsetTable::update_for_block_work(HeapWord* blk_start, HeapWord* blk_end) {
HeapWord* const cur_card_boundary = align_up_by_card_size(blk_start);
- uint8_t* const offset_card = entry_for_addr(cur_card_boundary);
+ Atomic* const offset_card = entry_for_addr(cur_card_boundary);
assert(blk_start != nullptr && blk_end > blk_start,
"phantom block");
@@ -209,7 +214,7 @@ void G1BlockOffsetTable::update_for_block_work(HeapWord* blk_start, HeapWord* bl
// We need to now mark the subsequent cards that this block spans.
// Index of card on which the block ends.
- uint8_t* end_card = entry_for_addr(blk_end - 1);
+ Atomic* end_card = entry_for_addr(blk_end - 1);
// Are there more cards left to be updated?
if (offset_card + 1 <= end_card) {
@@ -224,7 +229,7 @@ void G1BlockOffsetTable::update_for_block_work(HeapWord* blk_start, HeapWord* bl
// The offset can be 0 if the block starts on a boundary. That
// is checked by an assertion above.
- uint8_t* previous_card = entry_for_addr(blk_start);
+ Atomic* previous_card = entry_for_addr(blk_start);
HeapWord* boundary = addr_for_entry(previous_card);
assert((offset_array(offset_card) == 0 && blk_start == boundary) ||
(offset_array(offset_card) > 0 && offset_array(offset_card) < CardTable::card_size_in_words()),
@@ -240,7 +245,7 @@ void G1BlockOffsetTable::update_for_block_work(HeapWord* blk_start, HeapWord* bl
}
#ifdef ASSERT
-void G1BlockOffsetTable::verify_offset(uint8_t* card_index, uint8_t upper_boundary) const {
+void G1BlockOffsetTable::verify_offset(Atomic* card_index, uint8_t upper_boundary) const {
assert(offset_array(card_index) <= upper_boundary,
"Offset %u should not be larger than upper boundary %u.",
(uint) offset_array(card_index),
@@ -250,19 +255,19 @@ void G1BlockOffsetTable::verify_offset(uint8_t* card_index, uint8_t upper_bounda
void G1BlockOffsetTable::verify_for_block(HeapWord* blk_start, HeapWord* blk_end) const {
assert(is_crossing_card_boundary(blk_start, blk_end), "precondition");
- uint8_t* start_card = entry_for_addr(align_up_by_card_size(blk_start));
- uint8_t* end_card = entry_for_addr(blk_end - 1);
+ Atomic* start_card = entry_for_addr(align_up_by_card_size(blk_start));
+ Atomic* end_card = entry_for_addr(blk_end - 1);
// Check cards in [start_card, end_card]
verify_offset(start_card, CardTable::card_size_in_words());
- for (uint8_t* current_card = start_card + 1; current_card <= end_card; ++current_card) {
+ for (Atomic* current_card = start_card + 1; current_card <= end_card; ++current_card) {
assert(offset_array(current_card) > 0,
"Offset %u is not larger than 0.",
(uint) offset_array(current_card));
verify_offset(current_card, (uint8_t) (CardTable::card_size_in_words() + BOTConstants::N_powers - 1));
- uint8_t* prev = current_card - 1;
- uint8_t* value = current_card;
+ Atomic* prev = current_card - 1;
+ Atomic* value = current_card;
if (offset_array(prev) != offset_array(value)) {
assert(offset_array(value) >= offset_array(prev), "monotonic");
size_t n_cards_back = BOTConstants::entry_to_cards_back(offset_array(value));
diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.hpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.hpp
index 3b97efc4f0f..21d447549a6 100644
--- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.hpp
+++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
#include "gc/shared/blockOffsetTable.hpp"
#include "gc/shared/cardTable.hpp"
#include "memory/memRegion.hpp"
+#include "runtime/atomic.hpp"
#include "utilities/globalDefinitions.hpp"
// This implementation of "G1BlockOffsetTable" divides the covered region
@@ -36,47 +37,40 @@
// for each such subregion indicates how far back one must go to find the
// start of the chunk that includes the first word of the subregion.
class G1BlockOffsetTable : public CHeapObj {
-private:
// The reserved region covered by the table.
MemRegion _reserved;
// Biased array-start of BOT array for fast BOT entry translation
- volatile uint8_t* _offset_base;
-
- void check_offset(size_t offset, const char* msg) const {
- assert(offset < CardTable::card_size_in_words(),
- "%s - offset: %zu, N_words: %u",
- msg, offset, CardTable::card_size_in_words());
- }
+ Atomic* _offset_base;
// Bounds checking accessors:
// For performance these have to devolve to array accesses in product builds.
- inline uint8_t offset_array(uint8_t* addr) const;
+ inline uint8_t offset_array(Atomic* addr) const;
- inline void set_offset_array(uint8_t* addr, uint8_t offset);
+ inline void set_offset_array(Atomic* addr, uint8_t offset);
- inline void set_offset_array(uint8_t* addr, HeapWord* high, HeapWord* low);
+ inline void set_offset_array(Atomic* addr, HeapWord* high, HeapWord* low);
- inline void set_offset_array(uint8_t* left, uint8_t* right, uint8_t offset);
+ inline void set_offset_array(Atomic* left, Atomic* right, uint8_t offset);
// Mapping from address to object start array entry
- inline uint8_t* entry_for_addr(const void* const p) const;
+ inline Atomic* entry_for_addr(const void* const p) const;
// Mapping from object start array entry to address of first word
- inline HeapWord* addr_for_entry(const uint8_t* const p) const;
+ inline HeapWord* addr_for_entry(const Atomic* const p) const;
- void check_address(uint8_t* addr, const char* msg) const NOT_DEBUG_RETURN;
+ void check_address(Atomic* addr, const char* msg) const NOT_DEBUG_RETURN;
// Sets the entries corresponding to the cards starting at "start" and ending
// at "end" to point back to the card before "start"; [start, end]
- void set_remainder_to_point_to_start_incl(uint8_t* start, uint8_t* end);
+ void set_remainder_to_point_to_start_incl(Atomic* start, Atomic* end);
// Update BOT entries corresponding to the mem range [blk_start, blk_end).
void update_for_block_work(HeapWord* blk_start, HeapWord* blk_end);
- void check_all_cards(uint8_t* left_card, uint8_t* right_card) const NOT_DEBUG_RETURN;
+ void check_all_cards(Atomic* left_card, Atomic* right_card) const NOT_DEBUG_RETURN;
- void verify_offset(uint8_t* card_index, uint8_t upper) const NOT_DEBUG_RETURN;
+ void verify_offset(Atomic* card_index, uint8_t upper) const NOT_DEBUG_RETURN;
void verify_for_block(HeapWord* blk_start, HeapWord* blk_end) const NOT_DEBUG_RETURN;
static HeapWord* align_up_by_card_size(HeapWord* const addr) {
@@ -84,7 +78,6 @@ private:
}
public:
-
// Return the number of slots needed for an offset array
// that covers mem_region_words words.
static size_t compute_size(size_t mem_region_words);
@@ -98,22 +91,14 @@ public:
// in the heap parameter.
G1BlockOffsetTable(MemRegion heap, G1RegionToSpaceMapper* storage);
- static bool is_crossing_card_boundary(HeapWord* const obj_start,
- HeapWord* const obj_end) {
- HeapWord* cur_card_boundary = align_up_by_card_size(obj_start);
- // strictly greater-than
- return obj_end > cur_card_boundary;
- }
+ inline static bool is_crossing_card_boundary(HeapWord* const obj_start,
+ HeapWord* const obj_end);
// Returns the address of the start of the block reaching into the card containing
// "addr".
inline HeapWord* block_start_reaching_into_card(const void* addr) const;
- void update_for_block(HeapWord* blk_start, HeapWord* blk_end) {
- if (is_crossing_card_boundary(blk_start, blk_end)) {
- update_for_block_work(blk_start, blk_end);
- }
- }
+ inline void update_for_block(HeapWord* blk_start, HeapWord* blk_end);
};
#endif // SHARE_GC_G1_G1BLOCKOFFSETTABLE_HPP
diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp
index 900e9516c1a..b707e310781 100644
--- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp
+++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,16 +27,12 @@
#include "gc/g1/g1BlockOffsetTable.hpp"
-#include "gc/g1/g1HeapRegion.hpp"
#include "gc/shared/cardTable.hpp"
-#include "gc/shared/memset_with_concurrent_readers.hpp"
-#include "oops/oop.inline.hpp"
-#include "runtime/atomicAccess.hpp"
inline HeapWord* G1BlockOffsetTable::block_start_reaching_into_card(const void* addr) const {
assert(_reserved.contains(addr), "invalid address");
- uint8_t* entry = entry_for_addr(addr);
+ Atomic* entry = entry_for_addr(addr);
uint8_t offset = offset_array(entry);
while (offset >= CardTable::card_size_in_words()) {
// The excess of the offset from N_words indicates a power of Base
@@ -50,19 +46,19 @@ inline HeapWord* G1BlockOffsetTable::block_start_reaching_into_card(const void*
return q - offset;
}
-uint8_t G1BlockOffsetTable::offset_array(uint8_t* addr) const {
+uint8_t G1BlockOffsetTable::offset_array(Atomic* addr) const {
check_address(addr, "Block offset table address out of range");
- return AtomicAccess::load(addr);
+ return addr->load_relaxed();
}
-inline uint8_t* G1BlockOffsetTable::entry_for_addr(const void* const p) const {
+inline Atomic* G1BlockOffsetTable::entry_for_addr(const void* const p) const {
assert(_reserved.contains(p),
"out of bounds access to block offset table");
- uint8_t* result = const_cast(&_offset_base[uintptr_t(p) >> CardTable::card_shift()]);
+ Atomic* result = const_cast*>(&_offset_base[uintptr_t(p) >> CardTable::card_shift()]);
return result;
}
-inline HeapWord* G1BlockOffsetTable::addr_for_entry(const uint8_t* const p) const {
+inline HeapWord* G1BlockOffsetTable::addr_for_entry(const Atomic* const p) const {
// _offset_base can be "negative", so can't use pointer_delta().
size_t delta = p - _offset_base;
HeapWord* result = (HeapWord*) (delta << CardTable::card_shift());
@@ -71,4 +67,17 @@ inline HeapWord* G1BlockOffsetTable::addr_for_entry(const uint8_t* const p) cons
return result;
}
+inline bool G1BlockOffsetTable::is_crossing_card_boundary(HeapWord* const obj_start,
+ HeapWord* const obj_end) {
+ HeapWord* cur_card_boundary = align_up_by_card_size(obj_start);
+ // strictly greater-than
+ return obj_end > cur_card_boundary;
+}
+
+inline void G1BlockOffsetTable::update_for_block(HeapWord* blk_start, HeapWord* blk_end) {
+ if (is_crossing_card_boundary(blk_start, blk_end)) {
+ update_for_block_work(blk_start, blk_end);
+ }
+}
+
#endif // SHARE_GC_G1_G1BLOCKOFFSETTABLE_INLINE_HPP
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
index 9424a804bd8..3f5d674c443 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
@@ -138,6 +138,26 @@ void G1RegionMappingChangedListener::on_commit(uint start_idx, size_t num_region
reset_from_card_cache(start_idx, num_regions);
}
+// Collects commonly used scoped objects that are related to initial setup.
+class G1GCMark : StackObj {
+ ResourceMark _rm;
+ IsSTWGCActiveMark _active_gc_mark;
+ GCIdMark _gc_id_mark;
+ SvcGCMarker _sgcm;
+ GCTraceCPUTime _tcpu;
+
+public:
+ G1GCMark(GCTracer* tracer, bool is_full_gc) :
+ _rm(),
+ _active_gc_mark(),
+ _gc_id_mark(),
+ _sgcm(is_full_gc ? SvcGCMarker::FULL : SvcGCMarker::MINOR),
+ _tcpu(tracer) {
+
+ assert_at_safepoint_on_vm_thread();
+ }
+};
+
void G1CollectedHeap::run_batch_task(G1BatchedTask* cl) {
uint num_workers = MAX2(1u, MIN2(cl->num_workers_estimate(), workers()->active_workers()));
cl->set_max_workers(num_workers);
@@ -914,12 +934,11 @@ void G1CollectedHeap::verify_after_full_collection() {
void G1CollectedHeap::do_full_collection(size_t allocation_word_size,
bool clear_all_soft_refs,
bool do_maximal_compaction) {
- assert_at_safepoint_on_vm_thread();
-
- G1FullGCMark gc_mark;
+ G1FullGCTracer tracer;
+ G1GCMark gc_mark(&tracer, true /* is_full_gc */);
GCTraceTime(Info, gc) tm("Pause Full", nullptr, gc_cause(), true);
- G1FullCollector collector(this, clear_all_soft_refs, do_maximal_compaction, gc_mark.tracer());
+ G1FullCollector collector(this, clear_all_soft_refs, do_maximal_compaction, &tracer);
collector.prepare_collection();
collector.collect();
collector.complete_collection(allocation_word_size);
@@ -1704,8 +1723,7 @@ void G1CollectedHeap::ref_processing_init() {
// * Reference discovery is MT (see below).
// * Reference discovery requires a barrier (see below).
// * Reference processing may or may not be MT
- // (depending on the value of ParallelRefProcEnabled
- // and ParallelGCThreads).
+ // (depending on the value of ParallelGCThreads).
// * A full GC disables reference discovery by the CM
// ref processor and abandons any entries on it's
// discovered lists.
@@ -2714,16 +2732,7 @@ void G1CollectedHeap::flush_region_pin_cache() {
}
void G1CollectedHeap::do_collection_pause_at_safepoint(size_t allocation_word_size) {
- assert_at_safepoint_on_vm_thread();
- assert(!is_stw_gc_active(), "collection is not reentrant");
-
- ResourceMark rm;
-
- IsSTWGCActiveMark active_gc_mark;
- GCIdMark gc_id_mark;
- SvcGCMarker sgcm(SvcGCMarker::MINOR);
-
- GCTraceCPUTime tcpu(_gc_tracer_stw);
+ G1GCMark gcm(_gc_tracer_stw, false /* is_full_gc */);
_bytes_used_during_gc = 0;
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
index 5f096c2b9d7..ec5649f4fe2 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
@@ -24,6 +24,7 @@
#include "classfile/classLoaderData.hpp"
#include "classfile/classLoaderDataGraph.hpp"
+#include "cppstdlib/new.hpp"
#include "gc/g1/g1BarrierSet.hpp"
#include "gc/g1/g1BatchedTask.hpp"
#include "gc/g1/g1CardSetMemory.hpp"
@@ -519,8 +520,8 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h,
_max_concurrent_workers(0),
_region_mark_stats(NEW_C_HEAP_ARRAY(G1RegionMarkStats, _g1h->max_num_regions(), mtGC)),
- _top_at_mark_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_num_regions(), mtGC)),
- _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_num_regions(), mtGC)),
+ _top_at_mark_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)),
+ _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)),
_needs_remembered_set_rebuild(false)
{
assert(G1CGC_lock != nullptr, "CGC_lock must be initialized");
@@ -564,6 +565,11 @@ void G1ConcurrentMark::fully_initialize() {
_tasks[i] = new G1CMTask(i, this, task_queue, _region_mark_stats);
}
+ uint max_num_regions = _g1h->max_num_regions();
+ ::new (_region_mark_stats) G1RegionMarkStats[max_num_regions]{};
+ ::new (_top_at_mark_starts) Atomic[max_num_regions]{};
+ ::new (_top_at_rebuild_starts) Atomic[max_num_regions]{};
+
reset_at_marking_complete();
}
@@ -576,7 +582,7 @@ PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const
}
void G1ConcurrentMark::reset() {
- _has_aborted = false;
+ _has_aborted.store_relaxed(false);
reset_marking_for_restart();
@@ -587,8 +593,8 @@ void G1ConcurrentMark::reset() {
}
uint max_num_regions = _g1h->max_num_regions();
+ ::new (_top_at_rebuild_starts) Atomic[max_num_regions]{};
for (uint i = 0; i < max_num_regions; i++) {
- _top_at_rebuild_starts[i] = nullptr;
_region_mark_stats[i].clear();
}
@@ -600,7 +606,7 @@ void G1ConcurrentMark::clear_statistics(G1HeapRegion* r) {
for (uint j = 0; j < _max_num_tasks; ++j) {
_tasks[j]->clear_mark_stats_cache(region_idx);
}
- _top_at_rebuild_starts[region_idx] = nullptr;
+ _top_at_rebuild_starts[region_idx].store_relaxed(nullptr);
_region_mark_stats[region_idx].clear();
}
@@ -636,11 +642,10 @@ void G1ConcurrentMark::reset_marking_for_restart() {
}
clear_has_overflown();
- _finger = _heap.start();
+ _finger.store_relaxed(_heap.start());
for (uint i = 0; i < _max_num_tasks; ++i) {
- G1CMTaskQueue* queue = _task_queues->queue(i);
- queue->set_empty();
+ _tasks[i]->reset_for_restart();
}
}
@@ -658,14 +663,14 @@ void G1ConcurrentMark::set_concurrency(uint active_tasks) {
void G1ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurrent) {
set_concurrency(active_tasks);
- _concurrent = concurrent;
+ _concurrent.store_relaxed(concurrent);
if (!concurrent) {
// At this point we should be in a STW phase, and completed marking.
assert_at_safepoint_on_vm_thread();
assert(out_of_regions(),
"only way to get here: _finger: " PTR_FORMAT ", _heap_end: " PTR_FORMAT,
- p2i(_finger), p2i(_heap.end()));
+ p2i(finger()), p2i(_heap.end()));
}
}
@@ -696,8 +701,8 @@ void G1ConcurrentMark::reset_at_marking_complete() {
}
G1ConcurrentMark::~G1ConcurrentMark() {
- FREE_C_HEAP_ARRAY(HeapWord*, _top_at_mark_starts);
- FREE_C_HEAP_ARRAY(HeapWord*, _top_at_rebuild_starts);
+ FREE_C_HEAP_ARRAY(Atomic, _top_at_mark_starts);
+ FREE_C_HEAP_ARRAY(Atomic, _top_at_rebuild_starts);
FREE_C_HEAP_ARRAY(G1RegionMarkStats, _region_mark_stats);
// The G1ConcurrentMark instance is never freed.
ShouldNotReachHere();
@@ -922,6 +927,8 @@ public:
bool do_heap_region(G1HeapRegion* r) override {
if (r->is_old_or_humongous() && !r->is_collection_set_candidate() && !r->in_collection_set()) {
_cm->update_top_at_mark_start(r);
+ } else {
+ _cm->reset_top_at_mark_start(r);
}
return false;
}
@@ -1164,7 +1171,7 @@ void G1ConcurrentMark::concurrent_cycle_start() {
}
uint G1ConcurrentMark::completed_mark_cycles() const {
- return AtomicAccess::load(&_completed_mark_cycles);
+ return _completed_mark_cycles.load_relaxed();
}
void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) {
@@ -1173,7 +1180,7 @@ void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) {
_g1h->trace_heap_after_gc(_gc_tracer_cm);
if (mark_cycle_completed) {
- AtomicAccess::inc(&_completed_mark_cycles, memory_order_relaxed);
+ _completed_mark_cycles.add_then_fetch(1u, memory_order_relaxed);
}
if (has_aborted()) {
@@ -1187,7 +1194,7 @@ void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) {
}
void G1ConcurrentMark::mark_from_roots() {
- _restart_for_overflow = false;
+ _restart_for_overflow.store_relaxed(false);
uint active_workers = calc_active_marking_workers();
@@ -1356,7 +1363,7 @@ void G1ConcurrentMark::remark() {
}
} else {
// We overflowed. Restart concurrent marking.
- _restart_for_overflow = true;
+ _restart_for_overflow.store_relaxed(true);
verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyLocation::RemarkOverflow);
@@ -1785,44 +1792,45 @@ void G1ConcurrentMark::clear_bitmap_for_region(G1HeapRegion* hr) {
}
G1HeapRegion* G1ConcurrentMark::claim_region(uint worker_id) {
- // "checkpoint" the finger
- HeapWord* finger = _finger;
+ // "Checkpoint" the finger.
+ HeapWord* local_finger = finger();
- while (finger < _heap.end()) {
- assert(_g1h->is_in_reserved(finger), "invariant");
+ while (local_finger < _heap.end()) {
+ assert(_g1h->is_in_reserved(local_finger), "invariant");
- G1HeapRegion* curr_region = _g1h->heap_region_containing_or_null(finger);
+ G1HeapRegion* curr_region = _g1h->heap_region_containing_or_null(local_finger);
// Make sure that the reads below do not float before loading curr_region.
OrderAccess::loadload();
// Above heap_region_containing may return null as we always scan claim
// until the end of the heap. In this case, just jump to the next region.
- HeapWord* end = curr_region != nullptr ? curr_region->end() : finger + G1HeapRegion::GrainWords;
+ HeapWord* end = curr_region != nullptr ? curr_region->end() : local_finger + G1HeapRegion::GrainWords;
// Is the gap between reading the finger and doing the CAS too long?
- HeapWord* res = AtomicAccess::cmpxchg(&_finger, finger, end);
- if (res == finger && curr_region != nullptr) {
- // we succeeded
+ HeapWord* res = _finger.compare_exchange(local_finger, end);
+ if (res == local_finger && curr_region != nullptr) {
+ // We succeeded.
HeapWord* bottom = curr_region->bottom();
HeapWord* limit = top_at_mark_start(curr_region);
log_trace(gc, marking)("Claim region %u bottom " PTR_FORMAT " tams " PTR_FORMAT, curr_region->hrm_index(), p2i(curr_region->bottom()), p2i(top_at_mark_start(curr_region)));
- // notice that _finger == end cannot be guaranteed here since,
- // someone else might have moved the finger even further
- assert(_finger >= end, "the finger should have moved forward");
+ // Notice that _finger == end cannot be guaranteed here since,
+ // someone else might have moved the finger even further.
+ assert(finger() >= end, "The finger should have moved forward");
if (limit > bottom) {
return curr_region;
} else {
assert(limit == bottom,
- "the region limit should be at bottom");
+ "The region limit should be at bottom");
// We return null and the caller should try calling
// claim_region() again.
return nullptr;
}
} else {
- assert(_finger > finger, "the finger should have moved forward");
- // read it again
- finger = _finger;
+ // Read the finger again.
+ HeapWord* next_finger = finger();
+ assert(next_finger > local_finger, "The finger should have moved forward " PTR_FORMAT " " PTR_FORMAT, p2i(local_finger), p2i(next_finger));
+ local_finger = next_finger;
}
}
@@ -1943,11 +1951,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() {
return false;
}
- // Empty mark stack
reset_marking_for_restart();
- for (uint i = 0; i < _max_num_tasks; ++i) {
- _tasks[i]->clear_region_fields();
- }
abort_marking_threads();
@@ -1962,7 +1966,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() {
void G1ConcurrentMark::abort_marking_threads() {
assert(!_root_regions.scan_in_progress(), "still doing root region scan");
- _has_aborted = true;
+ _has_aborted.store_relaxed(true);
_first_overflow_barrier_sync.abort();
_second_overflow_barrier_sync.abort();
}
@@ -2118,6 +2122,13 @@ void G1CMTask::reset(G1CMBitMap* mark_bitmap) {
_mark_stats_cache.reset();
}
+void G1CMTask::reset_for_restart() {
+ clear_region_fields();
+ _task_queue->set_empty();
+ TASKQUEUE_STATS_ONLY(_partial_array_splitter.stats()->reset());
+ TASKQUEUE_STATS_ONLY(_task_queue->stats.reset());
+}
+
void G1CMTask::register_partial_array_splitter() {
::new (&_partial_array_splitter) PartialArraySplitter(_cm->partial_array_state_manager(),
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp
index 3a4cbf1b83e..11da6dae5b3 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp
@@ -368,7 +368,7 @@ class G1ConcurrentMark : public CHeapObj {
// For grey objects
G1CMMarkStack _global_mark_stack; // Grey objects behind global finger
- HeapWord* volatile _finger; // The global finger, region aligned,
+ Atomic _finger; // The global finger, region aligned,
// always pointing to the end of the
// last claimed region
@@ -395,19 +395,19 @@ class G1ConcurrentMark : public CHeapObj {
WorkerThreadsBarrierSync _second_overflow_barrier_sync;
// Number of completed mark cycles.
- volatile uint _completed_mark_cycles;
+ Atomic _completed_mark_cycles;
// This is set by any task, when an overflow on the global data
// structures is detected
- volatile bool _has_overflown;
+ Atomic _has_overflown;
// True: marking is concurrent, false: we're in remark
- volatile bool _concurrent;
+ Atomic _concurrent;
// Set at the end of a Full GC so that marking aborts
- volatile bool _has_aborted;
+ Atomic _has_aborted;
// Used when remark aborts due to an overflow to indicate that
// another concurrent marking phase should start
- volatile bool _restart_for_overflow;
+ Atomic _restart_for_overflow;
ConcurrentGCTimer* _gc_timer_cm;
@@ -461,8 +461,8 @@ class G1ConcurrentMark : public CHeapObj {
void print_and_reset_taskqueue_stats();
- HeapWord* finger() { return _finger; }
- bool concurrent() { return _concurrent; }
+ HeapWord* finger() { return _finger.load_relaxed(); }
+ bool concurrent() { return _concurrent.load_relaxed(); }
uint active_tasks() { return _num_active_tasks; }
TaskTerminator* terminator() { return &_terminator; }
@@ -487,7 +487,7 @@ class G1ConcurrentMark : public CHeapObj {
// to satisfy an allocation without doing a GC. This is fine, because all
// objects in those regions will be considered live anyway because of
// SATB guarantees (i.e. their TAMS will be equal to bottom).
- bool out_of_regions() { return _finger >= _heap.end(); }
+ bool out_of_regions() { return finger() >= _heap.end(); }
// Returns the task with the given id
G1CMTask* task(uint id) {
@@ -499,10 +499,10 @@ class G1ConcurrentMark : public CHeapObj {
// Access / manipulation of the overflow flag which is set to
// indicate that the global stack has overflown
- bool has_overflown() { return _has_overflown; }
- void set_has_overflown() { _has_overflown = true; }
- void clear_has_overflown() { _has_overflown = false; }
- bool restart_for_overflow() { return _restart_for_overflow; }
+ bool has_overflown() { return _has_overflown.load_relaxed(); }
+ void set_has_overflown() { _has_overflown.store_relaxed(true); }
+ void clear_has_overflown() { _has_overflown.store_relaxed(false); }
+ bool restart_for_overflow() { return _restart_for_overflow.load_relaxed(); }
// Methods to enter the two overflow sync barriers
void enter_first_sync_barrier(uint worker_id);
@@ -516,12 +516,12 @@ class G1ConcurrentMark : public CHeapObj {
G1RegionMarkStats* _region_mark_stats;
// Top pointer for each region at the start of marking. Must be valid for all committed
// regions.
- HeapWord* volatile* _top_at_mark_starts;
+ Atomic* _top_at_mark_starts;
// Top pointer for each region at the start of the rebuild remembered set process
// for regions which remembered sets need to be rebuilt. A null for a given region
// means that this region does not be scanned during the rebuilding remembered
// set phase at all.
- HeapWord* volatile* _top_at_rebuild_starts;
+ Atomic* _top_at_rebuild_starts;
// True when Remark pause selected regions for rebuilding.
bool _needs_remembered_set_rebuild;
public:
@@ -679,7 +679,7 @@ public:
uint completed_mark_cycles() const;
- bool has_aborted() { return _has_aborted; }
+ bool has_aborted() { return _has_aborted.load_relaxed(); }
void print_summary_info();
@@ -844,8 +844,10 @@ private:
// Apply the closure to the given range of elements in the objArray.
inline void process_array_chunk(objArrayOop obj, size_t start, size_t end);
public:
- // Resets the task; should be called right at the beginning of a marking phase.
+ // Resets the task completely for a new marking; should be called right at the beginning of a marking phase.
void reset(G1CMBitMap* mark_bitmap);
+ // Minimal reset of the task, making it ready for continuing to mark.
+ void reset_for_restart();
// Register/unregister Partial Array Splitter Allocator with the PartialArrayStateManager.
// This allows us to discard memory arenas used for partial object array states at the end
// of a concurrent mark cycle.
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp
index 2f4824e4cae..21167d5cae9 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp
@@ -194,11 +194,11 @@ inline void G1CMTask::process_array_chunk(objArrayOop obj, size_t start, size_t
inline void G1ConcurrentMark::update_top_at_mark_start(G1HeapRegion* r) {
uint const region = r->hrm_index();
assert(region < _g1h->max_num_regions(), "Tried to access TAMS for region %u out of bounds", region);
- _top_at_mark_starts[region] = r->top();
+ _top_at_mark_starts[region].store_relaxed(r->top());
}
inline void G1ConcurrentMark::reset_top_at_mark_start(G1HeapRegion* r) {
- _top_at_mark_starts[r->hrm_index()] = r->bottom();
+ _top_at_mark_starts[r->hrm_index()].store_relaxed(r->bottom());
}
inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) const {
@@ -207,7 +207,7 @@ inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) cons
inline HeapWord* G1ConcurrentMark::top_at_mark_start(uint region) const {
assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region);
- return _top_at_mark_starts[region];
+ return _top_at_mark_starts[region].load_relaxed();
}
inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const {
@@ -217,7 +217,7 @@ inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const {
}
inline HeapWord* G1ConcurrentMark::top_at_rebuild_start(G1HeapRegion* r) const {
- return _top_at_rebuild_starts[r->hrm_index()];
+ return _top_at_rebuild_starts[r->hrm_index()].load_relaxed();
}
inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) {
@@ -225,10 +225,10 @@ inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) {
uint const region = r->hrm_index();
assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region);
- assert(_top_at_rebuild_starts[region] == nullptr,
+ assert(top_at_rebuild_start(r) == nullptr,
"TARS for region %u has already been set to " PTR_FORMAT " should be null",
- region, p2i(_top_at_rebuild_starts[region]));
- _top_at_rebuild_starts[region] = r->top();
+ region, p2i(top_at_rebuild_start(r)));
+ _top_at_rebuild_starts[region].store_relaxed(r->top());
}
inline void G1CMTask::update_liveness(oop const obj, const size_t obj_size) {
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp
index 5160d5ed036..7da0066e2f1 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp
@@ -23,21 +23,8 @@
*/
#include "gc/g1/g1ConcurrentRefineStats.inline.hpp"
-#include "runtime/atomicAccess.hpp"
#include "runtime/timer.hpp"
-G1ConcurrentRefineStats::G1ConcurrentRefineStats() :
- _sweep_duration(0),
- _yield_during_sweep_duration(0),
- _cards_scanned(0),
- _cards_clean(0),
- _cards_not_parsable(0),
- _cards_already_refer_to_cset(0),
- _cards_refer_to_cset(0),
- _cards_no_cross_region(0),
- _refine_duration(0)
-{}
-
void G1ConcurrentRefineStats::add_atomic(G1ConcurrentRefineStats* other) {
_sweep_duration.add_then_fetch(other->_sweep_duration.load_relaxed(), memory_order_relaxed);
_yield_during_sweep_duration.add_then_fetch(other->yield_during_sweep_duration(), memory_order_relaxed);
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp
index 5f57c56ba6c..a91ad0eb2e4 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp
@@ -47,8 +47,6 @@ class G1ConcurrentRefineStats : public CHeapObj {
Atomic _refine_duration; // Time spent during actual refinement.
public:
- G1ConcurrentRefineStats();
-
// Time spent performing sweeping the refinement table (includes actual refinement,
// but not yield time).
inline jlong sweep_duration() const;
diff --git a/src/hotspot/share/gc/g1/g1EvacStats.cpp b/src/hotspot/share/gc/g1/g1EvacStats.cpp
index 1d54b184e64..d93f63383c4 100644
--- a/src/hotspot/share/gc/g1/g1EvacStats.cpp
+++ b/src/hotspot/share/gc/g1/g1EvacStats.cpp
@@ -48,11 +48,11 @@ void G1EvacStats::log_plab_allocation() {
"used: %zuB, "
"undo waste: %zuB, ",
_description,
- _allocated * HeapWordSize,
- _wasted * HeapWordSize,
- _unused * HeapWordSize,
+ allocated() * HeapWordSize,
+ wasted() * HeapWordSize,
+ unused() * HeapWordSize,
used() * HeapWordSize,
- _undo_wasted * HeapWordSize);
+ undo_wasted() * HeapWordSize);
log_debug(gc, plab)("%s other allocation: "
"region end waste: %zuB, "
"regions filled: %u, "
@@ -157,13 +157,13 @@ void G1EvacStats::adjust_desired_plab_size() {
assert(is_object_aligned(max_size()) && min_size() <= max_size(),
"PLAB clipping computation may be incorrect");
- assert(_allocated != 0 || _unused == 0,
+ assert(allocated() != 0 || unused() == 0,
"Inconsistency in PLAB stats: "
"_allocated: %zu, "
"_wasted: %zu, "
"_unused: %zu, "
"_undo_wasted: %zu",
- _allocated, _wasted, _unused, _undo_wasted);
+ allocated(), wasted(), unused(), undo_wasted());
size_t plab_size = compute_desired_plab_size();
// Take historical weighted average
diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp
index 6c8cc7028cc..e8498250f85 100644
--- a/src/hotspot/share/gc/g1/g1FullCollector.cpp
+++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp
@@ -23,6 +23,7 @@
*/
#include "classfile/classLoaderDataGraph.hpp"
+#include "cppstdlib/new.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/g1FullCollector.inline.hpp"
#include "gc/g1/g1FullGCAdjustTask.hpp"
@@ -110,7 +111,7 @@ uint G1FullCollector::calc_active_workers() {
G1FullCollector::G1FullCollector(G1CollectedHeap* heap,
bool clear_soft_refs,
bool do_maximal_compaction,
- G1FullGCTracer* tracer) :
+ GCTracer* tracer) :
_heap(heap),
_scope(heap->monitoring_support(), clear_soft_refs, do_maximal_compaction, tracer),
_num_workers(calc_active_workers()),
@@ -134,12 +135,13 @@ G1FullCollector::G1FullCollector(G1CollectedHeap* heap,
_compaction_points = NEW_C_HEAP_ARRAY(G1FullGCCompactionPoint*, _num_workers, mtGC);
_live_stats = NEW_C_HEAP_ARRAY(G1RegionMarkStats, _heap->max_num_regions(), mtGC);
- _compaction_tops = NEW_C_HEAP_ARRAY(Atomic, _heap->max_num_regions(), mtGC);
for (uint j = 0; j < heap->max_num_regions(); j++) {
_live_stats[j].clear();
- ::new (&_compaction_tops[j]) Atomic{};
}
+ _compaction_tops = NEW_C_HEAP_ARRAY(Atomic, _heap->max_num_regions(), mtGC);
+ ::new (_compaction_tops) Atomic[heap->max_num_regions()]{};
+
_partial_array_state_manager = new PartialArrayStateManager(_num_workers);
for (uint i = 0; i < _num_workers; i++) {
@@ -276,6 +278,21 @@ void G1FullCollector::before_marking_update_attribute_table(G1HeapRegion* hr) {
class G1FullGCRefProcProxyTask : public RefProcProxyTask {
G1FullCollector& _collector;
+ // G1 Full GC specific closure for handling discovered fields. Do NOT need any
+ // barriers as Full GC discards all this information anyway.
+ class G1FullGCDiscoveredFieldClosure : public EnqueueDiscoveredFieldClosure {
+ G1CollectedHeap* _g1h;
+
+ public:
+ G1FullGCDiscoveredFieldClosure() : _g1h(G1CollectedHeap::heap()) { }
+
+ void enqueue(HeapWord* discovered_field_addr, oop value) override {
+ assert(_g1h->is_in(discovered_field_addr), PTR_FORMAT " is not in heap ", p2i(discovered_field_addr));
+ // Store the value and done.
+ RawAccess<>::oop_store(discovered_field_addr, value);
+ }
+ };
+
public:
G1FullGCRefProcProxyTask(G1FullCollector &collector, uint max_workers)
: RefProcProxyTask("G1FullGCRefProcProxyTask", max_workers),
@@ -286,7 +303,7 @@ public:
G1IsAliveClosure is_alive(&_collector);
uint index = (_tm == RefProcThreadModel::Single) ? 0 : worker_id;
G1FullKeepAliveClosure keep_alive(_collector.marker(index));
- BarrierEnqueueDiscoveredFieldClosure enqueue;
+ G1FullGCDiscoveredFieldClosure enqueue;
G1MarkStackClosure* complete_marking = _collector.marker(index)->stack_closure();
_rp_task->rp_work(worker_id, &is_alive, &keep_alive, &enqueue, complete_marking);
}
diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp
index 7e455b07013..605556a2ba6 100644
--- a/src/hotspot/share/gc/g1/g1FullCollector.hpp
+++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp
@@ -58,18 +58,6 @@ public:
}
};
-// Full GC Mark that holds GC id and CPU time trace. Needs to be separate
-// from the G1FullCollector and G1FullGCScope to allow the Full GC logging
-// to have the same structure as the Young GC logging.
-class G1FullGCMark : StackObj {
- GCIdMark _gc_id;
- G1FullGCTracer _tracer;
- GCTraceCPUTime _cpu_time;
-public:
- G1FullGCMark() : _gc_id(), _tracer(), _cpu_time(&_tracer) { }
- G1FullGCTracer* tracer() { return &_tracer; }
-};
-
// The G1FullCollector holds data associated with the current Full GC.
class G1FullCollector : StackObj {
G1CollectedHeap* _heap;
@@ -102,7 +90,7 @@ public:
G1FullCollector(G1CollectedHeap* heap,
bool clear_soft_refs,
bool do_maximal_compaction,
- G1FullGCTracer* tracer);
+ GCTracer* tracer);
~G1FullCollector();
void prepare_collection();
diff --git a/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp
index 0c201f0e43f..ef6344d349d 100644
--- a/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp
+++ b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp
@@ -30,7 +30,6 @@
#include "gc/g1/g1FullGCHeapRegionAttr.hpp"
#include "gc/g1/g1HeapRegion.inline.hpp"
#include "oops/oopsHierarchy.hpp"
-#include "runtime/atomicAccess.hpp"
bool G1FullCollector::is_compacting(oop obj) const {
return _region_attr_table.is_compacting(cast_from_oop(obj));
diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.cpp b/src/hotspot/share/gc/g1/g1FullGCScope.cpp
index 8b92d51a8a3..cb4ebe423ff 100644
--- a/src/hotspot/share/gc/g1/g1FullGCScope.cpp
+++ b/src/hotspot/share/gc/g1/g1FullGCScope.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,18 +38,14 @@ G1FullGCJFRTracerMark::~G1FullGCJFRTracerMark() {
G1FullGCScope::G1FullGCScope(G1MonitoringSupport* monitoring_support,
bool clear_soft,
bool do_maximal_compaction,
- G1FullGCTracer* tracer) :
- _rm(),
+ GCTracer* tracer) :
_should_clear_soft_refs(clear_soft),
_do_maximal_compaction(do_maximal_compaction),
- _g1h(G1CollectedHeap::heap()),
- _svc_marker(SvcGCMarker::FULL),
_timer(),
_tracer(tracer),
- _active(),
_tracer_mark(&_timer, _tracer),
_monitoring_scope(monitoring_support),
- _heap_printer(_g1h),
+ _heap_printer(G1CollectedHeap::heap()),
_region_compaction_threshold(do_maximal_compaction ?
G1HeapRegion::GrainWords :
(1 - MarkSweepDeadRatio / 100.0) * G1HeapRegion::GrainWords) { }
@@ -58,7 +54,7 @@ STWGCTimer* G1FullGCScope::timer() {
return &_timer;
}
-G1FullGCTracer* G1FullGCScope::tracer() {
+GCTracer* G1FullGCScope::tracer() {
return _tracer;
}
diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.hpp b/src/hotspot/share/gc/g1/g1FullGCScope.hpp
index 7a0d0a5395e..fc9d5a71f92 100644
--- a/src/hotspot/share/gc/g1/g1FullGCScope.hpp
+++ b/src/hotspot/share/gc/g1/g1FullGCScope.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -46,14 +46,10 @@ public:
// Class used to group scoped objects used in the Full GC together.
class G1FullGCScope : public StackObj {
- ResourceMark _rm;
bool _should_clear_soft_refs;
bool _do_maximal_compaction;
- G1CollectedHeap* _g1h;
- SvcGCMarker _svc_marker;
STWGCTimer _timer;
- G1FullGCTracer* _tracer;
- IsSTWGCActiveMark _active;
+ GCTracer* _tracer;
G1FullGCJFRTracerMark _tracer_mark;
G1FullGCMonitoringScope _monitoring_scope;
G1HeapPrinterMark _heap_printer;
@@ -63,13 +59,13 @@ public:
G1FullGCScope(G1MonitoringSupport* monitoring_support,
bool clear_soft,
bool do_maximal_compaction,
- G1FullGCTracer* tracer);
+ GCTracer* tracer);
bool should_clear_soft_refs() const { return _should_clear_soft_refs; }
bool do_maximal_compaction() { return _do_maximal_compaction; }
STWGCTimer* timer();
- G1FullGCTracer* tracer();
+ GCTracer* tracer();
size_t region_compaction_threshold() const;
};
diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp
index 44897c8a277..fdd3b919590 100644
--- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp
+++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp
@@ -34,7 +34,6 @@
#include "jfr/jfrEvents.hpp"
#include "logging/logStream.hpp"
#include "memory/allocation.hpp"
-#include "runtime/atomicAccess.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/orderAccess.hpp"
#include "utilities/bitMap.inline.hpp"
diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp
index 46d12df575c..a0013d27172 100644
--- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp
+++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp
@@ -24,6 +24,7 @@
#include "compiler/oopMap.hpp"
+#include "cppstdlib/new.hpp"
#include "gc/g1/g1CardSetMemory.hpp"
#include "gc/g1/g1CardTableEntryClosure.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
@@ -497,10 +498,6 @@ class G1PostEvacuateCollectionSetCleanupTask2::ProcessEvacuationFailedRegionsTas
G1CollectedHeap* g1h = G1CollectedHeap::heap();
G1ConcurrentMark* cm = g1h->concurrent_mark();
- HeapWord* top_at_mark_start = cm->top_at_mark_start(r);
- assert(top_at_mark_start == r->bottom(), "TAMS must not have been set for region %u", r->hrm_index());
- assert(cm->live_bytes(r->hrm_index()) == 0, "Marking live bytes must not be set for region %u", r->hrm_index());
-
// Concurrent mark does not mark through regions that we retain (they are root
// regions wrt to marking), so we must clear their mark data (tams, bitmap, ...)
// set eagerly or during evacuation failure.
@@ -818,9 +815,7 @@ public:
void set_max_workers(uint max_workers) override {
_active_workers = max_workers;
_worker_stats = NEW_C_HEAP_ARRAY(FreeCSetStats, max_workers, mtGC);
- for (uint worker = 0; worker < _active_workers; worker++) {
- ::new (&_worker_stats[worker]) FreeCSetStats();
- }
+ ::new (_worker_stats) FreeCSetStats[_active_workers]{};
_claimer.set_n_workers(_active_workers);
}
@@ -854,7 +849,7 @@ public:
void do_thread(Thread* thread) {
if (UseTLAB && ResizeTLAB) {
- static_cast(thread)->tlab().resize();
+ thread->tlab().resize();
}
G1BarrierSet::g1_barrier_set()->update_card_table_base(thread);
diff --git a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp
index c0870b7a726..936457659b6 100644
--- a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp
+++ b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
*
*/
+#include "cppstdlib/new.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1RegionPinCache.inline.hpp"
#include "gc/g1/g1ThreadLocalData.hpp"
@@ -89,10 +90,7 @@ public:
void set_max_workers(uint max_workers) override {
_num_workers = max_workers;
_local_tlab_stats = NEW_C_HEAP_ARRAY(ThreadLocalAllocStats, _num_workers, mtGC);
-
- for (uint i = 0; i < _num_workers; i++) {
- ::new (&_local_tlab_stats[i]) ThreadLocalAllocStats();
- }
+ ::new (_local_tlab_stats) ThreadLocalAllocStats[_num_workers]{};
}
ThreadLocalAllocStats tlab_stats() const {
diff --git a/src/hotspot/share/gc/parallel/parMarkBitMap.cpp b/src/hotspot/share/gc/parallel/parMarkBitMap.cpp
index c613c8615f0..bbd6669df8d 100644
--- a/src/hotspot/share/gc/parallel/parMarkBitMap.cpp
+++ b/src/hotspot/share/gc/parallel/parMarkBitMap.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,6 @@
#include "memory/memoryReserver.hpp"
#include "nmt/memTracker.hpp"
#include "oops/oop.inline.hpp"
-#include "runtime/atomicAccess.hpp"
#include "runtime/os.hpp"
#include "utilities/align.hpp"
#include "utilities/bitMap.inline.hpp"
diff --git a/src/hotspot/share/gc/parallel/parallelArguments.cpp b/src/hotspot/share/gc/parallel/parallelArguments.cpp
index be9673224f5..a856078edab 100644
--- a/src/hotspot/share/gc/parallel/parallelArguments.cpp
+++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp
@@ -128,10 +128,6 @@ void ParallelArguments::initialize() {
}
}
- if (FLAG_IS_DEFAULT(ParallelRefProcEnabled) && ParallelGCThreads > 1) {
- FLAG_SET_DEFAULT(ParallelRefProcEnabled, true);
- }
-
FullGCForwarding::initialize_flags(heap_reserved_size_bytes());
}
diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp
index 0d8a3166f79..f49419595e1 100644
--- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp
+++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -48,6 +48,7 @@
#include "memory/reservedSpace.hpp"
#include "memory/universe.hpp"
#include "oops/oop.inline.hpp"
+#include "runtime/atomic.hpp"
#include "runtime/cpuTimeCounters.hpp"
#include "runtime/globals_extension.hpp"
#include "runtime/handles.inline.hpp"
@@ -438,7 +439,7 @@ bool ParallelScavengeHeap::check_gc_overhead_limit() {
log_debug(gc)("GC Overhead Limit: GC Time %f Free Space Young %f Old %f Counter %zu",
(100 - _size_policy->mutator_time_percent()),
percent_of(_young_gen->free_in_bytes(), _young_gen->capacity_in_bytes()),
- percent_of(_old_gen->free_in_bytes(), _young_gen->capacity_in_bytes()),
+ percent_of(_old_gen->free_in_bytes(), _old_gen->capacity_in_bytes()),
_gc_overhead_counter);
if (little_mutator_time && little_free_space) {
@@ -594,7 +595,7 @@ void ParallelScavengeHeap::object_iterate(ObjectClosure* cl) {
// these spaces.
// The old space is divided into fixed-size blocks.
class HeapBlockClaimer : public StackObj {
- size_t _claimed_index;
+ Atomic _claimed_index;
public:
static const size_t InvalidIndex = SIZE_MAX;
@@ -606,7 +607,7 @@ public:
// Claim the block and get the block index.
size_t claim_and_get_block() {
size_t block_index;
- block_index = AtomicAccess::fetch_then_add(&_claimed_index, 1u);
+ block_index = _claimed_index.fetch_then_add(1u);
PSOldGen* old_gen = ParallelScavengeHeap::heap()->old_gen();
size_t num_claims = old_gen->num_iterable_blocks() + NumNonOldGenClaims;
diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.inline.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.inline.hpp
index f257f4c9177..0f00b3ec2f9 100644
--- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.inline.hpp
+++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.inline.hpp
@@ -30,8 +30,11 @@
#include "gc/parallel/psScavenge.hpp"
inline bool ParallelScavengeHeap::should_alloc_in_eden(const size_t size) const {
- const size_t eden_size = young_gen()->eden_space()->capacity_in_words();
- return size < eden_size / 2;
+ const size_t max_young_gen_bytes = young_gen()->max_gen_size();
+ const size_t survivor_size_bytes = young_gen()->from_space()->capacity_in_bytes();
+ const size_t max_eden_size_bytes = max_young_gen_bytes - survivor_size_bytes * 2;
+ const size_t max_eden_size_words = max_eden_size_bytes / HeapWordSize;
+ return size < max_eden_size_words / 2;
}
inline bool ParallelScavengeHeap::is_in_young(const void* p) const {
diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp
index 7515031f01f..ff7a0aee088 100644
--- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp
+++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
#include "gc/shared/gcPolicyCounters.hpp"
#include "gc/shared/gcUtil.hpp"
#include "logging/log.hpp"
+#include "runtime/atomicAccess.hpp"
#include "runtime/timer.hpp"
#include "utilities/align.hpp"
diff --git a/src/hotspot/share/gc/parallel/psCardTable.cpp b/src/hotspot/share/gc/parallel/psCardTable.cpp
index fcd0dd67a45..6429766309a 100644
--- a/src/hotspot/share/gc/parallel/psCardTable.cpp
+++ b/src/hotspot/share/gc/parallel/psCardTable.cpp
@@ -108,7 +108,7 @@ void PSCardTable::scan_obj_with_limit(PSPromotionManager* pm,
}
void PSCardTable::pre_scavenge(uint active_workers) {
- _preprocessing_active_workers = active_workers;
+ _preprocessing_active_workers.store_relaxed(active_workers);
}
// The "shadow" table is a copy of the card table entries of the current stripe.
@@ -382,9 +382,9 @@ void PSCardTable::scavenge_contents_parallel(ObjectStartArray* start_array,
preprocess_card_table_parallel(object_start, old_gen_bottom, old_gen_top, stripe_index, n_stripes);
// Sync with other workers.
- AtomicAccess::dec(&_preprocessing_active_workers);
+ _preprocessing_active_workers.sub_then_fetch(1);
SpinYield spin_yield;
- while (AtomicAccess::load_acquire(&_preprocessing_active_workers) > 0) {
+ while (_preprocessing_active_workers.load_acquire() > 0) {
spin_yield.wait();
}
diff --git a/src/hotspot/share/gc/parallel/psCardTable.hpp b/src/hotspot/share/gc/parallel/psCardTable.hpp
index 70c32d23b7f..033933bcbf1 100644
--- a/src/hotspot/share/gc/parallel/psCardTable.hpp
+++ b/src/hotspot/share/gc/parallel/psCardTable.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
#include "gc/shared/cardTable.hpp"
#include "oops/oop.hpp"
+#include "runtime/atomic.hpp"
class MutableSpace;
class ObjectStartArray;
@@ -37,7 +38,7 @@ class PSCardTable: public CardTable {
static constexpr size_t num_cards_in_stripe = 128;
static_assert(num_cards_in_stripe >= 1, "progress");
- volatile int _preprocessing_active_workers;
+ Atomic _preprocessing_active_workers;
bool is_dirty(CardValue* card) {
return !is_clean(card);
diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp
index 974cd6aca59..6a9f8bfeedc 100644
--- a/src/hotspot/share/gc/parallel/psOldGen.cpp
+++ b/src/hotspot/share/gc/parallel/psOldGen.cpp
@@ -30,6 +30,7 @@
#include "gc/parallel/psOldGen.hpp"
#include "gc/shared/cardTableBarrierSet.hpp"
#include "gc/shared/gcLocker.hpp"
+#include "gc/shared/hSpaceCounters.hpp"
#include "gc/shared/spaceDecorator.hpp"
#include "logging/log.hpp"
#include "oops/oop.inline.hpp"
@@ -113,9 +114,11 @@ void PSOldGen::initialize_performance_counters() {
const char* perf_data_name = "old";
_gen_counters = new GenerationCounters(perf_data_name, 1, 1, min_gen_size(),
max_gen_size(), virtual_space()->committed_size());
- _space_counters = new SpaceCounters(perf_data_name, 0,
- virtual_space()->reserved_size(),
- _object_space, _gen_counters);
+ _space_counters = new HSpaceCounters(_gen_counters->name_space(),
+ perf_data_name,
+ 0,
+ virtual_space()->reserved_size(),
+ _object_space->capacity_in_bytes());
}
HeapWord* PSOldGen::expand_and_allocate(size_t word_size) {
@@ -266,7 +269,7 @@ bool PSOldGen::expand_by(size_t bytes) {
}
post_resize();
if (UsePerfData) {
- _space_counters->update_capacity();
+ _space_counters->update_capacity(_object_space->capacity_in_bytes());
_gen_counters->update_capacity(_virtual_space->committed_size());
}
}
@@ -394,7 +397,7 @@ void PSOldGen::print_on(outputStream* st) const {
void PSOldGen::update_counters() {
if (UsePerfData) {
- _space_counters->update_all();
+ _space_counters->update_all(_object_space->capacity_in_bytes(), _object_space->used_in_bytes());
_gen_counters->update_capacity(_virtual_space->committed_size());
}
}
diff --git a/src/hotspot/share/gc/parallel/psOldGen.hpp b/src/hotspot/share/gc/parallel/psOldGen.hpp
index 05359b12836..7e3975036d4 100644
--- a/src/hotspot/share/gc/parallel/psOldGen.hpp
+++ b/src/hotspot/share/gc/parallel/psOldGen.hpp
@@ -28,7 +28,8 @@
#include "gc/parallel/mutableSpace.hpp"
#include "gc/parallel/objectStartArray.hpp"
#include "gc/parallel/psVirtualspace.hpp"
-#include "gc/parallel/spaceCounters.hpp"
+#include "gc/shared/generationCounters.hpp"
+#include "gc/shared/hSpaceCounters.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
@@ -43,7 +44,7 @@ class PSOldGen : public CHeapObj {
// Performance Counters
GenerationCounters* _gen_counters;
- SpaceCounters* _space_counters;
+ HSpaceCounters* _space_counters;
// Sizing information, in bytes, set in constructor
const size_t _min_gen_size;
diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
index bab72296d4c..d03bc3cda45 100644
--- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
@@ -30,6 +30,7 @@
#include "code/codeCache.hpp"
#include "code/nmethod.hpp"
#include "compiler/oopMap.hpp"
+#include "cppstdlib/new.hpp"
#include "gc/parallel/objectStartArray.inline.hpp"
#include "gc/parallel/parallelArguments.hpp"
#include "gc/parallel/parallelScavengeHeap.inline.hpp"
@@ -83,7 +84,6 @@
#include "oops/methodData.hpp"
#include "oops/objArrayKlass.inline.hpp"
#include "oops/oop.inline.hpp"
-#include "runtime/atomicAccess.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/safepoint.hpp"
@@ -135,8 +135,8 @@ bool ParallelCompactData::RegionData::is_clear() {
(_source_region == 0) &&
(_partial_obj_addr == nullptr) &&
(_partial_obj_size == 0) &&
- (_dc_and_los == 0) &&
- (_shadow_state == 0);
+ (dc_and_los() == 0) &&
+ (shadow_state() == 0);
}
#ifdef ASSERT
@@ -145,8 +145,8 @@ void ParallelCompactData::RegionData::verify_clear() {
assert(_source_region == 0, "inv");
assert(_partial_obj_addr == nullptr, "inv");
assert(_partial_obj_size == 0, "inv");
- assert(_dc_and_los == 0, "inv");
- assert(_shadow_state == 0, "inv");
+ assert(dc_and_los() == 0, "inv");
+ assert(shadow_state() == 0, "inv");
}
#endif
@@ -296,7 +296,9 @@ void ParallelCompactData::clear_range(size_t beg_region, size_t end_region) {
assert(end_region <= _region_count, "end_region out of range");
const size_t region_cnt = end_region - beg_region;
- memset(_region_data + beg_region, 0, region_cnt * sizeof(RegionData));
+ for (size_t i = beg_region; i < end_region; i++) {
+ ::new (&_region_data[i]) RegionData{};
+ }
}
// The total live words on src_region would overflow the target space, so find
@@ -1294,7 +1296,7 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) {
}
template