mirror of
https://github.com/openjdk/jdk.git
synced 2026-08-03 14:47:03 +00:00
Merge branch 'master' into JDK-8344159
This commit is contained in:
commit
ff753a88a7
@ -185,77 +185,30 @@ endif
|
||||
|
||||
ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), )
|
||||
|
||||
SYMBOLS_EXCLUDE_PATTERN := %.debuginfo %.diz %.map
|
||||
|
||||
# There may be files with spaces in the names, so use ShellFindFiles
|
||||
# explicitly.
|
||||
# There may be files with spaces in the names, so use ShellFindFiles explicitly.
|
||||
ALL_JDK_FILES := $(call ShellFindFiles, $(JDK_IMAGE_DIR))
|
||||
ifneq ($(JDK_IMAGE_DIR), $(JDK_SYMBOLS_IMAGE_DIR))
|
||||
ALL_JDK_SYMBOLS_FILES := $(call ShellFindFiles, $(JDK_SYMBOLS_IMAGE_DIR))
|
||||
else
|
||||
ALL_JDK_SYMBOLS_FILES := $(ALL_JDK_FILES)
|
||||
endif
|
||||
ifneq ($(JDK_IMAGE_DIR), $(JDK_DEMOS_IMAGE_DIR))
|
||||
ALL_JDK_DEMOS_FILES := $(call ShellFindFiles, $(JDK_DEMOS_IMAGE_DIR))
|
||||
else
|
||||
ALL_JDK_DEMOS_FILES := $(ALL_JDK_FILES)
|
||||
endif
|
||||
|
||||
# Create special filter rules when dealing with unzipped .dSYM directories on
|
||||
# macosx
|
||||
ifeq ($(call isTargetOs, macosx), true)
|
||||
ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), false)
|
||||
JDK_SYMBOLS_EXCLUDE_PATTERN := $(addprefix %, \
|
||||
$(call containing, .dSYM/, $(patsubst $(JDK_IMAGE_DIR)/%, %, \
|
||||
$(ALL_JDK_SYMBOLS_FILES))))
|
||||
endif
|
||||
endif
|
||||
|
||||
# Create special filter rules when dealing with debug symbols on windows
|
||||
ifeq ($(call isTargetOs, windows), true)
|
||||
ifeq ($(SHIP_DEBUG_SYMBOLS), )
|
||||
JDK_SYMBOLS_EXCLUDE_PATTERN := %.pdb
|
||||
endif
|
||||
endif
|
||||
|
||||
JDK_BUNDLE_FILES := \
|
||||
$(filter-out \
|
||||
$(JDK_SYMBOLS_EXCLUDE_PATTERN) \
|
||||
$(JDK_EXTRA_EXCLUDES) \
|
||||
$(SYMBOLS_EXCLUDE_PATTERN) \
|
||||
$(JDK_IMAGE_HOMEDIR)/demo/% \
|
||||
, \
|
||||
$(ALL_JDK_FILES) \
|
||||
)
|
||||
|
||||
JDK_SYMBOLS_BUNDLE_FILES := \
|
||||
$(call FindFiles, $(SYMBOLS_IMAGE_DIR))
|
||||
JDK_SYMBOLS_BUNDLE_FILES := $(call FindFiles, $(SYMBOLS_IMAGE_DIR))
|
||||
|
||||
TEST_DEMOS_BUNDLE_FILES := $(filter $(JDK_DEMOS_IMAGE_HOMEDIR)/demo/%, \
|
||||
$(ALL_JDK_DEMOS_FILES))
|
||||
|
||||
ALL_JRE_FILES := $(call ShellFindFiles, $(JRE_IMAGE_DIR))
|
||||
|
||||
# Create special filter rules when dealing with unzipped .dSYM directories on
|
||||
# macosx
|
||||
ifeq ($(OPENJDK_TARGET_OS), macosx)
|
||||
ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), false)
|
||||
JRE_SYMBOLS_EXCLUDE_PATTERN := $(addprefix %, \
|
||||
$(call containing, .dSYM/, $(patsubst $(JRE_IMAGE_DIR)/%, %, $(ALL_JRE_FILES))))
|
||||
endif
|
||||
endif
|
||||
|
||||
# Create special filter rules when dealing with debug symbols on windows
|
||||
ifeq ($(call isTargetOs, windows), true)
|
||||
ifeq ($(SHIP_DEBUG_SYMBOLS), )
|
||||
JRE_SYMBOLS_EXCLUDE_PATTERN := %.pdb
|
||||
endif
|
||||
endif
|
||||
|
||||
JRE_BUNDLE_FILES := $(filter-out \
|
||||
$(JRE_SYMBOLS_EXCLUDE_PATTERN) \
|
||||
$(SYMBOLS_EXCLUDE_PATTERN), \
|
||||
$(ALL_JRE_FILES))
|
||||
JRE_BUNDLE_FILES := $(ALL_JRE_FILES)
|
||||
|
||||
ifeq ($(MACOSX_CODESIGN_MODE), hardened)
|
||||
# Macosx release build and code signing available.
|
||||
|
||||
@ -218,10 +218,14 @@ ifeq ($(call isTargetOs, windows), true)
|
||||
ifeq ($(SHIP_DEBUG_SYMBOLS), )
|
||||
JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.pdb,*.map}'
|
||||
else
|
||||
JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.map}'
|
||||
JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.map}'
|
||||
endif
|
||||
else
|
||||
JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.debuginfo,*.dSYM/**,*.dSYM}'
|
||||
ifeq ($(SHIP_DEBUG_SYMBOLS), )
|
||||
JMOD_FLAGS += --exclude '**{_the.*,_*.marker*,*.diz,*.debuginfo,*.dSYM/**,*.dSYM}'
|
||||
else
|
||||
JMOD_FLAGS += --exclude '**{_the.*,_*.marker*}'
|
||||
endif
|
||||
endif
|
||||
|
||||
# Unless we are creating a very large module, use the small tool JVM options
|
||||
|
||||
@ -93,16 +93,19 @@ JAVADOC_DISABLED_DOCLINT_WARNINGS := missing
|
||||
JAVADOC_DISABLED_DOCLINT_PACKAGES := org.w3c.* javax.smartcardio
|
||||
|
||||
# The initial set of options for javadoc
|
||||
# -XDaccessInternalAPI is a temporary workaround, see 8373909
|
||||
JAVADOC_OPTIONS := -use -keywords -notimestamp \
|
||||
-serialwarn -encoding utf-8 -docencoding utf-8 -breakiterator \
|
||||
-splitIndex --system none -javafx --expand-requires transitive \
|
||||
--override-methods=summary
|
||||
--override-methods=summary \
|
||||
-XDaccessInternalAPI
|
||||
|
||||
# The reference options must stay stable to allow for comparisons across the
|
||||
# development cycle.
|
||||
REFERENCE_OPTIONS := -XDignore.symbol.file=true -use -keywords -notimestamp \
|
||||
-serialwarn -encoding utf-8 -breakiterator -splitIndex --system none \
|
||||
-html5 -javafx --expand-requires transitive
|
||||
-html5 -javafx --expand-requires transitive \
|
||||
-XDaccessInternalAPI
|
||||
|
||||
# Should we add DRAFT stamps to the generated javadoc?
|
||||
ifeq ($(VERSION_IS_GA), true)
|
||||
|
||||
@ -63,7 +63,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
|
||||
fi
|
||||
|
||||
BASIC_LDFLAGS_JVM_ONLY=""
|
||||
LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing"
|
||||
LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing $DEBUG_PREFIX_CFLAGS"
|
||||
|
||||
LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r"
|
||||
|
||||
@ -71,7 +71,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
|
||||
BASIC_LDFLAGS_JVM_ONLY="-mno-omit-leaf-frame-pointer -mstack-alignment=16 \
|
||||
-fPIC"
|
||||
|
||||
LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing"
|
||||
LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing $DEBUG_PREFIX_CFLAGS"
|
||||
LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r"
|
||||
|
||||
if test "x$OPENJDK_TARGET_OS" = xlinux; then
|
||||
|
||||
@ -316,23 +316,36 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS],
|
||||
AC_MSG_CHECKING([if we should add external native debug symbols to the shipped bundles])
|
||||
AC_ARG_WITH([external-symbols-in-bundles],
|
||||
[AS_HELP_STRING([--with-external-symbols-in-bundles],
|
||||
[which type of external native debug symbol information shall be shipped in product bundles (none, public, full)
|
||||
(e.g. ship full/stripped pdbs on Windows) @<:@none@:>@])])
|
||||
[which type of external native debug symbol information shall be shipped with bundles/images (none, public, full).
|
||||
@<:@none in release builds, full otherwise. --with-native-debug-symbols=external/zipped is a prerequisite. public is only supported on Windows@:>@])],
|
||||
[],
|
||||
[with_external_symbols_in_bundles=default])
|
||||
|
||||
if test "x$with_external_symbols_in_bundles" = x || test "x$with_external_symbols_in_bundles" = xnone ; then
|
||||
AC_MSG_RESULT([no])
|
||||
elif test "x$with_external_symbols_in_bundles" = xfull || test "x$with_external_symbols_in_bundles" = xpublic ; then
|
||||
if test "x$OPENJDK_TARGET_OS" != xwindows ; then
|
||||
AC_MSG_ERROR([--with-external-symbols-in-bundles currently only works on windows!])
|
||||
elif test "x$COPY_DEBUG_SYMBOLS" != xtrue ; then
|
||||
AC_MSG_ERROR([--with-external-symbols-in-bundles only works when --with-native-debug-symbols=external is used!])
|
||||
elif test "x$with_external_symbols_in_bundles" = xfull ; then
|
||||
if test "x$COPY_DEBUG_SYMBOLS" != xtrue ; then
|
||||
AC_MSG_ERROR([--with-external-symbols-in-bundles only works when --with-native-debug-symbols=external/zipped is used!])
|
||||
elif test "x$with_external_symbols_in_bundles" = xpublic && test "x$OPENJDK_TARGET_OS" != xwindows ; then
|
||||
AC_MSG_ERROR([--with-external-symbols-in-bundles=public is only supported on Windows!])
|
||||
fi
|
||||
|
||||
if test "x$with_external_symbols_in_bundles" = xfull ; then
|
||||
AC_MSG_RESULT([full])
|
||||
SHIP_DEBUG_SYMBOLS=full
|
||||
else
|
||||
AC_MSG_RESULT([public])
|
||||
SHIP_DEBUG_SYMBOLS=public
|
||||
fi
|
||||
elif test "x$with_external_symbols_in_bundles" = xdefault ; then
|
||||
if test "x$DEBUG_LEVEL" = xrelease ; then
|
||||
AC_MSG_RESULT([no (default)])
|
||||
elif test "x$COPY_DEBUG_SYMBOLS" = xtrue ; then
|
||||
AC_MSG_RESULT([full (default)])
|
||||
SHIP_DEBUG_SYMBOLS=full
|
||||
else
|
||||
AC_MSG_RESULT([no (default, native debug symbols are not external/zipped)])
|
||||
fi
|
||||
else
|
||||
AC_MSG_ERROR([$with_external_symbols_in_bundles is an unknown value for --with-external-symbols-in-bundles])
|
||||
fi
|
||||
|
||||
@ -234,6 +234,9 @@ define SetupLinkerFlags
|
||||
ifeq ($(call isTargetOs, macosx), true)
|
||||
$1_EXTRA_LDFLAGS += -Wl,-object_path_lto,$$($1_OBJECT_DIR)/$$($1_NAME)_lto_helper.o
|
||||
endif
|
||||
ifeq ($(TOOLCHAIN_TYPE), microsoft)
|
||||
$1_EXTRA_LDFLAGS += -LTCGOUT:$$($1_OBJECT_DIR)/$$($1_NAME).iobj
|
||||
endif
|
||||
endif
|
||||
|
||||
$1_EXTRA_LDFLAGS += $$($1_LDFLAGS_$(OPENJDK_TARGET_OS_TYPE)) $$($1_LDFLAGS_$(OPENJDK_TARGET_OS)) \
|
||||
|
||||
@ -31,10 +31,9 @@ import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.LiteralTree;
|
||||
@ -160,9 +159,10 @@ public class JSpec implements Taglet {
|
||||
if (m.find()) {
|
||||
String chapter = m.group("chapter");
|
||||
String section = m.group("section");
|
||||
String rootParent = currentPath().replaceAll("[^/]+", "..");
|
||||
|
||||
String url = String.format("%1$s/../specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s",
|
||||
docRoot(elem), idPrefix, chapter, section);
|
||||
String url = String.format("%1$s/specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s",
|
||||
rootParent, idPrefix, chapter, section);
|
||||
|
||||
sb.append("<a href=\"")
|
||||
.append(url)
|
||||
@ -183,6 +183,22 @@ public class JSpec implements Taglet {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static ThreadLocal<String> CURRENT_PATH = null;
|
||||
|
||||
private String currentPath() {
|
||||
if (CURRENT_PATH == null) {
|
||||
try {
|
||||
Field f = Class.forName("jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter")
|
||||
.getField("CURRENT_PATH");
|
||||
@SuppressWarnings("unchecked")
|
||||
ThreadLocal<String> tl = (ThreadLocal<String>) f.get(null);
|
||||
CURRENT_PATH = tl;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Cannot determine current path", e);
|
||||
}
|
||||
}
|
||||
return CURRENT_PATH.get();
|
||||
}
|
||||
|
||||
private String expand(List<? extends DocTree> trees) {
|
||||
return (new SimpleDocTreeVisitor<StringBuilder, StringBuilder>() {
|
||||
@ -209,34 +225,4 @@ public class JSpec implements Taglet {
|
||||
}).visit(trees, new StringBuilder()).toString();
|
||||
}
|
||||
|
||||
private String docRoot(Element elem) {
|
||||
switch (elem.getKind()) {
|
||||
case MODULE:
|
||||
return "..";
|
||||
|
||||
case PACKAGE:
|
||||
PackageElement pe = (PackageElement)elem;
|
||||
String pkgPart = pe.getQualifiedName()
|
||||
.toString()
|
||||
.replace('.', '/')
|
||||
.replaceAll("[^/]+", "..");
|
||||
return pe.getEnclosingElement() != null
|
||||
? "../" + pkgPart
|
||||
: pkgPart;
|
||||
|
||||
case CLASS, ENUM, RECORD, INTERFACE, ANNOTATION_TYPE:
|
||||
TypeElement te = (TypeElement)elem;
|
||||
return te.getQualifiedName()
|
||||
.toString()
|
||||
.replace('.', '/')
|
||||
.replaceAll("[^/]+", "..");
|
||||
|
||||
default:
|
||||
var enclosing = elem.getEnclosingElement();
|
||||
if (enclosing == null)
|
||||
throw new IllegalArgumentException(elem.getKind().toString());
|
||||
return docRoot(enclosing);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -31,10 +31,9 @@ import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.UnknownBlockTagTree;
|
||||
@ -68,7 +67,7 @@ public class ToolGuide implements Taglet {
|
||||
|
||||
static final String TAG_NAME = "toolGuide";
|
||||
|
||||
static final String BASE_URL = "../specs/man";
|
||||
static final String BASE_URL = "specs/man";
|
||||
|
||||
static final Pattern TAG_PATTERN = Pattern.compile("(?s)(?<name>[A-Za-z0-9]+)\\s*(?<label>.*)$");
|
||||
|
||||
@ -119,9 +118,10 @@ public class ToolGuide implements Taglet {
|
||||
if (label.isEmpty()) {
|
||||
label = name;
|
||||
}
|
||||
String rootParent = currentPath().replaceAll("[^/]+", "..");
|
||||
|
||||
String url = String.format("%s/%s/%s.html",
|
||||
docRoot(elem), BASE_URL, name);
|
||||
rootParent, BASE_URL, name);
|
||||
|
||||
if (needComma) {
|
||||
sb.append(",\n");
|
||||
@ -142,33 +142,21 @@ public class ToolGuide implements Taglet {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String docRoot(Element elem) {
|
||||
switch (elem.getKind()) {
|
||||
case MODULE:
|
||||
return "..";
|
||||
private static ThreadLocal<String> CURRENT_PATH = null;
|
||||
|
||||
case PACKAGE:
|
||||
PackageElement pe = (PackageElement)elem;
|
||||
String pkgPart = pe.getQualifiedName()
|
||||
.toString()
|
||||
.replace('.', '/')
|
||||
.replaceAll("[^/]+", "..");
|
||||
return pe.getEnclosingElement() != null
|
||||
? "../" + pkgPart
|
||||
: pkgPart;
|
||||
|
||||
case CLASS, ENUM, RECORD, INTERFACE, ANNOTATION_TYPE:
|
||||
TypeElement te = (TypeElement)elem;
|
||||
return te.getQualifiedName()
|
||||
.toString()
|
||||
.replace('.', '/')
|
||||
.replaceAll("[^/]+", "..");
|
||||
|
||||
default:
|
||||
var enclosing = elem.getEnclosingElement();
|
||||
if (enclosing == null)
|
||||
throw new IllegalArgumentException(elem.getKind().toString());
|
||||
return docRoot(enclosing);
|
||||
private String currentPath() {
|
||||
if (CURRENT_PATH == null) {
|
||||
try {
|
||||
Field f = Class.forName("jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter")
|
||||
.getField("CURRENT_PATH");
|
||||
@SuppressWarnings("unchecked")
|
||||
ThreadLocal<String> tl = (ThreadLocal<String>) f.get(null);
|
||||
CURRENT_PATH = tl;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Cannot determine current path", e);
|
||||
}
|
||||
}
|
||||
return CURRENT_PATH.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -226,6 +226,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
|
||||
EXCLUDE_FILES := imageioJPEG.c jpegdecoder.c pngtest.c, \
|
||||
EXCLUDES := $(LIBSPLASHSCREEN_EXCLUDES), \
|
||||
OPTIMIZATION := SIZE, \
|
||||
LINK_TIME_OPTIMIZATION := true, \
|
||||
CFLAGS := $(LIBSPLASHSCREEN_CFLAGS) \
|
||||
$(GIFLIB_CFLAGS) $(LIBJPEG_CFLAGS) $(PNG_CFLAGS) $(LIBZ_CFLAGS) \
|
||||
$(ICONV_CFLAGS), \
|
||||
|
||||
@ -346,8 +346,14 @@ source %{
|
||||
}
|
||||
|
||||
bool Matcher::vector_needs_partial_operations(Node* node, const TypeVect* vt) {
|
||||
// Only SVE has partial vector operations
|
||||
if (UseSVE == 0) {
|
||||
// 1. Only SVE requires partial vector operations.
|
||||
// 2. The vector size in bytes must be smaller than MaxVectorSize.
|
||||
// 3. Predicated vectors have a mask input, which guarantees that
|
||||
// out-of-bounds lanes remain inactive.
|
||||
int length_in_bytes = vt->length_in_bytes();
|
||||
if (UseSVE == 0 ||
|
||||
length_in_bytes == MaxVectorSize ||
|
||||
node->is_predicated_vector()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -370,21 +376,22 @@ source %{
|
||||
return !node->in(1)->is_Con();
|
||||
case Op_LoadVector:
|
||||
case Op_StoreVector:
|
||||
// We use NEON load/store instructions if the vector length is <= 128 bits.
|
||||
return vt->length_in_bytes() > 16;
|
||||
case Op_AddReductionVI:
|
||||
case Op_AddReductionVL:
|
||||
// We may prefer using NEON instructions rather than SVE partial operations.
|
||||
return !VM_Version::use_neon_for_vector(vt->length_in_bytes());
|
||||
// For these ops, we prefer using NEON instructions rather than SVE
|
||||
// predicated instructions for better performance.
|
||||
return !VM_Version::use_neon_for_vector(length_in_bytes);
|
||||
case Op_MinReductionV:
|
||||
case Op_MaxReductionV:
|
||||
// For BYTE/SHORT/INT/FLOAT/DOUBLE types, we may prefer using NEON
|
||||
// instructions rather than SVE partial operations.
|
||||
// For BYTE/SHORT/INT/FLOAT/DOUBLE types, we prefer using NEON
|
||||
// instructions rather than SVE predicated instructions for
|
||||
// better performance.
|
||||
return vt->element_basic_type() == T_LONG ||
|
||||
!VM_Version::use_neon_for_vector(vt->length_in_bytes());
|
||||
!VM_Version::use_neon_for_vector(length_in_bytes);
|
||||
default:
|
||||
// For other ops whose vector size is smaller than the max vector size, a
|
||||
// full-sized unpredicated operation does not impact the final vector result.
|
||||
// For other ops whose vector size is smaller than the max vector
|
||||
// size, a full-sized unpredicated operation does not impact the
|
||||
// vector result.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,8 +336,14 @@ source %{
|
||||
}
|
||||
|
||||
bool Matcher::vector_needs_partial_operations(Node* node, const TypeVect* vt) {
|
||||
// Only SVE has partial vector operations
|
||||
if (UseSVE == 0) {
|
||||
// 1. Only SVE requires partial vector operations.
|
||||
// 2. The vector size in bytes must be smaller than MaxVectorSize.
|
||||
// 3. Predicated vectors have a mask input, which guarantees that
|
||||
// out-of-bounds lanes remain inactive.
|
||||
int length_in_bytes = vt->length_in_bytes();
|
||||
if (UseSVE == 0 ||
|
||||
length_in_bytes == MaxVectorSize ||
|
||||
node->is_predicated_vector()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -360,21 +366,22 @@ source %{
|
||||
return !node->in(1)->is_Con();
|
||||
case Op_LoadVector:
|
||||
case Op_StoreVector:
|
||||
// We use NEON load/store instructions if the vector length is <= 128 bits.
|
||||
return vt->length_in_bytes() > 16;
|
||||
case Op_AddReductionVI:
|
||||
case Op_AddReductionVL:
|
||||
// We may prefer using NEON instructions rather than SVE partial operations.
|
||||
return !VM_Version::use_neon_for_vector(vt->length_in_bytes());
|
||||
// For these ops, we prefer using NEON instructions rather than SVE
|
||||
// predicated instructions for better performance.
|
||||
return !VM_Version::use_neon_for_vector(length_in_bytes);
|
||||
case Op_MinReductionV:
|
||||
case Op_MaxReductionV:
|
||||
// For BYTE/SHORT/INT/FLOAT/DOUBLE types, we may prefer using NEON
|
||||
// instructions rather than SVE partial operations.
|
||||
// For BYTE/SHORT/INT/FLOAT/DOUBLE types, we prefer using NEON
|
||||
// instructions rather than SVE predicated instructions for
|
||||
// better performance.
|
||||
return vt->element_basic_type() == T_LONG ||
|
||||
!VM_Version::use_neon_for_vector(vt->length_in_bytes());
|
||||
!VM_Version::use_neon_for_vector(length_in_bytes);
|
||||
default:
|
||||
// For other ops whose vector size is smaller than the max vector size, a
|
||||
// full-sized unpredicated operation does not impact the final vector result.
|
||||
// For other ops whose vector size is smaller than the max vector
|
||||
// size, a full-sized unpredicated operation does not impact the
|
||||
// vector result.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,7 +310,18 @@ static void restore_live_registers(StubAssembler* sasm, bool restore_fpu_registe
|
||||
__ add(sp, sp, 32 * wordSize);
|
||||
}
|
||||
|
||||
#ifdef R18_RESERVED
|
||||
/*
|
||||
Do not modify r18_tls when restoring registers if it is a reserved register. On Windows,
|
||||
for example, r18_tls is used to store the pointer to the current thread's TEB (where TLS
|
||||
variables are stored). Therefore, modifying r18_tls would corrupt the TEB pointer.
|
||||
*/
|
||||
__ pop(RegSet::range(r0, r17), sp);
|
||||
__ ldp(zr, r19, Address(__ post(sp, 2 * wordSize)));
|
||||
__ pop(RegSet::range(r20, r29), sp);
|
||||
#else
|
||||
__ pop(RegSet::range(r0, r29), sp);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void restore_live_registers_except_r0(StubAssembler* sasm, bool restore_fpu_registers = true) {
|
||||
@ -323,8 +334,20 @@ static void restore_live_registers_except_r0(StubAssembler* sasm, bool restore_f
|
||||
__ add(sp, sp, 32 * wordSize);
|
||||
}
|
||||
|
||||
#ifdef R18_RESERVED
|
||||
/*
|
||||
Do not modify r18_tls when restoring registers if it is a reserved register. On Windows,
|
||||
for example, r18_tls is used to store the pointer to the current thread's TEB (where TLS
|
||||
variables are stored). Therefore, modifying r18_tls would corrupt the TEB pointer.
|
||||
*/
|
||||
__ ldp(zr, r1, Address(__ post(sp, 2 * wordSize)));
|
||||
__ pop(RegSet::range(r2, r17), sp);
|
||||
__ ldp(zr, r19, Address(__ post(sp, 2 * wordSize)));
|
||||
__ pop(RegSet::range(r20, r29), sp);
|
||||
#else
|
||||
__ ldp(zr, r1, Address(__ post(sp, 16)));
|
||||
__ pop(RegSet::range(r2, r29), sp);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -2662,6 +2662,9 @@ enum Nf {
|
||||
INSN(vsha2ch_vv, 0b1110111, 0b010, 0b1, 0b101110);
|
||||
INSN(vsha2cl_vv, 0b1110111, 0b010, 0b1, 0b101111);
|
||||
|
||||
// Vector GHASH (Zvkg) Extension
|
||||
INSN(vghsh_vv, 0b1110111, 0b010, 0b1, 0b101100);
|
||||
|
||||
#undef INSN
|
||||
|
||||
#define INSN(NAME, op, funct3, Vs1, funct6) \
|
||||
|
||||
@ -123,6 +123,7 @@ define_pd_global(intx, InlineSmallCode, 1000);
|
||||
product(bool, UseZvkn, false, EXPERIMENTAL, \
|
||||
"Use Zvkn group extension, Zvkned, Zvknhb, Zvkb, Zvkt") \
|
||||
product(bool, UseCtxFencei, false, EXPERIMENTAL, \
|
||||
"Use PR_RISCV_CTX_SW_FENCEI_ON to avoid explicit icache flush")
|
||||
"Use PR_RISCV_CTX_SW_FENCEI_ON to avoid explicit icache flush") \
|
||||
product(bool, UseZvkg, false, EXPERIMENTAL, "Use Zvkg instructions")
|
||||
|
||||
#endif // CPU_RISCV_GLOBALS_RISCV_HPP
|
||||
|
||||
@ -8542,7 +8542,7 @@ instruct convD2F_reg(fRegF dst, fRegD src) %{
|
||||
|
||||
// single <-> half precision
|
||||
|
||||
instruct convHF2F_reg_reg(fRegF dst, iRegINoSp src, iRegINoSp tmp) %{
|
||||
instruct convHF2F_reg_reg(fRegF dst, iRegIorL2I src, iRegINoSp tmp) %{
|
||||
match(Set dst (ConvHF2F src));
|
||||
effect(TEMP tmp);
|
||||
format %{ "fmv.h.x $dst, $src\t# move source from $src to $dst\n\t"
|
||||
|
||||
@ -2606,6 +2606,182 @@ class StubGenerator: public StubCodeGenerator {
|
||||
return start;
|
||||
}
|
||||
|
||||
void cipherBlockChaining_encryptAESCrypt(int round, Register from, Register to, Register key,
|
||||
Register rvec, Register input_len) {
|
||||
const Register len = x29;
|
||||
|
||||
VectorRegister working_vregs[] = {
|
||||
v1, v2, v3, v4, v5, v6, v7, v8,
|
||||
v9, v10, v11, v12, v13, v14, v15
|
||||
};
|
||||
|
||||
const unsigned int BLOCK_SIZE = 16;
|
||||
|
||||
__ mv(len, input_len);
|
||||
// load init rvec
|
||||
__ vsetivli(x0, 4, Assembler::e32, Assembler::m1);
|
||||
__ vle32_v(v16, rvec);
|
||||
|
||||
generate_aes_loadkeys(key, working_vregs, round);
|
||||
Label L_enc_loop;
|
||||
__ bind(L_enc_loop);
|
||||
// Encrypt from source by block size
|
||||
__ vle32_v(v17, from);
|
||||
__ addi(from, from, BLOCK_SIZE);
|
||||
__ vxor_vv(v16, v16, v17);
|
||||
generate_aes_encrypt(v16, working_vregs, round);
|
||||
__ vse32_v(v16, to);
|
||||
__ addi(to, to, BLOCK_SIZE);
|
||||
__ subi(len, len, BLOCK_SIZE);
|
||||
__ bnez(len, L_enc_loop);
|
||||
|
||||
// save current rvec and return
|
||||
__ vse32_v(v16, rvec);
|
||||
__ mv(x10, input_len);
|
||||
__ leave();
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Arguments:
|
||||
//
|
||||
// Inputs:
|
||||
// c_rarg0 - source byte array address
|
||||
// c_rarg1 - destination byte array address
|
||||
// c_rarg2 - K (key) in little endian int array
|
||||
// c_rarg3 - r vector byte array address
|
||||
// c_rarg4 - input length
|
||||
//
|
||||
// Output:
|
||||
// x10 - input length
|
||||
//
|
||||
address generate_cipherBlockChaining_encryptAESCrypt() {
|
||||
assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support");
|
||||
__ align(CodeEntryAlignment);
|
||||
StubId stub_id = StubId::stubgen_cipherBlockChaining_encryptAESCrypt_id;
|
||||
StubCodeMark mark(this, stub_id);
|
||||
|
||||
const Register from = c_rarg0;
|
||||
const Register to = c_rarg1;
|
||||
const Register key = c_rarg2;
|
||||
const Register rvec = c_rarg3;
|
||||
const Register input_len = c_rarg4;
|
||||
|
||||
const Register keylen = x28;
|
||||
|
||||
address start = __ pc();
|
||||
__ enter();
|
||||
|
||||
Label L_aes128, L_aes192;
|
||||
// Compute #rounds for AES based on the length of the key array
|
||||
__ lwu(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)));
|
||||
__ mv(t0, 52);
|
||||
__ bltu(keylen, t0, L_aes128);
|
||||
__ beq(keylen, t0, L_aes192);
|
||||
// Else we fallthrough to the biggest case (256-bit key size)
|
||||
|
||||
// Note: the following function performs key += 15*16
|
||||
cipherBlockChaining_encryptAESCrypt(15, from, to, key, rvec, input_len);
|
||||
|
||||
// Note: the following function performs key += 11*16
|
||||
__ bind(L_aes128);
|
||||
cipherBlockChaining_encryptAESCrypt(11, from, to, key, rvec, input_len);
|
||||
|
||||
// Note: the following function performs key += 13*16
|
||||
__ bind(L_aes192);
|
||||
cipherBlockChaining_encryptAESCrypt(13, from, to, key, rvec, input_len);
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
void cipherBlockChaining_decryptAESCrypt(int round, Register from, Register to, Register key,
|
||||
Register rvec, Register input_len) {
|
||||
const Register len = x29;
|
||||
|
||||
VectorRegister working_vregs[] = {
|
||||
v1, v2, v3, v4, v5, v6, v7, v8,
|
||||
v9, v10, v11, v12, v13, v14, v15
|
||||
};
|
||||
|
||||
const unsigned int BLOCK_SIZE = 16;
|
||||
|
||||
__ mv(len, input_len);
|
||||
// load init rvec
|
||||
__ vsetivli(x0, 4, Assembler::e32, Assembler::m1);
|
||||
__ vle32_v(v16, rvec);
|
||||
|
||||
generate_aes_loadkeys(key, working_vregs, round);
|
||||
Label L_dec_loop;
|
||||
// Decrypt from source by block size
|
||||
__ bind(L_dec_loop);
|
||||
__ vle32_v(v17, from);
|
||||
__ addi(from, from, BLOCK_SIZE);
|
||||
__ vmv_v_v(v18, v17);
|
||||
generate_aes_decrypt(v17, working_vregs, round);
|
||||
__ vxor_vv(v17, v17, v16);
|
||||
__ vse32_v(v17, to);
|
||||
__ vmv_v_v(v16, v18);
|
||||
__ addi(to, to, BLOCK_SIZE);
|
||||
__ subi(len, len, BLOCK_SIZE);
|
||||
__ bnez(len, L_dec_loop);
|
||||
|
||||
// save current rvec and return
|
||||
__ vse32_v(v16, rvec);
|
||||
__ mv(x10, input_len);
|
||||
__ leave();
|
||||
__ ret();
|
||||
}
|
||||
|
||||
// Arguments:
|
||||
//
|
||||
// Inputs:
|
||||
// c_rarg0 - source byte array address
|
||||
// c_rarg1 - destination byte array address
|
||||
// c_rarg2 - K (key) in little endian int array
|
||||
// c_rarg3 - r vector byte array address
|
||||
// c_rarg4 - input length
|
||||
//
|
||||
// Output:
|
||||
// x10 - input length
|
||||
//
|
||||
address generate_cipherBlockChaining_decryptAESCrypt() {
|
||||
assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support");
|
||||
__ align(CodeEntryAlignment);
|
||||
StubId stub_id = StubId::stubgen_cipherBlockChaining_decryptAESCrypt_id;
|
||||
StubCodeMark mark(this, stub_id);
|
||||
|
||||
const Register from = c_rarg0;
|
||||
const Register to = c_rarg1;
|
||||
const Register key = c_rarg2;
|
||||
const Register rvec = c_rarg3;
|
||||
const Register input_len = c_rarg4;
|
||||
|
||||
const Register keylen = x28;
|
||||
|
||||
address start = __ pc();
|
||||
__ enter();
|
||||
|
||||
Label L_aes128, L_aes192, L_aes128_loop, L_aes192_loop, L_aes256_loop;
|
||||
// Compute #rounds for AES based on the length of the key array
|
||||
__ lwu(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)));
|
||||
__ mv(t0, 52);
|
||||
__ bltu(keylen, t0, L_aes128);
|
||||
__ beq(keylen, t0, L_aes192);
|
||||
// Else we fallthrough to the biggest case (256-bit key size)
|
||||
|
||||
// Note: the following function performs key += 15*16
|
||||
cipherBlockChaining_decryptAESCrypt(15, from, to, key, rvec, input_len);
|
||||
|
||||
// Note: the following function performs key += 11*16
|
||||
__ bind(L_aes128);
|
||||
cipherBlockChaining_decryptAESCrypt(11, from, to, key, rvec, input_len);
|
||||
|
||||
// Note: the following function performs key += 13*16
|
||||
__ bind(L_aes192);
|
||||
cipherBlockChaining_decryptAESCrypt(13, from, to, key, rvec, input_len);
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
// Load big-endian 128-bit from memory.
|
||||
void be_load_counter_128(Register counter_hi, Register counter_lo, Register counter) {
|
||||
__ ld(counter_lo, Address(counter, 8)); // Load 128-bits from counter
|
||||
@ -2772,9 +2948,7 @@ class StubGenerator: public StubCodeGenerator {
|
||||
// x10 - input length
|
||||
//
|
||||
address generate_counterMode_AESCrypt() {
|
||||
assert(UseZvkn, "need AES instructions (Zvkned extension) support");
|
||||
assert(UseAESCTRIntrinsics, "need AES instructions (Zvkned extension) support");
|
||||
assert(UseZbb, "need basic bit manipulation (Zbb extension) support");
|
||||
assert(UseAESCTRIntrinsics, "need AES instructions (Zvkned extension) and Zbb extension support");
|
||||
|
||||
__ align(CodeEntryAlignment);
|
||||
StubId stub_id = StubId::stubgen_counterMode_AESCrypt_id;
|
||||
@ -2823,6 +2997,63 @@ class StubGenerator: public StubCodeGenerator {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments:
|
||||
*
|
||||
* Input:
|
||||
* c_rarg0 - current state address
|
||||
* c_rarg1 - H key address
|
||||
* c_rarg2 - data address
|
||||
* c_rarg3 - number of blocks
|
||||
*
|
||||
* Output:
|
||||
* Updated state at c_rarg0
|
||||
*/
|
||||
address generate_ghash_processBlocks() {
|
||||
assert(UseGHASHIntrinsics, "need GHASH instructions (Zvkg extension) and Zvbb support");
|
||||
|
||||
__ align(CodeEntryAlignment);
|
||||
StubId stub_id = StubId::stubgen_ghash_processBlocks_id;
|
||||
StubCodeMark mark(this, stub_id);
|
||||
|
||||
address start = __ pc();
|
||||
__ enter();
|
||||
|
||||
Register state = c_rarg0;
|
||||
Register subkeyH = c_rarg1;
|
||||
Register data = c_rarg2;
|
||||
Register blocks = c_rarg3;
|
||||
|
||||
VectorRegister partial_hash = v1;
|
||||
VectorRegister hash_subkey = v2;
|
||||
VectorRegister cipher_text = v3;
|
||||
|
||||
const unsigned int BLOCK_SIZE = 16;
|
||||
|
||||
__ vsetivli(x0, 2, Assembler::e64, Assembler::m1);
|
||||
__ vle64_v(hash_subkey, subkeyH);
|
||||
__ vrev8_v(hash_subkey, hash_subkey);
|
||||
__ vle64_v(partial_hash, state);
|
||||
__ vrev8_v(partial_hash, partial_hash);
|
||||
|
||||
__ vsetivli(x0, 4, Assembler::e32, Assembler::m1);
|
||||
Label L_ghash_loop;
|
||||
__ bind(L_ghash_loop);
|
||||
__ vle32_v(cipher_text, data);
|
||||
__ addi(data, data, BLOCK_SIZE);
|
||||
__ vghsh_vv(partial_hash, hash_subkey, cipher_text);
|
||||
__ subi(blocks, blocks, 1);
|
||||
__ bnez(blocks, L_ghash_loop);
|
||||
|
||||
__ vsetivli(x0, 2, Assembler::e64, Assembler::m1);
|
||||
__ vrev8_v(partial_hash, partial_hash);
|
||||
__ vse64_v(partial_hash, state);
|
||||
__ leave();
|
||||
__ ret();
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
// code for comparing 8 characters of strings with Latin1 and Utf16 encoding
|
||||
void compare_string_8_x_LU(Register tmpL, Register tmpU,
|
||||
Register strL, Register strU, Label& DIFF) {
|
||||
@ -7041,12 +7272,18 @@ static const int64_t right_3_bits = right_n_bits(3);
|
||||
if (UseAESIntrinsics) {
|
||||
StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock();
|
||||
StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock();
|
||||
StubRoutines::_cipherBlockChaining_encryptAESCrypt = generate_cipherBlockChaining_encryptAESCrypt();
|
||||
StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt();
|
||||
}
|
||||
|
||||
if (UseAESCTRIntrinsics) {
|
||||
StubRoutines::_counterMode_AESCrypt = generate_counterMode_AESCrypt();
|
||||
}
|
||||
|
||||
if (UseGHASHIntrinsics) {
|
||||
StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks();
|
||||
}
|
||||
|
||||
if (UsePoly1305Intrinsics) {
|
||||
StubRoutines::_poly1305_processBlocks = generate_poly1305_processBlocks();
|
||||
}
|
||||
|
||||
@ -457,6 +457,22 @@ void VM_Version::c2_initialize() {
|
||||
FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (UseZvkg) {
|
||||
if (FLAG_IS_DEFAULT(UseGHASHIntrinsics) && UseZvbb) {
|
||||
FLAG_SET_DEFAULT(UseGHASHIntrinsics, true);
|
||||
}
|
||||
|
||||
if (UseGHASHIntrinsics && !UseZvbb) {
|
||||
warning("Cannot enable UseGHASHIntrinsics on cpu without UseZvbb support");
|
||||
FLAG_SET_DEFAULT(UseGHASHIntrinsics, false);
|
||||
}
|
||||
} else {
|
||||
if (UseGHASHIntrinsics) {
|
||||
warning("Cannot enable UseGHASHIntrinsics on cpu without UseZvkg support");
|
||||
FLAG_SET_DEFAULT(UseGHASHIntrinsics, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // COMPILER2
|
||||
|
||||
@ -289,6 +289,8 @@ class VM_Version : public Abstract_VM_Version {
|
||||
decl(Zvfh , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfh, &ext_v, &ext_Zfh, nullptr)) \
|
||||
/* Shorthand for Zvkned + Zvknhb + Zvkb + Zvkt */ \
|
||||
decl(Zvkn , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkn, &ext_v, nullptr)) \
|
||||
/* Zvkg crypto extension for ghash and gcm */ \
|
||||
decl(Zvkg , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkg, &ext_v, nullptr)) \
|
||||
|
||||
#define DECLARE_RV_EXT_FEATURE(PRETTY, LINUX_BIT, FSTRING, FLAGF) \
|
||||
struct ext_##PRETTY##RVExtFeatureValue : public RVExtFeatureValue { \
|
||||
|
||||
@ -449,8 +449,8 @@ const int FPUStateSizeInWords = 2688 / wordSize;
|
||||
// imm8[1:0] = 00 (min) / 01 (max)
|
||||
//
|
||||
// [1] https://www.intel.com/content/www/us/en/content-details/856721/intel-advanced-vector-extensions-10-2-intel-avx10-2-architecture-specification.html?wapkw=AVX10
|
||||
const int AVX10_MINMAX_MAX_COMPARE_SIGN = 0x5;
|
||||
const int AVX10_MINMAX_MIN_COMPARE_SIGN = 0x4;
|
||||
const int AVX10_2_MINMAX_MAX_COMPARE_SIGN = 0x5;
|
||||
const int AVX10_2_MINMAX_MIN_COMPARE_SIGN = 0x4;
|
||||
|
||||
// The Intel x86/Amd64 Assembler: Pure assembler doing NO optimizations on the instruction
|
||||
// level (e.g. mov rax, 0 is not translated into xor rax, rax!); i.e., what you write
|
||||
|
||||
@ -1033,8 +1033,8 @@ void C2_MacroAssembler::vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst,
|
||||
assert(opc == Op_MinV || opc == Op_MinReductionV ||
|
||||
opc == Op_MaxV || opc == Op_MaxReductionV, "sanity");
|
||||
|
||||
int imm8 = (opc == Op_MinV || opc == Op_MinReductionV) ? AVX10_MINMAX_MIN_COMPARE_SIGN
|
||||
: AVX10_MINMAX_MAX_COMPARE_SIGN;
|
||||
int imm8 = (opc == Op_MinV || opc == Op_MinReductionV) ? AVX10_2_MINMAX_MIN_COMPARE_SIGN
|
||||
: AVX10_2_MINMAX_MAX_COMPARE_SIGN;
|
||||
if (elem_bt == T_FLOAT) {
|
||||
evminmaxps(dst, mask, src1, src2, true, imm8, vlen_enc);
|
||||
} else {
|
||||
@ -5163,7 +5163,7 @@ void C2_MacroAssembler::vector_castD2X_evex(BasicType to_elem_bt, XMMRegister ds
|
||||
}
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc) {
|
||||
void C2_MacroAssembler::vector_castF2X_avx10_2(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc) {
|
||||
switch(to_elem_bt) {
|
||||
case T_LONG:
|
||||
evcvttps2qqs(dst, src, vec_enc);
|
||||
@ -5183,7 +5183,7 @@ void C2_MacroAssembler::vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister d
|
||||
}
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc) {
|
||||
void C2_MacroAssembler::vector_castF2X_avx10_2(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc) {
|
||||
switch(to_elem_bt) {
|
||||
case T_LONG:
|
||||
evcvttps2qqs(dst, src, vec_enc);
|
||||
@ -5203,7 +5203,7 @@ void C2_MacroAssembler::vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister d
|
||||
}
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::vector_castD2X_avx10(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc) {
|
||||
void C2_MacroAssembler::vector_castD2X_avx10_2(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc) {
|
||||
switch(to_elem_bt) {
|
||||
case T_LONG:
|
||||
evcvttpd2qqs(dst, src, vec_enc);
|
||||
@ -5223,7 +5223,7 @@ void C2_MacroAssembler::vector_castD2X_avx10(BasicType to_elem_bt, XMMRegister d
|
||||
}
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::vector_castD2X_avx10(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc) {
|
||||
void C2_MacroAssembler::vector_castD2X_avx10_2(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc) {
|
||||
switch(to_elem_bt) {
|
||||
case T_LONG:
|
||||
evcvttpd2qqs(dst, src, vec_enc);
|
||||
|
||||
@ -347,13 +347,13 @@ public:
|
||||
XMMRegister xtmp2, XMMRegister xtmp3, XMMRegister xtmp4, XMMRegister xtmp5,
|
||||
AddressLiteral float_sign_flip, Register rscratch, int vec_enc);
|
||||
|
||||
void vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc);
|
||||
void vector_castF2X_avx10_2(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc);
|
||||
|
||||
void vector_castF2X_avx10(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc);
|
||||
void vector_castF2X_avx10_2(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc);
|
||||
|
||||
void vector_castD2X_avx10(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc);
|
||||
void vector_castD2X_avx10_2(BasicType to_elem_bt, XMMRegister dst, XMMRegister src, int vec_enc);
|
||||
|
||||
void vector_castD2X_avx10(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc);
|
||||
void vector_castD2X_avx10_2(BasicType to_elem_bt, XMMRegister dst, Address src, int vec_enc);
|
||||
|
||||
void vector_cast_double_to_int_special_cases_avx(XMMRegister dst, XMMRegister src, XMMRegister xtmp1, XMMRegister xtmp2,
|
||||
XMMRegister xtmp3, XMMRegister xtmp4, XMMRegister xtmp5, Register rscratch,
|
||||
|
||||
@ -8899,9 +8899,9 @@ void MacroAssembler::evpmins(BasicType type, XMMRegister dst, KRegister mask, XM
|
||||
case T_LONG:
|
||||
evpminsq(dst, mask, nds, src, merge, vector_len); break;
|
||||
case T_FLOAT:
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_2_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
|
||||
case T_DOUBLE:
|
||||
evminmaxpd(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
|
||||
evminmaxpd(dst, mask, nds, src, merge, AVX10_2_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
|
||||
default:
|
||||
fatal("Unexpected type argument %s", type2name(type)); break;
|
||||
}
|
||||
@ -8918,9 +8918,9 @@ void MacroAssembler::evpmaxs(BasicType type, XMMRegister dst, KRegister mask, XM
|
||||
case T_LONG:
|
||||
evpmaxsq(dst, mask, nds, src, merge, vector_len); break;
|
||||
case T_FLOAT:
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
|
||||
case T_DOUBLE:
|
||||
evminmaxpd(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
|
||||
evminmaxpd(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
|
||||
default:
|
||||
fatal("Unexpected type argument %s", type2name(type)); break;
|
||||
}
|
||||
@ -8937,9 +8937,9 @@ void MacroAssembler::evpmins(BasicType type, XMMRegister dst, KRegister mask, XM
|
||||
case T_LONG:
|
||||
evpminsq(dst, mask, nds, src, merge, vector_len); break;
|
||||
case T_FLOAT:
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_2_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
|
||||
case T_DOUBLE:
|
||||
evminmaxpd(dst, mask, nds, src, merge, AVX10_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
|
||||
evminmaxpd(dst, mask, nds, src, merge, AVX10_2_MINMAX_MIN_COMPARE_SIGN, vector_len); break;
|
||||
default:
|
||||
fatal("Unexpected type argument %s", type2name(type)); break;
|
||||
}
|
||||
@ -8956,9 +8956,9 @@ void MacroAssembler::evpmaxs(BasicType type, XMMRegister dst, KRegister mask, XM
|
||||
case T_LONG:
|
||||
evpmaxsq(dst, mask, nds, src, merge, vector_len); break;
|
||||
case T_FLOAT:
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
|
||||
case T_DOUBLE:
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
|
||||
evminmaxps(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break;
|
||||
default:
|
||||
fatal("Unexpected type argument %s", type2name(type)); break;
|
||||
}
|
||||
|
||||
@ -7289,12 +7289,12 @@ instruct loadD(regD dst, memory mem)
|
||||
%}
|
||||
|
||||
// max = java.lang.Math.max(float a, float b)
|
||||
instruct maxF_avx10_reg(regF dst, regF a, regF b) %{
|
||||
instruct maxF_reg_avx10_2(regF dst, regF a, regF b) %{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (MaxF a b));
|
||||
format %{ "maxF $dst, $a, $b" %}
|
||||
ins_encode %{
|
||||
__ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MAX_COMPARE_SIGN);
|
||||
__ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MAX_COMPARE_SIGN);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
@ -7325,12 +7325,12 @@ instruct maxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRe
|
||||
%}
|
||||
|
||||
// max = java.lang.Math.max(double a, double b)
|
||||
instruct maxD_avx10_reg(regD dst, regD a, regD b) %{
|
||||
instruct maxD_reg_avx10_2(regD dst, regD a, regD b) %{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (MaxD a b));
|
||||
format %{ "maxD $dst, $a, $b" %}
|
||||
ins_encode %{
|
||||
__ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MAX_COMPARE_SIGN);
|
||||
__ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MAX_COMPARE_SIGN);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
@ -7361,12 +7361,12 @@ instruct maxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRe
|
||||
%}
|
||||
|
||||
// max = java.lang.Math.min(float a, float b)
|
||||
instruct minF_avx10_reg(regF dst, regF a, regF b) %{
|
||||
instruct minF_reg_avx10_2(regF dst, regF a, regF b) %{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (MinF a b));
|
||||
format %{ "minF $dst, $a, $b" %}
|
||||
ins_encode %{
|
||||
__ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MIN_COMPARE_SIGN);
|
||||
__ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MIN_COMPARE_SIGN);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
@ -7397,12 +7397,12 @@ instruct minF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRe
|
||||
%}
|
||||
|
||||
// max = java.lang.Math.min(double a, double b)
|
||||
instruct minD_avx10_reg(regD dst, regD a, regD b) %{
|
||||
instruct minD_reg_avx10_2(regD dst, regD a, regD b) %{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (MinD a b));
|
||||
format %{ "minD $dst, $a, $b" %}
|
||||
ins_encode %{
|
||||
__ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_MINMAX_MIN_COMPARE_SIGN);
|
||||
__ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MIN_COMPARE_SIGN);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
@ -14586,7 +14586,7 @@ instruct convF2I_reg_reg(rRegI dst, regF src, rFlagsReg cr)
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct convF2I_reg_reg_avx10(rRegI dst, regF src)
|
||||
instruct convF2I_reg_reg_avx10_2(rRegI dst, regF src)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (ConvF2I src));
|
||||
@ -14597,7 +14597,7 @@ instruct convF2I_reg_reg_avx10(rRegI dst, regF src)
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct convF2I_reg_mem_avx10(rRegI dst, memory src)
|
||||
instruct convF2I_reg_mem_avx10_2(rRegI dst, memory src)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (ConvF2I (LoadF src)));
|
||||
@ -14620,7 +14620,7 @@ instruct convF2L_reg_reg(rRegL dst, regF src, rFlagsReg cr)
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct convF2L_reg_reg_avx10(rRegL dst, regF src)
|
||||
instruct convF2L_reg_reg_avx10_2(rRegL dst, regF src)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (ConvF2L src));
|
||||
@ -14631,7 +14631,7 @@ instruct convF2L_reg_reg_avx10(rRegL dst, regF src)
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct convF2L_reg_mem_avx10(rRegL dst, memory src)
|
||||
instruct convF2L_reg_mem_avx10_2(rRegL dst, memory src)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (ConvF2L (LoadF src)));
|
||||
@ -14654,7 +14654,7 @@ instruct convD2I_reg_reg(rRegI dst, regD src, rFlagsReg cr)
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct convD2I_reg_reg_avx10(rRegI dst, regD src)
|
||||
instruct convD2I_reg_reg_avx10_2(rRegI dst, regD src)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (ConvD2I src));
|
||||
@ -14665,7 +14665,7 @@ instruct convD2I_reg_reg_avx10(rRegI dst, regD src)
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct convD2I_reg_mem_avx10(rRegI dst, memory src)
|
||||
instruct convD2I_reg_mem_avx10_2(rRegI dst, memory src)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (ConvD2I (LoadD src)));
|
||||
@ -14688,7 +14688,7 @@ instruct convD2L_reg_reg(rRegL dst, regD src, rFlagsReg cr)
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct convD2L_reg_reg_avx10(rRegL dst, regD src)
|
||||
instruct convD2L_reg_reg_avx10_2(rRegL dst, regD src)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (ConvD2L src));
|
||||
@ -14699,7 +14699,7 @@ instruct convD2L_reg_reg_avx10(rRegL dst, regD src)
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct convD2L_reg_mem_avx10(rRegL dst, memory src)
|
||||
instruct convD2L_reg_mem_avx10_2(rRegL dst, memory src)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (ConvD2L (LoadD src)));
|
||||
@ -19660,7 +19660,7 @@ instruct minmax_reductionF_av(legRegF dst, legVec src, legVec tmp, legVec atmp,
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct minmax_reduction2F_avx10(regF dst, immF src1, vec src2, vec xtmp1) %{
|
||||
instruct minmax_reduction2F_avx10_2(regF dst, immF src1, vec src2, vec xtmp1) %{
|
||||
predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
|
||||
((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) ||
|
||||
(n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) &&
|
||||
@ -19678,7 +19678,7 @@ instruct minmax_reduction2F_avx10(regF dst, immF src1, vec src2, vec xtmp1) %{
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct minmax_reductionF_avx10(regF dst, immF src1, vec src2, vec xtmp1, vec xtmp2) %{
|
||||
instruct minmax_reductionF_avx10_2(regF dst, immF src1, vec src2, vec xtmp1, vec xtmp2) %{
|
||||
predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
|
||||
((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeF::POS_INF) ||
|
||||
(n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeF::NEG_INF)) &&
|
||||
@ -19696,7 +19696,7 @@ instruct minmax_reductionF_avx10(regF dst, immF src1, vec src2, vec xtmp1, vec x
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct minmax_reduction2F_avx10_av(regF dst, vec src, vec xtmp1) %{
|
||||
instruct minmax_reduction2F_av_avx10_2(regF dst, vec src, vec xtmp1) %{
|
||||
predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
|
||||
Matcher::vector_length(n->in(2)) == 2);
|
||||
match(Set dst (MinReductionV dst src));
|
||||
@ -19712,7 +19712,7 @@ instruct minmax_reduction2F_avx10_av(regF dst, vec src, vec xtmp1) %{
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct minmax_reductionF_avx10_av(regF dst, vec src, vec xtmp1, vec xtmp2) %{
|
||||
instruct minmax_reductionF_av_avx10_2(regF dst, vec src, vec xtmp1, vec xtmp2) %{
|
||||
predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_FLOAT &&
|
||||
Matcher::vector_length(n->in(2)) >= 4);
|
||||
match(Set dst (MinReductionV dst src));
|
||||
@ -19810,7 +19810,7 @@ instruct minmax_reductionD_av(legRegD dst, legVec src, legVec tmp1, legVec tmp2,
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct minmax_reduction2D_avx10(regD dst, immD src1, vec src2, vec xtmp1) %{
|
||||
instruct minmax_reduction2D_avx10_2(regD dst, immD src1, vec src2, vec xtmp1) %{
|
||||
predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
|
||||
((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) ||
|
||||
(n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) &&
|
||||
@ -19828,7 +19828,7 @@ instruct minmax_reduction2D_avx10(regD dst, immD src1, vec src2, vec xtmp1) %{
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct minmax_reductionD_avx10(regD dst, immD src1, vec src2, vec xtmp1, vec xtmp2) %{
|
||||
instruct minmax_reductionD_avx10_2(regD dst, immD src1, vec src2, vec xtmp1, vec xtmp2) %{
|
||||
predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
|
||||
((n->Opcode() == Op_MinReductionV && n->in(1)->bottom_type() == TypeD::POS_INF) ||
|
||||
(n->Opcode() == Op_MaxReductionV && n->in(1)->bottom_type() == TypeD::NEG_INF)) &&
|
||||
@ -19847,7 +19847,7 @@ instruct minmax_reductionD_avx10(regD dst, immD src1, vec src2, vec xtmp1, vec x
|
||||
%}
|
||||
|
||||
|
||||
instruct minmax_reduction2D_av_avx10(regD dst, vec src, vec xtmp1) %{
|
||||
instruct minmax_reduction2D_av_avx10_2(regD dst, vec src, vec xtmp1) %{
|
||||
predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
|
||||
Matcher::vector_length(n->in(2)) == 2);
|
||||
match(Set dst (MinReductionV dst src));
|
||||
@ -19863,7 +19863,7 @@ instruct minmax_reduction2D_av_avx10(regD dst, vec src, vec xtmp1) %{
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct minmax_reductionD_av_avx10(regD dst, vec src, vec xtmp1, vec xtmp2) %{
|
||||
instruct minmax_reductionD_av_avx10_2(regD dst, vec src, vec xtmp1, vec xtmp2) %{
|
||||
predicate(VM_Version::supports_avx10_2() && Matcher::vector_element_basic_type(n->in(2)) == T_DOUBLE &&
|
||||
Matcher::vector_length(n->in(2)) >= 4);
|
||||
match(Set dst (MinReductionV dst src));
|
||||
@ -20766,7 +20766,7 @@ instruct vminmaxL_reg_evex(vec dst, vec src1, vec src2) %{
|
||||
%}
|
||||
|
||||
// Float/Double vector Min/Max
|
||||
instruct minmaxFP_avx10_reg(vec dst, vec a, vec b) %{
|
||||
instruct minmaxFP_reg_avx10_2(vec dst, vec a, vec b) %{
|
||||
predicate(VM_Version::supports_avx10_2() &&
|
||||
is_floating_point_type(Matcher::vector_element_basic_type(n))); // T_FLOAT, T_DOUBLE
|
||||
match(Set dst (MinV a b));
|
||||
@ -22113,29 +22113,29 @@ instruct castFtoX_reg_evex(vec dst, vec src, vec xtmp1, vec xtmp2, kReg ktmp1, k
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct castFtoX_reg_avx10(vec dst, vec src) %{
|
||||
instruct castFtoX_reg_avx10_2(vec dst, vec src) %{
|
||||
predicate(VM_Version::supports_avx10_2() &&
|
||||
is_integral_type(Matcher::vector_element_basic_type(n)));
|
||||
match(Set dst (VectorCastF2X src));
|
||||
format %{ "vector_cast_f2x_avx10 $dst, $src\t!" %}
|
||||
format %{ "vector_cast_f2x_avx10_2 $dst, $src\t!" %}
|
||||
ins_encode %{
|
||||
BasicType to_elem_bt = Matcher::vector_element_basic_type(this);
|
||||
int vlen_enc = (to_elem_bt == T_LONG) ? vector_length_encoding(this) : vector_length_encoding(this, $src);
|
||||
__ vector_castF2X_avx10(to_elem_bt, $dst$$XMMRegister, $src$$XMMRegister, vlen_enc);
|
||||
__ vector_castF2X_avx10_2(to_elem_bt, $dst$$XMMRegister, $src$$XMMRegister, vlen_enc);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct castFtoX_mem_avx10(vec dst, memory src) %{
|
||||
instruct castFtoX_mem_avx10_2(vec dst, memory src) %{
|
||||
predicate(VM_Version::supports_avx10_2() &&
|
||||
is_integral_type(Matcher::vector_element_basic_type(n)));
|
||||
match(Set dst (VectorCastF2X (LoadVector src)));
|
||||
format %{ "vector_cast_f2x_avx10 $dst, $src\t!" %}
|
||||
format %{ "vector_cast_f2x_avx10_2 $dst, $src\t!" %}
|
||||
ins_encode %{
|
||||
int vlen = Matcher::vector_length(this);
|
||||
BasicType to_elem_bt = Matcher::vector_element_basic_type(this);
|
||||
int vlen_enc = (to_elem_bt == T_LONG) ? vector_length_encoding(this) : vector_length_encoding(vlen * sizeof(jfloat));
|
||||
__ vector_castF2X_avx10(to_elem_bt, $dst$$XMMRegister, $src$$Address, vlen_enc);
|
||||
__ vector_castF2X_avx10_2(to_elem_bt, $dst$$XMMRegister, $src$$Address, vlen_enc);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
@ -22187,29 +22187,29 @@ instruct castDtoX_reg_evex(vec dst, vec src, vec xtmp1, vec xtmp2, kReg ktmp1, k
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct castDtoX_reg_avx10(vec dst, vec src) %{
|
||||
instruct castDtoX_reg_avx10_2(vec dst, vec src) %{
|
||||
predicate(VM_Version::supports_avx10_2() &&
|
||||
is_integral_type(Matcher::vector_element_basic_type(n)));
|
||||
match(Set dst (VectorCastD2X src));
|
||||
format %{ "vector_cast_d2x_avx10 $dst, $src\t!" %}
|
||||
format %{ "vector_cast_d2x_avx10_2 $dst, $src\t!" %}
|
||||
ins_encode %{
|
||||
int vlen_enc = vector_length_encoding(this, $src);
|
||||
BasicType to_elem_bt = Matcher::vector_element_basic_type(this);
|
||||
__ vector_castD2X_avx10(to_elem_bt, $dst$$XMMRegister, $src$$XMMRegister, vlen_enc);
|
||||
__ vector_castD2X_avx10_2(to_elem_bt, $dst$$XMMRegister, $src$$XMMRegister, vlen_enc);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct castDtoX_mem_avx10(vec dst, memory src) %{
|
||||
instruct castDtoX_mem_avx10_2(vec dst, memory src) %{
|
||||
predicate(VM_Version::supports_avx10_2() &&
|
||||
is_integral_type(Matcher::vector_element_basic_type(n)));
|
||||
match(Set dst (VectorCastD2X (LoadVector src)));
|
||||
format %{ "vector_cast_d2x_avx10 $dst, $src\t!" %}
|
||||
format %{ "vector_cast_d2x_avx10_2 $dst, $src\t!" %}
|
||||
ins_encode %{
|
||||
int vlen = Matcher::vector_length(this);
|
||||
int vlen_enc = vector_length_encoding(vlen * sizeof(jdouble));
|
||||
BasicType to_elem_bt = Matcher::vector_element_basic_type(this);
|
||||
__ vector_castD2X_avx10(to_elem_bt, $dst$$XMMRegister, $src$$Address, vlen_enc);
|
||||
__ vector_castD2X_avx10_2(to_elem_bt, $dst$$XMMRegister, $src$$Address, vlen_enc);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
@ -25181,14 +25181,14 @@ instruct scalar_binOps_HF_reg(regF dst, regF src1, regF src2)
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct scalar_minmax_HF_avx10_reg(regF dst, regF src1, regF src2)
|
||||
instruct scalar_minmax_HF_reg_avx10_2(regF dst, regF src1, regF src2)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (MaxHF src1 src2));
|
||||
match(Set dst (MinHF src1 src2));
|
||||
format %{ "scalar_min_max_fp16 $dst, $src1, $src2" %}
|
||||
ins_encode %{
|
||||
int function = this->ideal_Opcode() == Op_MinHF ? AVX10_MINMAX_MIN_COMPARE_SIGN : AVX10_MINMAX_MAX_COMPARE_SIGN;
|
||||
int function = this->ideal_Opcode() == Op_MinHF ? AVX10_2_MINMAX_MIN_COMPARE_SIGN : AVX10_2_MINMAX_MAX_COMPARE_SIGN;
|
||||
__ eminmaxsh($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, function);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
@ -25296,7 +25296,7 @@ instruct vector_fma_HF_mem(vec dst, memory src1, vec src2)
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct vector_minmax_HF_avx10_mem(vec dst, vec src1, memory src2)
|
||||
instruct vector_minmax_HF_mem_avx10_2(vec dst, vec src1, memory src2)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (MinVHF src1 (VectorReinterpret (LoadVector src2))));
|
||||
@ -25304,13 +25304,13 @@ instruct vector_minmax_HF_avx10_mem(vec dst, vec src1, memory src2)
|
||||
format %{ "vector_min_max_fp16_mem $dst, $src1, $src2" %}
|
||||
ins_encode %{
|
||||
int vlen_enc = vector_length_encoding(this);
|
||||
int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_MINMAX_MIN_COMPARE_SIGN : AVX10_MINMAX_MAX_COMPARE_SIGN;
|
||||
int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_2_MINMAX_MIN_COMPARE_SIGN : AVX10_2_MINMAX_MAX_COMPARE_SIGN;
|
||||
__ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$Address, true, function, vlen_enc);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct vector_minmax_HF_avx10_reg(vec dst, vec src1, vec src2)
|
||||
instruct vector_minmax_HF_reg_avx10_2(vec dst, vec src1, vec src2)
|
||||
%{
|
||||
predicate(VM_Version::supports_avx10_2());
|
||||
match(Set dst (MinVHF src1 src2));
|
||||
@ -25318,7 +25318,7 @@ instruct vector_minmax_HF_avx10_reg(vec dst, vec src1, vec src2)
|
||||
format %{ "vector_min_max_fp16 $dst, $src1, $src2" %}
|
||||
ins_encode %{
|
||||
int vlen_enc = vector_length_encoding(this);
|
||||
int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_MINMAX_MIN_COMPARE_SIGN : AVX10_MINMAX_MAX_COMPARE_SIGN;
|
||||
int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_2_MINMAX_MIN_COMPARE_SIGN : AVX10_2_MINMAX_MAX_COMPARE_SIGN;
|
||||
__ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$XMMRegister, true, function, vlen_enc);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
|
||||
@ -861,6 +861,90 @@ pid_t os::Bsd::gettid() {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the uid of a process or -1 on error.
|
||||
uid_t os::Bsd::get_process_uid(pid_t pid) {
|
||||
struct kinfo_proc kp;
|
||||
size_t size = sizeof kp;
|
||||
int mib_kern[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
|
||||
if (sysctl(mib_kern, 4, &kp, &size, nullptr, 0) == 0) {
|
||||
if (size > 0 && kp.kp_proc.p_pid == pid) {
|
||||
return kp.kp_eproc.e_ucred.cr_uid;
|
||||
}
|
||||
}
|
||||
return (uid_t)-1;
|
||||
}
|
||||
|
||||
// Returns true if the process is running as root.
|
||||
bool os::Bsd::is_process_root(pid_t pid) {
|
||||
uid_t uid = get_process_uid(pid);
|
||||
return (uid != (uid_t)-1) ? os::Posix::is_root(uid) : false;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
// macOS has a secure per-user temporary directory.
|
||||
// Root can attach to a non-root process, hence it needs
|
||||
// to lookup /var/folders for the user specific temporary directory
|
||||
// of the form /var/folders/*/*/T, that contains PERFDATA_NAME_user
|
||||
// directory.
|
||||
static const char VAR_FOLDERS[] = "/var/folders/";
|
||||
int os::Bsd::get_user_tmp_dir_macos(const char* user, int vmid, char* output_path, int output_size) {
|
||||
|
||||
// read the var/folders directory
|
||||
DIR* varfolders_dir = os::opendir(VAR_FOLDERS);
|
||||
if (varfolders_dir != nullptr) {
|
||||
|
||||
// var/folders directory contains 2-characters subdirectories (buckets)
|
||||
struct dirent* bucket_de;
|
||||
|
||||
// loop until the PERFDATA_NAME_user directory has been found
|
||||
while ((bucket_de = os::readdir(varfolders_dir)) != nullptr) {
|
||||
// skip over files and special "." and ".."
|
||||
if (bucket_de->d_type != DT_DIR || bucket_de->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
// absolute path to the bucket
|
||||
char bucket[PATH_MAX];
|
||||
int b = os::snprintf(bucket, PATH_MAX, "%s%s/", VAR_FOLDERS, bucket_de->d_name);
|
||||
|
||||
// the total length of the absolute path must not exceed the buffer size
|
||||
if (b >= PATH_MAX || b < 0) {
|
||||
continue;
|
||||
}
|
||||
// each bucket contains next level subdirectories
|
||||
DIR* bucket_dir = os::opendir(bucket);
|
||||
if (bucket_dir == nullptr) {
|
||||
continue;
|
||||
}
|
||||
// read each subdirectory, skipping over regular files
|
||||
struct dirent* subbucket_de;
|
||||
while ((subbucket_de = os::readdir(bucket_dir)) != nullptr) {
|
||||
if (subbucket_de->d_type != DT_DIR || subbucket_de->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
// If the PERFDATA_NAME_user directory exists in the T subdirectory,
|
||||
// this means the subdirectory is the temporary directory of the user.
|
||||
char perfdata_path[PATH_MAX];
|
||||
int p = os::snprintf(perfdata_path, PATH_MAX, "%s%s/T/%s_%s/", bucket, subbucket_de->d_name, PERFDATA_NAME, user);
|
||||
|
||||
// the total length must not exceed the output buffer size
|
||||
if (p >= PATH_MAX || p < 0) {
|
||||
continue;
|
||||
}
|
||||
// check if the subdirectory exists
|
||||
if (os::file_exists(perfdata_path)) {
|
||||
// the return value of snprintf is not checked for the second time
|
||||
return os::snprintf(output_path, output_size, "%s%s/T", bucket, subbucket_de->d_name);
|
||||
}
|
||||
}
|
||||
os::closedir(bucket_dir);
|
||||
}
|
||||
os::closedir(varfolders_dir);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
intx os::current_thread_id() {
|
||||
#ifdef __APPLE__
|
||||
return (intx)os::Bsd::gettid();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -61,6 +61,12 @@ class os::Bsd {
|
||||
static pthread_t main_thread(void) { return _main_thread; }
|
||||
|
||||
static pid_t gettid();
|
||||
static uid_t get_process_uid(pid_t pid);
|
||||
static bool is_process_root(pid_t pid);
|
||||
|
||||
#ifdef __APPLE__
|
||||
static int get_user_tmp_dir_macos(const char* user, int vmid, char* output_buffer, int buffer_size);
|
||||
#endif
|
||||
|
||||
static intptr_t* ucontext_get_sp(const ucontext_t* uc);
|
||||
static intptr_t* ucontext_get_fp(const ucontext_t* uc);
|
||||
|
||||
@ -467,9 +467,9 @@ void CgroupV1MemoryController::print_version_specific_info(outputStream* st, phy
|
||||
kmem_max_usage.set_value(temp);
|
||||
}
|
||||
|
||||
OSContainer::print_container_helper(st, kmem_limit, "kernel_memory_limit_in_bytes");
|
||||
OSContainer::print_container_helper(st, kmem_usage, "kernel_memory_usage_in_bytes");
|
||||
OSContainer::print_container_helper(st, kmem_max_usage, "kernel_memory_max_usage_in_bytes");
|
||||
OSContainer::print_container_helper(st, kmem_limit, "kernel_memory_limit");
|
||||
OSContainer::print_container_helper(st, kmem_usage, "kernel_memory_usage");
|
||||
OSContainer::print_container_helper(st, kmem_max_usage, "kernel_memory_max_usage");
|
||||
}
|
||||
|
||||
char* CgroupV1Subsystem::cpu_cpuset_cpus() {
|
||||
|
||||
@ -378,8 +378,8 @@ void CgroupV2MemoryController::print_version_specific_info(outputStream* st, phy
|
||||
if (memory_swap_limit_value(reader(), swap_limit_val)) {
|
||||
swap_limit.set_value(swap_limit_val);
|
||||
}
|
||||
OSContainer::print_container_helper(st, swap_current, "memory_swap_current_in_bytes");
|
||||
OSContainer::print_container_helper(st, swap_limit, "memory_swap_max_limit_in_bytes");
|
||||
OSContainer::print_container_helper(st, swap_current, "memory_swap_current");
|
||||
OSContainer::print_container_helper(st, swap_limit, "memory_swap_max_limit");
|
||||
}
|
||||
|
||||
char* CgroupV2Controller::construct_path(char* mount_path, const char* cgroup_path) {
|
||||
|
||||
@ -287,20 +287,44 @@ bool OSContainer::pids_current(uint64_t& value) {
|
||||
return cgroup_subsystem->pids_current(value);
|
||||
}
|
||||
|
||||
template<typename T> struct metric_fmt;
|
||||
template<> struct metric_fmt<unsigned long long int> { static constexpr const char* fmt = "%llu"; };
|
||||
template<> struct metric_fmt<unsigned long int> { static constexpr const char* fmt = "%lu"; };
|
||||
template<> struct metric_fmt<int> { static constexpr const char* fmt = "%d"; };
|
||||
template<> struct metric_fmt<const char*> { static constexpr const char* fmt = "%s"; };
|
||||
|
||||
template void OSContainer::print_container_metric<unsigned long long int>(outputStream*, const char*, unsigned long long int, const char*);
|
||||
template void OSContainer::print_container_metric<unsigned long int>(outputStream*, const char*, unsigned long int, const char*);
|
||||
template void OSContainer::print_container_metric<int>(outputStream*, const char*, int, const char*);
|
||||
template void OSContainer::print_container_metric<const char*>(outputStream*, const char*, const char*, const char*);
|
||||
|
||||
template <typename T>
|
||||
void OSContainer::print_container_metric(outputStream* st, const char* metrics, T value, const char* unit) {
|
||||
constexpr int max_length = 38; // Longest "metric: value" string ("maximum number of tasks: not supported")
|
||||
constexpr int longest_value = max_length - 11; // Max length - shortest "metric: " string ("cpu_quota: ")
|
||||
char value_str[longest_value + 1] = {};
|
||||
os::snprintf_checked(value_str, longest_value, metric_fmt<T>::fmt, value);
|
||||
st->print("%s: %*s", metrics, max_length - static_cast<int>(strlen(metrics)) - 2, value_str); // -2 for the ": "
|
||||
if (unit[0] != '\0') {
|
||||
st->print_cr(" %s", unit);
|
||||
} else {
|
||||
st->print_cr("");
|
||||
}
|
||||
}
|
||||
|
||||
void OSContainer::print_container_helper(outputStream* st, MetricResult& res, const char* metrics) {
|
||||
st->print("%s: ", metrics);
|
||||
if (res.success()) {
|
||||
if (res.value() != value_unlimited) {
|
||||
if (res.value() >= 1024) {
|
||||
st->print_cr(PHYS_MEM_TYPE_FORMAT " k", (physical_memory_size_type)(res.value() / K));
|
||||
print_container_metric(st, metrics, res.value() / K, "kB");
|
||||
} else {
|
||||
st->print_cr(PHYS_MEM_TYPE_FORMAT, res.value());
|
||||
print_container_metric(st, metrics, res.value(), "B");
|
||||
}
|
||||
} else {
|
||||
st->print_cr("%s", "unlimited");
|
||||
print_container_metric(st, metrics, "unlimited");
|
||||
}
|
||||
} else {
|
||||
// Not supported
|
||||
st->print_cr("%s", "unavailable");
|
||||
print_container_metric(st, metrics, "unavailable");
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,8 @@ class OSContainer: AllStatic {
|
||||
static void init();
|
||||
static void print_version_specific_info(outputStream* st);
|
||||
static void print_container_helper(outputStream* st, MetricResult& res, const char* metrics);
|
||||
template <typename T>
|
||||
static void print_container_metric(outputStream* st, const char* metrics, T value, const char* unit = "");
|
||||
|
||||
static inline bool is_containerized();
|
||||
static const char * container_type();
|
||||
|
||||
@ -2432,62 +2432,57 @@ bool os::Linux::print_container_info(outputStream* st) {
|
||||
st->print_cr("container (cgroup) information:");
|
||||
|
||||
const char *p_ct = OSContainer::container_type();
|
||||
st->print_cr("container_type: %s", p_ct != nullptr ? p_ct : "not supported");
|
||||
OSContainer::print_container_metric(st, "container_type", p_ct != nullptr ? p_ct : "not supported");
|
||||
|
||||
char *p = OSContainer::cpu_cpuset_cpus();
|
||||
st->print_cr("cpu_cpuset_cpus: %s", p != nullptr ? p : "not supported");
|
||||
OSContainer::print_container_metric(st, "cpu_cpuset_cpus", p != nullptr ? p : "not supported");
|
||||
free(p);
|
||||
|
||||
p = OSContainer::cpu_cpuset_memory_nodes();
|
||||
st->print_cr("cpu_memory_nodes: %s", p != nullptr ? p : "not supported");
|
||||
OSContainer::print_container_metric(st, "cpu_memory_nodes", p != nullptr ? p : "not supported");
|
||||
free(p);
|
||||
|
||||
int i = -1;
|
||||
bool supported = OSContainer::active_processor_count(i);
|
||||
st->print("active_processor_count: ");
|
||||
if (supported) {
|
||||
assert(i > 0, "must be");
|
||||
if (ActiveProcessorCount > 0) {
|
||||
st->print_cr("%d, but overridden by -XX:ActiveProcessorCount %d", i, ActiveProcessorCount);
|
||||
OSContainer::print_container_metric(st, "active_processor_count", ActiveProcessorCount, "(from -XX:ActiveProcessorCount)");
|
||||
} else {
|
||||
st->print_cr("%d", i);
|
||||
OSContainer::print_container_metric(st, "active_processor_count", i);
|
||||
}
|
||||
} else {
|
||||
st->print_cr("not supported");
|
||||
OSContainer::print_container_metric(st, "active_processor_count", "not supported");
|
||||
}
|
||||
|
||||
|
||||
supported = OSContainer::cpu_quota(i);
|
||||
st->print("cpu_quota: ");
|
||||
if (supported && i > 0) {
|
||||
st->print_cr("%d", i);
|
||||
OSContainer::print_container_metric(st, "cpu_quota", i);
|
||||
} else {
|
||||
st->print_cr("%s", !supported ? "not supported" : "no quota");
|
||||
OSContainer::print_container_metric(st, "cpu_quota", !supported ? "not supported" : "no quota");
|
||||
}
|
||||
|
||||
supported = OSContainer::cpu_period(i);
|
||||
st->print("cpu_period: ");
|
||||
if (supported && i > 0) {
|
||||
st->print_cr("%d", i);
|
||||
OSContainer::print_container_metric(st, "cpu_period", i);
|
||||
} else {
|
||||
st->print_cr("%s", !supported ? "not supported" : "no period");
|
||||
OSContainer::print_container_metric(st, "cpu_period", !supported ? "not supported" : "no period");
|
||||
}
|
||||
|
||||
supported = OSContainer::cpu_shares(i);
|
||||
st->print("cpu_shares: ");
|
||||
if (supported && i > 0) {
|
||||
st->print_cr("%d", i);
|
||||
OSContainer::print_container_metric(st, "cpu_shares", i);
|
||||
} else {
|
||||
st->print_cr("%s", !supported ? "not supported" : "no shares");
|
||||
OSContainer::print_container_metric(st, "cpu_shares", !supported ? "not supported" : "no shares");
|
||||
}
|
||||
|
||||
uint64_t j = 0;
|
||||
supported = OSContainer::cpu_usage_in_micros(j);
|
||||
st->print("cpu_usage_in_micros: ");
|
||||
if (supported && j > 0) {
|
||||
st->print_cr(UINT64_FORMAT, j);
|
||||
OSContainer::print_container_metric(st, "cpu_usage", j, "us");
|
||||
} else {
|
||||
st->print_cr("%s", !supported ? "not supported" : "no usage");
|
||||
OSContainer::print_container_metric(st, "cpu_usage", !supported ? "not supported" : "no usage");
|
||||
}
|
||||
|
||||
MetricResult memory_limit;
|
||||
@ -2530,31 +2525,29 @@ bool os::Linux::print_container_info(outputStream* st) {
|
||||
if (OSContainer::cache_usage_in_bytes(val)) {
|
||||
cache_usage.set_value(val);
|
||||
}
|
||||
OSContainer::print_container_helper(st, memory_limit, "memory_limit_in_bytes");
|
||||
OSContainer::print_container_helper(st, mem_swap_limit, "memory_and_swap_limit_in_bytes");
|
||||
OSContainer::print_container_helper(st, mem_soft_limit, "memory_soft_limit_in_bytes");
|
||||
OSContainer::print_container_helper(st, mem_throttle_limit, "memory_throttle_limit_in_bytes");
|
||||
OSContainer::print_container_helper(st, mem_usage, "memory_usage_in_bytes");
|
||||
OSContainer::print_container_helper(st, mem_max_usage, "memory_max_usage_in_bytes");
|
||||
OSContainer::print_container_helper(st, rss_usage, "rss_usage_in_bytes");
|
||||
OSContainer::print_container_helper(st, cache_usage, "cache_usage_in_bytes");
|
||||
OSContainer::print_container_helper(st, memory_limit, "memory_limit");
|
||||
OSContainer::print_container_helper(st, mem_swap_limit, "memory_and_swap_limit");
|
||||
OSContainer::print_container_helper(st, mem_soft_limit, "memory_soft_limit");
|
||||
OSContainer::print_container_helper(st, mem_throttle_limit, "memory_throttle_limit");
|
||||
OSContainer::print_container_helper(st, mem_usage, "memory_usage");
|
||||
OSContainer::print_container_helper(st, mem_max_usage, "memory_max_usage");
|
||||
OSContainer::print_container_helper(st, rss_usage, "rss_usage");
|
||||
OSContainer::print_container_helper(st, cache_usage, "cache_usage");
|
||||
|
||||
OSContainer::print_version_specific_info(st);
|
||||
|
||||
supported = OSContainer::pids_max(j);
|
||||
st->print("maximum number of tasks: ");
|
||||
if (supported && j != value_unlimited) {
|
||||
st->print_cr(UINT64_FORMAT, j);
|
||||
OSContainer::print_container_metric(st, "maximum number of tasks", j);
|
||||
} else {
|
||||
st->print_cr("%s", !supported ? "not supported" : "unlimited");
|
||||
OSContainer::print_container_metric(st, "maximum number of tasks", !supported ? "not supported" : "unlimited");
|
||||
}
|
||||
|
||||
supported = OSContainer::pids_current(j);
|
||||
st->print("current number of tasks: ");
|
||||
if (supported && j > 0) {
|
||||
st->print_cr(UINT64_FORMAT, j);
|
||||
OSContainer::print_container_metric(st, "current number of tasks", j);
|
||||
} else {
|
||||
st->print_cr("%s", !supported ? "not supported" : "no current tasks");
|
||||
OSContainer::print_container_metric(st, "current number of tasks", !supported ? "not supported" : "no tasks");
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -4298,13 +4291,6 @@ OSReturn os::get_native_priority(const Thread* const thread,
|
||||
return (*priority_ptr != -1 || errno == 0 ? OS_OK : OS_ERR);
|
||||
}
|
||||
|
||||
// This is the fastest way to get thread cpu time on Linux.
|
||||
// Returns cpu time (user+sys) for any thread, not only for current.
|
||||
// POSIX compliant clocks are implemented in the kernels 2.6.16+.
|
||||
// It might work on 2.6.10+ with a special kernel/glibc patch.
|
||||
// For reference, please, see IEEE Std 1003.1-2004:
|
||||
// http://www.unix.org/single_unix_specification
|
||||
|
||||
jlong os::Linux::thread_cpu_time(clockid_t clockid) {
|
||||
struct timespec tp;
|
||||
int status = clock_gettime(clockid, &tp);
|
||||
|
||||
@ -1352,6 +1352,10 @@ bool os::Posix::is_root(uid_t uid){
|
||||
return ROOT_UID == uid;
|
||||
}
|
||||
|
||||
bool os::Posix::is_current_user_root(){
|
||||
return is_root(geteuid());
|
||||
}
|
||||
|
||||
bool os::Posix::matches_effective_uid_or_root(uid_t uid) {
|
||||
return is_root(uid) || geteuid() == uid;
|
||||
}
|
||||
|
||||
@ -76,6 +76,9 @@ public:
|
||||
// Returns true if given uid is root.
|
||||
static bool is_root(uid_t uid);
|
||||
|
||||
// Returns true if the current user is root.
|
||||
static bool is_current_user_root();
|
||||
|
||||
// Returns true if given uid is effective or root uid.
|
||||
static bool matches_effective_uid_or_root(uid_t uid);
|
||||
|
||||
|
||||
@ -40,6 +40,9 @@
|
||||
#if defined(LINUX)
|
||||
#include "os_linux.hpp"
|
||||
#endif
|
||||
#if defined(BSD)
|
||||
#include "os_bsd.hpp"
|
||||
#endif
|
||||
|
||||
# include <errno.h>
|
||||
# include <pwd.h>
|
||||
@ -142,6 +145,18 @@ static char* get_user_tmp_dir(const char* user, int vmid, int nspid) {
|
||||
jio_snprintf(buffer, TMP_BUFFER_LEN, "/proc/%d/root%s", vmid, tmpdir);
|
||||
tmpdir = buffer;
|
||||
}
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
char buffer[PATH_MAX] = {0};
|
||||
// Check if the current user is root and the target VM is running as non-root.
|
||||
// Otherwise the output of os::get_temp_directory() is used.
|
||||
//
|
||||
if (os::Posix::is_current_user_root() && !os::Bsd::is_process_root(vmid)) {
|
||||
int path_size = os::Bsd::get_user_tmp_dir_macos(user, vmid, buffer, sizeof buffer);
|
||||
if (path_size > 0 && (size_t)path_size < sizeof buffer) {
|
||||
tmpdir = buffer;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
const char* perfdir = PERFDATA_NAME;
|
||||
size_t nbytes = strlen(tmpdir) + strlen(perfdir) + strlen(user) + 3;
|
||||
@ -1138,7 +1153,8 @@ static void mmap_attach_shared(int vmid, char** addr, size_t* sizep, TRAPS) {
|
||||
|
||||
// for linux, determine if vmid is for a containerized process
|
||||
int nspid = LINUX_ONLY(os::Linux::get_namespace_pid(vmid)) NOT_LINUX(-1);
|
||||
const char* luser = get_user_name(vmid, &nspid, CHECK);
|
||||
const char* luser = NOT_MACOS(get_user_name(vmid, &nspid, CHECK))
|
||||
MACOS_ONLY(get_user_name(os::Bsd::get_process_uid(vmid)));
|
||||
|
||||
if (luser == nullptr) {
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
|
||||
|
||||
@ -1669,21 +1669,21 @@ static void SR_handler(int sig, siginfo_t* siginfo, void* context) {
|
||||
|
||||
// On some systems we have seen signal delivery get "stuck" until the signal
|
||||
// mask is changed as part of thread termination. Check that the current thread
|
||||
// has not already terminated - else the following assertion
|
||||
// will fail because the thread is no longer a JavaThread as the ~JavaThread
|
||||
// destructor has completed.
|
||||
|
||||
// has not already terminated, else the osthread may already have been freed.
|
||||
if (thread->has_terminated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(thread->is_VM_thread() || thread->is_Java_thread(), "Must be VMThread or JavaThread");
|
||||
|
||||
OSThread* osthread = thread->osthread();
|
||||
|
||||
SuspendResume::State current = osthread->sr.state();
|
||||
|
||||
if (current == SuspendResume::SR_SUSPEND_REQUEST) {
|
||||
// Only check this on an active suspend request. It is possible to get a late delivered
|
||||
// signal from a cancelled suspend request that hits after the JavaThread destructor
|
||||
// completes, but before the Thread destructor causes `is_terminated()` to be true.
|
||||
assert(thread->is_VM_thread() || thread->is_Java_thread(), "Must be VMThread or JavaThread");
|
||||
|
||||
suspend_save_context(osthread, siginfo, context);
|
||||
|
||||
// attempt to switch the state, we assume we had a SUSPEND_REQUEST
|
||||
|
||||
@ -256,6 +256,9 @@ void RiscvHwprobe::add_features_from_query_result() {
|
||||
is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKT)) {
|
||||
VM_Version::ext_Zvkn.enable_feature();
|
||||
}
|
||||
if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKG)) {
|
||||
VM_Version::ext_Zvkg.enable_feature();
|
||||
}
|
||||
#endif
|
||||
|
||||
// ====== non-extensions ======
|
||||
|
||||
@ -145,7 +145,7 @@ void AOTArtifactFinder::find_artifacts() {
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
// Keep scanning until we discover no more class that need to be AOT-initialized.
|
||||
if (CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
while (_pending_aot_inited_classes->length() > 0) {
|
||||
InstanceKlass* ik = _pending_aot_inited_classes->pop();
|
||||
HeapShared::copy_and_rescan_aot_inited_mirror(ik);
|
||||
@ -188,7 +188,7 @@ void AOTArtifactFinder::end_scanning_for_oops() {
|
||||
}
|
||||
|
||||
void AOTArtifactFinder::add_aot_inited_class(InstanceKlass* ik) {
|
||||
if (CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
if (RegeneratedClasses::is_regenerated_object(ik)) {
|
||||
precond(RegeneratedClasses::get_original_object(ik)->is_initialized());
|
||||
} else {
|
||||
@ -258,7 +258,7 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) {
|
||||
return;
|
||||
}
|
||||
scan_oops_in_instance_class(ik);
|
||||
if (ik->is_hidden() && CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
if (ik->is_hidden() && CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
bool succeed = AOTClassLinker::try_add_candidate(ik);
|
||||
guarantee(succeed, "All cached hidden classes must be aot-linkable");
|
||||
add_aot_inited_class(ik);
|
||||
|
||||
@ -40,7 +40,7 @@ DEBUG_ONLY(InstanceKlass* _aot_init_class = nullptr;)
|
||||
|
||||
bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
|
||||
assert(!ArchiveBuilder::is_active() || !ArchiveBuilder::current()->is_in_buffer_space(ik), "must be source klass");
|
||||
if (!CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
if (!CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
|
||||
// Automatic selection for aot-inited classes
|
||||
// ==========================================
|
||||
//
|
||||
// When CDSConfig::is_initing_classes_at_dump_time is enabled,
|
||||
// When CDSConfig::is_dumping_aot_linked_classes is enabled,
|
||||
// AOTArtifactFinder::find_artifacts() finds the classes of all
|
||||
// heap objects that are reachable from HeapShared::_run_time_special_subgraph,
|
||||
// and mark these classes as aot-inited. This preserves the initialized
|
||||
@ -310,7 +310,7 @@ void AOTClassInitializer::init_test_class(TRAPS) {
|
||||
//
|
||||
// -XX:AOTInitTestClass is NOT a general mechanism for including user-defined objects into
|
||||
// the AOT cache. Therefore, this option is NOT available in product JVM.
|
||||
if (AOTInitTestClass != nullptr && CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
if (AOTInitTestClass != nullptr && CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
log_info(aot)("Debug build only: force initialization of AOTInitTestClass %s", AOTInitTestClass);
|
||||
TempNewSymbol class_name = SymbolTable::new_symbol(AOTInitTestClass);
|
||||
Handle app_loader(THREAD, SystemDictionary::java_system_loader());
|
||||
|
||||
@ -96,6 +96,7 @@
|
||||
#include "runtime/vmOperations.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "sanitizers/leak.hpp"
|
||||
#include "services/management.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/bitMap.inline.hpp"
|
||||
#include "utilities/defaultStream.hpp"
|
||||
@ -1140,7 +1141,7 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
|
||||
AOTReferenceObjSupport::initialize(CHECK);
|
||||
AOTReferenceObjSupport::stabilize_cached_reference_objects(CHECK);
|
||||
|
||||
if (CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
// java.lang.Class::reflectionFactory cannot be archived yet. We set this field
|
||||
// to null, and it will be initialized again at runtime.
|
||||
log_debug(aot)("Resetting Class::reflectionFactory");
|
||||
@ -2204,7 +2205,7 @@ void AOTMetaspace::initialize_shared_spaces() {
|
||||
CountSharedSymbols cl;
|
||||
SymbolTable::shared_symbols_do(&cl);
|
||||
tty->print_cr("Number of shared symbols: %zu", cl.total());
|
||||
if (HeapShared::is_loading_mapping_mode()) {
|
||||
if (HeapShared::is_loading() && HeapShared::is_loading_mapping_mode()) {
|
||||
tty->print_cr("Number of shared strings: %zu", StringTable::shared_entry_count());
|
||||
}
|
||||
tty->print_cr("VM version: %s\r\n", static_mapinfo->vm_version());
|
||||
|
||||
@ -1026,23 +1026,19 @@ void CDSConfig::set_has_aot_linked_classes(bool has_aot_linked_classes) {
|
||||
_has_aot_linked_classes |= has_aot_linked_classes;
|
||||
}
|
||||
|
||||
bool CDSConfig::is_initing_classes_at_dump_time() {
|
||||
return is_dumping_heap() && is_dumping_aot_linked_classes();
|
||||
}
|
||||
|
||||
bool CDSConfig::is_dumping_invokedynamic() {
|
||||
// Requires is_dumping_aot_linked_classes(). Otherwise the classes of some archived heap
|
||||
// objects used by the archive indy callsites may be replaced at runtime.
|
||||
return AOTInvokeDynamicLinking && is_dumping_aot_linked_classes() && is_dumping_heap();
|
||||
}
|
||||
|
||||
// When we are dumping aot-linked classes and we are able to write archived heap objects, we automatically
|
||||
// enable the archiving of MethodHandles. This will in turn enable the archiving of MethodTypes and hidden
|
||||
// When we are dumping aot-linked classes, we automatically enable the archiving of MethodHandles.
|
||||
// This will in turn enable the archiving of MethodTypes and hidden
|
||||
// classes that are used in the implementation of MethodHandles.
|
||||
// Archived MethodHandles are required for higher-level optimizations such as AOT resolution of invokedynamic
|
||||
// and dynamic proxies.
|
||||
bool CDSConfig::is_dumping_method_handles() {
|
||||
return is_initing_classes_at_dump_time();
|
||||
return is_dumping_aot_linked_classes();
|
||||
}
|
||||
|
||||
#endif // INCLUDE_CDS_JAVA_HEAP
|
||||
|
||||
@ -187,7 +187,6 @@ public:
|
||||
static void disable_heap_dumping() { CDS_ONLY(_disable_heap_dumping = true); }
|
||||
static bool is_dumping_heap() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||
static bool is_loading_heap() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||
static bool is_initing_classes_at_dump_time() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||
|
||||
static bool is_dumping_invokedynamic() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||
static bool is_dumping_method_handles() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||
|
||||
@ -40,7 +40,7 @@ bool CDSEnumKlass::is_enum_obj(oop orig_obj) {
|
||||
}
|
||||
|
||||
// !!! This is legacy support for enum classes before JEP 483. This file is not used when
|
||||
// !!! CDSConfig::is_initing_classes_at_dump_time()==true.
|
||||
// !!! CDSConfig::is_dumping_aot_linked_classes()==true.
|
||||
//
|
||||
// Java Enum classes have synthetic <clinit> methods that look like this
|
||||
// enum MyEnum {FOO, BAR}
|
||||
@ -63,7 +63,7 @@ bool CDSEnumKlass::is_enum_obj(oop orig_obj) {
|
||||
void CDSEnumKlass::handle_enum_obj(int level,
|
||||
KlassSubGraphInfo* subgraph_info,
|
||||
oop orig_obj) {
|
||||
assert(!CDSConfig::is_initing_classes_at_dump_time(), "only for legacy support of enums");
|
||||
assert(!CDSConfig::is_dumping_aot_linked_classes(), "only for legacy support of enums");
|
||||
assert(level > 1, "must never be called at the first (outermost) level");
|
||||
assert(is_enum_obj(orig_obj), "must be");
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ class JavaFieldStream;
|
||||
class KlassSubGraphInfo;
|
||||
|
||||
// This is legacy support for enum classes before JEP 483. This code is not needed when
|
||||
// CDSConfig::is_initing_classes_at_dump_time()==true.
|
||||
// CDSConfig::is_dumping_aot_linked_classes()==true.
|
||||
class CDSEnumKlass: AllStatic {
|
||||
public:
|
||||
static bool is_enum_obj(oop orig_obj);
|
||||
|
||||
@ -156,7 +156,7 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
|
||||
|
||||
# undef ADD_EXCL
|
||||
|
||||
if (CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
add_shared_secret_accessors();
|
||||
}
|
||||
ClassLoaderDataGraph::classes_do(this);
|
||||
|
||||
@ -206,6 +206,8 @@ void FinalImageRecipes::load_all_classes(TRAPS) {
|
||||
|
||||
if (ik->has_aot_safe_initializer() && (flags & WAS_INITED) != 0) {
|
||||
assert(ik->class_loader() == nullptr, "supported only for boot classes for now");
|
||||
ResourceMark rm(THREAD);
|
||||
log_info(aot, init)("Initializing %s", ik->external_name());
|
||||
ik->initialize(CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,8 +209,14 @@ static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], Instan
|
||||
}
|
||||
|
||||
bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) {
|
||||
return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) ||
|
||||
is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik);
|
||||
assert(CDSConfig::is_dumping_heap(), "dump-time only");
|
||||
if (!CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
// Legacy CDS archive support (to be deprecated)
|
||||
return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) ||
|
||||
is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
oop HeapShared::CachedOopInfo::orig_referrer() const {
|
||||
@ -934,12 +940,16 @@ void HeapShared::scan_java_class(Klass* orig_k) {
|
||||
void HeapShared::archive_subgraphs() {
|
||||
assert(CDSConfig::is_dumping_heap(), "must be");
|
||||
|
||||
archive_object_subgraphs(archive_subgraph_entry_fields,
|
||||
false /* is_full_module_graph */);
|
||||
if (!CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
archive_object_subgraphs(archive_subgraph_entry_fields,
|
||||
false /* is_full_module_graph */);
|
||||
if (CDSConfig::is_dumping_full_module_graph()) {
|
||||
archive_object_subgraphs(fmg_archive_subgraph_entry_fields,
|
||||
true /* is_full_module_graph */);
|
||||
}
|
||||
}
|
||||
|
||||
if (CDSConfig::is_dumping_full_module_graph()) {
|
||||
archive_object_subgraphs(fmg_archive_subgraph_entry_fields,
|
||||
true /* is_full_module_graph */);
|
||||
Modules::verify_archived_modules();
|
||||
}
|
||||
}
|
||||
@ -1295,8 +1305,10 @@ void HeapShared::resolve_classes(JavaThread* current) {
|
||||
if (!is_archived_heap_in_use()) {
|
||||
return; // nothing to do
|
||||
}
|
||||
resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields);
|
||||
resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields);
|
||||
if (!CDSConfig::is_using_aot_linked_classes()) {
|
||||
resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields);
|
||||
resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields);
|
||||
}
|
||||
}
|
||||
|
||||
void HeapShared::resolve_classes_for_subgraphs(JavaThread* current, ArchivableStaticFieldInfo fields[]) {
|
||||
@ -1734,13 +1746,13 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap
|
||||
}
|
||||
}
|
||||
|
||||
if (CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
if (java_lang_Class::is_instance(orig_obj)) {
|
||||
orig_obj = scratch_java_mirror(orig_obj);
|
||||
assert(orig_obj != nullptr, "must be archived");
|
||||
}
|
||||
} else if (java_lang_Class::is_instance(orig_obj) && subgraph_info != _dump_time_special_subgraph) {
|
||||
// Without CDSConfig::is_initing_classes_at_dump_time(), we only allow archived objects to
|
||||
// Without CDSConfig::is_dumping_aot_linked_classes(), we only allow archived objects to
|
||||
// point to the mirrors of (1) j.l.Object, (2) primitive classes, and (3) box classes. These are initialized
|
||||
// very early by HeapShared::init_box_classes().
|
||||
if (orig_obj == vmClasses::Object_klass()->java_mirror()
|
||||
@ -1808,9 +1820,9 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap
|
||||
orig_obj->oop_iterate(&pusher);
|
||||
}
|
||||
|
||||
if (CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
// The classes of all archived enum instances have been marked as aot-init,
|
||||
// so there's nothing else to be done in the production run.
|
||||
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
// The enum klasses are archived with aot-initialized mirror.
|
||||
// See AOTClassInitializer::can_archive_initialized_mirror().
|
||||
} else {
|
||||
// This is legacy support for enum classes before JEP 483 -- we cannot rerun
|
||||
// the enum's <clinit> in the production run, so special handling is needed.
|
||||
@ -1949,7 +1961,7 @@ void HeapShared::verify_reachable_objects_from(oop obj) {
|
||||
#endif
|
||||
|
||||
void HeapShared::check_special_subgraph_classes() {
|
||||
if (CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
// We can have aot-initialized classes (such as Enums) that can reference objects
|
||||
// of arbitrary types. Currently, we trust the JEP 483 implementation to only
|
||||
// aot-initialize classes that are "safe".
|
||||
@ -2136,9 +2148,11 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[],
|
||||
void HeapShared::init_subgraph_entry_fields(TRAPS) {
|
||||
assert(CDSConfig::is_dumping_heap(), "must be");
|
||||
_dump_time_subgraph_info_table = new (mtClass)DumpTimeKlassSubGraphInfoTable();
|
||||
init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK);
|
||||
if (CDSConfig::is_dumping_full_module_graph()) {
|
||||
init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK);
|
||||
if (!CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK);
|
||||
if (CDSConfig::is_dumping_full_module_graph()) {
|
||||
init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -28,9 +28,10 @@
|
||||
#include "memory/iterator.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
#include "oops/klass.hpp"
|
||||
#include "oops/klass.inline.hpp"
|
||||
#include "oops/method.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
class ClassPrinter::KlassPrintClosure : public LockedClassesDo {
|
||||
@ -42,16 +43,15 @@ class ClassPrinter::KlassPrintClosure : public LockedClassesDo {
|
||||
outputStream* _st;
|
||||
int _num;
|
||||
bool _has_printed_methods;
|
||||
GrowableArray<InstanceKlass*> _klasses;
|
||||
|
||||
public:
|
||||
KlassPrintClosure(const char* class_name_pattern,
|
||||
const char* method_name_pattern,
|
||||
const char* method_signature_pattern,
|
||||
bool always_print_class_name,
|
||||
int flags, outputStream* st)
|
||||
: _class_name_pattern(class_name_pattern),
|
||||
_method_name_pattern(method_name_pattern),
|
||||
_method_signature_pattern(method_signature_pattern),
|
||||
_always_print_class_name(always_print_class_name),
|
||||
: _always_print_class_name(always_print_class_name),
|
||||
_flags(flags), _st(st), _num(0), _has_printed_methods(false)
|
||||
{
|
||||
if (has_mode(_flags, PRINT_METHOD_HANDLE)) {
|
||||
@ -66,70 +66,150 @@ public:
|
||||
if (has_mode(_flags, PRINT_BYTECODE)) {
|
||||
_flags |= (PRINT_METHOD_NAME);
|
||||
}
|
||||
|
||||
if (has_mode(_flags, PRINT_CLASS_DETAILS)) {
|
||||
_always_print_class_name = true;
|
||||
}
|
||||
|
||||
_class_name_pattern = copy_pattern(class_name_pattern);
|
||||
_method_name_pattern = copy_pattern(method_name_pattern);
|
||||
_method_signature_pattern = copy_pattern(method_signature_pattern);
|
||||
}
|
||||
|
||||
static const char* copy_pattern(const char* pattern) {
|
||||
if (pattern == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
char* copy = ResourceArea::strdup(pattern);
|
||||
for (char* p = copy; *p; p++) {
|
||||
if (*p == '.') {
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
virtual void do_klass(Klass* k) {
|
||||
if (!k->is_instance_klass()) {
|
||||
return;
|
||||
}
|
||||
print_instance_klass(InstanceKlass::cast(k));
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
if (ik->is_loaded() && ik->name()->is_star_match(_class_name_pattern)) {
|
||||
_klasses.append(ik);
|
||||
}
|
||||
}
|
||||
|
||||
void print() {
|
||||
_klasses.sort(compare_klasses_alphabetically);
|
||||
for (int i = 0; i < _klasses.length(); i++) {
|
||||
print_instance_klass(_klasses.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool match(const char* pattern, Symbol* sym) {
|
||||
return (pattern == nullptr || sym->is_star_match(pattern));
|
||||
}
|
||||
|
||||
static int compare_klasses_alphabetically(InstanceKlass** a, InstanceKlass** b) {
|
||||
return compare_symbols_alphabetically((*a)->name(), (*b)->name());
|
||||
}
|
||||
|
||||
static int compare_methods_alphabetically(const void* a, const void* b) {
|
||||
Method* ma = *(Method**)a;
|
||||
Method* mb = *(Method**)b;
|
||||
int n = compare_symbols_alphabetically(ma->name(), mb->name());
|
||||
if (n == 0) {
|
||||
n = compare_symbols_alphabetically(ma->signature(), mb->signature());
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int compare_symbols_alphabetically(Symbol* a, Symbol *b) {
|
||||
if (a == b) {
|
||||
return 0;
|
||||
}
|
||||
if (a != nullptr && b == nullptr) {
|
||||
return 1;
|
||||
}
|
||||
if (a == nullptr && b != nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return strcmp(a->as_C_string(), b->as_C_string());
|
||||
}
|
||||
|
||||
void print_klass_name(InstanceKlass* ik) {
|
||||
_st->print("[%3d] " INTPTR_FORMAT " class %s ", _num++, p2i(ik), ik->name()->as_C_string());
|
||||
_st->print("[%3d] " INTPTR_FORMAT " class: %s mirror: " INTPTR_FORMAT " ", _num++,
|
||||
p2i(ik), ik->name()->as_C_string(), p2i(ik->java_mirror()));
|
||||
ik->class_loader_data()->print_value_on(_st);
|
||||
_st->cr();
|
||||
}
|
||||
|
||||
void print_instance_klass(InstanceKlass* ik) {
|
||||
if (ik->is_loaded() && ik->name()->is_star_match(_class_name_pattern)) {
|
||||
ResourceMark rm;
|
||||
if (_has_printed_methods) {
|
||||
// We have printed some methods in the previous class.
|
||||
// Print a new line to separate the two classes
|
||||
_st->cr();
|
||||
ResourceMark rm;
|
||||
if (_has_printed_methods) {
|
||||
// We have printed some methods in the previous class.
|
||||
// Print a new line to separate the two classes
|
||||
_st->cr();
|
||||
}
|
||||
_has_printed_methods = false;
|
||||
if (_always_print_class_name) {
|
||||
print_klass_name(ik);
|
||||
}
|
||||
|
||||
if (has_mode(_flags, ClassPrinter::PRINT_CLASS_DETAILS)) {
|
||||
_st->print("InstanceKlass: ");
|
||||
ik->print_on(_st);
|
||||
oop mirror = ik->java_mirror();
|
||||
if (mirror != nullptr) {
|
||||
_st->print("\nJava mirror oop for %s: ", ik->name()->as_C_string());
|
||||
mirror->print_on(_st);
|
||||
}
|
||||
_has_printed_methods = false;
|
||||
if (_always_print_class_name) {
|
||||
print_klass_name(ik);
|
||||
}
|
||||
|
||||
if (has_mode(_flags, ClassPrinter::PRINT_METHOD_NAME)) {
|
||||
bool print_codes = has_mode(_flags, ClassPrinter::PRINT_BYTECODE);
|
||||
int len = ik->methods()->length();
|
||||
int num_methods_printed = 0;
|
||||
|
||||
Method** sorted_methods = NEW_RESOURCE_ARRAY(Method*, len);
|
||||
for (int index = 0; index < len; index++) {
|
||||
sorted_methods[index] = ik->methods()->at(index);
|
||||
}
|
||||
|
||||
if (has_mode(_flags, ClassPrinter::PRINT_METHOD_NAME)) {
|
||||
bool print_codes = has_mode(_flags, ClassPrinter::PRINT_BYTECODE);
|
||||
int len = ik->methods()->length();
|
||||
int num_methods_printed = 0;
|
||||
qsort(sorted_methods, len, sizeof(Method*), compare_methods_alphabetically);
|
||||
|
||||
for (int index = 0; index < len; index++) {
|
||||
Method* m = ik->methods()->at(index);
|
||||
if (match(_method_name_pattern, m->name()) &&
|
||||
match(_method_signature_pattern, m->signature())) {
|
||||
if (print_codes && num_methods_printed++ > 0) {
|
||||
_st->cr();
|
||||
}
|
||||
|
||||
if (_has_printed_methods == false) {
|
||||
if (!_always_print_class_name) {
|
||||
print_klass_name(ik);
|
||||
}
|
||||
_has_printed_methods = true;
|
||||
}
|
||||
print_method(m);
|
||||
for (int index = 0; index < len; index++) {
|
||||
Method* m = sorted_methods[index];
|
||||
if (match(_method_name_pattern, m->name()) &&
|
||||
match(_method_signature_pattern, m->signature())) {
|
||||
if (print_codes && num_methods_printed++ > 0) {
|
||||
_st->cr();
|
||||
}
|
||||
|
||||
if (_has_printed_methods == false) {
|
||||
if (!_always_print_class_name) {
|
||||
print_klass_name(ik);
|
||||
}
|
||||
_has_printed_methods = true;
|
||||
}
|
||||
print_method(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_method(Method* m) {
|
||||
bool print_codes = has_mode(_flags, ClassPrinter::PRINT_BYTECODE);
|
||||
_st->print_cr(INTPTR_FORMAT " %smethod %s : %s", p2i(m),
|
||||
m->is_static() ? "static " : "",
|
||||
m->name()->as_C_string(), m->signature()->as_C_string());
|
||||
if (print_codes) {
|
||||
|
||||
if (has_mode(_flags, ClassPrinter::PRINT_METHOD_DETAILS)) {
|
||||
m->print_on(_st);
|
||||
}
|
||||
|
||||
if (has_mode(_flags, ClassPrinter::PRINT_BYTECODE)) {
|
||||
m->print_codes_on(_st, _flags);
|
||||
}
|
||||
}
|
||||
@ -142,12 +222,16 @@ void ClassPrinter::print_flags_help(outputStream* os) {
|
||||
os->print_cr(" 0x%02x - print the address of bytecodes", PRINT_BYTECODE_ADDR);
|
||||
os->print_cr(" 0x%02x - print info for invokedynamic", PRINT_DYNAMIC);
|
||||
os->print_cr(" 0x%02x - print info for invokehandle", PRINT_METHOD_HANDLE);
|
||||
os->print_cr(" 0x%02x - print details of the C++ and Java objects that represent classes", PRINT_CLASS_DETAILS);
|
||||
os->print_cr(" 0x%02x - print details of the C++ objects that represent methods", PRINT_METHOD_DETAILS);
|
||||
os->cr();
|
||||
}
|
||||
|
||||
void ClassPrinter::print_classes(const char* class_name_pattern, int flags, outputStream* os) {
|
||||
ResourceMark rm;
|
||||
KlassPrintClosure closure(class_name_pattern, nullptr, nullptr, true, flags, os);
|
||||
ClassLoaderDataGraph::classes_do(&closure);
|
||||
closure.print();
|
||||
}
|
||||
|
||||
void ClassPrinter::print_methods(const char* class_name_pattern,
|
||||
@ -174,4 +258,5 @@ void ClassPrinter::print_methods(const char* class_name_pattern,
|
||||
KlassPrintClosure closure(class_name_pattern, method_name_pattern, method_signature_pattern,
|
||||
false, flags | PRINT_METHOD_NAME, os);
|
||||
ClassLoaderDataGraph::classes_do(&closure);
|
||||
closure.print();
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -52,6 +52,8 @@ public:
|
||||
PRINT_BYTECODE_ADDR = 1 << 2,
|
||||
PRINT_DYNAMIC = 1 << 3, // extra information for invokedynamic (and dynamic constant ...)
|
||||
PRINT_METHOD_HANDLE = 1 << 4, // extra information for invokehandle
|
||||
PRINT_CLASS_DETAILS = 1 << 5, // print details of the C++ and Java objects that represent classes
|
||||
PRINT_METHOD_DETAILS = 1 << 6, // print details of the C++ objects that represent methods
|
||||
};
|
||||
static bool has_mode(int flags, Mode mode) {
|
||||
return (flags & static_cast<int>(mode)) != 0;
|
||||
|
||||
@ -127,10 +127,8 @@ ResolutionErrorEntry::~ResolutionErrorEntry() {
|
||||
}
|
||||
|
||||
void ResolutionErrorEntry::set_nest_host_error(const char* message) {
|
||||
// If a message is already set, free it.
|
||||
if (nest_host_error() != nullptr) {
|
||||
FREE_C_HEAP_ARRAY(char, _nest_host_error);
|
||||
}
|
||||
assert(_nest_host_error == nullptr, "caller should have checked");
|
||||
assert_lock_strong(SystemDictionary_lock);
|
||||
_nest_host_error = message;
|
||||
}
|
||||
|
||||
|
||||
@ -1859,7 +1859,7 @@ Symbol* SystemDictionary::find_resolution_error(const constantPoolHandle& pool,
|
||||
|
||||
void SystemDictionary::add_nest_host_error(const constantPoolHandle& pool,
|
||||
int which,
|
||||
const char* message) {
|
||||
const stringStream& message) {
|
||||
{
|
||||
MutexLocker ml(Thread::current(), SystemDictionary_lock);
|
||||
ResolutionErrorEntry* entry = ResolutionErrorTable::find_entry(pool, which);
|
||||
@ -1868,14 +1868,19 @@ void SystemDictionary::add_nest_host_error(const constantPoolHandle& pool,
|
||||
// constant pool index. In this case resolution succeeded but there's an error in this nest host
|
||||
// that we use the table to record.
|
||||
assert(pool->resolved_klass_at(which) != nullptr, "klass should be resolved if there is no entry");
|
||||
ResolutionErrorTable::add_entry(pool, which, message);
|
||||
ResolutionErrorTable::add_entry(pool, which, message.as_string(true /* on C-heap */));
|
||||
} else {
|
||||
// An existing entry means we had a true resolution failure (LinkageError) with our nest host, but we
|
||||
// still want to add the error message for the higher-level access checks to report. We should
|
||||
// only reach here under the same error condition, so we can ignore the potential race with setting
|
||||
// the message, and set it again.
|
||||
assert(entry->nest_host_error() == nullptr || strcmp(entry->nest_host_error(), message) == 0, "should be the same message");
|
||||
entry->set_nest_host_error(message);
|
||||
// the message.
|
||||
const char* nhe = entry->nest_host_error();
|
||||
if (nhe == nullptr) {
|
||||
entry->set_nest_host_error(message.as_string(true /* on C-heap */));
|
||||
} else {
|
||||
DEBUG_ONLY(const char* msg = message.base();)
|
||||
assert(strcmp(nhe, msg) == 0, "New message %s, differs from original %s", msg, nhe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,7 +280,7 @@ public:
|
||||
|
||||
// Record a nest host resolution/validation error
|
||||
static void add_nest_host_error(const constantPoolHandle& pool, int which,
|
||||
const char* message);
|
||||
const stringStream& message);
|
||||
static const char* find_nest_host_error(const constantPoolHandle& pool, int which);
|
||||
|
||||
static void add_to_initiating_loader(JavaThread* current, InstanceKlass* k,
|
||||
|
||||
@ -190,6 +190,9 @@
|
||||
/* GC support */ \
|
||||
do_klass(FillerObject_klass, jdk_internal_vm_FillerObject ) \
|
||||
\
|
||||
/* Scoped Values */ \
|
||||
do_klass(ScopedValue_Carrier_klass, java_lang_ScopedValue_Carrier ) \
|
||||
\
|
||||
/*end*/
|
||||
|
||||
#endif // SHARE_CLASSFILE_VMCLASSMACROS_HPP
|
||||
|
||||
@ -227,11 +227,6 @@ void CodeCache::initialize_heaps() {
|
||||
|
||||
if (!non_nmethod.set) {
|
||||
non_nmethod.size += compiler_buffer_size;
|
||||
// Further down, just before FLAG_SET_ERGO(), all segment sizes are
|
||||
// aligned down to the next lower multiple of min_size. For large page
|
||||
// sizes, this may result in (non_nmethod.size == 0) which is not acceptable.
|
||||
// Therefore, force non_nmethod.size to at least min_size.
|
||||
non_nmethod.size = MAX2(non_nmethod.size, min_size);
|
||||
}
|
||||
|
||||
if (!profiled.set && !non_profiled.set) {
|
||||
@ -307,11 +302,10 @@ 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_profiled.size += non_nmethod.size & alignment_mask(min_size);
|
||||
non_profiled.size += profiled.size & alignment_mask(min_size);
|
||||
non_nmethod.size = align_down(non_nmethod.size, min_size);
|
||||
profiled.size = align_down(profiled.size, min_size);
|
||||
non_profiled.size = align_down(non_profiled.size, min_size);
|
||||
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);
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
|
||||
#include "compiler/compiler_globals.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/globals.hpp"
|
||||
|
||||
inline bool CompilerConfig::is_interpreter_only() {
|
||||
return Arguments::is_interpreter_only() || TieredStopAtLevel == CompLevel_none;
|
||||
|
||||
@ -561,6 +561,20 @@ bool DirectiveSet::should_not_inline(ciMethod* inlinee) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DirectiveSet::should_delay_inline(ciMethod* inlinee) {
|
||||
inlinee->check_is_loaded();
|
||||
VM_ENTRY_MARK;
|
||||
methodHandle mh(THREAD, inlinee->get_Method());
|
||||
|
||||
if (_inlinematchers != nullptr) {
|
||||
return matches_inline(mh, InlineMatcher::delay_inline);
|
||||
}
|
||||
if (!CompilerDirectivesIgnoreCompileCommandsOption) {
|
||||
return CompilerOracle::should_delay_inline(mh);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DirectiveSet::parse_and_add_inline(char* str, const char*& error_msg) {
|
||||
InlineMatcher* m = InlineMatcher::parse_inline_pattern(str, error_msg);
|
||||
if (m != nullptr) {
|
||||
|
||||
@ -142,6 +142,7 @@ public:
|
||||
void append_inline(InlineMatcher* m);
|
||||
bool should_inline(ciMethod* inlinee);
|
||||
bool should_not_inline(ciMethod* inlinee);
|
||||
bool should_delay_inline(ciMethod* inlinee);
|
||||
void print_inline(outputStream* st);
|
||||
DirectiveSet* compilecommand_compatibility_init(const methodHandle& method);
|
||||
bool is_exclusive_copy() { return _directive == nullptr; }
|
||||
|
||||
@ -480,6 +480,10 @@ bool CompilerOracle::should_not_inline(const methodHandle& method) {
|
||||
return check_predicate(CompileCommandEnum::DontInline, method) || check_predicate(CompileCommandEnum::Exclude, method);
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_delay_inline(const methodHandle& method) {
|
||||
return (check_predicate(CompileCommandEnum::DelayInline, method));
|
||||
}
|
||||
|
||||
bool CompilerOracle::should_print(const methodHandle& method) {
|
||||
return check_predicate(CompileCommandEnum::Print, method);
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ class methodHandle;
|
||||
option(Log, "log", Bool) \
|
||||
option(Print, "print", Bool) \
|
||||
option(Inline, "inline", Bool) \
|
||||
option(DelayInline, "delayinline", Bool) \
|
||||
option(DontInline, "dontinline", Bool) \
|
||||
option(Blackhole, "blackhole", Bool) \
|
||||
option(CompileOnly, "compileonly", Bool)\
|
||||
@ -150,6 +151,9 @@ class CompilerOracle : AllStatic {
|
||||
// Tells whether we want to disallow inlining of this method
|
||||
static bool should_not_inline(const methodHandle& method);
|
||||
|
||||
// Tells whether we want to delay inlining of this method
|
||||
static bool should_delay_inline(const methodHandle& method);
|
||||
|
||||
// Tells whether this method changes Thread.currentThread()
|
||||
static bool changes_current_thread(const methodHandle& method);
|
||||
|
||||
|
||||
@ -100,6 +100,7 @@ public:
|
||||
enum InlineType {
|
||||
unknown_inline,
|
||||
dont_inline,
|
||||
delay_inline,
|
||||
force_inline
|
||||
};
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
#include "gc/g1/g1HeapSizingPolicy.hpp"
|
||||
#include "gc/g1/jvmFlagConstraintsG1.hpp"
|
||||
#include "gc/shared/bufferNode.hpp"
|
||||
#include "gc/shared/ptrQueue.hpp"
|
||||
#include "gc/shared/satbMarkQueue.hpp"
|
||||
#include "runtime/globals_extension.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
|
||||
@ -70,8 +70,8 @@
|
||||
nonstatic_field(G1HeapRegionSetBase, _length, uint) \
|
||||
\
|
||||
nonstatic_field(SATBMarkQueue, _active, bool) \
|
||||
nonstatic_field(PtrQueue, _buf, void**) \
|
||||
nonstatic_field(PtrQueue, _index, size_t)
|
||||
nonstatic_field(SATBMarkQueue, _buf, void**) \
|
||||
nonstatic_field(SATBMarkQueue, _index, size_t)
|
||||
|
||||
#define VM_INT_CONSTANTS_G1GC(declare_constant, declare_constant_with_value) \
|
||||
declare_constant(G1HeapRegionType::FreeTag) \
|
||||
@ -96,7 +96,6 @@
|
||||
declare_toplevel_type(G1HeapRegionManager) \
|
||||
declare_toplevel_type(G1HeapRegionSetBase) \
|
||||
declare_toplevel_type(G1MonitoringSupport) \
|
||||
declare_toplevel_type(PtrQueue) \
|
||||
declare_toplevel_type(G1HeapRegionType) \
|
||||
declare_toplevel_type(SATBMarkQueue) \
|
||||
\
|
||||
|
||||
@ -132,14 +132,14 @@ jint ParallelScavengeHeap::initialize() {
|
||||
|
||||
void ParallelScavengeHeap::initialize_serviceability() {
|
||||
|
||||
_eden_pool = new EdenMutableSpacePool(_young_gen,
|
||||
_young_gen->eden_space(),
|
||||
"PS Eden Space",
|
||||
false /* support_usage_threshold */);
|
||||
_eden_pool = new PSEdenSpacePool(_young_gen,
|
||||
_young_gen->eden_space(),
|
||||
"PS Eden Space",
|
||||
false /* support_usage_threshold */);
|
||||
|
||||
_survivor_pool = new SurvivorMutableSpacePool(_young_gen,
|
||||
"PS Survivor Space",
|
||||
false /* support_usage_threshold */);
|
||||
_survivor_pool = new PSSurvivorSpacePool(_young_gen,
|
||||
"PS Survivor Space",
|
||||
false /* support_usage_threshold */);
|
||||
|
||||
_old_pool = new PSOldGenerationPool(_old_gen,
|
||||
"PS Old Gen",
|
||||
|
||||
@ -33,10 +33,7 @@
|
||||
constraint) \
|
||||
product(bool, UseMaximumCompactionOnSystemGC, true, \
|
||||
"Use maximum compaction in the Parallel Old garbage collector " \
|
||||
"for a system GC") \
|
||||
\
|
||||
product(bool, PSChunkLargeArrays, true, \
|
||||
"(Deprecated) Process large arrays in chunks")
|
||||
"for a system GC")
|
||||
|
||||
// end of GC_PARALLEL_FLAGS
|
||||
|
||||
|
||||
@ -39,16 +39,16 @@ MemoryUsage PSOldGenerationPool::get_memory_usage() {
|
||||
return MemoryUsage(initial_size(), used, committed, maxSize);
|
||||
}
|
||||
|
||||
// The max size of EdenMutableSpacePool =
|
||||
// The max size of PSEdenSpacePool =
|
||||
// max size of the PSYoungGen - capacity of two survivor spaces
|
||||
//
|
||||
// Max size of PS eden space is changing due to ergonomic.
|
||||
// PSYoungGen, PSOldGen, Eden, Survivor spaces are all resizable.
|
||||
//
|
||||
EdenMutableSpacePool::EdenMutableSpacePool(PSYoungGen* young_gen,
|
||||
MutableSpace* space,
|
||||
const char* name,
|
||||
bool support_usage_threshold) :
|
||||
PSEdenSpacePool::PSEdenSpacePool(PSYoungGen* young_gen,
|
||||
MutableSpace* space,
|
||||
const char* name,
|
||||
bool support_usage_threshold) :
|
||||
CollectedMemoryPool(name, space->capacity_in_bytes(),
|
||||
(young_gen->max_gen_size() -
|
||||
young_gen->from_space()->capacity_in_bytes() -
|
||||
@ -58,7 +58,7 @@ EdenMutableSpacePool::EdenMutableSpacePool(PSYoungGen* young_gen,
|
||||
_space(space) {
|
||||
}
|
||||
|
||||
MemoryUsage EdenMutableSpacePool::get_memory_usage() {
|
||||
MemoryUsage PSEdenSpacePool::get_memory_usage() {
|
||||
size_t maxSize = (available_for_allocation() ? max_size() : 0);
|
||||
size_t used = used_in_bytes();
|
||||
size_t committed = _space->capacity_in_bytes();
|
||||
@ -66,20 +66,20 @@ MemoryUsage EdenMutableSpacePool::get_memory_usage() {
|
||||
return MemoryUsage(initial_size(), used, committed, maxSize);
|
||||
}
|
||||
|
||||
// The max size of SurvivorMutableSpacePool =
|
||||
// The max size of PSSurvivorSpacePool =
|
||||
// current capacity of the from-space
|
||||
//
|
||||
// PS from and to survivor spaces could have different sizes.
|
||||
//
|
||||
SurvivorMutableSpacePool::SurvivorMutableSpacePool(PSYoungGen* young_gen,
|
||||
const char* name,
|
||||
bool support_usage_threshold) :
|
||||
PSSurvivorSpacePool::PSSurvivorSpacePool(PSYoungGen* young_gen,
|
||||
const char* name,
|
||||
bool support_usage_threshold) :
|
||||
CollectedMemoryPool(name, young_gen->from_space()->capacity_in_bytes(),
|
||||
young_gen->from_space()->capacity_in_bytes(),
|
||||
support_usage_threshold), _young_gen(young_gen) {
|
||||
}
|
||||
|
||||
MemoryUsage SurvivorMutableSpacePool::get_memory_usage() {
|
||||
MemoryUsage PSSurvivorSpacePool::get_memory_usage() {
|
||||
size_t maxSize = (available_for_allocation() ? max_size() : 0);
|
||||
size_t used = used_in_bytes();
|
||||
size_t committed = committed_in_bytes();
|
||||
|
||||
@ -43,16 +43,16 @@ public:
|
||||
size_t max_size() const { return _old_gen->reserved().byte_size(); }
|
||||
};
|
||||
|
||||
class EdenMutableSpacePool : public CollectedMemoryPool {
|
||||
class PSEdenSpacePool : public CollectedMemoryPool {
|
||||
private:
|
||||
PSYoungGen* _young_gen;
|
||||
MutableSpace* _space;
|
||||
|
||||
public:
|
||||
EdenMutableSpacePool(PSYoungGen* young_gen,
|
||||
MutableSpace* space,
|
||||
const char* name,
|
||||
bool support_usage_threshold);
|
||||
PSEdenSpacePool(PSYoungGen* young_gen,
|
||||
MutableSpace* space,
|
||||
const char* name,
|
||||
bool support_usage_threshold);
|
||||
|
||||
MutableSpace* space() { return _space; }
|
||||
MemoryUsage get_memory_usage();
|
||||
@ -65,14 +65,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class SurvivorMutableSpacePool : public CollectedMemoryPool {
|
||||
class PSSurvivorSpacePool : public CollectedMemoryPool {
|
||||
private:
|
||||
PSYoungGen* _young_gen;
|
||||
|
||||
public:
|
||||
SurvivorMutableSpacePool(PSYoungGen* young_gen,
|
||||
const char* name,
|
||||
bool support_usage_threshold);
|
||||
PSSurvivorSpacePool(PSYoungGen* young_gen,
|
||||
const char* name,
|
||||
bool support_usage_threshold);
|
||||
|
||||
MemoryUsage get_memory_usage();
|
||||
|
||||
|
||||
@ -299,8 +299,7 @@ inline oop PSPromotionManager::copy_unmarked_to_survivor_space(oop o,
|
||||
// _min_array_size_for_chunking, and most of them will be arrays.
|
||||
// So, the objArray test would be very infrequent.
|
||||
if (new_obj_size > _min_array_size_for_chunking &&
|
||||
klass->is_objArray_klass() &&
|
||||
PSChunkLargeArrays) {
|
||||
klass->is_objArray_klass()) {
|
||||
push_objArray(o, new_obj);
|
||||
} else {
|
||||
// we'll just push its contents
|
||||
@ -344,7 +343,6 @@ inline void PSPromotionManager::copy_and_push_safe_barrier(T* p) {
|
||||
inline void PSPromotionManager::process_popped_location_depth(ScannerTask task,
|
||||
bool stolen) {
|
||||
if (task.is_partial_array_state()) {
|
||||
assert(PSChunkLargeArrays, "invariant");
|
||||
process_array_chunk(task.to_partial_array_state(), stolen);
|
||||
} else {
|
||||
if (task.is_narrow_oop_ptr()) {
|
||||
|
||||
@ -91,14 +91,16 @@ SerialHeap::SerialHeap() :
|
||||
CollectedHeap(),
|
||||
_young_gen(nullptr),
|
||||
_old_gen(nullptr),
|
||||
_young_gen_saved_top(nullptr),
|
||||
_old_gen_saved_top(nullptr),
|
||||
_rem_set(nullptr),
|
||||
_gc_policy_counters(new GCPolicyCounters("Copy:MSC", 2, 2)),
|
||||
_young_manager(nullptr),
|
||||
_old_manager(nullptr),
|
||||
_is_heap_almost_full(false),
|
||||
_eden_pool(nullptr),
|
||||
_survivor_pool(nullptr),
|
||||
_old_pool(nullptr) {
|
||||
_old_pool(nullptr),
|
||||
_is_heap_almost_full(false) {
|
||||
_young_manager = new GCMemoryManager("Copy");
|
||||
_old_manager = new GCMemoryManager("MarkSweepCompact");
|
||||
GCLocker::initialize();
|
||||
|
||||
@ -76,6 +76,8 @@ class SerialHeap : public CollectedHeap {
|
||||
private:
|
||||
DefNewGeneration* _young_gen;
|
||||
TenuredGeneration* _old_gen;
|
||||
|
||||
// Used during young-gc
|
||||
HeapWord* _young_gen_saved_top;
|
||||
HeapWord* _old_gen_saved_top;
|
||||
|
||||
@ -94,6 +96,10 @@ private:
|
||||
GCMemoryManager* _young_manager;
|
||||
GCMemoryManager* _old_manager;
|
||||
|
||||
MemoryPool* _eden_pool;
|
||||
MemoryPool* _survivor_pool;
|
||||
MemoryPool* _old_pool;
|
||||
|
||||
// Indicate whether heap is almost or approaching full.
|
||||
// Usually, there is some memory headroom for application/gc to run properly.
|
||||
// However, in extreme cases, e.g. young-gen is non-empty after a full gc, we
|
||||
@ -113,6 +119,19 @@ private:
|
||||
|
||||
static void verify_not_in_native_if_java_thread() NOT_DEBUG_RETURN;
|
||||
|
||||
// Try to allocate space by expanding the heap.
|
||||
HeapWord* expand_heap_and_allocate(size_t size, bool is_tlab);
|
||||
|
||||
HeapWord* mem_allocate_cas_noexpand(size_t size, bool is_tlab);
|
||||
HeapWord* mem_allocate_work(size_t size, bool is_tlab);
|
||||
|
||||
void initialize_serviceability() override;
|
||||
|
||||
// Set the saved marks of generations, if that makes sense.
|
||||
// In particular, if any generation might iterate over the oops
|
||||
// in other generations, it should call this method.
|
||||
void save_marks();
|
||||
|
||||
public:
|
||||
// Returns JNI_OK on success
|
||||
jint initialize() override;
|
||||
@ -211,26 +230,6 @@ public:
|
||||
// generations in a fully generational heap.
|
||||
CardTableRS* rem_set() { return _rem_set; }
|
||||
|
||||
public:
|
||||
// Set the saved marks of generations, if that makes sense.
|
||||
// In particular, if any generation might iterate over the oops
|
||||
// in other generations, it should call this method.
|
||||
void save_marks();
|
||||
|
||||
private:
|
||||
// Try to allocate space by expanding the heap.
|
||||
HeapWord* expand_heap_and_allocate(size_t size, bool is_tlab);
|
||||
|
||||
HeapWord* mem_allocate_cas_noexpand(size_t size, bool is_tlab);
|
||||
HeapWord* mem_allocate_work(size_t size, bool is_tlab);
|
||||
|
||||
MemoryPool* _eden_pool;
|
||||
MemoryPool* _survivor_pool;
|
||||
MemoryPool* _old_pool;
|
||||
|
||||
void initialize_serviceability() override;
|
||||
|
||||
public:
|
||||
static SerialHeap* heap();
|
||||
|
||||
SerialHeap();
|
||||
|
||||
@ -281,9 +281,9 @@ public:
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length);
|
||||
static OopCopyResult oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length);
|
||||
|
||||
// Off-heap oop accesses. These accessors get resolved when
|
||||
// IN_HEAP is not set (e.g. when using the NativeAccess API), it is
|
||||
|
||||
@ -34,15 +34,16 @@
|
||||
|
||||
template <DecoratorSet decorators, typename BarrierSetT>
|
||||
template <typename T>
|
||||
inline bool BarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length) {
|
||||
inline OopCopyResult BarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length) {
|
||||
T* src = arrayOopDesc::obj_offset_to_raw(src_obj, src_offset_in_bytes, src_raw);
|
||||
T* dst = arrayOopDesc::obj_offset_to_raw(dst_obj, dst_offset_in_bytes, dst_raw);
|
||||
|
||||
if (!HasDecorator<decorators, ARRAYCOPY_CHECKCAST>::value) {
|
||||
// Covariant, copy without checks
|
||||
return Raw::oop_arraycopy(nullptr, 0, src, nullptr, 0, dst, length);
|
||||
Raw::oop_arraycopy(nullptr, 0, src, nullptr, 0, dst, length);
|
||||
return OopCopyResult::ok;
|
||||
}
|
||||
|
||||
// Copy each element with checking casts
|
||||
@ -50,12 +51,12 @@ inline bool BarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in
|
||||
for (const T* const end = src + length; src < end; src++, dst++) {
|
||||
const T elem = *src;
|
||||
if (!oopDesc::is_instanceof_or_null(CompressedOops::decode(elem), dst_klass)) {
|
||||
return false;
|
||||
return OopCopyResult::failed_check_class_cast;
|
||||
}
|
||||
*dst = elem;
|
||||
}
|
||||
|
||||
return true;
|
||||
return OopCopyResult::ok;
|
||||
}
|
||||
|
||||
#endif // SHARE_GC_SHARED_BARRIERSET_INLINE_HPP
|
||||
|
||||
@ -104,9 +104,9 @@ public:
|
||||
static oop oop_atomic_xchg_in_heap(T* addr, oop new_value);
|
||||
|
||||
template <typename T>
|
||||
static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length);
|
||||
static OopCopyResult oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length);
|
||||
|
||||
static void clone_in_heap(oop src, oop dst, size_t size);
|
||||
|
||||
|
||||
@ -99,7 +99,7 @@ oop_atomic_xchg_in_heap(T* addr, oop new_value) {
|
||||
|
||||
template <DecoratorSet decorators, typename BarrierSetT>
|
||||
template <typename T>
|
||||
inline bool CardTableBarrierSet::AccessBarrier<decorators, BarrierSetT>::
|
||||
inline OopCopyResult CardTableBarrierSet::AccessBarrier<decorators, BarrierSetT>::
|
||||
oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length) {
|
||||
@ -131,12 +131,13 @@ oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
// objArrayOop) which we assume is 32 bit.
|
||||
assert(pd == (size_t)(int)pd, "length field overflow");
|
||||
bs->write_ref_array((HeapWord*)dst_raw, pd);
|
||||
return false;
|
||||
return OopCopyResult::failed_check_class_cast;
|
||||
}
|
||||
}
|
||||
bs->write_ref_array((HeapWord*)dst_raw, length);
|
||||
}
|
||||
return true;
|
||||
|
||||
return OopCopyResult::ok;
|
||||
}
|
||||
|
||||
template <DecoratorSet decorators, typename BarrierSetT>
|
||||
|
||||
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* 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 "gc/shared/bufferNode.hpp"
|
||||
#include "gc/shared/ptrQueue.hpp"
|
||||
|
||||
PtrQueue::PtrQueue(PtrQueueSet* qset) :
|
||||
_index(0),
|
||||
_buf(nullptr)
|
||||
{}
|
||||
|
||||
PtrQueue::~PtrQueue() {
|
||||
assert(_buf == nullptr, "queue must be flushed before delete");
|
||||
}
|
||||
|
||||
size_t PtrQueue::current_capacity() const {
|
||||
if (_buf == nullptr) {
|
||||
return 0;
|
||||
} else {
|
||||
return BufferNode::make_node_from_buffer(_buf)->capacity();
|
||||
}
|
||||
}
|
||||
|
||||
PtrQueueSet::PtrQueueSet(BufferNode::Allocator* allocator) :
|
||||
_allocator(allocator)
|
||||
{}
|
||||
|
||||
PtrQueueSet::~PtrQueueSet() {}
|
||||
|
||||
void PtrQueueSet::reset_queue(PtrQueue& queue) {
|
||||
queue.set_index(queue.current_capacity());
|
||||
}
|
||||
|
||||
void PtrQueueSet::flush_queue(PtrQueue& queue) {
|
||||
void** buffer = queue.buffer();
|
||||
if (buffer != nullptr) {
|
||||
size_t index = queue.index();
|
||||
queue.set_buffer(nullptr);
|
||||
queue.set_index(0);
|
||||
BufferNode* node = BufferNode::make_node_from_buffer(buffer, index);
|
||||
if (index == node->capacity()) {
|
||||
deallocate_buffer(node);
|
||||
} else {
|
||||
enqueue_completed_buffer(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PtrQueueSet::try_enqueue(PtrQueue& queue, void* value) {
|
||||
size_t index = queue.index();
|
||||
if (index == 0) return false;
|
||||
void** buffer = queue.buffer();
|
||||
assert(buffer != nullptr, "no buffer but non-zero index");
|
||||
buffer[--index] = value;
|
||||
queue.set_index(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PtrQueueSet::retry_enqueue(PtrQueue& queue, void* value) {
|
||||
assert(queue.index() != 0, "precondition");
|
||||
assert(queue.buffer() != nullptr, "precondition");
|
||||
size_t index = queue.index();
|
||||
queue.buffer()[--index] = value;
|
||||
queue.set_index(index);
|
||||
}
|
||||
|
||||
BufferNode* PtrQueueSet::exchange_buffer_with_new(PtrQueue& queue) {
|
||||
BufferNode* node = nullptr;
|
||||
void** buffer = queue.buffer();
|
||||
if (buffer != nullptr) {
|
||||
node = BufferNode::make_node_from_buffer(buffer, queue.index());
|
||||
}
|
||||
install_new_buffer(queue);
|
||||
return node;
|
||||
}
|
||||
|
||||
void PtrQueueSet::install_new_buffer(PtrQueue& queue) {
|
||||
BufferNode* node = _allocator->allocate();
|
||||
queue.set_buffer(BufferNode::make_buffer_from_node(node));
|
||||
queue.set_index(node->capacity());
|
||||
}
|
||||
|
||||
void** PtrQueueSet::allocate_buffer() {
|
||||
BufferNode* node = _allocator->allocate();
|
||||
return BufferNode::make_buffer_from_node(node);
|
||||
}
|
||||
|
||||
void PtrQueueSet::deallocate_buffer(BufferNode* node) {
|
||||
_allocator->release(node);
|
||||
}
|
||||
@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_GC_SHARED_PTRQUEUE_HPP
|
||||
#define SHARE_GC_SHARED_PTRQUEUE_HPP
|
||||
|
||||
#include "gc/shared/bufferNode.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/sizes.hpp"
|
||||
|
||||
// There are various techniques that require threads to be able to log
|
||||
// addresses. For example, a generational write barrier might log
|
||||
// the addresses of modified old-generation objects. This type supports
|
||||
// this operation.
|
||||
|
||||
class PtrQueueSet;
|
||||
class PtrQueue {
|
||||
friend class VMStructs;
|
||||
|
||||
NONCOPYABLE(PtrQueue);
|
||||
|
||||
// The (byte) index at which an object was last enqueued. Starts at
|
||||
// capacity (in bytes) (indicating an empty buffer) and goes towards zero.
|
||||
// Value is always pointer-size aligned.
|
||||
size_t _index;
|
||||
|
||||
static const size_t _element_size = sizeof(void*);
|
||||
|
||||
static size_t byte_index_to_index(size_t ind) {
|
||||
assert(is_aligned(ind, _element_size), "precondition");
|
||||
return ind / _element_size;
|
||||
}
|
||||
|
||||
static size_t index_to_byte_index(size_t ind) {
|
||||
return ind * _element_size;
|
||||
}
|
||||
|
||||
protected:
|
||||
// The buffer.
|
||||
void** _buf;
|
||||
|
||||
// Initialize this queue to contain a null buffer, and be part of the
|
||||
// given PtrQueueSet.
|
||||
PtrQueue(PtrQueueSet* qset);
|
||||
|
||||
// Requires queue flushed.
|
||||
~PtrQueue();
|
||||
|
||||
public:
|
||||
|
||||
void** buffer() const { return _buf; }
|
||||
void set_buffer(void** buffer) { _buf = buffer; }
|
||||
|
||||
size_t index() const {
|
||||
return byte_index_to_index(_index);
|
||||
}
|
||||
|
||||
void set_index(size_t new_index) {
|
||||
assert(new_index <= current_capacity(), "precondition");
|
||||
_index = index_to_byte_index(new_index);
|
||||
}
|
||||
|
||||
// Returns the capacity of the buffer, or 0 if the queue doesn't currently
|
||||
// have a buffer.
|
||||
size_t current_capacity() const;
|
||||
|
||||
bool is_empty() const { return index() == current_capacity(); }
|
||||
size_t size() const { return current_capacity() - index(); }
|
||||
|
||||
protected:
|
||||
// To support compiler.
|
||||
template<typename Derived>
|
||||
static ByteSize byte_offset_of_index() {
|
||||
return byte_offset_of(Derived, _index);
|
||||
}
|
||||
|
||||
static constexpr ByteSize byte_width_of_index() { return in_ByteSize(sizeof(size_t)); }
|
||||
|
||||
template<typename Derived>
|
||||
static ByteSize byte_offset_of_buf() {
|
||||
return byte_offset_of(Derived, _buf);
|
||||
}
|
||||
|
||||
static ByteSize byte_width_of_buf() { return in_ByteSize(_element_size); }
|
||||
};
|
||||
|
||||
// A PtrQueueSet represents resources common to a set of pointer queues.
|
||||
// In particular, the individual queues allocate buffers from this shared
|
||||
// set, and return completed buffers to the set.
|
||||
class PtrQueueSet {
|
||||
BufferNode::Allocator* _allocator;
|
||||
|
||||
NONCOPYABLE(PtrQueueSet);
|
||||
|
||||
protected:
|
||||
// Create an empty ptr queue set.
|
||||
PtrQueueSet(BufferNode::Allocator* allocator);
|
||||
~PtrQueueSet();
|
||||
|
||||
// Discard any buffered enqueued data.
|
||||
void reset_queue(PtrQueue& queue);
|
||||
|
||||
// If queue has any buffered enqueued data, transfer it to this qset.
|
||||
// Otherwise, deallocate queue's buffer.
|
||||
void flush_queue(PtrQueue& queue);
|
||||
|
||||
// Add value to queue's buffer, returning true. If buffer is full
|
||||
// or if queue doesn't have a buffer, does nothing and returns false.
|
||||
bool try_enqueue(PtrQueue& queue, void* value);
|
||||
|
||||
// Add value to queue's buffer. The queue must have a non-full buffer.
|
||||
// Used after an initial try_enqueue has failed and the situation resolved.
|
||||
void retry_enqueue(PtrQueue& queue, void* value);
|
||||
|
||||
// Installs a new buffer into queue.
|
||||
// Returns the old buffer, or null if queue didn't have a buffer.
|
||||
BufferNode* exchange_buffer_with_new(PtrQueue& queue);
|
||||
|
||||
// Installs a new buffer into queue.
|
||||
void install_new_buffer(PtrQueue& queue);
|
||||
|
||||
public:
|
||||
|
||||
// Return the associated BufferNode allocator.
|
||||
BufferNode::Allocator* allocator() const { return _allocator; }
|
||||
|
||||
// Return the buffer for a BufferNode of size buffer_capacity().
|
||||
void** allocate_buffer();
|
||||
|
||||
// Return an empty buffer to the free list. The node is required
|
||||
// to have been allocated with a size of buffer_capacity().
|
||||
void deallocate_buffer(BufferNode* node);
|
||||
|
||||
// A completed buffer is a buffer the mutator is finished with, and
|
||||
// is ready to be processed by the collector. It need not be full.
|
||||
|
||||
// Adds node to the completed buffer list.
|
||||
virtual void enqueue_completed_buffer(BufferNode* node) = 0;
|
||||
|
||||
size_t buffer_capacity() const {
|
||||
return _allocator->buffer_capacity();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_SHARED_PTRQUEUE_HPP
|
||||
@ -36,14 +36,19 @@
|
||||
#include "utilities/globalCounter.inline.hpp"
|
||||
|
||||
SATBMarkQueue::SATBMarkQueue(SATBMarkQueueSet* qset) :
|
||||
PtrQueue(qset),
|
||||
_buf(nullptr),
|
||||
_index(0),
|
||||
// SATB queues are only active during marking cycles. We create them
|
||||
// with their active field set to false. If a thread is created
|
||||
// during a cycle, it's SATB queue needs to be activated before the
|
||||
// thread starts running. This is handled by the collector-specific
|
||||
// BarrierSet thread attachment protocol.
|
||||
_active(false)
|
||||
{ }
|
||||
{}
|
||||
|
||||
SATBMarkQueue::~SATBMarkQueue() {
|
||||
assert(_buf == nullptr, "queue must be flushed before delete");
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Helpful for debugging
|
||||
@ -64,7 +69,7 @@ void SATBMarkQueue::print(const char* name) {
|
||||
#endif // PRODUCT
|
||||
|
||||
SATBMarkQueueSet::SATBMarkQueueSet(BufferNode::Allocator* allocator) :
|
||||
PtrQueueSet(allocator),
|
||||
_allocator(allocator),
|
||||
_list(),
|
||||
_count_and_process_flag(0),
|
||||
_process_completed_buffers_threshold(SIZE_MAX),
|
||||
@ -214,13 +219,6 @@ bool SATBMarkQueueSet::apply_closure_to_completed_buffer(SATBBufferClosure* cl)
|
||||
}
|
||||
}
|
||||
|
||||
void SATBMarkQueueSet::flush_queue(SATBMarkQueue& queue) {
|
||||
// Filter now to possibly save work later. If filtering empties the
|
||||
// buffer then flush_queue can deallocate the buffer.
|
||||
filter(queue);
|
||||
PtrQueueSet::flush_queue(queue);
|
||||
}
|
||||
|
||||
void SATBMarkQueueSet::enqueue_known_active(SATBMarkQueue& queue, oop obj) {
|
||||
assert(queue.is_active(), "precondition");
|
||||
void* value = cast_from_oop<void*>(obj);
|
||||
@ -355,3 +353,76 @@ void SATBMarkQueueSet::abandon_partial_marking() {
|
||||
} closure(*this);
|
||||
Threads::threads_do(&closure);
|
||||
}
|
||||
|
||||
size_t SATBMarkQueue::current_capacity() const {
|
||||
if (_buf == nullptr) {
|
||||
return 0;
|
||||
} else {
|
||||
return BufferNode::make_node_from_buffer(_buf)->capacity();
|
||||
}
|
||||
}
|
||||
|
||||
void SATBMarkQueueSet::reset_queue(SATBMarkQueue& queue) {
|
||||
queue.set_index(queue.current_capacity());
|
||||
}
|
||||
|
||||
void SATBMarkQueueSet::flush_queue(SATBMarkQueue& queue) {
|
||||
// Filter now to possibly save work later. If filtering empties the
|
||||
// buffer then flush_queue can deallocate the buffer.
|
||||
filter(queue);
|
||||
void** buffer = queue.buffer();
|
||||
if (buffer != nullptr) {
|
||||
size_t index = queue.index();
|
||||
queue.set_buffer(nullptr);
|
||||
queue.set_index(0);
|
||||
BufferNode* node = BufferNode::make_node_from_buffer(buffer, index);
|
||||
if (index == node->capacity()) {
|
||||
deallocate_buffer(node);
|
||||
} else {
|
||||
enqueue_completed_buffer(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SATBMarkQueueSet::try_enqueue(SATBMarkQueue& queue, void* value) {
|
||||
size_t index = queue.index();
|
||||
if (index == 0) return false;
|
||||
void** buffer = queue.buffer();
|
||||
assert(buffer != nullptr, "no buffer but non-zero index");
|
||||
buffer[--index] = value;
|
||||
queue.set_index(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SATBMarkQueueSet::retry_enqueue(SATBMarkQueue& queue, void* value) {
|
||||
assert(queue.index() != 0, "precondition");
|
||||
assert(queue.buffer() != nullptr, "precondition");
|
||||
size_t index = queue.index();
|
||||
queue.buffer()[--index] = value;
|
||||
queue.set_index(index);
|
||||
}
|
||||
|
||||
BufferNode* SATBMarkQueueSet::exchange_buffer_with_new(SATBMarkQueue& queue) {
|
||||
BufferNode* node = nullptr;
|
||||
void** buffer = queue.buffer();
|
||||
if (buffer != nullptr) {
|
||||
node = BufferNode::make_node_from_buffer(buffer, queue.index());
|
||||
}
|
||||
install_new_buffer(queue);
|
||||
return node;
|
||||
}
|
||||
|
||||
void SATBMarkQueueSet::install_new_buffer(SATBMarkQueue& queue) {
|
||||
BufferNode* node = _allocator->allocate();
|
||||
queue.set_buffer(BufferNode::make_buffer_from_node(node));
|
||||
queue.set_index(node->capacity());
|
||||
}
|
||||
|
||||
void** SATBMarkQueueSet::allocate_buffer() {
|
||||
BufferNode* node = _allocator->allocate();
|
||||
return BufferNode::make_buffer_from_node(node);
|
||||
}
|
||||
|
||||
void SATBMarkQueueSet::deallocate_buffer(BufferNode* node) {
|
||||
_allocator->release(node);
|
||||
}
|
||||
|
||||
@ -25,11 +25,15 @@
|
||||
#ifndef SHARE_GC_SHARED_SATBMARKQUEUE_HPP
|
||||
#define SHARE_GC_SHARED_SATBMARKQUEUE_HPP
|
||||
|
||||
#include "gc/shared/ptrQueue.hpp"
|
||||
#include "gc/shared/bufferNode.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/padded.hpp"
|
||||
#include "oops/oopsHierarchy.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/sizes.hpp"
|
||||
|
||||
class Thread;
|
||||
class Monitor;
|
||||
@ -45,12 +49,33 @@ public:
|
||||
virtual void do_buffer(void** buffer, size_t size) = 0;
|
||||
};
|
||||
|
||||
// A PtrQueue whose elements are (possibly stale) pointers to object heads.
|
||||
class SATBMarkQueue: public PtrQueue {
|
||||
// A queue whose elements are (possibly stale) pointers to object heads.
|
||||
class SATBMarkQueue {
|
||||
friend class VMStructs;
|
||||
friend class SATBMarkQueueSet;
|
||||
|
||||
private:
|
||||
NONCOPYABLE(SATBMarkQueue);
|
||||
|
||||
// The buffer.
|
||||
void** _buf;
|
||||
|
||||
// The (byte) index at which an object was last enqueued. Starts at
|
||||
// capacity (in bytes) (indicating an empty buffer) and goes towards zero.
|
||||
// Value is always pointer-size aligned.
|
||||
size_t _index;
|
||||
|
||||
static const size_t _element_size = sizeof(void*);
|
||||
|
||||
static size_t byte_index_to_index(size_t ind) {
|
||||
assert(is_aligned(ind, _element_size), "precondition");
|
||||
return ind / _element_size;
|
||||
}
|
||||
|
||||
static size_t index_to_byte_index(size_t ind) {
|
||||
return ind * _element_size;
|
||||
}
|
||||
|
||||
// Per-queue (so thread-local) cache of the SATBMarkQueueSet's
|
||||
// active state, to support inline barriers in compiled code.
|
||||
bool _active;
|
||||
@ -58,6 +83,29 @@ private:
|
||||
public:
|
||||
SATBMarkQueue(SATBMarkQueueSet* qset);
|
||||
|
||||
// Queue must be flushed
|
||||
~SATBMarkQueue();
|
||||
|
||||
void** buffer() const { return _buf; }
|
||||
|
||||
void set_buffer(void** buffer) { _buf = buffer; }
|
||||
|
||||
size_t index() const {
|
||||
return byte_index_to_index(_index);
|
||||
}
|
||||
|
||||
void set_index(size_t new_index) {
|
||||
assert(new_index <= current_capacity(), "precondition");
|
||||
_index = index_to_byte_index(new_index);
|
||||
}
|
||||
|
||||
// Returns the capacity of the buffer, or 0 if the queue doesn't currently
|
||||
// have a buffer.
|
||||
size_t current_capacity() const;
|
||||
|
||||
bool is_empty() const { return index() == current_capacity(); }
|
||||
size_t size() const { return current_capacity() - index(); }
|
||||
|
||||
bool is_active() const { return _active; }
|
||||
void set_active(bool value) { _active = value; }
|
||||
|
||||
@ -68,14 +116,16 @@ public:
|
||||
|
||||
// Compiler support.
|
||||
static ByteSize byte_offset_of_index() {
|
||||
return PtrQueue::byte_offset_of_index<SATBMarkQueue>();
|
||||
return byte_offset_of(SATBMarkQueue, _index);
|
||||
}
|
||||
using PtrQueue::byte_width_of_index;
|
||||
|
||||
static constexpr ByteSize byte_width_of_index() { return in_ByteSize(sizeof(size_t)); }
|
||||
|
||||
static ByteSize byte_offset_of_buf() {
|
||||
return PtrQueue::byte_offset_of_buf<SATBMarkQueue>();
|
||||
return byte_offset_of(SATBMarkQueue, _buf);
|
||||
}
|
||||
using PtrQueue::byte_width_of_buf;
|
||||
|
||||
static ByteSize byte_width_of_buf() { return in_ByteSize(_element_size); }
|
||||
|
||||
static ByteSize byte_offset_of_active() {
|
||||
return byte_offset_of(SATBMarkQueue, _active);
|
||||
@ -84,7 +134,18 @@ public:
|
||||
static ByteSize byte_width_of_active() { return in_ByteSize(sizeof(bool)); }
|
||||
};
|
||||
|
||||
class SATBMarkQueueSet: public PtrQueueSet {
|
||||
|
||||
// A SATBMarkQueueSet represents resources common to a set of SATBMarkQueues.
|
||||
// In particular, the individual queues allocate buffers from this shared
|
||||
// set, and return completed buffers to the set.
|
||||
// A completed buffer is a buffer the mutator is finished with, and
|
||||
// is ready to be processed by the collector. It need not be full.
|
||||
|
||||
class SATBMarkQueueSet {
|
||||
|
||||
BufferNode::Allocator* _allocator;
|
||||
|
||||
NONCOPYABLE(SATBMarkQueueSet);
|
||||
|
||||
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, 0);
|
||||
PaddedEnd<BufferNode::Stack> _list;
|
||||
@ -99,6 +160,24 @@ class SATBMarkQueueSet: public PtrQueueSet {
|
||||
BufferNode* get_completed_buffer();
|
||||
void abandon_completed_buffers();
|
||||
|
||||
// Discard any buffered enqueued data.
|
||||
void reset_queue(SATBMarkQueue& queue);
|
||||
|
||||
// Add value to queue's buffer, returning true. If buffer is full
|
||||
// or if queue doesn't have a buffer, does nothing and returns false.
|
||||
bool try_enqueue(SATBMarkQueue& queue, void* value);
|
||||
|
||||
// Add value to queue's buffer. The queue must have a non-full buffer.
|
||||
// Used after an initial try_enqueue has failed and the situation resolved.
|
||||
void retry_enqueue(SATBMarkQueue& queue, void* value);
|
||||
|
||||
// Installs a new buffer into queue.
|
||||
// Returns the old buffer, or null if queue didn't have a buffer.
|
||||
BufferNode* exchange_buffer_with_new(SATBMarkQueue& queue);
|
||||
|
||||
// Installs a new buffer into queue.
|
||||
void install_new_buffer(SATBMarkQueue& queue);
|
||||
|
||||
#ifdef ASSERT
|
||||
void dump_active_states(bool expected_active);
|
||||
void verify_active_states(bool expected_active);
|
||||
@ -106,6 +185,7 @@ class SATBMarkQueueSet: public PtrQueueSet {
|
||||
|
||||
protected:
|
||||
SATBMarkQueueSet(BufferNode::Allocator* allocator);
|
||||
|
||||
~SATBMarkQueueSet();
|
||||
|
||||
void handle_zero_index(SATBMarkQueue& queue);
|
||||
@ -131,6 +211,7 @@ public:
|
||||
void set_process_completed_buffers_threshold(size_t value);
|
||||
|
||||
size_t buffer_enqueue_threshold() const { return _buffer_enqueue_threshold; }
|
||||
|
||||
void set_buffer_enqueue_threshold_percentage(uint value);
|
||||
|
||||
// If there exists some completed buffer, pop and process it, and
|
||||
@ -144,7 +225,7 @@ public:
|
||||
// Add obj to queue. This qset and the queue must be active.
|
||||
void enqueue_known_active(SATBMarkQueue& queue, oop obj);
|
||||
virtual void filter(SATBMarkQueue& queue) = 0;
|
||||
virtual void enqueue_completed_buffer(BufferNode* node);
|
||||
void enqueue_completed_buffer(BufferNode* node);
|
||||
|
||||
// The number of buffers in the list. Racy and not updated atomically
|
||||
// with the set of completed buffers.
|
||||
@ -157,6 +238,20 @@ public:
|
||||
return (_count_and_process_flag.load_relaxed() & 1) != 0;
|
||||
}
|
||||
|
||||
// Return the associated BufferNode allocator.
|
||||
BufferNode::Allocator* allocator() const { return _allocator; }
|
||||
|
||||
// Return the buffer for a BufferNode of size buffer_capacity().
|
||||
void** allocate_buffer();
|
||||
|
||||
// Return an empty buffer to the free list. The node is required
|
||||
// to have been allocated with a size of buffer_capacity().
|
||||
void deallocate_buffer(BufferNode* node);
|
||||
|
||||
size_t buffer_capacity() const {
|
||||
return _allocator->buffer_capacity();
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Helpful for debugging
|
||||
void print_all(const char* msg);
|
||||
|
||||
@ -94,13 +94,17 @@ static double strdedup_elapsed_param_ms(Tickspan t) {
|
||||
void StringDedup::Stat::log_summary(const Stat* last_stat, const Stat* total_stat) {
|
||||
log_info(stringdedup)(
|
||||
"Concurrent String Deduplication "
|
||||
"%zu/" STRDEDUP_BYTES_FORMAT_NS " (new), "
|
||||
"%zu (inspected), "
|
||||
"%zu/" STRDEDUP_BYTES_FORMAT_NS " (new unknown), "
|
||||
"%zu/" STRDEDUP_BYTES_FORMAT_NS " (deduped), "
|
||||
"avg " STRDEDUP_PERCENT_FORMAT_NS ", "
|
||||
"total avg deduped/new unknown bytes " STRDEDUP_PERCENT_FORMAT_NS ", "
|
||||
STRDEDUP_BYTES_FORMAT_NS " (total deduped)," STRDEDUP_BYTES_FORMAT_NS " (total new unknown), "
|
||||
STRDEDUP_ELAPSED_FORMAT_MS " of " STRDEDUP_ELAPSED_FORMAT_MS,
|
||||
last_stat->_inspected,
|
||||
last_stat->_new, STRDEDUP_BYTES_PARAM(last_stat->_new_bytes),
|
||||
last_stat->_deduped, STRDEDUP_BYTES_PARAM(last_stat->_deduped_bytes),
|
||||
percent_of(total_stat->_deduped_bytes, total_stat->_new_bytes),
|
||||
STRDEDUP_BYTES_PARAM(total_stat->_deduped_bytes), STRDEDUP_BYTES_PARAM(total_stat->_new_bytes),
|
||||
strdedup_elapsed_param_ms(last_stat->_process_elapsed),
|
||||
strdedup_elapsed_param_ms(last_stat->_active_elapsed));
|
||||
}
|
||||
@ -210,14 +214,14 @@ void StringDedup::Stat::log_statistics() const {
|
||||
double deduped_bytes_percent = percent_of(_deduped_bytes, _new_bytes);
|
||||
double replaced_percent = percent_of(_replaced, _new);
|
||||
double deleted_percent = percent_of(_deleted, _new);
|
||||
log_debug(stringdedup)(" Inspected: %12zu", _inspected);
|
||||
log_debug(stringdedup)(" Known: %12zu(%5.1f%%)", _known, known_percent);
|
||||
log_debug(stringdedup)(" Shared: %12zu(%5.1f%%)", _known_shared, known_shared_percent);
|
||||
log_debug(stringdedup)(" New: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT,
|
||||
log_debug(stringdedup)(" Inspected: %12zu", _inspected);
|
||||
log_debug(stringdedup)(" Known: %12zu(%5.1f%%)", _known, known_percent);
|
||||
log_debug(stringdedup)(" Shared: %12zu(%5.1f%%)", _known_shared, known_shared_percent);
|
||||
log_debug(stringdedup)(" New unknown: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT,
|
||||
_new, new_percent, STRDEDUP_BYTES_PARAM(_new_bytes));
|
||||
log_debug(stringdedup)(" Replaced: %12zu(%5.1f%%)", _replaced, replaced_percent);
|
||||
log_debug(stringdedup)(" Deleted: %12zu(%5.1f%%)", _deleted, deleted_percent);
|
||||
log_debug(stringdedup)(" Deduplicated: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT "(%5.1f%%)",
|
||||
log_debug(stringdedup)(" Replaced: %12zu(%5.1f%%)", _replaced, replaced_percent);
|
||||
log_debug(stringdedup)(" Deleted: %12zu(%5.1f%%)", _deleted, deleted_percent);
|
||||
log_debug(stringdedup)(" Deduplicated: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT "(%5.1f%%)",
|
||||
_deduped, deduped_percent, STRDEDUP_BYTES_PARAM(_deduped_bytes), deduped_bytes_percent);
|
||||
log_debug(stringdedup)(" Skipped: %zu (dead), %zu (incomplete), %zu (shared)",
|
||||
_skipped_dead, _skipped_incomplete, _skipped_shared);
|
||||
|
||||
@ -1394,7 +1394,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
|
||||
}
|
||||
if (addr->Opcode() == Op_AddP) {
|
||||
Node* orig_base = addr->in(AddPNode::Base);
|
||||
Node* base = new CheckCastPPNode(ctrl, orig_base, orig_base->bottom_type(), ConstraintCastNode::StrongDependency);
|
||||
Node* base = new CheckCastPPNode(ctrl, orig_base, orig_base->bottom_type(), ConstraintCastNode::DependencyType::NonFloatingNarrowing);
|
||||
phase->register_new_node(base, ctrl);
|
||||
if (addr->in(AddPNode::Base) == addr->in((AddPNode::Address))) {
|
||||
// Field access
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
#include "gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp"
|
||||
#include "gc/shenandoah/shenandoahCollectionSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
|
||||
#include "gc/shenandoah/shenandoahFreeSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
|
||||
#include "logging/log.hpp"
|
||||
@ -234,11 +233,12 @@ static double saturate(double value, double min, double max) {
|
||||
// allocation rate computation independent.
|
||||
bool ShenandoahAdaptiveHeuristics::should_start_gc() {
|
||||
size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
|
||||
size_t available = _space_info->soft_available();
|
||||
size_t available = _space_info->soft_mutator_available();
|
||||
size_t allocated = _space_info->bytes_allocated_since_gc_start();
|
||||
|
||||
log_debug(gc)("should_start_gc? available: %zu, soft_max_capacity: %zu"
|
||||
", allocated: %zu", available, capacity, allocated);
|
||||
log_debug(gc, ergo)("should_start_gc calculation: available: " PROPERFMT ", soft_max_capacity: " PROPERFMT ", "
|
||||
"allocated_since_gc_start: " PROPERFMT,
|
||||
PROPERFMTARGS(available), PROPERFMTARGS(capacity), PROPERFMTARGS(allocated));
|
||||
|
||||
// Track allocation rate even if we decide to start a cycle for other reasons.
|
||||
double rate = _allocation_rate.sample(allocated);
|
||||
@ -252,9 +252,8 @@ bool ShenandoahAdaptiveHeuristics::should_start_gc() {
|
||||
|
||||
size_t min_threshold = min_free_threshold();
|
||||
if (available < min_threshold) {
|
||||
log_trigger("Free (%zu%s) is below minimum threshold (%zu%s)",
|
||||
byte_size_in_proper_unit(available), proper_unit_for_byte_size(available),
|
||||
byte_size_in_proper_unit(min_threshold), proper_unit_for_byte_size(min_threshold));
|
||||
log_trigger("Free (Soft) (" PROPERFMT ") is below minimum threshold (" PROPERFMT ")",
|
||||
PROPERFMTARGS(available), PROPERFMTARGS(min_threshold));
|
||||
accept_trigger_with_type(OTHER);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -26,7 +26,6 @@
|
||||
|
||||
#include "gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp"
|
||||
#include "gc/shenandoah/shenandoahCollectionSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahFreeSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
|
||||
#include "logging/log.hpp"
|
||||
@ -47,26 +46,25 @@ ShenandoahCompactHeuristics::ShenandoahCompactHeuristics(ShenandoahSpaceInfo* sp
|
||||
}
|
||||
|
||||
bool ShenandoahCompactHeuristics::should_start_gc() {
|
||||
size_t max_capacity = _space_info->max_capacity();
|
||||
size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
|
||||
size_t available = _space_info->available();
|
||||
size_t available = _space_info->soft_mutator_available();
|
||||
size_t bytes_allocated = _space_info->bytes_allocated_since_gc_start();
|
||||
|
||||
// Make sure the code below treats available without the soft tail.
|
||||
size_t soft_tail = max_capacity - capacity;
|
||||
available = (available > soft_tail) ? (available - soft_tail) : 0;
|
||||
log_debug(gc, ergo)("should_start_gc calculation: available: " PROPERFMT ", soft_max_capacity: " PROPERFMT ", "
|
||||
"allocated_since_gc_start: " PROPERFMT,
|
||||
PROPERFMTARGS(available), PROPERFMTARGS(capacity), PROPERFMTARGS(bytes_allocated));
|
||||
|
||||
size_t threshold_bytes_allocated = capacity / 100 * ShenandoahAllocationThreshold;
|
||||
size_t min_threshold = capacity / 100 * ShenandoahMinFreeThreshold;
|
||||
|
||||
if (available < min_threshold) {
|
||||
log_trigger("Free (%zu%s) is below minimum threshold (%zu%s)",
|
||||
log_trigger("Free (Soft) (%zu%s) is below minimum threshold (%zu%s)",
|
||||
byte_size_in_proper_unit(available), proper_unit_for_byte_size(available),
|
||||
byte_size_in_proper_unit(min_threshold), proper_unit_for_byte_size(min_threshold));
|
||||
accept_trigger();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t bytes_allocated = _space_info->bytes_allocated_since_gc_start();
|
||||
if (bytes_allocated > threshold_bytes_allocated) {
|
||||
log_trigger("Allocated since last cycle (%zu%s) is larger than allocation threshold (%zu%s)",
|
||||
byte_size_in_proper_unit(bytes_allocated), proper_unit_for_byte_size(bytes_allocated),
|
||||
|
||||
@ -104,7 +104,7 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio
|
||||
// Note that for GLOBAL GC, region may be OLD, and OLD regions do not qualify for pre-selection
|
||||
|
||||
// This region is old enough to be promoted but it was not preselected, either because its garbage is below
|
||||
// ShenandoahOldGarbageThreshold so it will be promoted in place, or because there is not sufficient room
|
||||
// old garbage threshold so it will be promoted in place, or because there is not sufficient room
|
||||
// in old gen to hold the evacuated copies of this region's live data. In both cases, we choose not to
|
||||
// place this region into the collection set.
|
||||
if (region->get_top_before_promote() != nullptr) {
|
||||
|
||||
@ -71,7 +71,8 @@ ShenandoahOldHeuristics::ShenandoahOldHeuristics(ShenandoahOldGeneration* genera
|
||||
_growth_trigger(false),
|
||||
_fragmentation_density(0.0),
|
||||
_fragmentation_first_old_region(0),
|
||||
_fragmentation_last_old_region(0)
|
||||
_fragmentation_last_old_region(0),
|
||||
_old_garbage_threshold(ShenandoahOldGarbageThreshold)
|
||||
{
|
||||
}
|
||||
|
||||
@ -334,7 +335,13 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() {
|
||||
|
||||
size_t garbage = region->garbage();
|
||||
size_t live_bytes = region->get_live_data_bytes();
|
||||
live_data += live_bytes;
|
||||
if (!region->was_promoted_in_place()) {
|
||||
// As currently implemented, region->get_live_data_bytes() represents bytes concurrently marked.
|
||||
// Expansion of the region by promotion during concurrent marking is above TAMS, and is not included
|
||||
// as live-data at [start of] old marking.
|
||||
live_data += live_bytes;
|
||||
}
|
||||
// else, regions that were promoted in place had 0 old live data at mark start
|
||||
|
||||
if (region->is_regular() || region->is_regular_pinned()) {
|
||||
// Only place regular or pinned regions with live data into the candidate set.
|
||||
@ -373,7 +380,7 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() {
|
||||
}
|
||||
}
|
||||
|
||||
_old_generation->set_live_bytes_after_last_mark(live_data);
|
||||
_old_generation->set_live_bytes_at_last_mark(live_data);
|
||||
|
||||
// Unlike young, we are more interested in efficiently packing OLD-gen than in reclaiming garbage first. We sort by live-data.
|
||||
// Some regular regions may have been promoted in place with no garbage but also with very little live data. When we "compact"
|
||||
@ -385,7 +392,7 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() {
|
||||
const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes();
|
||||
|
||||
// The convention is to collect regions that have more than this amount of garbage.
|
||||
const size_t garbage_threshold = region_size_bytes * ShenandoahOldGarbageThreshold / 100;
|
||||
const size_t garbage_threshold = region_size_bytes * get_old_garbage_threshold() / 100;
|
||||
|
||||
// Enlightened interpretation: collect regions that have less than this amount of live.
|
||||
const size_t live_threshold = region_size_bytes - garbage_threshold;
|
||||
@ -655,6 +662,7 @@ bool ShenandoahOldHeuristics::should_start_gc() {
|
||||
const double percent = percent_of(old_gen_capacity, heap_capacity);
|
||||
log_trigger("Expansion failure, current size: %zu%s which is %.1f%% of total heap size",
|
||||
byte_size_in_proper_unit(old_gen_capacity), proper_unit_for_byte_size(old_gen_capacity), percent);
|
||||
adjust_old_garbage_threshold();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -677,6 +685,7 @@ bool ShenandoahOldHeuristics::should_start_gc() {
|
||||
"%zu to %zu (%zu), density: %.1f%%",
|
||||
byte_size_in_proper_unit(fragmented_free), proper_unit_for_byte_size(fragmented_free),
|
||||
first_old_region, last_old_region, span_of_old_regions, density * 100);
|
||||
adjust_old_garbage_threshold();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -699,12 +708,13 @@ bool ShenandoahOldHeuristics::should_start_gc() {
|
||||
consecutive_young_cycles);
|
||||
_growth_trigger = false;
|
||||
} else if (current_usage > trigger_threshold) {
|
||||
const size_t live_at_previous_old = _old_generation->get_live_bytes_after_last_mark();
|
||||
const size_t live_at_previous_old = _old_generation->get_live_bytes_at_last_mark();
|
||||
const double percent_growth = percent_of(current_usage - live_at_previous_old, live_at_previous_old);
|
||||
log_trigger("Old has overgrown, live at end of previous OLD marking: "
|
||||
"%zu%s, current usage: %zu%s, percent growth: %.1f%%",
|
||||
byte_size_in_proper_unit(live_at_previous_old), proper_unit_for_byte_size(live_at_previous_old),
|
||||
byte_size_in_proper_unit(current_usage), proper_unit_for_byte_size(current_usage), percent_growth);
|
||||
adjust_old_garbage_threshold();
|
||||
return true;
|
||||
} else {
|
||||
// Mixed evacuations have decreased current_usage such that old-growth trigger is no longer relevant.
|
||||
@ -713,7 +723,41 @@ bool ShenandoahOldHeuristics::should_start_gc() {
|
||||
}
|
||||
|
||||
// Otherwise, defer to inherited heuristic for gc trigger.
|
||||
return this->ShenandoahHeuristics::should_start_gc();
|
||||
bool result = this->ShenandoahHeuristics::should_start_gc();
|
||||
if (result) {
|
||||
adjust_old_garbage_threshold();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ShenandoahOldHeuristics::adjust_old_garbage_threshold() {
|
||||
const uintx MinimumOldGarbageThreshold = 10;
|
||||
const uintx InterventionPercentage = 50;
|
||||
|
||||
const ShenandoahHeap* heap = ShenandoahHeap::heap();
|
||||
size_t old_regions_size = _old_generation->used_regions_size();
|
||||
size_t soft_max_size = heap->soft_max_capacity();
|
||||
uintx percent_used = (uintx) ((old_regions_size * 100) / soft_max_size);
|
||||
_old_garbage_threshold = ShenandoahOldGarbageThreshold;
|
||||
if (percent_used > InterventionPercentage) {
|
||||
uintx severity = percent_used - InterventionPercentage; // ranges from 0 to 50
|
||||
if (MinimumOldGarbageThreshold < ShenandoahOldGarbageThreshold) {
|
||||
uintx adjustment_potential = ShenandoahOldGarbageThreshold - MinimumOldGarbageThreshold;
|
||||
// With default values:
|
||||
// if percent_used > 80, garbage_threshold is 10
|
||||
// else if percent_used > 65, garbage_threshold is 15
|
||||
// else if percent_used > 50, garbage_threshold is 20
|
||||
if (severity > 30) {
|
||||
_old_garbage_threshold = ShenandoahOldGarbageThreshold - adjustment_potential;
|
||||
} else if (severity > 15) {
|
||||
_old_garbage_threshold = ShenandoahOldGarbageThreshold - 2 * adjustment_potential / 3;
|
||||
} else {
|
||||
_old_garbage_threshold = ShenandoahOldGarbageThreshold - adjustment_potential / 3;
|
||||
}
|
||||
log_info(gc)("Adjusting old garbage threshold to %lu because Old Generation used regions represents %lu%% of heap",
|
||||
_old_garbage_threshold, percent_used);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahOldHeuristics::record_success_concurrent() {
|
||||
|
||||
@ -102,6 +102,17 @@ private:
|
||||
size_t _fragmentation_first_old_region;
|
||||
size_t _fragmentation_last_old_region;
|
||||
|
||||
// The value of command-line argument ShenandoahOldGarbageThreshold represents the percent of garbage that must
|
||||
// be present within an old-generation region before that region is considered a good candidate for inclusion in
|
||||
// the collection set under normal circumstances. For our purposes, normal circustances are when the memory consumed
|
||||
// by the old generation is less than 50% of the soft heap capacity. When the old generation grows beyond the 50%
|
||||
// threshold, we dynamically adjust the old garbage threshold, allowing us to invest in packing the old generation
|
||||
// more tightly so that more memory can be made available to the more frequent young GC cycles. This variable
|
||||
// is used in place of ShenandoahOldGarbageThreshold. Under normal circumstances, its value is equal to
|
||||
// ShenandoahOldGarbageThreshold. When the GC is under duress, this value may be adjusted to a smaller value,
|
||||
// as scaled according to the severity of duress that we are experiencing.
|
||||
uintx _old_garbage_threshold;
|
||||
|
||||
// Compare by live is used to prioritize compaction of old-gen regions. With old-gen compaction, the goal is
|
||||
// to tightly pack long-lived objects into available regions. In most cases, there has not been an accumulation
|
||||
// of garbage within old-gen regions. The more likely opportunity will be to combine multiple sparsely populated
|
||||
@ -200,9 +211,28 @@ public:
|
||||
|
||||
bool is_experimental() override;
|
||||
|
||||
// Returns the current value of a dynamically adjusted threshold percentage of garbage above which an old region is
|
||||
// deemed eligible for evacuation.
|
||||
inline uintx get_old_garbage_threshold() { return _old_garbage_threshold; }
|
||||
|
||||
private:
|
||||
void slide_pinned_regions_to_front();
|
||||
bool all_candidates_are_pinned();
|
||||
|
||||
// The normal old_garbage_threshold is specified by ShenandoahOldGarbageThreshold command-line argument, with default
|
||||
// value 25, denoting that a region that has at least 25% garbage is eligible for evacuation. With default values for
|
||||
// all command-line arguments, we make the following adjustments:
|
||||
// 1. If the old generation has grown to consume more than 80% of the soft max capacity, adjust threshold to 10%
|
||||
// 2. Otherwise, if the old generation has grown to consume more than 65%, adjust threshold to 15%
|
||||
// 3. Otherwise, if the old generation has grown to consume more than 50%, adjust threshold to 20%
|
||||
// The effect is to compact the old generation more aggressively as the old generation consumes larger percentages
|
||||
// of the available heap memory. In these circumstances, we pack the old generation more tightly in order to make
|
||||
// more memory avaiable to the young generation so that the more frequent young collections can operate more
|
||||
// efficiently.
|
||||
//
|
||||
// If the ShenandoahOldGarbageThreshold is specified on the command line, the effect of adjusting the old garbage
|
||||
// threshold is scaled linearly.
|
||||
void adjust_old_garbage_threshold();
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHOLDHEURISTICS_HPP
|
||||
|
||||
@ -38,7 +38,7 @@ class ShenandoahSpaceInfo {
|
||||
public:
|
||||
virtual const char* name() const = 0;
|
||||
virtual size_t max_capacity() const = 0;
|
||||
virtual size_t soft_available() const = 0;
|
||||
virtual size_t soft_mutator_available() const = 0;
|
||||
virtual size_t available() const = 0;
|
||||
virtual size_t used() const = 0;
|
||||
|
||||
|
||||
@ -26,7 +26,6 @@
|
||||
|
||||
#include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp"
|
||||
#include "gc/shenandoah/shenandoahCollectionSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahFreeSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
|
||||
#include "logging/log.hpp"
|
||||
@ -41,20 +40,19 @@ ShenandoahStaticHeuristics::ShenandoahStaticHeuristics(ShenandoahSpaceInfo* spac
|
||||
ShenandoahStaticHeuristics::~ShenandoahStaticHeuristics() {}
|
||||
|
||||
bool ShenandoahStaticHeuristics::should_start_gc() {
|
||||
size_t max_capacity = _space_info->max_capacity();
|
||||
size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
|
||||
size_t available = _space_info->available();
|
||||
size_t available = _space_info->soft_mutator_available();
|
||||
size_t allocated = _space_info->bytes_allocated_since_gc_start();
|
||||
|
||||
// Make sure the code below treats available without the soft tail.
|
||||
size_t soft_tail = max_capacity - capacity;
|
||||
available = (available > soft_tail) ? (available - soft_tail) : 0;
|
||||
log_debug(gc, ergo)("should_start_gc calculation: available: " PROPERFMT ", soft_max_capacity: " PROPERFMT ", "
|
||||
"allocated_since_gc_start: " PROPERFMT,
|
||||
PROPERFMTARGS(available), PROPERFMTARGS(capacity), PROPERFMTARGS(allocated));
|
||||
|
||||
size_t threshold_available = capacity / 100 * ShenandoahMinFreeThreshold;
|
||||
|
||||
if (available < threshold_available) {
|
||||
log_trigger("Free (%zu%s) is below minimum threshold (%zu%s)",
|
||||
byte_size_in_proper_unit(available), proper_unit_for_byte_size(available),
|
||||
byte_size_in_proper_unit(threshold_available), proper_unit_for_byte_size(threshold_available));
|
||||
log_trigger("Free (Soft) (" PROPERFMT ") is below minimum threshold (" PROPERFMT ")",
|
||||
PROPERFMTARGS(available), PROPERFMTARGS(threshold_available));
|
||||
accept_trigger();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -173,9 +173,9 @@ public:
|
||||
static oop oop_atomic_xchg_in_heap_at(oop base, ptrdiff_t offset, oop new_value);
|
||||
|
||||
template <typename T>
|
||||
static bool oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length);
|
||||
static OopCopyResult oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length);
|
||||
|
||||
// Clone barrier support
|
||||
static void clone_in_heap(oop src, oop dst, size_t size);
|
||||
|
||||
@ -191,6 +191,20 @@ inline void ShenandoahBarrierSet::keep_alive_if_weak(DecoratorSet decorators, oo
|
||||
template <DecoratorSet decorators, typename T>
|
||||
inline void ShenandoahBarrierSet::write_ref_field_post(T* field) {
|
||||
assert(ShenandoahCardBarrier, "Should have been checked by caller");
|
||||
if (_heap->is_in_young(field)) {
|
||||
// Young field stores do not require card mark.
|
||||
return;
|
||||
}
|
||||
T heap_oop = RawAccess<>::oop_load(field);
|
||||
if (CompressedOops::is_null(heap_oop)) {
|
||||
// Null reference store do not require card mark.
|
||||
return;
|
||||
}
|
||||
oop obj = CompressedOops::decode_not_null(heap_oop);
|
||||
if (!_heap->is_in_young(obj)) {
|
||||
// Not an old->young reference store.
|
||||
return;
|
||||
}
|
||||
volatile CardTable::CardValue* byte = card_table()->byte_for(field);
|
||||
*byte = CardTable::dirty_card_val();
|
||||
}
|
||||
@ -368,15 +382,15 @@ void ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::clone_in_heap
|
||||
|
||||
template <DecoratorSet decorators, typename BarrierSetT>
|
||||
template <typename T>
|
||||
bool ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length) {
|
||||
OopCopyResult ShenandoahBarrierSet::AccessBarrier<decorators, BarrierSetT>::oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
arrayOop dst_obj, size_t dst_offset_in_bytes, T* dst_raw,
|
||||
size_t length) {
|
||||
T* src = arrayOopDesc::obj_offset_to_raw(src_obj, src_offset_in_bytes, src_raw);
|
||||
T* dst = arrayOopDesc::obj_offset_to_raw(dst_obj, dst_offset_in_bytes, dst_raw);
|
||||
|
||||
ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set();
|
||||
bs->arraycopy_barrier(src, dst, length);
|
||||
bool result = Raw::oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, src_raw, dst_obj, dst_offset_in_bytes, dst_raw, length);
|
||||
OopCopyResult result = Raw::oop_arraycopy_in_heap(src_obj, src_offset_in_bytes, src_raw, dst_obj, dst_offset_in_bytes, dst_raw, length);
|
||||
if (ShenandoahCardBarrier) {
|
||||
bs->write_ref_array((HeapWord*) dst, length);
|
||||
}
|
||||
|
||||
@ -2921,6 +2921,29 @@ void ShenandoahFreeSet::log_status_under_lock() {
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahFreeSet::log_freeset_stats(ShenandoahFreeSetPartitionId partition_id, LogStream& ls) {
|
||||
size_t max = 0;
|
||||
size_t total_free = 0;
|
||||
size_t total_used = 0;
|
||||
|
||||
for (idx_t idx = _partitions.leftmost(partition_id);
|
||||
idx <= _partitions.rightmost(partition_id); idx++) {
|
||||
if (_partitions.in_free_set(partition_id, idx)) {
|
||||
ShenandoahHeapRegion *r = _heap->get_region(idx);
|
||||
size_t free = alloc_capacity(r);
|
||||
max = MAX2(max, free);
|
||||
total_free += free;
|
||||
total_used += r->used();
|
||||
}
|
||||
}
|
||||
|
||||
ls.print(" %s freeset stats: Partition count: %zu, Reserved: " PROPERFMT ", Max free available in a single region: " PROPERFMT ";",
|
||||
partition_name(partition_id),
|
||||
_partitions.count(partition_id),
|
||||
PROPERFMTARGS(total_free), PROPERFMTARGS(max)
|
||||
);
|
||||
}
|
||||
|
||||
void ShenandoahFreeSet::log_status() {
|
||||
shenandoah_assert_heaplocked();
|
||||
|
||||
@ -3040,20 +3063,18 @@ void ShenandoahFreeSet::log_status() {
|
||||
// retired, the sum of used and capacities within regions that are still in the Mutator free partition may not match
|
||||
// my internally tracked values of used() and free().
|
||||
assert(free == total_free, "Free memory (%zu) should match calculated memory (%zu)", free, total_free);
|
||||
ls.print("Free: %zu%s, Max: %zu%s regular, %zu%s humongous, ",
|
||||
byte_size_in_proper_unit(total_free), proper_unit_for_byte_size(total_free),
|
||||
byte_size_in_proper_unit(max), proper_unit_for_byte_size(max),
|
||||
byte_size_in_proper_unit(max_humongous), proper_unit_for_byte_size(max_humongous)
|
||||
);
|
||||
ls.print("Whole heap stats: Total free: " PROPERFMT ", Total used: " PROPERFMT ", Max free in a single region: " PROPERFMT
|
||||
", Max humongous: " PROPERFMT "; ",
|
||||
PROPERFMTARGS(total_free), PROPERFMTARGS(total_used), PROPERFMTARGS(max), PROPERFMTARGS(max_humongous));
|
||||
|
||||
ls.print("Frag: ");
|
||||
ls.print("Frag stats: ");
|
||||
size_t frag_ext;
|
||||
if (total_free_ext > 0) {
|
||||
frag_ext = 100 - (100 * max_humongous / total_free_ext);
|
||||
} else {
|
||||
frag_ext = 0;
|
||||
}
|
||||
ls.print("%zu%% external, ", frag_ext);
|
||||
ls.print("External: %zu%%, ", frag_ext);
|
||||
|
||||
size_t frag_int;
|
||||
if (_partitions.count(ShenandoahFreeSetPartitionId::Mutator) > 0) {
|
||||
@ -3062,52 +3083,13 @@ void ShenandoahFreeSet::log_status() {
|
||||
} else {
|
||||
frag_int = 0;
|
||||
}
|
||||
ls.print("%zu%% internal; ", frag_int);
|
||||
ls.print("Used: %zu%s, Mutator Free: %zu",
|
||||
byte_size_in_proper_unit(total_used), proper_unit_for_byte_size(total_used),
|
||||
_partitions.count(ShenandoahFreeSetPartitionId::Mutator));
|
||||
}
|
||||
|
||||
{
|
||||
size_t max = 0;
|
||||
size_t total_free = 0;
|
||||
size_t total_used = 0;
|
||||
|
||||
for (idx_t idx = _partitions.leftmost(ShenandoahFreeSetPartitionId::Collector);
|
||||
idx <= _partitions.rightmost(ShenandoahFreeSetPartitionId::Collector); idx++) {
|
||||
if (_partitions.in_free_set(ShenandoahFreeSetPartitionId::Collector, idx)) {
|
||||
ShenandoahHeapRegion *r = _heap->get_region(idx);
|
||||
size_t free = alloc_capacity(r);
|
||||
max = MAX2(max, free);
|
||||
total_free += free;
|
||||
total_used += r->used();
|
||||
}
|
||||
}
|
||||
ls.print(" Collector Reserve: %zu%s, Max: %zu%s; Used: %zu%s",
|
||||
byte_size_in_proper_unit(total_free), proper_unit_for_byte_size(total_free),
|
||||
byte_size_in_proper_unit(max), proper_unit_for_byte_size(max),
|
||||
byte_size_in_proper_unit(total_used), proper_unit_for_byte_size(total_used));
|
||||
ls.print("Internal: %zu%%; ", frag_int);
|
||||
}
|
||||
|
||||
log_freeset_stats(ShenandoahFreeSetPartitionId::Mutator, ls);
|
||||
log_freeset_stats(ShenandoahFreeSetPartitionId::Collector, ls);
|
||||
if (_heap->mode()->is_generational()) {
|
||||
size_t max = 0;
|
||||
size_t total_free = 0;
|
||||
size_t total_used = 0;
|
||||
|
||||
for (idx_t idx = _partitions.leftmost(ShenandoahFreeSetPartitionId::OldCollector);
|
||||
idx <= _partitions.rightmost(ShenandoahFreeSetPartitionId::OldCollector); idx++) {
|
||||
if (_partitions.in_free_set(ShenandoahFreeSetPartitionId::OldCollector, idx)) {
|
||||
ShenandoahHeapRegion *r = _heap->get_region(idx);
|
||||
size_t free = alloc_capacity(r);
|
||||
max = MAX2(max, free);
|
||||
total_free += free;
|
||||
total_used += r->used();
|
||||
}
|
||||
}
|
||||
ls.print_cr(" Old Collector Reserve: %zu%s, Max: %zu%s; Used: %zu%s",
|
||||
byte_size_in_proper_unit(total_free), proper_unit_for_byte_size(total_free),
|
||||
byte_size_in_proper_unit(max), proper_unit_for_byte_size(max),
|
||||
byte_size_in_proper_unit(total_used), proper_unit_for_byte_size(total_used));
|
||||
log_freeset_stats(ShenandoahFreeSetPartitionId::OldCollector, ls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "gc/shenandoah/shenandoahHeap.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeapRegionSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahSimpleBitMap.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
|
||||
// Each ShenandoahHeapRegion is associated with a ShenandoahFreeSetPartitionId.
|
||||
enum class ShenandoahFreeSetPartitionId : uint8_t {
|
||||
@ -629,6 +630,7 @@ private:
|
||||
void establish_old_collector_alloc_bias();
|
||||
size_t get_usable_free_words(size_t free_bytes) const;
|
||||
|
||||
void log_freeset_stats(ShenandoahFreeSetPartitionId partition_id, LogStream& ls);
|
||||
// log status, assuming lock has already been acquired by the caller.
|
||||
void log_status();
|
||||
|
||||
|
||||
@ -505,10 +505,10 @@ inline void assert_no_in_place_promotions() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Preselect for inclusion into the collection set regions whose age is at or above tenure age which contain more than
|
||||
// ShenandoahOldGarbageThreshold amounts of garbage. We identify these regions by setting the appropriate entry of
|
||||
// the collection set's preselected regions array to true. All entries are initialized to false before calling this
|
||||
// function.
|
||||
// Preselect for inclusion into the collection set all regions whose age is at or above tenure age and for which the
|
||||
// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). We
|
||||
// identify these regions by setting the appropriate entry of the collection set's preselected regions array to true.
|
||||
// All entries are initialized to false before calling this function.
|
||||
//
|
||||
// During the subsequent selection of the collection set, we give priority to these promotion set candidates.
|
||||
// Without this prioritization, we found that the aged regions tend to be ignored because they typically have
|
||||
@ -531,7 +531,8 @@ size_t ShenandoahGeneration::select_aged_regions(const size_t old_promotion_rese
|
||||
bool* const candidate_regions_for_promotion_by_copy = heap->collection_set()->preselected_regions();
|
||||
ShenandoahMarkingContext* const ctx = heap->marking_context();
|
||||
|
||||
const size_t old_garbage_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold) / 100;
|
||||
const size_t old_garbage_threshold =
|
||||
(ShenandoahHeapRegion::region_size_bytes() * heap->old_generation()->heuristics()->get_old_garbage_threshold()) / 100;
|
||||
|
||||
const size_t pip_used_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahGenerationalMinPIPUsage) / 100;
|
||||
|
||||
@ -937,8 +938,8 @@ size_t ShenandoahGeneration::available_with_reserve() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t ShenandoahGeneration::soft_available() const {
|
||||
size_t result = available(ShenandoahHeap::heap()->soft_max_capacity());
|
||||
size_t ShenandoahGeneration::soft_mutator_available() const {
|
||||
size_t result = available(ShenandoahHeap::heap()->soft_max_capacity() * (100.0 - ShenandoahEvacReserve) / 100);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -71,7 +71,7 @@ private:
|
||||
// garbage-dense regions, including those that satisfy criteria 1 & 2 below,
|
||||
// and whose live bytes will fit within old_available budget:
|
||||
// Criterion 1. region age >= tenuring threshold
|
||||
// Criterion 2. region garbage percentage > ShenandoahOldGarbageThreshold
|
||||
// Criterion 2. region garbage percentage > old garbage threshold
|
||||
//
|
||||
// Identifies regions eligible for promotion in place,
|
||||
// being those of at least tenuring_threshold age that have lower garbage
|
||||
@ -126,7 +126,7 @@ private:
|
||||
// The soft max heap size may be adjusted lower than the max heap size to cause the trigger
|
||||
// to believe it has less memory available than is _really_ available. Lowering the soft
|
||||
// max heap size will cause the adaptive heuristic to run more frequent cycles.
|
||||
size_t soft_available() const override;
|
||||
size_t soft_mutator_available() const override;
|
||||
|
||||
void log_status(const char* msg) const;
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ void ShenandoahGenerationalEvacuationTask::maybe_promote_region(ShenandoahHeapRe
|
||||
// triggers the load-reference barrier (LRB) to copy on reference fetch.
|
||||
//
|
||||
// Aged humongous continuation regions are handled with their start region. If an aged regular region has
|
||||
// more garbage than ShenandoahOldGarbageThreshold, we'll promote by evacuation. If there is room for evacuation
|
||||
// more garbage than the old garbage threshold, we'll promote by evacuation. If there is room for evacuation
|
||||
// in this cycle, the region will be in the collection set. If there is not room, the region will be promoted
|
||||
// by evacuation in some future GC cycle.
|
||||
|
||||
@ -177,7 +177,8 @@ void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion
|
||||
size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes();
|
||||
|
||||
{
|
||||
const size_t old_garbage_threshold = (region_size_bytes * ShenandoahOldGarbageThreshold) / 100;
|
||||
const size_t old_garbage_threshold =
|
||||
(region_size_bytes * _heap->old_generation()->heuristics()->get_old_garbage_threshold()) / 100;
|
||||
assert(!_heap->is_concurrent_old_mark_in_progress(), "Cannot promote in place during old marking");
|
||||
assert(region->garbage_before_padded_for_promote() < old_garbage_threshold,
|
||||
"Region %zu has too much garbage for promotion", region->index());
|
||||
@ -222,7 +223,6 @@ void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion
|
||||
// We do not need to scan above TAMS because restored top equals tams
|
||||
assert(obj_addr == tams, "Expect loop to terminate when obj_addr equals tams");
|
||||
|
||||
|
||||
{
|
||||
ShenandoahHeapLocker locker(_heap->lock());
|
||||
|
||||
@ -250,6 +250,7 @@ void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion
|
||||
// Transfer this region from young to old, increasing promoted_reserve if available space exceeds plab_min_size()
|
||||
_heap->free_set()->add_promoted_in_place_region_to_old_collector(region);
|
||||
region->set_affiliation(OLD_GENERATION);
|
||||
region->set_promoted_in_place();
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,6 +289,7 @@ void ShenandoahGenerationalEvacuationTask::promote_humongous(ShenandoahHeapRegio
|
||||
r->index(), p2i(r->bottom()), p2i(r->top()));
|
||||
// We mark the entire humongous object's range as dirty after loop terminates, so no need to dirty the range here
|
||||
r->set_affiliation(OLD_GENERATION);
|
||||
r->set_promoted_in_place();
|
||||
}
|
||||
|
||||
ShenandoahFreeSet* freeset = _heap->free_set();
|
||||
|
||||
@ -83,7 +83,7 @@ void ShenandoahGenerationalFullGC::handle_completion(ShenandoahHeap* heap) {
|
||||
assert_usage_not_more_than_regions_used(young);
|
||||
|
||||
// Establish baseline for next old-has-grown trigger.
|
||||
old->set_live_bytes_after_last_mark(old->used());
|
||||
old->set_live_bytes_at_last_mark(old->used());
|
||||
}
|
||||
|
||||
void ShenandoahGenerationalFullGC::rebuild_remembered_set(ShenandoahHeap* heap) {
|
||||
|
||||
@ -78,15 +78,6 @@ size_t ShenandoahGlobalGeneration::available() const {
|
||||
return MIN2(available, ShenandoahHeap::heap()->free_set()->available());
|
||||
}
|
||||
|
||||
size_t ShenandoahGlobalGeneration::soft_available() const {
|
||||
size_t available = this->available();
|
||||
|
||||
// Make sure the code below treats available without the soft tail.
|
||||
assert(max_capacity() >= ShenandoahHeap::heap()->soft_max_capacity(), "Max capacity must be greater than soft max capacity.");
|
||||
size_t soft_tail = max_capacity() - ShenandoahHeap::heap()->soft_max_capacity();
|
||||
return (available > soft_tail) ? (available - soft_tail) : 0;
|
||||
}
|
||||
|
||||
void ShenandoahGlobalGeneration::set_concurrent_mark_in_progress(bool in_progress) {
|
||||
ShenandoahHeap* heap = ShenandoahHeap::heap();
|
||||
if (in_progress && heap->mode()->is_generational()) {
|
||||
|
||||
@ -56,7 +56,6 @@ public:
|
||||
size_t max_capacity() const override;
|
||||
|
||||
size_t available() const override;
|
||||
size_t soft_available() const override;
|
||||
|
||||
void set_concurrent_mark_in_progress(bool in_progress) override;
|
||||
|
||||
|
||||
@ -262,6 +262,7 @@ private:
|
||||
HeapWord* volatile _update_watermark;
|
||||
|
||||
uint _age;
|
||||
bool _promoted_in_place;
|
||||
CENSUS_NOISE(uint _youth;) // tracks epochs of retrograde ageing (rejuvenation)
|
||||
|
||||
ShenandoahSharedFlag _recycling; // Used to indicate that the region is being recycled; see try_recycle*().
|
||||
@ -354,6 +355,15 @@ public:
|
||||
|
||||
inline void save_top_before_promote();
|
||||
inline HeapWord* get_top_before_promote() const { return _top_before_promoted; }
|
||||
|
||||
inline void set_promoted_in_place() {
|
||||
_promoted_in_place = true;
|
||||
}
|
||||
|
||||
// Returns true iff this region was promoted in place subsequent to the most recent start of concurrent old marking.
|
||||
inline bool was_promoted_in_place() {
|
||||
return _promoted_in_place;
|
||||
}
|
||||
inline void restore_top_before_promote();
|
||||
inline size_t garbage_before_padded_for_promote() const;
|
||||
|
||||
@ -379,7 +389,13 @@ public:
|
||||
inline void increase_live_data_gc_words(size_t s);
|
||||
|
||||
inline bool has_live() const;
|
||||
|
||||
// Represents the number of live bytes identified by most recent marking effort. Does not include the bytes
|
||||
// above TAMS.
|
||||
inline size_t get_live_data_bytes() const;
|
||||
|
||||
// Represents the number of live words identified by most recent marking effort. Does not include the words
|
||||
// above TAMS.
|
||||
inline size_t get_live_data_words() const;
|
||||
|
||||
inline size_t garbage() const;
|
||||
|
||||
@ -152,6 +152,7 @@ inline void ShenandoahHeapRegion::internal_increase_live_data(size_t s) {
|
||||
|
||||
inline void ShenandoahHeapRegion::clear_live_data() {
|
||||
AtomicAccess::store(&_live_data, (size_t)0);
|
||||
_promoted_in_place = false;
|
||||
}
|
||||
|
||||
inline size_t ShenandoahHeapRegion::get_live_data_words() const {
|
||||
|
||||
@ -116,11 +116,10 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues)
|
||||
_is_parsable(true),
|
||||
_card_scan(nullptr),
|
||||
_state(WAITING_FOR_BOOTSTRAP),
|
||||
_growth_before_compaction(INITIAL_GROWTH_BEFORE_COMPACTION),
|
||||
_min_growth_before_compaction ((ShenandoahMinOldGenGrowthPercent * FRACTIONAL_DENOMINATOR) / 100)
|
||||
_growth_percent_before_collection(INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION)
|
||||
{
|
||||
assert(type() == ShenandoahGenerationType::OLD, "OO sanity");
|
||||
_live_bytes_after_last_mark = ShenandoahHeap::heap()->capacity() * INITIAL_LIVE_FRACTION / FRACTIONAL_DENOMINATOR;
|
||||
_live_bytes_at_last_mark = (ShenandoahHeap::heap()->soft_max_capacity() * INITIAL_LIVE_PERCENT) / 100;
|
||||
// Always clear references for old generation
|
||||
ref_processor()->set_soft_reference_policy(true);
|
||||
|
||||
@ -221,20 +220,20 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc
|
||||
}
|
||||
}
|
||||
|
||||
size_t ShenandoahOldGeneration::get_live_bytes_after_last_mark() const {
|
||||
return _live_bytes_after_last_mark;
|
||||
size_t ShenandoahOldGeneration::get_live_bytes_at_last_mark() const {
|
||||
return _live_bytes_at_last_mark;
|
||||
}
|
||||
|
||||
void ShenandoahOldGeneration::set_live_bytes_after_last_mark(size_t bytes) {
|
||||
void ShenandoahOldGeneration::set_live_bytes_at_last_mark(size_t bytes) {
|
||||
if (bytes == 0) {
|
||||
// Restart search for best old-gen size to the initial state
|
||||
_live_bytes_after_last_mark = ShenandoahHeap::heap()->capacity() * INITIAL_LIVE_FRACTION / FRACTIONAL_DENOMINATOR;
|
||||
_growth_before_compaction = INITIAL_GROWTH_BEFORE_COMPACTION;
|
||||
_live_bytes_at_last_mark = (ShenandoahHeap::heap()->soft_max_capacity() * INITIAL_LIVE_PERCENT) / 100;
|
||||
_growth_percent_before_collection = INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION;
|
||||
} else {
|
||||
_live_bytes_after_last_mark = bytes;
|
||||
_growth_before_compaction /= 2;
|
||||
if (_growth_before_compaction < _min_growth_before_compaction) {
|
||||
_growth_before_compaction = _min_growth_before_compaction;
|
||||
_live_bytes_at_last_mark = bytes;
|
||||
_growth_percent_before_collection /= 2;
|
||||
if (_growth_percent_before_collection < ShenandoahMinOldGenGrowthPercent) {
|
||||
_growth_percent_before_collection = ShenandoahMinOldGenGrowthPercent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -244,7 +243,19 @@ void ShenandoahOldGeneration::handle_failed_transfer() {
|
||||
}
|
||||
|
||||
size_t ShenandoahOldGeneration::usage_trigger_threshold() const {
|
||||
size_t result = _live_bytes_after_last_mark + (_live_bytes_after_last_mark * _growth_before_compaction) / FRACTIONAL_DENOMINATOR;
|
||||
size_t threshold_by_relative_growth =
|
||||
_live_bytes_at_last_mark + (_live_bytes_at_last_mark * _growth_percent_before_collection) / 100;
|
||||
size_t soft_max_capacity = ShenandoahHeap::heap()->soft_max_capacity();
|
||||
size_t threshold_by_growth_into_percent_remaining;
|
||||
if (_live_bytes_at_last_mark < soft_max_capacity) {
|
||||
threshold_by_growth_into_percent_remaining = (size_t)
|
||||
(_live_bytes_at_last_mark + ((soft_max_capacity - _live_bytes_at_last_mark)
|
||||
* ShenandoahMinOldGenGrowthRemainingHeapPercent / 100.0));
|
||||
} else {
|
||||
// we're already consuming more than soft max capacity, so we should start old GC right away.
|
||||
threshold_by_growth_into_percent_remaining = soft_max_capacity;
|
||||
}
|
||||
size_t result = MIN2(threshold_by_relative_growth, threshold_by_growth_into_percent_remaining);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -287,28 +287,23 @@ public:
|
||||
private:
|
||||
State _state;
|
||||
|
||||
static const size_t FRACTIONAL_DENOMINATOR = 65536;
|
||||
|
||||
// During initialization of the JVM, we search for the correct old-gen size by initially performing old-gen
|
||||
// collection when old-gen usage is 50% more (INITIAL_GROWTH_BEFORE_COMPACTION) than the initial old-gen size
|
||||
// estimate (3.125% of heap). The next old-gen trigger occurs when old-gen grows 25% larger than its live
|
||||
// memory at the end of the first old-gen collection. Then we trigger again when old-gen grows 12.5%
|
||||
// more than its live memory at the end of the previous old-gen collection. Thereafter, we trigger each time
|
||||
// old-gen grows more than 12.5% following the end of its previous old-gen collection.
|
||||
static const size_t INITIAL_GROWTH_BEFORE_COMPACTION = FRACTIONAL_DENOMINATOR / 2; // 50.0%
|
||||
// collection when old-gen usage is 50% more (INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION) than the initial old-gen size
|
||||
// estimate (16% of heap). With each successive old-gen collection, we divide the growth trigger by two, but
|
||||
// never use a growth trigger smaller than ShenandoahMinOldGenGrowthPercent.
|
||||
static const size_t INITIAL_GROWTH_PERCENT_BEFORE_COLLECTION = 50;
|
||||
|
||||
// INITIAL_LIVE_FRACTION represents the initial guess of how large old-gen should be. We estimate that old-gen
|
||||
// needs to consume 6.25% of the total heap size. And we "pretend" that we start out with this amount of live
|
||||
// INITIAL_LIVE_PERCENT represents the initial guess of how large old-gen should be. We estimate that old gen
|
||||
// needs to consume 16% of the total heap size. And we "pretend" that we start out with this amount of live
|
||||
// old-gen memory. The first old-collection trigger will occur when old-gen occupies 50% more than this initial
|
||||
// approximation of the old-gen memory requirement, in other words when old-gen usage is 150% of 6.25%, which
|
||||
// is 9.375% of the total heap size.
|
||||
static const uint16_t INITIAL_LIVE_FRACTION = FRACTIONAL_DENOMINATOR / 16; // 6.25%
|
||||
// approximation of the old-gen memory requirement, in other words when old-gen usage is 150% of 16%, which
|
||||
// is 24% of the heap size.
|
||||
static const size_t INITIAL_LIVE_PERCENT = 16;
|
||||
|
||||
size_t _live_bytes_after_last_mark;
|
||||
size_t _live_bytes_at_last_mark;
|
||||
|
||||
// How much growth in usage before we trigger old collection, per FRACTIONAL_DENOMINATOR (65_536)
|
||||
size_t _growth_before_compaction;
|
||||
const size_t _min_growth_before_compaction; // Default is 12.5%
|
||||
// How much growth in usage before we trigger old collection as a percent of soft_max_capacity
|
||||
size_t _growth_percent_before_collection;
|
||||
|
||||
void validate_transition(State new_state) NOT_DEBUG_RETURN;
|
||||
|
||||
@ -323,8 +318,8 @@ public:
|
||||
|
||||
void transition_to(State new_state);
|
||||
|
||||
size_t get_live_bytes_after_last_mark() const;
|
||||
void set_live_bytes_after_last_mark(size_t new_live);
|
||||
size_t get_live_bytes_at_last_mark() const;
|
||||
void set_live_bytes_at_last_mark(size_t new_live);
|
||||
|
||||
size_t usage_trigger_threshold() const;
|
||||
|
||||
|
||||
@ -138,8 +138,8 @@ size_t ShenandoahYoungGeneration::available() const {
|
||||
return MIN2(available, ShenandoahHeap::heap()->free_set()->available());
|
||||
}
|
||||
|
||||
size_t ShenandoahYoungGeneration::soft_available() const {
|
||||
size_t available = this->ShenandoahGeneration::soft_available();
|
||||
size_t ShenandoahYoungGeneration::soft_mutator_available() const {
|
||||
size_t available = this->ShenandoahGeneration::soft_mutator_available();
|
||||
return MIN2(available, ShenandoahHeap::heap()->free_set()->available());
|
||||
}
|
||||
|
||||
|
||||
@ -83,7 +83,7 @@ public:
|
||||
size_t max_capacity() const override;
|
||||
|
||||
size_t available() const override;
|
||||
size_t soft_available() const override;
|
||||
size_t soft_mutator_available() const override;
|
||||
|
||||
void prepare_gc() override;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user